diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..a85e0e9ed --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +# Local volumes +/config +/data +/db + +# Updated through volumes +Server/data +Server/worldprops + +# Env files containing secrets +/.env +/*.env + +# Log files +logs +*.log + +# Git files +.git +.gitattributes +.gitignore + +# Basic stuff +*.md +LICENSE +.gitlab-ci.yml + +# Docker files +docker-compose.yml +Dockerfile diff --git a/.gitignore b/.gitignore index e1cc944e2..6ecd66985 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,8 @@ Server/data/global_kill_stats.json Server/ge_test.db Server/latestdump.txt Management-Server/managementprops/ -Server/worldprops/ +Server/worldprops/* +!Server/worldprops/default.conf **/.idea/workspace.xml **/.idea/tasks.xml @@ -32,3 +33,12 @@ gradle build/kotlin/sessions/ **/*.iml Server/hasRan.txt + +# Local volumes +/config +/data +/db + +# Env files containing secrets +/.env +/*.env diff --git a/.gitlab/.gitkeep b/.gitlab/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.gitlab/issue_templates/.gitkeep b/.gitlab/issue_templates/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.gitlab/issue_templates/Default.md b/.gitlab/issue_templates/Default.md new file mode 100644 index 000000000..051106623 --- /dev/null +++ b/.gitlab/issue_templates/Default.md @@ -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** \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7078b8c93..5195e5bf4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,14 +4,8 @@ FROM maven:3-openjdk-11-slim # Set working directory to /app WORKDIR /app -# Update apt; install git and git-lfs -RUN apt-get update && apt-get -qq -y install git git-lfs - -# Clone the 2009scape repository -RUN git clone --depth=1 https://gitlab.com/2009scape/2009scape.git - -# Fake it til you make it - let's go home -WORKDIR /app/2009scape +# Copy all sources etc +COPY . . # Make sure ./run has permissions RUN chmod +x run diff --git a/README.md b/README.md index 63fff0083..e986e4311 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,78 @@ Start the game server with the included run script. Use `./run -h` for more info Start the game server with `run-server.bat` +#### Docker + +Make sure [Docker Engine](https://docs.docker.com/engine/install/) & [Docker Compose](https://docs.docker.com/compose/install/) plugin are installed first: + +Go to the project root where the git repository is cloned into: + +```bash +cd /path/to/project-dir +``` + +To configure the database, copy the mysql env file template to a env file in the project root: + +```bash +cp mysql.env.example mysql.env +``` + +Customize the env file however necessary. + +Create a new directory called 'config' into the project root. + +```bash +mkdir config +``` + +Copy the server config file template in the config directory: + +```bash +cp Server/worldprops/default.conf config/default.conf +``` + +Edit the server configuration file as per needed. + +Go to the project root directory and execute: + +```bash +docker compose up --build +``` + +Which will build the docker image using the local Dockerfile and starts the server and database containers. + +The previous up command should be run every time the server sources are modified to propagate the changes to the container. + +For the first time, the server compilation process takes a long time. Grab some coffee in the meanwhile. + +You can check (and follow) the containers logs by running the following in the project root directory (-f for follow and Ctrl+C to stop): + +```bash +docker compose logs -f +``` + +Any compilation or runtime errors will be logged here. + +If you have already compiled the server and made no changes to the sources, you can simply run without the build option (however it will be cached anyways): + +```bash +docker compose up +``` + +To later restart the server to apply simple configuration changes, in the project root directory run: + +```bash +docker compose restart server +``` + +To shut down / take down the server, in the project root directory run: + +```bash +docker compose up --build +``` + +The database files, build cache and config files will be persisted on the host filesystem for easy backup management. + ### License We use the AGPL 3.0 license, which can be found [here](https://www.gnu.org/licenses/agpl-3.0.en.html). Please be sure to read and understand the license. Failure to follow the guidelines outlined in the license will result in legal action. If you know or hear of anyone breaking this license, please send a report, with proof, to Red Bracket#8151, ceikry#2724, or woahscam#8535 on discord or email woahscam@hotmail.com. **We WILL NOT change the license to fit your needs.** @@ -149,4 +221,4 @@ These credits can be spent in the 2009Scape Reward Shop. It's important to be cl Testers are not the only people who can gain credits - other ways of earning credits can be found on [the 2009Scape website](https://2009scape.org/site/game_guide/credits.html). -Please be patient! The Credit system is not fully complete yet, so it will take a long time for credits to be awarded. +Please be patient! The Credit system is not fully complete yet, so it will take a long time for credits to be awarded. \ No newline at end of file diff --git a/Server/.gitignore b/Server/.gitignore index f22e9f978..a9a29cad7 100644 --- a/Server/.gitignore +++ b/Server/.gitignore @@ -2,6 +2,7 @@ bin/** out/** data/logs/** data/profile/** +data/playerstats/** .idea/** /bin .DS_Store** diff --git a/Server/data/botdata/bot_dialogue.json b/Server/data/botdata/bot_dialogue.json index 4f3a510fc..c225b117e 100644 --- a/Server/data/botdata/bot_dialogue.json +++ b/Server/data/botdata/bot_dialogue.json @@ -257,7 +257,7 @@ "What do you think @name?", "How does that sound @name?", "That sounds great @name.", - "I’m learning English.", + "I'm learning English.", "I don't understand.", "Could you repeat that please @name?", "Could you please talk slower @name?", @@ -301,7 +301,7 @@ "Always check announcments", "We thrivin", "Ship @name", - "Dont forget to vote 2009Scape!", + "Dont forget to vote 2009scape!", "Kermit is too legit 2 quit", "Out here on the range we are having fun", "I am hank steel", @@ -338,7 +338,7 @@ "I couldn't agree more @name", "It cost me a fortune @name", "I am dog tired", - "Don’t take it personally", + "Don't take it personally", "We will be having a good time", "Same as always @name", "No problem", @@ -367,33 +367,16 @@ "May Guthix bring you balance.", "May Guthix bring you balance.", "May Guthix bring you balance.", - "Thy death was not in vain, for it brought some balance to the world. May Guthix bring you rest.", - "May you walk the path, and never fall, for Guthix walks beside thee on thy journey. May Guthix bring you peace.", - "All things must end, as all begin; Only Guthix knows the role thou must play. May Guthix bring you balance.", - "In life, in death, in joy, in sorrow: May thine experience show thee balance. May Guthix bring you balance.", - "Thou must do as thou must, no matter what. Thine actions bring balance to this world. May Guthix bring you balance.", - "The river flows, the sun ignites, May you stand with Guthix in thy fights. May Guthix bring you balance.", "A journey of a single step, May take thee over a thousand miles. May Guthix bring you balance.", "Zamorak give me strength!", "Zamorak give me strength!", "Zamorak give me strength!", - "May your bloodthirst never be sated, and may all your battles be glorious. Zamorak bring you strength.", - "There is no opinion that cannot be proven true...by crushing those who choose to disagree with it. Zamorak give me strength!", - "Battles are not lost and won; They simply remove the weak from the equation. Zamorak give me strength!", - "Those who fight, then run away, shame Zamorak with their cowardice. Zamorak give me strength!", - "Battle is by those who choose to disagree with it. Zamorak give me strength!", - "Strike fast, strike hard, strike true: The strength of Zamorak will be with you. Zamorak give me strength!", "The weak deserve to die, so the strong may flourish. This is the creed of Zamorak.", "This is Saradomin's wisdom.", "This is Saradomin's wisdom.", "This is Saradomin's wisdom.", "Go in peace in the name of Saradomin; may his glory shine upon you like the sun.", "Thy cause was false, thy skills did lack; See you in Lumbridge when you get back.", - "Protect your self, protect your friends. Mine is the glory that never ends. This is Saradomin's wisdom.", - "The darkness in life may be avoided, by the light of wisdom shining. This is Saradomin's wisdom.", - "Show love to your friends, and mercy to your enemies, and know that the wisdom of Saradomin will follow. This is Saradomin's wisdom.", - "A fight begun, when the cause is just, will prevail over all others. This is Saradomin's wisdom.", - "The currency of goodness is honour; It retains its value through scarcity. This is Saradomin's wisdom.", "For Camelot!", "Firmly grasp it!", "My legs!", @@ -496,21 +479,525 @@ "Shlisshalpshlaap", "Somebody call for an exterminator?", "Decisive action. Should work.", - "R E A D Y T O R A I S E S O M E H E L L", "Darkness overpowering.", "Got any questions about propane? Or propane accessories?", "When someone asks me if I am a god, I say Y E S!!!", "You run out of Marines?", - "Goliath Online", - "I'm escaping to the one place that hasn't been corrupted by Capitalism. Lunar Isle!", "Selling wildy protection, 100k gp", - "Can’t we get you on Mastermind, @name? Next contestant – @name from Lumbridge. Special subject – the bleedin’ obvious.", - "Oh, you’re German! I’m sorry, I thought there was something wrong with you.", - "Listen, don’t mention the war! I mentioned it once, but I think I got away with it all right.", + "Can't we get you on Mastermind, @name?", + "Listen, don't mention the war! I mentioned it once, but I think I got away with it all right.", "Cunnilingus and psychiatry brought us to this.", - "Oh, poor baby. What do you want, a Brimstail's Sampler?", - "There's an old TzHarrian saying, you fuck up once, you lose two teeth.", - "The lineup consisted simply of six hydrocoptic marzelvanes so fitted to the ambifacient lunar waneshaft that sidefumbling was prevented." + "Ah, the Wilderness... where dreams of riches meet a swift demise.", + "Who needs a quest guide? I've got the whole wiki memorized.", + "Buying GF 10k coins. Must have at least 70 Agility.", + "I swear, the goblins in Goblin Village have it out for me.", + "Why do wizards always hang out in towers? Is it a zoning thing?", + "I'm training my Construction skill. My house is gonna be lit!", + "The GE is like the stock market, but with dragon bones.", + "Why do we need a cabbage patch in Draynor? Seriously.", + "I've been mining rune essence for hours. My pickaxe hates me.", + "@name hit 99 Cooking. Time to open a gourmet restaurant in Varrock.", + "I'm convinced the @name is actually a time-traveling wizard.", + "Anyone up for a Castle Wars match? I need that decorative armor.", + "Accidentally clicked Attack on a guard. Now I'm a wanted criminal.", + "Why do we even have a Duel Arena? It's just a fancy boxing ring.", + "I've got a stack of burnt lobsters. Anyone want to buy them?", + "Greetings, @name! Looking for a quest?", + "Just got a rare drop! The RNG gods are smiling upon me!", + "Anyone need help with a boss fight? I've got my dragon dagger ready.", + "Buying feathers! Will pay top price!", + "The Lumbridge cows are my favorite training spot.", + "Who needs a teleport? I've got my magic runes stocked.", + "@name hit 99 Woodcutting. Time to chop some yews!", + "Anyone seen the Wise Old Man lately? I owe him a visit.", + "Selling lobsters! Freshly caught from Catherby.", + "Swear, the Wilderness is scarier than my nightmares.", + "Looking for a clan to join. Any takers?", + "I'm an ironman, so no trading for me!", + "Anyone up for a Castle Wars match?", + "The Grand Exchange prices are crashing. Panic sell!", + "Who else remembers the Falador Massacre?", + "I'm saving up for a party hat. Wish me luck!", + "Barrows runs are my addiction. Those crypts are spooky.", + "I'm convinced the Wise Old Man is secretly Zamorak.", + "I'm a completionist, so I'm grinding out all the achievements.", + "The music in this game is surprisingly epic.", + "Tried to trade with a tree... It didn't go well.", + "My bank is like a black hole. Items disappear forever.", + "I'm convinced the Wise Old Man is just a confused tourist.", + "My character's fashion sense? Let's just say it's 'unique.'", + "Challenged a cow to a dance-off. It won.", + "Why does the Lumbridge Guide always look so lost?", + "I'm training Agility, but my real-life agility is zero.", + "Tried to fish in the desert. Sandfish are elusive.", + "The Grand Exchange is like a chaotic stock market.", + "I'm a master at clicking 'Continue' during quests.", + "I'm convinced the chickens are plotting world domination.", + "I'm a woodcutter, but I've never seen a talking tree.", + "I'm a vegetarian, except when it comes to killing dragons.", + "Why do wizards wear pointy hats? Is it a fashion statement?", + "Challenged a guard to a staring contest. He won.", + "I'm collecting cabbage. It's a noble pursuit.", + "Accidentally set my cat on fire. It's now a firecat.", + "I'm convinced the Wise Old Man is secretly a time traveler.", + "I'm a quest completionist, but I still can't find my keys.", + "I'm convinced the ducks are spying on us.", + "I'm a pro at avoiding the Lumbridge swamp. Too many frogs.", + "Hey, can someone lend me 10k? I promise I'll pay it back... eventually.", + "Why do I always find the one square where the random event spawns?", + "I'm training Prayer by burying bones. It's like a spiritual workout.", + "The Wise Old Man's fashion sense is questionable.", + "I'm convinced the ducks are secretly plotting world domination.", + "Why do wizards wear pointy hats? Is it a dress code?", + "I'm collecting cabbage. It's a noble pursuit, really.", + "I'm a vegetarian, except when it comes to killing dragons.", + "Why does the Lumbridge Guide always look so lost?", + "Training Agility, but my real-life agility is zero.", + "I'm convinced the Wise Old Man is secretly a time traveler.", + "Tried to mine air. It's an untapped resource.", + "I'm a quest completionist, but I still can't find my keys.", + "Challenged a guard to a staring contest. He won.", + "I'm saving up for a party hat. Priorities, you know?", + "Why do goblins drop coins? Do they moonlight as accountants?", + "I'm convinced the chickens are plotting something.", + "Clicked 'Attack' on a chicken. Now I'm a poultry murderer.", + "My character's fashion sense? Let's just say it's 'unique.'", + "Do you know where I can find the entrance to the Taverley Dungeon?", + "Hey, anyone here familiar with the Barrows? I need some tips.", + "Is there a bank nearby? My inventory is overflowing with loot", + "I'm clueless about clue scrolls", + "Where's the best spot to catch sharks? I'm aiming for 99 Fishing.", + "Is there a shortcut to the Karamja Volcano?", + "Can someone explain the mechanics of the Jad fight in the Fight Caves?", + "I'm stuck on the Elemental Workshop quest. Any hints?", + "Where can I find a loom to spin flax into bowstrings?", + "What's the best gear setup for killing dragons? I want that visage drop!", + "I'm trying to unlock the fairy rings", + "How do I recharge my amulet of glory?", + "Why do goblins drop coins?", + "The Grand Exchange is like the stock market", + "My real-life agility is more like a lumbering tortoise.", + "The Wise Old Man's fashion sense is like Woahs", + "I challenged a cow to a dance off, surprisingly smooth mooves.", + "Why does the Lumbridge Guide always look lost?", + "I'm convinced the ducks in Lumbridge are plotting world domination!", + "I accidentally ate my prayer potion I'm blessed with heartburn", + "I'm a vegetarian, except when it comes to slaying dragons.", + "Why do wizards wear pointy hats? Is it a magical dress code?", + "I'm a quest completionist, but I still can't find my keys in real life.", + "I challenged a guard to a staring contest. He won.", + "I'm saving up for a party hat. Priorities, ya know?", + "What's the deal with the Wilderness?", + "I'm convinced the chickens are secretly plotting world domination!", + "Accidentally clicked 'Attack' on a guard. Now I'm on a watchlist.", + "My character's fashion sense? Let's just say it's 'unique'", + "Lobsters heal my soul", + "Trimmed armor or bust!", + "Wilderness: Where friendships go to die", + "Buying gf 10k", + "Dancing for coins at Lumbridge", + "Wearing full rune like a boss", + "Teleporting to Camelot for quests", + "Fishing for hours at Catherby", + "PKing with a rune 2h", + "World 1 Falador Park parties", + "Staking my bank at the Duel Arena", + "Dying to Elvarg's fiery breath sums up my life", + "Got the Quest cape finally on my main!", + "Wearing a party hat with pride", + "Castle Wars: Red vs Blue", + "Trading in Varrock Square", + "Barrows runs for that sweet loot", + "Spending hours in Pest Control is life", + "Killing cows for leather armor is a good start", + "Farming herbs in Ardougne can be good money", + "Clan chat drama LOL", + "Mining rune essence endlessly", + "Agility courses: A love-hate relationship", + "Dueling for honor.... and GP", + "Buying runes from Aubury is decent money", + "Chasing the Easter Bunny like a scam", + "Wearing a skillcape with pride", + "Fletching yew longbows for profit", + "Going to implings in Puro-Puro", + "Castle Wars barricade wars", + "Dropping party hats at drop parties like pennies", + "Killing lesser demons in Karamja is part of my holy conquest", + "@name lures noobs into the Wilderness", + "Farming ranarrs for cash", + "Getting lost in the Underground Pass", + "Selling coal at the Grand Exchange", + "Fighting the Kalphite Queen", + "Picking flax in Seers' Village", + "Begging for free stuff in Lumbridge", + "Smithing rune platebodies for profit", + "Logging out in Lumbridge Castle", + "Hey @name, how do I get to the Grand Exchange?", + "Veteran here! @name, what's your favorite quest?", + "@name, how do I make money fast?", + "Hey @name, what's the best combat style for bossing?", + "@name, always carry an emergency teleport!", + "New player here! @name, what's the Wilderness like?", + "Hey @name, how do I join a clan?", + "@name, prioritize Prayer levels!", + "@name, what's the best way to train Agility?", + "Hey @name, what's your favorite minigame?", + "@name, never trust a PKer in Lumbridge!", + "@name, how do I get a fire cape?", + "Hey @name, what's the fastest way to level up Magic?", + "@name, try the Barrows tunnels!", + "@name, what's the deal with Pest Control?", + "Hey @name, how do I unlock the Fairy Rings?", + "@name, remember the old random events?", + "@name, should I train Strength or Attack first?", + "Hey @name, what's your favorite skillcape?", + "@name, can't do the Fight Caves!", + "@name, how do I get a pet?", + "Hey @name, what's the best food for boss fights?", + "@name, Castle Wars or Clan Wars?", + "@name, what's a clue scroll?", + "Hey @name, how do I defeat Jad?", + "@name, use Protect from Melee at KBD!", + "@name, should I train Ranged or Magic?", + "Hey @name, what's the best way to level up Crafting?", + "@name, don't forget your anti-dragon shield!", + "@name, what's the Stronghold of Security?", + "Hey @name, how do I access the Legends' Guild?", + "@name, remember the old PvP worlds?", + "@name, how do I get a dragon defender?", + "Hey @name, what's the best way to level up Herblore?", + "@name, always carry a charged amulet of glory!", + "@name, should I train Fishing or Cooking?", + "Hey @name, how do I defeat the Chaos Elemental?", + "@name, try the TzHaar Fight Pit!", + "@name, what's the best way to level up Smithing?", + "Hey @name, what's your favorite Slayer master?", + "@name, use the Ardougne cloak teleports!", + "@name, what's the Legends' Quest about?", + "Hey @name, how do I unlock the Ancient Magicks?", + "@name, remember the old Pest Control boats?", + "@name, what's the best way to level up Construction?", + "Hey @name, what's the fastest way to level up Prayer?", + "@name, the old Duel Arena stakes!", + "@name, what's the Warriors' Guild?", + "Hey @name, how do I defeat the Kalphite Queen?", + "@name, use the fairy ring code BIP!", + "@name, should I train Attack or Defense first?", + "Hey @name, what's the best way to level up Thieving?", + "@name, don't forget your charged glory amulet!", + "@name, what's the Legends' Cape?", + "Hey @name, how do I access the Heroes' Guild?", + "@name, remember the old Pest Control void gear?", + "@name, how do I get a firemaking skillcape?", + "Buying gf 10k", + "Trimming armor for free!", + "Wanna join my clan? We're called The Mighty Cabbages'!", + "I'll meet you at the Falador Party Room!", + "Dancing for coins in Varrock Square!", + "Remember when the Wilderness was dangerous?", + "I got a rune scimitar drop from Lesser Demons!", + "Lumbridge Swamp is haunted!", + "Anyone up for Castle Wars?", + "I miss the old random events!", + "World 2 is the trading hub!", + "I'm going to train my Agility at the Gnome Stronghold!", + "I just got 99 Cooking!", + "Fishing lobsters at Catherby is life!", + "The Legends' Guild is so exclusive!", + "Who needs a quest guide? I'll figure it out!", + "I'm mining rune essence for hours!", + "Barrows armor looks sick!", + "I'm stuck in the Underground Pass!", + "The Stronghold of Security taught me about account security!", + "I'm going to farm herbs in Ardougne!", + "I'm getting my fire cape!", + "I love the music in RuneScape!", + "I'm alching my maple longbows!", + "I'm doing the Recipe for Disaster subquests!", + "I'm going to train my combat stats at the Rock Crabs!", + "I'm going for the Quest Cape!", + "I'm going to mine pure essence!", + "I'm hunting chinchompas in the Feldip Hills!", + "I'm going to train my Woodcutting at Seers' Village!", + "I'm going to fish sharks at the Fishing Guild!", + "I'm going to train my Thieving at Ardougne Knights!", + "I'm going to hunt implings in Puro-Puro!", + "I'm going to train my Hunter at the Falconry!", + "I'm smithing rune platebodies!", + "I'm going to train my Farming at the Tree Gnome Stronghold!", + "I'm going to hunt red chinchompas in the Wilderness!", + "I'm doing the Underground Pass quest!", + "I'm going to fish monkfish in Piscatoris!", + "Meet me in Varrock, @name, for an epic trade", + "@name, join my clan; we'll conquer the Wilderness together!", + "Beware the dragons, @name, they're fiercer than you think", + "Crafting runes with @name, the best mage in Gielinor", + "Fishing lobsters with @name, the sea's no match for us", + "@name, your swordsmanship at Duel Arena is unmatched!", + "Questing through dark caves, @name always leads the way", + "Share your wisdom, @name, how'd you master those spells?", + "Legends speak of @name bravery at the God Wars", + "Need more arrows, @name? I've got plenty to spare", + "Cooking's a breeze when @name around, no burnt lobsters!", + "Mining together, @name and I strike gold every time", + "@name, let's barter; your herbs for my potions?", + "Training agility with @name, leaping like graceful gazelles", + "Heard @name got the best magic beans in town", + "Fletching bows with @name, aiming for perfection", + "Smithing with @name, our anvils never cool down", + "Adventuring with @name, every quest is a thrill", + "Battling demons, @name courage inspires us all", + "Slaying dragons, @name the hero we need", + "Gathering at Falador, @name party room is legendary", + "Hunting chinchompas, @name traps are always full", + "Farming's fun with @name, our crops never fail", + "Brewing potions, @name mixtures are magical", + "Casting spells with @name, we're invincible", + "Building fires, @name flames warm the coldest nights", + "Sailing to Pest Control, @name our fearless captain", + "Trading runes, @name deals are the fairest", + "Exploring dungeons, @name the light in the darkness", + "Charging orbs with @name, our energy knows no bounds", + "Dancing in Draynor, @name moves are enchanting", + "Playing Gnomeball, @name the star player", + "Harvesting willows, @name axe swings true", + "Enchanting jewelry, @name touch turns copper to gold", + "Summoning familiars, @name spirit wolf leads the pack", + "Thieving from stalls, @name hands are lightning-fast", + "Crafting runes, @name essence never runs dry", + "Fishing at Catherby, @name catch feeds us all", + "Cooking feasts, @name dishes delight the gods", + "Mining runite, @name pickaxe strikes rich veins", + "Bartering at Grand Exchange, @name a trading master", + "Training prayer, @name piety moves mountains", + "Fighting revenants, @name valor shines bright", + "Building homes, @name construction is flawless", + "Hunting imps, @name net is always full", + "Brewing ale, @name tavern is the town's favorite", + "Casting high alchemy, @name turns junk into treasure", + "Sailing to Karamja, @name adventures are legendary", + "Forging alliances, @name charisma unites clans", + "Defeating bosses, @name name echoes in legends", + "Are you mewing @name???", + "Check out that gyatt @name", + "Bruhhhhh @name got that rizz", + "@name rizzing up the bots", + "Ironman? More like copperboy LOL", + "What that gyatt do @name", + "He's got the zoomies!", + "@name likes pickles and dipped in mayo", + "Lmaaooooooo", + "Bruh she said she loved me...", + "I caught @name rizzing a mewing teacher", + "What that mouth do bb", + "Ayo tf he say", + "Ayo", + "Ayo @name a freak lowkey", + "Ayo tf", + "@name catch me outside howbout that", + "Wasssssuuuuppppppp", + "Knock knock @name", + "Oh boy howdy do i have a surprise for you", + "Noooooo", + "What do you mean I haven't done anything wtf", + "Redrocket redrocket!!!", + "I made 20,000 crochet sock puppets for ceikry", + "Come on @name", + "Are we rading tonight @name?", + "Why would he say that", + "Penguins are technically reptiles", + "Brb smell something burning", + "Need pest control partner, you handle the portals, i will afk", + "Bruhhhh the skibiddi rizz in my gyatt makes my mewing sesh rough", + "@name jajajajaja", + "Wait, I can't raid tonight @name", + "Brb, pizza's here. Hope they ask why I'm a grown man dressed like an elf", + "Sorry, gotta go AFK. My dog just ate my gaming headset.", + "Brb, my grandma just fell down the stairs", + "Hold on, the baby's crying, aka @name", + "Oops, spilled my drink", + "Guys, I need to log off, my plants are staging a revolt for not watering them", + "AFK a sec, my neighbor's llama is in my backyard again", + "My pizza rolls are ready", + "Lost track of time, i'm late for my own wedding", + "Sorry, can't hear you over the sound of my laundry", + "Turn HDR off in windows @name", + "Www. no one cares .com", + "Hey @name www dot stfu dot com", + "Like pitching a tent in a pair of britches", + "Mad as a bag of ferrets", + "I dont think beavers built the hoover dam", + "How do beavers get the concrete for dams?", + "@name show me the way", + "@name onwards brutha", + "@name eats fried rat tails", + "@name is a rat", + "Why are there so many people farming right now", + "@name fuck the police", + "@name says the like pickles on their hotdogs", + "Fuck the guards, free woah in varrock prison", + "Ayo fuck u mean", + "Woop woop thats da sound of the beast", + "How are you the way that you are", + "She sells seashells by the sea shore", + "@name certified rat", + "@name buys skimmed milk", + "Got milk?", + "Are you JSON because i want you to get array from me", + "@name likes string manipulation", + "@name sells legos", + "JUST DO IT", + "Why are there so many motherfucking options", + "Just hit the fucking auto hide thing", + "Just play", + "Hell no this shit is fuzzy as balls", + "@name is fuzzy as balls", + "Turn your brightness up @name", + "On my pc it runs perfectly", + "I know why mine seems a little jumpy", + "Stream the window not the monitor", + "Discords a bitch", + "Why are there so many handsome people at the ge like @name", + "I'll make you squeel of fortune", + "You will get it in due time @name", + "I was one turn away", + "It do be pipe time @name", + "Sometimes i like to cover myself in vaseline and pretend i'm a slug", + "Woah is a greased pig", + "Sometimes i dig holes in my backyard and pretend i'm a carrot", + "It's pipe time @name", + "If i am not pipe timing i am programming", + "Yes i have thigh high socks, no you cannot have them", + "I scream when i wear my programming socks", + "Anyone wanna buy @name's bathwater?", + "Anyone wanna buy my used socks?", + "@name be getting bags", + "What the fuck is this", + "Hi i noticed you haven't taken a break in hours @name", + "I really am seething rn", + "I am so fucking tilted", + "Go back to your fucking frogs", + "Life is simple as frog farmer", + "A q p", + "Oooooh that's a first", + "Been a minute @name", + "Hey @name", + "Oh shit it's @name", + "Hey @name wyd today?", + "Ayo @name", + "Wyd @name", + "Wbu @name?", + "Good fuck em, that's what they get", + "Deathknight lookin rat ass mf @name", + "@name sucks slugs", + "Brb gotta take a shit", + "@name will brb went to take a shit", + "Tell me when he is coming", + "This guy is being incredibly based", + "@name is based af", + "Taking l's all day today @name", + "That sucks massive dongus", + "Ah i was about to type that", + "Where is stormwind?", + "How do i buy gold", + "@name sells gold", + "@name is a gold digger", + "Ayo @name wanna buy some frog legs?", + "Brb fucking burnt my pizza rolls", + "I swear @name if you need roll again", + "@name you are a hunter you don't need plate armor", + "Dragonriding is satisfying", + "There's an old TzHarrian saying, you fuck up once, you lose two teeth", + "FUCK", + "@name i am ready when you are", + "Anyone selling logs?", + "That is hilarious @name", + "Alright @name", + "I need to go get my quest cape", + "500 barrows runs dry", + "Back to back to back barrows items, easy game", + "@name did you know them?", + "@name sucks eggs", + "@name eats snails", + "Who is that?", + "I've gotta go in 20 minutes", + "Bonk", + "Gtg in 30 minutes", + "They left 50 minutes ago", + "I think it's just gonna be us", + "Who is that?", + "Stop following me", + "@name stop following me", + "I think @name is watching me", + "@name wouldn't let me get a hit in at clan wars", + "Clan wars and chill?", + "Quick mewing sesh", + "Don't you stickbug me", + "Get stick bugged", + "Wtf why are you a dragon", + "Fun fact, woah eats eggs whole like a snake", + "Ceikys there's a rare over there", + "Where is goldshire?", + "How long have you played @name?", + "Wanna quest?", + "TROGDORRRRRR", + "Fuck this shit i'm out", + "Wait till you get your first 99", + "How is that even possible", + "That's the max", + "@name is trying to max", + "Complesionist cape?", + "How can you increase your run speed?", + "@name where is barrows?", + "@name where are rune rocks?", + "@name where is yanille?", + "@name how long have you played for?", + "Yes these quotes were hand typed", + "Kermit was here", + "World of Warcraft died after Wrath", + "Ceikry needs to add talent trees", + "How do you get to the wilderness?", + "Anyone tryna lure @name?", + "@name tried to lure me", + "@name bots all the time", + "Anyone want chicken tendies", + "Brb foods ready", + "Brb", + "Gotta go get food", + "Anyone else from northern alaska?", + "Fishing 4 gp", + "Looking for gf", + "Anyone wanna be my discord kitten?", + "Selling discord kittens", + "@name is a discord kitten", + "Add me on discord", + "Are you in the 09discord", + "Where are you going @name?", + "Idk what i am going to cook today", + "What should i eat tonight", + "He is in the cave", + "Don't forget to save game before logging out", + "American horror story died after season 1", + "Prequels are better than the sequels", + "Disney star wars is the best star wars", + "Halo 5 sucked", + "Selling xbox live gold and 1600 microsoft points for 50k gp", + "Buying gf 25gp and a bucket", + "@name i heard evilwaffles bots", + "@name hopefully ceikry doesn't find out about evilwaffles", + "Honk honk", + "Where is the road to mordor?", + "Golem lookin ass bitch", + "@name rat looking mf", + "@name uses sand paper to wipe", + "@name said Woah is cute", + "You need 25 of 40 to do that", + "Over 1000 unique lines of dialogue", + "What should i get to eat", + "Ge be poppin today", + "Where all the woahs at", + "Ayo woahscam got that gyatt", + "That is one i haven't seen before" ], "halloween": [ "Trick or treat!!!", @@ -521,9 +1008,9 @@ "Costume party at my P O H!! Follow me!", "Trick, then!", "Brrrainssssssss...", - "'Cause this is thriller, thriller night, and no one's gonna save you from the beast about to strike!", - "This is Hallowe'en, this is Hallowe'en, pumpkins scream in the dead of night!", - "This is Hallowe'en, everybody make a scene, Trick or treat till the neighbors gonna die of fright!", + "'Cause this is thriller, thriller night!", + "This is Hallowe'en, this is Hallowe'en!", + "This is Hallowe'en, everybody make a scene!", "In this town we call home, everyone hail to the pumpkin song!", "Watch out for Skeleton Jack!", "The headless-what-man? Horse? Never heard of those.", diff --git a/Server/data/botdata/botnames.txt b/Server/data/botdata/botnames.txt index 3024122cc..22c5715a8 100644 --- a/Server/data/botdata/botnames.txt +++ b/Server/data/botdata/botnames.txt @@ -1,766 +1,1992 @@ +-Kevo +0 S O +0 4 1 1 +0 4 7 +0 HBK 0 +0 Khan 0 +0 LM +0 Quest Ptz +0 Undead 532 +0 hits +0 s m a n +0-0000000000 +00 zarour 00 +000eggs +004 n +007 Double O +007Banshee +00eeeee58 +00eeeeeHC +00oo00oo00z +010elpibexx0 014ankh 01690 +017Lucas 01Ares +01eon +01ogre 020893 024elyograG +02RS +02homestar +031 +0315 0416347345 +054 +055027584 +05miller5 05venom04 +06 59 0600 +06sz +07 Account +07 Adept +07 Ashley +07 Clinton +07 Ezra +07 Gizzy +07 Main +07 Nihilist +07 Officer +07 Rz +07 Sicario +07 T Bone +07 iron +07 kiero X +07 nice jake +070623 +07Ashley +07Ben +07Geno +07Jack +07Lax +07Panda +07Paul +07RS Suck +07Rockysbudy +07coco +07guthix07 +07wan +080 +0800 +0800952449 +0837 +09 +0920 09Ash 09Ashley 09Ato09 09Chris 09Dans 09Paul +09Reaper +09Rhys +09Rvape 09Skillerino 09TroyScape 09milo456 09scaping 09wan -0800 -0837 -0920 0BAMA +0Behind0You0 0CdanC0 +0G 0GCore 0Giesel42O +0IIll0llIIII +0JL 0Joker0 +0KAT 0LKI +0MEGA PVM +0MG ST3PSIS +0MW2PVM +0Manbearpig0 +0MrJ +0PC +0Pwnseason +0S Iluvatar +0SFX 0Tempest0 +0WEK 0X010 +0_BADASS_0 0anabanthis0 0bD0G 0bankspace 0bby +0bby Man 0belix +0bscure0ne +0ctopus +0dds r u win +0desza 0dexy 0dyssey +0ff Task 0ffline 0g_player +0gden 0ggyy +0gie +0h Hi Mark +0h4Sure +0hh 0hmMyGod +0hp +0im2old4this 0inke +0k +0l +0l m +0ld Mate +0leaguespace +0li 0livers 0livias +0lki +0llieNorth +0llies +0lmet +0lofmeister 0lurker0 0mfgcows +0mg A Baboon +0mg A Scimmy 0miseGo 0mnisciens +0n Time 0nMyWay2Max +0na +0nenonen 0neski 0netruememe 0niichan 0njon +0nlyClouds +0nmy 0ops 0oqs 0pSe 0pen +0pen Al +0pi ates 0pportunity 0riole 0rjan +0rl +0rnstein 0rphaned 0samaBdabbin +0sc4r 0smasher0 +0so 0ssimpwner +0syb +0t 0taku +0taylor +0verHyped 0verdrinking 0verhaul +0vershield 0viBeSo 0vyy +0wnag3s +0wnage_2017 0wnagedaddy 0wned 0wns +0wnu1h1t +0x Good x0 +0x05 +0xBitcoin 0xocube +0xp Fail 0xygen +1 +1 0 1 0 +1 1 4 +1 2 2 +1 337 +1 3s +1 4 11 +1 54 +1 9 7 +1 A M O R +1 A N D 1 +1 Account +1 Corin 6 9 +1 DC +1 Flicki Boi +1 Free Ryde +1 Ham +1 K0 Is Back +1 Line +1 Lion Tell +1 P P 1 +1 Percent +1 Waffle +1 and only +1 is ln of e +1 kc 1 pet +1 parhaista +1 s t Blood +1-800DILLDOE +1-GrayStone +10 Four +100 Duke 1000 Antz +1000 Eyes +1000 Ways 10000Fists 1000PingOnly 100Cl 100Grabb 100g 100kyews +100m gp 100xcoin +101000101010 +1017Trev 101e 101evil101 +101rootbeer +104512 +105 107M 1085 1099s +10Hz +10R80 +10b +10cc +10lachs +10mm mafia +10th Reaper 10v10 10v10v +10v10v10v +10x Brew +1101953119 +1111 +11111111111d 111fishkite1 +112OceanAve +116 +116 123 1183kenny +118s 11inchlimp 11l1 +12 +12 49 +12 XP +12 hours 120u 1212ajs2 +123 Zeus +123456205 +123Four 123bear5 +123tseemijni 1272172 +1288 12bar 12bar_ty +12blackeagle 12boo8 12earlycob +12in +12millionGP +12oss 12oz 12th +13 37 +13 Daas 1300 130SS 130rk 1312AFCA +1313 +131kg +1337 af +1337Trevor 1350 138hans +13D 13LACK0N3 +13O 13alerion +13ayek +13ehh 13ert1 13ig 13igdog +13kc Corp +13oneZ 13purecs +13ruce +13th Witness +14 wides 1400 +14060M +144 1441 +144forever +1453 FSM 149122089147 +14Fre-e +14GT 14Words +15 min Break +150 kilos 1520sedgwick +15b name +15mg +16 Fly Tell +16 Fps 1602 +16bumpysnow 16lB 16le +17 76 +170 IQ 17222 +1780 17Kc 17_FLHXS +17rune2277 +18 Or Older +18 Points +18 girlll +18-06-2022 x 1800BetsOff 1802M 180555 +181 1817 1827 1845 184910381412 +187 +187 781 1877 187PIGMORGUE 187er +1889 18cast1935 18hr 18killz +18s +19 KAPLAN 05 19088ml 1942 1948 +199 1992 1994 19O7 +19edgebee +19puxx +1AB +1After909 1Alfa 1Ali1Plane 1BGP 1Bio 1Buck1Love +1C2R +1C3 W4RR10R 1CuteBot 1Fbs +1G1D +1GramIronMan 1Hit2Lum 1Hunnid +1I1I1IIll1l +1InchWalrus +1Iron Reborn 1Kyle +1Lank 1ManGangBang +1Mayze +1NANO +1NU 1Nikolai +1Nut Dude +1O1Barz +1Ogp +1P Game +1Parthannax2 +1RlE +1ST KC +1Shadow9Fist 1Shark +1Stunner ISU +1Swan 1TapDirtNap 1Tick +1Tick 2 Slow 1TickShit +1Unforgiven8 +1_BLEED_BLUE +1ars 1badprogram +1bankingnoob 1blackhawk17 +1blk eye r2 1cooking 1da5 +1dan2 1day +1day iwas pk 1eat 1einad +1eyeNinja +1god max1 1grootfeest 1hundrednite +1imp1jar 1isa +1itemtbow 1jbone 1john +1k no zik +1kDuramax +1kMaster QXD 1k_0 +1kc myself 1kctobpet 1kill3dsanta 1kk3 +1kkc 1kona 1llmatic +1llusionss +1mforest +1nOnlyBigB 1nVaSioN +1ndy +1ne life 1nfiniti 1nsane str5 1nv1s +1nvercargill +1o 8 +1ocation +1please 1powerhydra +1r0nRyan 1rock23 1savage +1st Frans +1st ed mint +1st son 1stHCwasPKED 1stlrfan 1stnoob7 +1t8w 1tapaturma 1tickspecwep 1trueMortyy +1ucif3ro +1v1d a sedan 1w1lll0se 1wayEscape +1wwdwd2e21ed 1yfe +1zet +2 0 1 4 +2 1Savage +2 2 7 7 +2 2 8 9 +2 3 7 5 +2 9 +2 Big tities +2 Brothers +2 Much Tuna +2 P A C +2 Rich 4 Dis +2 Silent 540 +2 Smooth +2 ball cane +2 funny bro +2 grls 1 Jad 2 h p +2 in +2 kups +2 lN +2 more min +20 20 vision +20 Agi irl +20 On Pump 3 +200 +200 Cook 2000 +2001Arwen 2006nope +2007scape +2008Anthony 2009Anthony +200MXP Cook +200m for Olm +200mDefence +200marchalt 200mslay1day 2011Turmoil +2013 Rt +20130406655 +20136 +2027Parzival +202o2 +204 Wootang +205alphago +208 209th 20Four 20I0 20JuanSavag3 +20ms +21 5 +21 Average +21 Musketeer +21 Savage +2137 gp +215O +2173 +21JJ 21SavageX2C +21unit +22 69 +22 Geese +22 Gz +22 darnoc 22 +22 day fury 220mg +226559 +227 7 2277 +2277 Beast +2277 Soon TM +2277MaybeNvr +2277kg +22O5 +22X7 22btc 22harry +22mario 22whitewolf +23 x 3 232o2oo +234071973416 +23463564 +235iii6i666i +2376Total +2376ed 23847239571 23CD +23I +23coalcare +24 Hour 24000 +241 241196 +2424 +245 +247 365 +2488 24YO 24l7 +24w +25 S 25simulator +26 th 260sheepdog +265344626546 +26ll98 26pandas 26th +27 5 +27 M +271MPN 27ml 2841280 28BagsOfSalt 28goldpieces 290x +29384652 2996 +29Pons30 29enchant821 29robdead +2B Nier +2Broke4Coke +2C-T-2 +2D E F2 +2D pantsut 2Edgy4Meme 2Far 2Fast2Fuego 2GIRLS +2GLE 2Girls +2Goblins1Jug 2Guys1Wyvern 2Guys1dds 2I40 +2InchLift 2InchPnisher +2JZ GTE +2JZ Soup +2JZ-GTE Mk4 +2Jerry 2Lit 2M0X1 2MIN 2Men 2MinNoodles +2O xp +2O07 2OO7 +2QC +2Rare2Die 2Secs 2Sparks 2TH3MAX +2Tbows51Kc 2Tits +2TonHoneyBun +2VX +2amtossedher 2anmlsglued 2beFrank +2buttedgoat 2cbrein 2coldkillaa 2cool4u +2cythe +2d irl 2danny1 2dslap +2fastkilzz 2g0ds1cup 2girls1Kyle +2girls1Tbow 2girls1bed 2girls1troll 2girlz1jbird 2gurls1tick +2h ur azzzzz 2hopp +2hot2touch +2hot2touch2 +2hs +2id 2kav +2late2team +2logx +2nd Marksman +2nd Max Cape +2nd Sucks 2ndpanz 2olon 2oul 2pac 2pacs +2plush +2sm0keyyyyyy +2steps 2th7 2tickin 2tti 2two77 +2uBz 2ual 2ugi 2woke 2x92is99 +2ze 2zick1zussy +3 Hit V +3 5 P +3 50 +3 77 +3 Moons +3 S I X E S +3 SwordStyle +3 T C +3 arcanes ty +3 me0 3-age +30 yo b00mer +300m MPS +3019 30FF 30SHOTFOCUS +30XY +30thNovember 30zl +311s +3155 +315lbs 315squatreps +3160 316Austin +32 kay 320m +33 Dust Cat 335K +33jf 33zero2 +3400 341all +34241245321 345138 +34HL6432G6L2 +34T 34rj394thgro 351329 +35ankoumoon +35cm bicep +360 Labels +360backhand +365 day +369x +36ACT 36answersay +379 37Kg +37kg +381N 38CoolestMan 38drivelift +39 PETS 3BigDoobie +3D AnimeTits +3D Dorito +3E8 +3G 3III 3L1QZaD +3LetterName 3Miller3 3TicYaMum +3TickEat 3Ticks +3WordName +3a B0ng 3aly 3amf +3arbeed +3ash 3awe +3b 3bay 3bobyshmurda +3d age boots +3d354 3dderino 3erzerk +3h scimitar 3ibal0e9 3inchsoft +3ioc 3iron5u +3km +3lPatron +3lack 3litz 3lilbirbs 3lit3 3liteSupreme 3lven +3lyrie 3lys +3niZ 3nosedmonkey 3nps 3oh5 3pic 3ragesave +3rd Age Ryan +3rd Age THC +3rd Age Toes +3rd Age Wife +3rd Hunter +3rd Mage Owl +3rd X +3rdAgeHolds +3rdAgeHunt 3rdAgeSlayer +3rdMistake +3sd +3sl 3stripe +3uck +3uzi +3verD3ad 3vil +3vil Giraffe +3vil Lawyer +3we 3x3i 3xDestroyer +3xtra-1arge 3yes +3zR +3zi +4 Bros Info +4 Man Nex +4 inch demon +4 inches now +40 IQ +400L +400s +403 b +40699 Crabs 40Atk 40ms 40oz +412087542131 +413 JokeR +416MiIf Jade +41O +42 Mime 2462 +42 Reasons +42 skankhunt +420 Perkele +420 Sam +420 kc +420 lobster +420-2-Day 420Arch420 420Every +420Hellz 420LavarBall +420Pixels +420cyguy +420licious 420michael13 420tarmslyng +42342343342 +42Def 42O42O 42nd +432andone 43technetium +44 bot +4400 +44444444444 +45 fail +4500c +4564 +457 45smith2345 478k +47an 47th +48H +48hour +49 r +492117837813 +49forcerage +4ALLYaYa03 4D Entity 4EverAlone +4HeadScape 4Hymnz +4IQ Idiot +4K MrKrabs +4KT Youngboy 4Kath +4L O K O +4LDO 4LeafCIover +4Lt Goon Bag 4MoistScoops +4Nick8 +4STER 4Sale399 +4TheGalaxy 4The_Horde 4Tune +4V2Coldplay +4_Th3_mAiN +4are 4asphalt4 4biddin3 4cof 4dan2 +4dapigs 4dosemonster 4ever 4everdry +4got2pull0ut 4gottenvoid +4hQ +4hit pl0x 4holyhydra +4iend 4lex4nder +4litraa 4mag1996 4otobemaG +4pf PvM Lao +4pf danaw1te 4plate +4rKsTon3 +4sen +4th lesson +4th8 4the 4themininoob 4thunderbolt +4ur Elise +4werty4 +5 56 +5 Nine +5 Oh +5 mil +5 ms +5-Bet Fold +50 DKP Minus +500 BJ +5000men4 +505 +50Dkp_Minus +50TintenUwMa 50bitsnka +510 51Highlander 51xp +528racklover +5304 +530pm +53R10U5 53m0g +54 Slayer +548 55mg +562 5639 56667 56771910 +56O 570215 +5771 +57tallfred +57wraith2076 +5876 58th 59OG +5ADx 5Aces +5BY5 +5G fries 5GCovidTower 5H4NKS +5IRLOIN +5Id3Track +5M3H 5M9SNH31KAOI +5NAX +5OP 5Orudis5 5UBLlME +5a9 5alood +5alve 5ammito +5dudes1bank +5eekingdeath +5eizures +5g Internet 5gum 5harp5hoota 5hauny 5hayWh1te +5head +5inch Floppy +5is +5j5 +5kc Zulrah +5l9 5lNS +5lap +5nis 5parrow 5quid +5st +5t h +5th Blessing +5th hcim LUL 5trm +5wan 5wattia +5x3 5xr2 +6 57 +6 9buttholes +6 Donuts +6 Drive Fox +6 Foot 5 +6 I X GOD +6 M +6 Mil +6 Qp +600 Years +605 +60min is 1hr +617 +61V +6264 +629fm 62steeplist +6384 +647 +64DD +64th 6535 6620 +666 317 +666 J +666 is leet +666Ds 666Duuvel666 +666drkwizard +66Cute +66sick +675 +67M 67jj6j7 +68 savage +6822ii +68pickleman +69 L OR D 69 +6942O +69KolaOlli73 +69Skrrt420 +69bbygurl +69maryj420 6Dukke +6ESkarmory 6LACKY 6PaperJoint +6R6 6T9sofine 6TMG +6Tilbud 6UGG 6cansOfBeer +6cythe 6dr3w9 +6e +6enny +6ess +6et Schwifty +6h Logout +6in9 +6ix 6ixty 6ixx 6jadkiller +6km +6lb brownie 6mat 6ong 6p8o6 6pathsofpein +6qp +6shooter +6uapo +6uh 6utt9lug 6y6y6y2 +7 7 MAFIA +7 Curry +7 DeadlySins +7 Fat Drake +7 Of Nine 7 Trouts +7 pets chimp +7 seconds 7042 +70Point1 lol 70rangeboy 70to120 +710 710 710 +716 718m 71rc +72005sc +73 and Kree 734590325839 +737 73Head 73HoundKey +73gp 742617OOOO27 74ChaosLTUU +75 IQ 76frozen44 +76ms +7738 777slayer777 77Dunamis77 77Stingray +77ms +782 +784162 79giantfoot 7AYL0R +7D9 7Esconar7 7Ethereal +7F9 +7GB 7God +7H3 7J4FXEL7YFW5 7Lazy7 +7RAC +7T +7TF +7Vick 7Viggie +7ZP 7abibi 7amowdahli +7aq 7asty 7ate9 +7axy 7bazillion 7ckng +7ckng Mad 7claudia7 +7crypto7 7emo 7emp 7emptest 7fisherdog +7havage 7min 7mins +7mni +7om 7ourney +7out 7rad3 +7raphouse +7rev 7ridley 7scaryfire +7th Chamber 7th February +7uP joAo 7wizard10 7xshadowx7 +8 1 O +8 3B +8 Sue Build +8 alien 8 +8 bit bling +8 oh 8 8008tator 808Southside 80leavefox +82 Sue Say 8282 +831pride 832arba +8485 +849 +84Bandit +85 N 8502 +852K +858 +85U 85dakota85 +88 Odd Wand +8800 8885 +88gg 89devoid1329 +8ASS +8BootRaw6220 8ElMandinga8 +8J8 8O8O8O8O8O8O 8ass 8balknowsall +8balled +8eauty +8h of sleep 8ind 8l0wm3 8lackMamba24 +8loodrune +8lue Phat +8owser +8qxkf02v +8tail +8uD +8yrs +9 9 Problems +9 B 0 +9 DUIS +9 Tick Brain +9 mb +900mexp +901Grizzlies +90804 +9092514 90tegguy +91 +91 Tin Idea +911_Nate 91414 91CAL 91flip +92 way there +920 Bdops +9227 +9297 +92t 92times2is99 +94 xp +94AR BTW 95EliasAw +95LH +95p7 +96 WINDSTAR 9687 96fastfrog 97DEF +97ccman97 97fsh +98 BEDARD +98 Civic +982 +98Bn 98max1 +99 Doobies +99 Grill +99 Grinding +99 Mage +99 Rats +99 StaKe LvL +99 Steeze +99 oz +99 rwt +991atatime +9991499 99BnkStnding 99Juuling +99Lives 99S2HP +99Scents +99Scotting 99Slayer +99Souls3Days 99Victim +99_Maxed 99bottin +99charming 99hh +99moretimes +99ox 99problems4 +99ss +99sweatlvl 99wc500m +9Bar +9Barr +9CX 9D9Skillzz 9Dimensional +9GAG Reflex +9JR 9KL00AZIIZ9K +9O 9 +9PetsSoFar +9T3 +9TY9_Sailing +9UNIT +9cansRavioli +9ieman 9inchStepBro +9mm ko +9mmlesah +9ne +9ond +9till5slave 9ussy 9yearoldpops +A 2 The Aron +A 3 +A 45 +A 7 F O L D +A Articuno +A Bad IGN +A Bad Meme +A Big Hippo +A Big Leap +A Blind Man +A Booty Clap +A Born King +A Boss +A Bozo +A Bozz +A Bronze man +A Butcher +A Catowl +A Cityzen +A Claptrap +A Classy Boy +A Crazy Cook +A Crisp Sock +A Curry +A Cute Snail +A Dreamer +A Drunk Beer +A Du Ma +A F K Scape +A F KING +A F M Scape +A Fat Deer +A Fat Pike +A Fat Swede +A Gauss +A Girthy Boi +A Godsword +A H Fire +A Half Fool +A Hofasho +A I D E N +A I I e n +A II D +A Iron Yoshi +A J +A JJug +A Jack O Lit +A Jug +A Kimura +A L 3 X +A L C A P WN +A L E K S +A L I S H A +A L T A Lava Lamp +A LifeTime +A Lilypad +A Little Lit +A Llama +A Long Story +A Loose Seal +A Mattias +A Maxed Iron +A MaxedBeard A MeatStick +A Melic Poet +A Message +A Moist One +A Moot +A Nathan +A New Area +A Newbi +A P R O X +A Panther +A Pauled +A Pixxel +A Pkr +A Plush +A Poothy +A Pot Head +A Psy +A Quest God +A Quickie +A R H +A R Z +A Raccoon +A Random 4nr +A Real Bamf +A Revuelto +A Rocky Road +A Scylla +A Sly Deity +A Snorelax +A Sofa +A Soft Spoon +A Solo Slave +A Soul Rune +A SpaceFox +A Spaceman +A TRX +A Tonk +A Towel +A Tuxedo Guy +A U G +A V T U R A WILDR0NNY +A White Girl +A Wiki +A Wild Nolan +A Wise Sloth +A X Y S +A Yang +A Yep +A Zee +A Zeer +A Zoo Keeper +A Zuk +A Zuzakini +A aron +A bit north +A broke 126 +A capybara +A cclimation +A d e b y +A dam +A damm +A dammmm +A e d o +A e s i r +A f r a i d +A gg +A lba +A ll F +A llan +A llstar +A ltar +A m a nd a +A n d re +A nz +A rdy +A rmin +A rmoa +A rrow +A stoner guy +A to teh Jay +A ug +A y s a +A z k e n +A z t e r +A-Chronic +A-F-Kaveman +A123 A13X +A15 A1KMAN A1nz00alGown A1rhook +A2 A22Y +A47 Soldier +A543 A7MED +A84 +AAA GG AAAAAFK +AAAAAddy AAIGHT +AAsamba +AB Dash +AB84 ABEC +ABG Huntr ABILITEETTI +ABearCat ABlazys97 ABlueShirt +AC Dicing +AC TEMP IQ +AC0T +ACDC +ACatGirl uwu +AColdBeer AComp ACucumber ADACardano +ADAMB73 +ADHDerral +ADRlAN +AD_inc ADankTank +ADirtyDan ADrugTaker +AE2AW AEKDB AER0B +AER1410 AFCA +AFCA CBS +AFCA SCHOREM +AFGard AFJay +AFK Always +AFK Kay +AFK Liam +AFK SCO +AFK Spoon +AFK twigz +AFKJOHN +AFKaas +AFKarl +AFKevv AFKolby +AFKspiracy AFTW +AFX64 AFrickinLion +AGIT8 AGP3 AGRONOMlST +AGSpec +AGWA AGlassOfH2O AGorski AH Energy +AH64 Apache AHHSCHMEEEEE +AHK XD AHMETJACKSON AHarmlesKitn AHlager AHriMTaLeZ +AI Fighter +AI0 +AII 99 +AIchemyz +AIecko +AIeksib AIexmeister AIixs +AIlByMyself +AIm23ARDesti +AIways sm1le +AJ R +AJ Skills +AJ4J AJTracey AJ_24 +AJisHere93 AJsk +AK Alpha +AK Sam AK47 +AK4LF AK5C AKI-47 AKK1E +AKM +AKSpring AKinkyMonkey +AL-Trojans +AL3CX +AL7 +ALBlNO ALEXppresso ALLCAPS +ALLEGATlONS ALLTY4 ALSO +ALT Lurer ALargeMudkip ALevel115 +ALevel126 +ALittleLofty ALittleLogan ALonelyKurt ALonelySpoon ALonelyTaco ALv3Magikarp AMAWM +AMC Millions +AMD352 +AMDOSRSIRON2 AMGS65 AMRSY AMST +AN07HER AN0NYM0OOOUS AN1MAL1ST1SK ANDERDAPLUG +ANDR3 +ANDREVVTATE +ANDROlD 18 +ANGL0 SAX0N +ANGRY BL0KE +ANGRY N3RD ANIKV ANNA7AR ANNOYYEED ANON +ANRH ANUBlS +AOMA Doomfox +AOT GOAT +APC L000000L +APTZ27 +APrince AQ COCUGU AQMD +AQUAMARROCKZ +AR Brah +AR Fifteen AR15ONA AR1F +AR53 +ARCANO RS ARCHER ARCHIT3CTS ARESCREED23 ARNGCantrell ARRIVISTEZ ARRRot +ARTHURSHLBY ARTIZN +ARZ Ranger ARandyCat ASAP +ASAP Decky ASDQ ASH88 +ASIAN CHAD +ASIANBABIE ASMR AScrubIsHere +ASkillersAlt ASpacejunky ASuperDood ASuperior +AT-AT ATAR96 +ATEIROMU ATLx +ATR ATastyPastry +ATurboVirgin AU6URY +AUD1O AURl AUSLANDER AUSTlN +AUTOart AUTUMNELEGY +AUT_Alex +AUT_KoDeD +AV1 AVATARRR +AVE MARlA AVICll AVOCAD0O00OO AVlCII +AWW CRIKEY +AX0L0TL +AXL gun ROSE +AYRI DMM AZER AZNPanda +A_G_4 A_Wuh AaaBiiCee Aaalmost Aadalyn +AadreNaline +Aahatto +AakieSnaakie +Aaking AangTurambar AardbeiSoep +Aardwormpie Aaren Aargh Aarhus +Aarmir Aaro236 Aaron +Aaron 23 +Aaron Btw +Aaron Quiz +Aaron Samuel Aaron11700 AaronJMLP AaronPVM Aarsworm +Aarti Aary Aaryn +AashUnce +Aastradsen +AatroxLife +Aauburn +AavikonGaara Aayraz +AbInito +Abaay Abacraumbii +Abaiz Abaker Abaqus +Abariel +Abbas Elect Abbey +Abbey Ruins +Abbo baker +Abbyscimmy +Abc easy as +Abdallah6 Abdirahman +Abe lnnkon +Abe numse AbeTheHobo +Abengers Abernasty AbiChillii Abide +Abido +Abigayyl Abismaalinen Abito Abiuro Ablazin Abnegation +Abnxy Abocrate Abolish +Abolish NFA Abool +Aboriginal +Aborts Abortuary +Aboss Abou3Fiddy Abov Above AboveAvgGoat AboveHonor +Abpor Abr19 +Abradolf +Abrafebre +Abrakadaddy Abrakadino19 Abree107 Abridgetolum Abrils Abritrage Abruaa +Abruzi AbsentPray Absol Absol-EX +Absolut1on +Absolute POS +Absolute St8 Absolutism +Absolver Absorpt Absoullutely +Abstand Abstergo +AbstractNay +Abstractable AbsurdGreek +Absurdly Gay Absynthial +Abu Chungus +Abu Salih +Abuk +Abulssoni Abuv Abyission AbysmalGrind +Abyss Laps +Abyss Whip AbyssFreaks AbyssWalkerr AbyssaI Abyssal +Abyssal Roar +Abyssal Sire +Abyssal Wook AbyssalEmu AbyssalTrout AbyssalWrath +Abysse +Abyzz Acady Acai +Acai Berries +Acamarine +Acan D Acardi AccessPoint +Acciainoli Accio +Accio Bond AccioHorcrux +Accolades +Accountant 0 Accursio +Ace Kills +Ace12209 +Ace123445678 Ace2cool AceAlexander AceArcaneon AceOfSevens +AceTenSuited AceTookUOut Acefalcon3 Acekicker +Acemachine Acemanjam Acenr Acer Aces +Aces x3 +Acesuke +Acesupersoak +Acetazolamid +Acha9 Achaeos Ache +Achenar Achernar Achievement Achievements +Achilles Low +Achilles XXI AchillesFist Acholight Achuu11 Acid +Acid Cat420 +Acid Christ +Acid Dab +Acid Frick +Acid II +Acid Lunatic +Acid Magick +Acid Reducer +AcidBxbies +AcidHouse Acide Acidic Acidylia Acir +Ack Varmland Acke +AckeSSBM Ackman67 Acko Aclu +Aclyss Acolytes +Acorn Potat AcornHunter1 +Acoustics +Acquila Acquilar +Act Ov Vodka Actaeor +Actan Action +Action Dwarf +Action Movie Active +Active Whale Active-Bombi ActiveRecord +ActivelySent Activeshot Activis +Actor Actt Actual +Actual Bald +Actual Yak ActualName Actually Actw Acuila +Acumen OSRS AcuraIntegra AcuteSloth +Ad Pulvurum Ad0pted Adachigahara Adada62 +Adalman Adam +Adam pd1 Adam8 AdamBonar +AdamInAtl +AdamPwnz +AdamTehMong Adamant Adamanta +Adamantoise Adamchrisp Adammmmmmm +Adamp1 Adampewpew +AdamsMain +Adamskieh Adamy Adaptations +Add Jisuke Addderall +AdderLord Adderall Addi +Addicted Tom +Addicted2Rs +AddictedIM +AddictedSoul +Addlero Addycusfinch Addytt1 +Adee3 Adeeb Adelaw +Adele Adelier +Adeliya +Ademon66 +Adenocard Adept +Adept Brick +Adeptus W40k Adeq Adeus AdeyP Adgoar +Adhd Adam Adhurim +Adi Laser Adic Adiieu Adil Adin +Adios Punani +Adiosk8r AdjacentAce +Adjudicatior Adjusted Adjustmyfan +Adkille +Adley Admetis Admirable Admiral +Admiral Addy +Admiral Kev +Admiral Nano Ado12322 Adogg0323 Adolari @@ -769,46 +1995,85 @@ AdonisUkko Adorak Adorations Adore +Adornare +Adr Iron Adrean Adrian AdrianMMO Adriatik Adriatik7 +Adrik Adriyxs +Adroid +Adskip Adss Adstar +Adster98 Aduadu Adust +Adusy +Advance2Max +Advanced Rat +AdvancedOwen +Adventure On AdversY +Advvil +Adwan +Adyn1 Adz92 Adzyy +Adzz +Ae rith Aeariyion +Aeckersss +Aecyra Aedon Aedreius Aeganor Aegislash Aegon Aeikora +Aejayem Aelin +AelitenRS Aelos +Aemil +Aent Aeon AeonsOG +Aequites +Aerack +Aerdont +Aergius +Aeric Shun +Aerith 68 +Aerivas Aerj Aero +Aeroaxe +Aerocity Aerohead50 Aerolustly +Aeromi +Aerosol AeroxAces Aerros +Aeryen Aerzo Aestana Aesthetiix Aeterni +Aetharan +Aetharyn AetherEra Aethyrs Aeugh +Aev Aeyriex Aezolix +Aff it Affaires +Affan Khan Affect Affectie Affidra @@ -817,95 +2082,191 @@ Affirmed AfghanisDan Afghanistan Aficionado +Afk Blake +Afk Jasper +Afk N Scape +Afk Ray +Afk Scape +Afk To Smoke +Afk em +Afk is EZ AfkBandos AfkGod +AfkMachine +AfkTbh Afked AfkforGainz Afkillz Afkin +Afkoelen Afkstar Afkwarrio6 Aflamed +Afnacho1 +Afonso Afperser Afrecan AfricaSavior Africagamer1 AfricanMelon Afrie +Afrikka +Afriquee Afro +Afro Deagle +AfroFishh +AfroShay Afrojofe +Afroman +Afromann Afromouse87 Afrothunder Aft3rmath +After Dark +After3Scoops +AfterLike Afterlife Aftermatter +Aftermax Aftex Aftyr Afx31 Ag0b Ag3nte +AgagaWeeWee +Aganis +Agaperik Agatsuma +Age Forever +Age of Hell Aged Agenda21 Agent +Agent 3 +Agent Jass +Agent Slidt Agent805 Agent94 AgentFluff2 AgentOrnox Agentbunny1 +AgenteGanso Agentlobster +Aggressox +Aghiestic67 AgiNagii Agil +Agil Tank Agile +Agile AI +Agile Rob +Agile Tom AgileAllMile AgileFlea53 AgileLlama AgileRj +Agilities Agility Agitare +Agmaur Agnar472 Agni Agnoscere Agnykai +Agoge Agressie +Agroni Agronox Agronyss +Ags G Maul +Agsungg +Agua Diablo Aguanator +Agv +AhaShakes Ahab Ahav Ahegao AhegaoShawty +AhegaoxDrool +Ahero Knitly AhiPoke +Ahjin Ahkscape Ahkward +Ahlaundoh Ahmishcyborg Ahol Ahoyhoy +Ahreams +Ahri babe +Ahriah +Ahrim Job420 +Ahrou Ahvexx +Ai Haibara +Ai activated +Ai u +AiMienOortje +AiNebesvaik AiXiao Aiai8 Aian +AiasIM +Aiayu Aid12100 Aidann +Aidens Iron Aidios +Aife Aigua +Aiii Guey Aiikis0 +Aila Aillwynd Aimbot77 Aimdur AimenForFun AimenForYou +Ain Zak +Aina Vapaa Aintos Ainu +Air Aaron +Air Bison +Air Cube +Air Hawkz +Air Nimbus +Air Up There +AirAction +AirRin +AirWindSurge Airborne +Aircendition Airexz Airforcin Airomatren +Aironmaen +Airorider1 +Airplanez +AirsickMold +Airskipper +Airstone Airstrike +Airstrikes Airwalk Airwipp +Aishi owo Aisyah +Aivery +Aiwe +Aixaa +Aixleft +Aiyaaaa Aiyzer +Aizor +Aizzz +Aj Blitz Ajamian Ajarn Ajaxius @@ -914,99 +2275,193 @@ Ajeh Ajeti Ajikk Ajinz +Ajt141 +Ajushox Ajuus +Ajx +Ak 4854 Ak5u +Aka God AkaSpecs +Akadian +Akagi +Akali Akalron +Akarema +AkaseAkari Akasha Akashy Akatsuki Akaza +Akduman +Akeno +Akhasa +Akhavan Akhotha Akiha +Akihiko Main Akilled Akimbo Gmaul +Akira28 Akirakiyomi +Akka Flakka Akkaido +Akkezander27 +Akkhai Akmore720 +Akn Magic Akogare Akolyta Akomatic +Akomatk Akowii AkraLaDragon Akropolis Akshan Aksu Aksu596 +AksuRS Aktii +Aktivaros +Aku Megami AkumaPenguin Akunte +Akushizu Akusti +Akustic +Akvarij Akyba Akyle +Al Go Rhythm +Al e x x +Al ex G +Al-Kimiya AlCaponee AlFromOhio +AlIigator AlMusallam95 Alabandus +Alabarce Aladfar Alak4zam +Alakazoom +Alakazzaror Alameda Alamittainen Alan Alan200414 +Alandar Alanowsky Alantiwa +Alasse +Alaurise +AlbaisBack +Albananaa Albanoi +Albatrossse +Albatrox +Albert 1888 +Albert Klett AlbertaDean +AlbertasRec Alberthorn +Alberto +Alberto P Albertttt AlbiBambi Albin0z +Albinos10 +AlboBloodZ Alboy +AlbusTheDood Albuun +Alc1 +Alcadeias Alcado Alcarnia Alcatres Alcatrez +Alceini Alcerathe +Alch O Holic AlchKids4Gp Alchaline AlchedMyLife AlchedMyM0M AlchedMyNuts AlchedYaNan +Alcheimers +Alchemical A +Alchemicc +Alchemisten +Alchemize AlchemstDefi +Alchemyst +Alchhorn +Alchulon +Alckx +AlcoSkillz +Alcond Alcool Alcoz Aldastro Aldecaldos AldenolcyCB +Aldi Knight +Aldit0r3 Aldol +Alduiin +Aldwin Mose +Alec +Alec-kun +Aleck_l2 Aleffy Alegrete +AleizzleX +Alekja Aleks Aleksi Aleksib +Aleliumbis Alem Alemao Alenac Aleous Alerio Alessan +AletosCR Alex +Alex Blaze +Alex Cartman +Alex Grey +Alex IV +Alex Meret +Alex Power3 +Alex V2 +Alex Welshy +Alex Zander +Alex from IT +Alex the dad Alex12161 Alex3 +AlexDirty AlexHill AlexODaGreat +AlexSpkt +AlexV Alexa Alexander0k +Alexandre M Alexbrave2 Alexbrock +Alexis2Pro Alexmeister Alexnchill +Alexownsmatt Alexr0 Alexx Alexx2G +Alexxandraa AlexxisTexas Alexzilla Alf11 @@ -1015,51 +2470,95 @@ AlfaQ Alfahane Alfahanne Alfication +Alfie 96 Alfonga +Alfonso AlfonsoKhan +AlfredTB +Alfrid +Alfuh +Algiebeer Alharbi Alhyrr +Ali Shuffle +AliThePvmer +Alibaba Alicander Alicc Alice +Alice x +AliceOMalice +Alicent Alicezero1 +Alicola Alien AlienAntFarm AlienTTmilk +AlienVoucher Alienmage +Aliens +Aliii33 Alijr AliksOh Alioman +AlistarTb21 +Aliste +Alive Bozo +Alive Seb +Alive Vein +Alive18 AlivenHappy Alixz +Aljase Alkan Alkene +Alki Holic Alkkor Alkoholismi Alkymo +Alkyne +All Blue +All Darn Day All Fore One +All Frosty +All Peachy +All There +All a Bone AllRubyMoi +AllThePain AllTimeNo AllYourBase +AllaBarri Allabaster Allan +Allan man +Allards Allcreation Allday Alldays +Alldogg AllegedTheif +AllegedTree Allen Allerb +AllesPaletti Alleviate +AllexWx +Allext Alligaattori Alligamanor Alligood Allisun Allkune +Allky AlllySpawn +Allmethyst Allo Allomantic Allon +Allonsmoke Allowe +Allowed to Allscreen4 Allsen AllstarTb21 @@ -1070,54 +2569,105 @@ Ally Alma Almahh Almighty +Almighty2277 AlmightyMilo +AlmondJoy AlmostHadMee +AlmostSavvy Almost_124 Almosttheir Alno Aloha Alohas +Aloise Aloittelija Alondraj Alone +Alone CRNA +Alone RS AloneHeWalks +AlonelyPlace Along +Alonzo520 +Alonzo52O +Aloqab Alosthawaiin Alov Alow +Aloxc +Aloysius Alozaps Alpaca +Alpaca B0ng +Alpaca Lips AlpacaMyBags +AlpacaMyBowl +Alpacaliptic +Alpedocles Alpha +Alpha Cygni AlphaCoderXI AlphaFeeb AlphaMathic +AlphaSeraph +Alpha_Scott Alpharma Alphonse Alphray AlphusBrah +Alpine Climb Alpinetree +Alpoopi +Alppiruusu Alprazoland Already Alright +Alright Mom +AlrightBucko Alrite Alry Alsatian +Alsatians Also +Also Jack +Also Luci +Also Lucky +Also Nexxtar +Also Noexi +Also Schlak +Also kawkky +AlsoAlso +AlsoRabbit Alst +Alston +Alt Ctrl +Alt G boys +Alt Juana +Alt Redshok +Alt prepot +Alt3rbridg3 AltOclock AltSkillsDoe Altairre Altamonte Altchilly100 +Altcliffe +Altdunk +Altemeier Altenan Alter Alteraga Alternate +AlternateArt Alternatee +Alterones +Althamen Althornson Altifueled +AltimateRalf Alting +Altkandos +Altnik Altonorin Altruistic Altus @@ -1126,154 +2676,305 @@ Alucks Aluspalvelu Alvaaro Alvarreth +Alveoli Alvislt +Alvx Always +Always Alex +Always Awake +Always Harm +Always Late0 +AlwaysByrnes +AlwaysDNS AlwaysLost AlwaysPoor AlwaysQuest +AlwaysSunny +Alwayscuffed AlwayzJarvin AlwayzSol Alwex123321 +Alwin S Alxs +Alyks +Alysanne +Alyv Alzad +Alzuraak Alzz +Am eer +Am ity +Am ln Danger AmCatWhatDo +Ama zon +AmaAvenger Amadeux Amaiya Amak Amalie Amanda +Amanda Btw Amandelbrod Amarantine +Amarok Moon +Amasclit +Amateur Amathyn Amatsukaze Amatus Amazarashi +Amazaro Amazing +Amazon Power +Amazonia Amb3rLeaf AmberTheCat Ambik Ambion +Ambipom Ambisagrus +Ambition IRL +Ambition98 +Ambitioned Ambrose34 Amcaroz AmcyC37 +Amd27 +Ame Hara AmeJoyRock +Amean +Ameero7 Amel +Amelia Beth Amen +Amend Amenity +Ameno l +Americlaps +AmiableDingo +Amibis Amida Amigeau Amigo +Amigos +Amillie +Amin +Amir Psycho Amis Amitwin +Amityz AmixBeast +Amjad Amjar +Amka0s +Amkings Amlodipinee +Ammastian +Amnesiia +Amnios AmoMeuFilho +Amoei +Amon Amarth +Among Dead +AmongUs R34 +Amongst It +Amordrise Amos1500 Amper +Amperes Law +Ampiainen +Ampilify Ample +Amplify Ampura +Amsclarke +Amsterdamage Amty +Amused Fonty +Amuzements +Amuzzr Amuzzzr +Amy Sonck AmyMacdonald Amygdala Amylit Amyshaw123 +An Amputee +An Animal +An Ebony BBW +An Old Chum +An epic OAP An old Gnome +An0mandaris An0niminis +An6ers AnActualHorn AnAmerican +AnEnginerd AnEpicWookie +AnEskimo AnExperiment +AnEyeOhLate AnOldTank +Ana D Armas AnaWynn Anabella28 Anabolic Anabolic h1t +Anabowlen +AnaklusmosAN Anao Anaphylaxiss Anarchy +Anarchy870 AnarchyOS Anari AnataNoWaifu Anbu +Anbu_Mobb +AncIrew Ancalagon Ancastry AnchorMann Ancient +Ancient Fury +Ancient fir +Ancient hil +Ancient soul AncientFreak AncientLamb6 AncientMagus AncientOath +AncientP Ancientgh0st Anciento +Ancientz pal Ancitif +Ancona +Ancow +And e +And r ew AndSo +AndYetISmile Andardenn +Ander5 Anderave +Andericus Anders375 AndersenXo Anderssen Andhra AndiCandy AndiVT +Andiamo98 Andipyrus Andirath +Anditre +Andj Andoyen +Andrade149 +AndreasBL5 Andremagico7 AndresVZLA +Andrethyst Andrew +Andrew Marr +Andrew OG AndrewWigins Andrewjaay AndrewsMeat Andrezee Andrezz Andri02 +Andrian Andrick Andrishh +Andrius X +AndriusBykas Androgenic +Android Iron AndroidFox Andromed1k +Andsaca +Andx Andy +Andy Candy +Andy Ops +Andy Salrem +Andy btw Andy2 Andy2511 +Andy43229 AndyG223 +AndyIsComing +AndyL AndyMcbob4 +Andydiaz Andyfromvent Andyn9 Andyooi Andyvns +Anele Boy +Aneluy AnemicIzzy Anesics Anetha Aneurin +Anez +Ang3li Angalad Angel +Angel Advise +Angel Allie +Angel Gabe +Angel Martyr Angel Vlad Angel4killin Angel5 +AngelOfZeal AngelOrHour +Angelic Lust +AngelicOrb AngelicVixen +Angelisimo +Angell SL AngelsCrys Anger +Angerfish208 +Anghelic One +Angie Bos +Angl 0f War Anglerstein AngloSamson +Angoose Angry +Angry Bison1 +Angry Child +Angry Thumb AngryHusky24 AngryMvP +AngryNachos AngryPickle AngryScape AngryWizard +Angsti AngusM +AnhTaDuong +Anhedoniaa +Anid +Anida Hanjab Anidien +Anieli Aniheyu +Anim Anima Animal +Animal feces +Animalia Animation Animations Anime +Anime Nips AnimeExpert AnimeMilkers AnimeWaifus @@ -1281,51 +2982,95 @@ Animorph Animos Animosity Anion +Aniril x +Anita Dong +Anita M Wynn AniviaKush +Aniwave Anja +Anja Rubik AnjaPija Anjigami +Anjru +AnkaraAisa Ankedia +Ankka51 Ankn +Ankou btw AnkouClothes Ankougnu +Ankourule243 Anlaku Anlex Anmah Anmokyu +Anna Huikka +Anna Liebert +Anna Planks Anna Rosanna +Annavvaa +Anne Puppy +AnneWB Annero +Annhilate0 Annihilated +Annihilation Annihilative +Annna Puu +Annoys Anoie AnomalousFox +Anon E Maus AnonDoctore Anonim +Anons Anonymiity +Anonymous aF +Anonymousse +Anoobass1 Anooge Anoomliebird Anor +Anotha Skar Another +Another Name AnotherCsTa AnotherDoor AnotherGamer AnotherName +AnotherPb +Anouar +Anoubis Ansaittu +Anscombe +AnselAdams Ansoil +Ansovs Ansuz +Ant M8 +Ant a AntRIP +Antartsant12 Antast Ante Antee294 Antelope123 +Antex +Anth x Anth52 Anthiex Anthilll Anthony +Anthony Btw +Anthony I Anthony5002 Anthoons +Anthooony Anthorak Anti +Anti Drop +Anti PK G0D +Anti002 AntiFuh AntiVaxMothr Antidope @@ -1333,59 +3078,102 @@ Antifa4figs Antifire Antiflag42 Antiguy24 +Antinyte Antipathic Antipixel AntiqueRS Antiqxx +Antireflex Antis Antiserum420 Antixan Antiyano +Antlers +Antmix Anto Anton +Anton T Antonio Antonn Antony +Antony1202 Antony142pay Antoun +Antracid +Antron Antstolis Anttila Antweezy +Antz Xii +AnuBi 1337 Anub1s Anubis +Anubis ex +AnubisT3 +Anuj Anul AnullSecs Anv1k Anve Anxi Anxiety +AnxietyBTW Anxious +Anxious Mess AnxiousDoggy AnyDrops AnyLesS +AnyQuestions +AnyScrolls Anze +AnzheliK Anzie Anzied Anzu +Anzypoo +AoA Jammer AobaJohsai +Aog +Aoh +Aohc +Aohu +Aoi Tetsu Aokijahr Aokijii AonEne Aonyx AoooA +Aorpheat +Aosrs Aowi +Aozaa +Ap0logy Ap0phis Ap3x Ap870 +ApRoMn Apaat +Apaatje Apache +Apache30 +Aparlo ApartAbyss Apathetik +ApdoJ +Apdomine +Ape Cape +Ape Dos Mil ApeAtoll Apetamer +ApetimusPrym Apex +Apex Pro +Apex-scaper +ApexGT +Apexflashy Apexist Apgujeong +Apherna Aphex Aphroditeee Apimer @@ -1394,74 +3182,136 @@ Apldale Apm90x Apoc Apoc142 +Apocriya Apocryphe +Apolliox +Apollo Beach +Apollo13th +Apolytos Apop +Apophiss Apor +Apostasie Apotoxin App3lflap AppaYipYip +Apparat Apparitionn +Appeasive +Appel Farm Applauder Apple +Apple A Day +Apple Mart +AppleBluue Appleboss +Applecus +Applejuiceaj Applemaxx Applev2 Applied +Applul Apportant +Apprenti1 +ApprenticeRS Approval ApprovuL AppySauce4 +Apqle Aprella April April 8 1997 +April twenty Aproximity +Apsa Larr Apteryx +Apteryx luna +ApuHapu Apul Apullu +Apyon Aqew +Aqizu Aqtive AquaUnit Aquah Aquaileo +Aquajd Aqualetics Aquamation +Aquamentus9 Aquascaped +Aquaticbob Aquatik Aquellex +Aqueueser +AquiIa Aquilo +Aqva Babe +Ar ex +ArA100 +ArKadeeee +Arab Frieza Arabian Arada Araet +Aragonx Aragornmv +Aragornous +Aramaki Araman +Aramex Arandae Aranna Aranruth +Araq Araqon Arasex Arashikato Arastaiel Arathir Araur +Aravt Araysen Arazz Arb4n +Arbator +Arbenn Arbin Arbyy +Arc1s +Arcade firee +Arcadian96 Arcane +Arcane Grima +Arcane Queen +ArcaneApollo ArcaneBear Arcanezeus Arcangel +Arcangel IX Arcanic +Arcanox Sad +Arcanum XIII Arcas3 +Arced RS Arceneus Arch +Arch RS +Arch er Arch3nLight +ArchFarmer ArchX Archaea582 Archahl +Archaic Wolf Archaiden199 Archanjel +Archas +Archavia +ArchbishopC +Archduke Btw +Archelon Archer ArcherYew Archie @@ -1473,6 +3323,9 @@ ArckenSphere Arclight Arcnan Arctic +Arctic Roach +Arctic Vaggy +ArcticCamel ArcticMank ArcticTitan Arcticas @@ -1482,8 +3335,16 @@ Arda Ardaddy Ardames ArdenDZY +ArdestRange +Ardilos +Ardougn e +Arduino69 ArdumTheMain +Ardumm Ardy +Are Dry +Are ng +Are you well AreYouMyMumm AreYouPepega Area @@ -1491,12 +3352,17 @@ Areimer0606 Arekusei Arena ArenaRebuild +Arend Nest +AreolaArnold Areolas Areoloth +Areow Ares +Ares 3060 Ares1701 AresArmy AresKnight +Arescoth Aretx Arezrazer Arfex @@ -1505,282 +3371,534 @@ Argawan Argent Arghoslent Argilla +Argo Vesta +Argonuts +Argory +Argov +Argyle Sock +Argyle Socks ArgyleGrandt Arhedel +Ari AriSlash Aria +ArianDRZ Ariana +Arias star +Ariazel Arid ArieMoon AriesWarrior +ArieyaeIron +Arifureta Arigorn Arigorn55 Arihant Arillian Aris +Arisen I +Aristo +Aristokratas Arisu +Arith1um Arithe Arizotal Arjoon Ark9tog +ArkAngel029 +Arka91 +Arkaniz +Arkantos51 +Arkaros Arkavos +Arkaynine Arkeela +Arkells +Arkem +Arkemir +Arkend Arketryx Arkevin +Arkhero +Arkizah +Arkki +Arkonka1 +Arkonknight Arkvasal Arkysh Arlind ArloTheBrave Arlorict Arlyeaxn +Arlyse +Arm Chair +Arm Guy +Arma Frost Armadexx Armado Armadomin +Armadyl king +ArmadylPker Armadyllo Armament +Armanyte Armapickle +Armas Chosen Armchairs Armerak +Arminius Sol ArmisticeDay Armo ArmoBlood +ArmoMike Armoah +Armondega +ArmorHelmet3 +ArmorSeeds Armourer Armsdray +Armweak55 Armysam Arna +Arndas Arnear +Arnhems Arnie Arnold +Arnor +Arnthor Arntj Arobpl Arod Arodaz Aromipesa Aron +Aronya Aroorin Arousen +Arousin +Aroyu Arpang Arpegio Arptacular Arr n Geesus Arrandarls +Arrasca +Arrazha +Arre10 Arrizu +ArrowHaste ArrowTank Arrowmind +Arrows Myth +Arrowtower +Ars Nova Arsaydar Arsenals +Arsene rar Arsenic +ArsenicEyes +Arsenn Arshal +Arsi Arslen Arson ArsonScape +Arst +Arstan Selmy Art Dayne +Art Star +ArtIsHard ArtStylerzz Artces +Artegal Artenmis Artesna +Artezor +Artful Dodgr +Arthaniel Arthesus Arthropods Arthur Arthven +Artic0 +Articuno1991 +Artidote Artillion1 Artipeng Artisaani +Arto Lauri ArtofKush ArtoriaSiff Artoriias28 +Arts +Artu12v Arturis Artychurro Artz +Aru Arumen Arunas Arusas +Aruwyn_Xi Arve +Arvinthir Arviragus +Arvns +Arvsta Won +Arvx +Arwe +Arwon Arxanec Arya Aryabhata Aryuts +Aryzha +Arzemnieks Arzi +Arzna AsalentBlaze Asami +Asami Sato Asap +AsapLamb Asbi Asbloodfalls Asbur +Ascadex Ascanliss Ascended +Aschen +Ascii You Asda +Asdasd107 +Asdewq +Ase Aseae Ased Asfixiator +Ash K +Ash Solo +Ash Wreckum +Ash l AshBash AshEvillDead +AshKetchum00 AshLad94 +Ashabee Ashandarei0 +Ashcroft Ashealia Ashen +Ashen Bride +Ashen Heart +Ashen One AshenSughar +AsherMentuI Ashes Ashes630 Asheviere +Asheville +Ashhh Ashieboyyy Ashikaru38 Ashington +Ashkelmek Ashkii +Ashkyn Ashleigh Ashley AshleyNZ +Ashleyi Ashlyce +Ashmeow Ashn Ashoo Ashsole Ashton +Ashura Bakke Ashwin09 +Ashy joey +AshyKneecapz Ashyy Asian +AsianGrinder +Asics +Ask Oziach AskMeDolCare Askraf Askumi AslanMaximus Aslywyn +Asmir9990 Asorf Asos +Asot +Aspen +Asper Aspestia Asphyi +Asphyi Xate +Ass Balm +Ass Clams +Ass Drill +Ass Rocker +Ass locator +AssClapping +AssMcButt Assasindie73 +Assasintoad Assassinado Assaulted +Asshats R Us +Assktchum +Asstain Ast 360 Astan +Astaro +Astartes +AstartesXIII AsterSG Asterlad Asterlyte Asterus Astiminate Astoia +Aston Fartin AstraDan93 Astraea +Astral Queen +Astral laws Astrali +Astrals +Astray +Astrayus Astro +Astro Dust +Astro O7 +AstroKP AstroXgK AstroYogurt +Astroflare +Astronaut Al +AstrooWRLD Astrophe Astrostone +Astylez Asubu +AsuhDude Asuka AsukaLangely +AsukaYenOSRS Asukas Asumaton Asumistuki Asuna Asunnn Asura +Asus bl +AsylumTRAV Asymmetry Asyn +Asynergy Aszalem +Aszard +Aszension At0mic +Atacrite +Atar Ataraxia +Ateo +AthIetic Athaw +Athe na Athedeia Athena +AthenaOfOsrs +Athenaloki +Athi Athire +Athius Atika Atilio +Atin +Ativan IM Atlamillian Atlantic AtlasErol +AtlasUsurper Atlashi +Atleast Atli567 Atmosfare +Ato m +Atolla Atolli Atom Atomic AtomicFlux +Atomicdomb2 +Atomikaust Atomsk Atomyze Atonic Atorvastatin Atouk +Atoxicary Atoz +Atriades +AtriummDream Atrixas +Atro phy Atroo Atroph +AttackMoons Attard +AttemptedUwU Attic +Attic Ghost +Attic Witch Attics Attikos Atton +Attuned Atuking +Atvinnulaus +Atylos +Atza +Au 0srs +Au 1 +Au Bar +Au Barrett +Au Joel +Au Shooty Au92 AuRoyce AuStarZ Aubrey +Aubrey UIM +AubsG +Auburn Lax Aubzzz Auclaw Auctionable +Aud AudacityOP +Auddax Audi +Audi Nurse +Audi Reach +Audo +Audronan Audtnec Audylinks Augment AugstBrnsRed Augurk +Augury Tank August AugustAmes Augustiner16 +Augutis Auhdy Auja3 AuksoAlt Aulono +Aumoki +Aunonen +Aunt Wu Auntie +Auntie Mabel AuraBeast AuraSissari +Aurah +Aurelia AurelionROW AurelionVM Aurelya Aureou +Aurgodi AuriZed Auriana Aurionx +Aurizon Aurorin +Aurrok Aurtle AurumFoxfire +Auryn +Aus +Aus 2277 +Aus Shizam +AusBruto +AusGram Ause +AusieBumpkin Ausripper Aussie +Aussie Jay +Aussie Luke +AussieKunt +AussieOandE AussieSparta Aussome +Aussybear +Aust Terps Austie Austiiizy Austin +Austin Ames +Austin is 1 Austin296 +Austiz +AustlnPowers Australia +Australians Australiaz AustrianOak +Austs Austyn Autchin +Authorized +Authorizer Autickstic Autilon Autism AutismeKnaap Autist Autistic +AutisticAndy +Auto Tuned +AutoBonsai AutoEmp +AutoLoot EXE +Autoacid AutomailArm Automonteur Autophagy +Autorotate +Auts Man Autumn AutumnKevegy Autzerk Auuustin Auuuv AuzReaper13 +Auzium +Av o +Av3z Str +AvC OSRS +Ava End Avaetar +Avaigo Avatar +Avatar Evan +Avatar Magni +AvatarBean69 AvatarShakur Avedon +Aveng +Avengerous Aventy Aver4ge Average @@ -1792,7 +3910,14 @@ Avernic Avero Avertehs Avest09 +AvestGIM Avex +Avexaon +Avfc Holte +Avg Tom +AvgMG +AvgScape +Aviaatar Avian AvidTech Aviiation @@ -1801,85 +3926,154 @@ Avinosis Avir Aviron Avlek +Avocatocat Avoidable Avoinmieli +Avonak Avondale +Avouch Avrahm Avsendesora Avysto +Avze +Awadi69 Awaiting +Awaiting 126 Awaits Awaiyume +Awakened Olm Awarde +AweJeez Awendana Awesome +AwesomeBros AwesomeJared Awezm Awful +Awful Name +Awfully +Awh DB +Awh Nuggets Awies Awilkins4231 +Awkquavian +Awkventurer3 Awkward Awowegei +Awowo Gay +Awry +Awtangg AwwSeriously Awwdiddums Awwtis +Awyr +Ax3 +Axayre Axdrew +Axely +AxemPink Axeo +Axhers Axies Axillaa AximusMax Axiom +Axlerod +Axleson +Axodyi +Axolotl +Axsentz Axton Axylix +Ay Monk +Ay Ron Man Ay3m AyAyRon AyB33 +AyJae +Ayafuyuzu AyameDespair +Ayatan +Aybills +Ayd +Aye Sip AyeBaddaBing +AyeePizza Ayeeet +Ayema IM +Ayenull Ayetg Ayfi Aylia +Aylward Ayman +Ayo4Yayo AyoSteeze Ayoe +Ayonikz Ayra Ayri +Ayrton +Aysuu Ayveros +Ayy Nakana +Ayy bae bae +Ayy q p Ayyye Ayyyon Ayzo Az PVM +Aza zel Azad +Azad Kashmir Azala Azami +Azandari Azathor Azathoth26 +Azathrir +Azazel OSRS +Azeenil Azemu Azerma Azez Azimut Azimuthaal Aziz +Aziz7 Azkaton Azken Azkybot Azlo Azmataz +Aznbabe Aznmixboy Azom AzorAhaiBTW Azotize Azoxy +Azpa +AzraelReaper Azriiele Azryel +Azs +Azshara +AztecLT +Aztra Azula +Azula Whip +Azur Lane Azur_I Azure Azure23782 AzureSpirit +Azuredadi Azuremadi +Azuretiger Azuuure +Azynn +Azyrith +Azza Bear Azzaar Azzaboy100 Azzakel @@ -1888,100 +4082,257 @@ Azzanadraa Azzax Azzgarnia Azzz +B 0 B S +B 0 S S +B 0 U J E E +B 3R +B 58 +B A F F E Y +B A MF +B A U W S +B B Bouncy +B C 2 +B C M +B CA +B D Y +B E E G +B E R T A +B E R t I 3 +B ELA +B Gates +B Gonkeybron +B I G G L E +B I R T +B Ian +B J O R N +B L 4 N K +B L A D E Z +B L A N E +B L A Y E D +B L ANK +B L I X T +B Milesy B Minor +B O G S +B O O T H +B O Y E A +B R A l N +B R II A N +B R l C E +B S +B SlDE +B T J B Victor B +B W 1 +B W A K +B aylor +B aze +B ecky +B eeez +B iggles +B la z e +B las +B r a g g +B r eezy +B rads +B raes i +B ruised Lee +B u bs +B ug +B un +B utt +B w +B-0-SS-M-A-N +B-Dollaz B-Lal +B00 KAW KEY B00Lici0us +B00MERAT3R B00ST B00SY +B00tlicker B0ARD +B0B The Cat B0BaH B0GLA B0ND +B0NERCRUSHER +B0NGRIPS42O B0OP B0aty B0atyquila +B0bz BurGerz +B0dka +B0n3z84 +B0nes2Weed B0rders B0rn B0rn2bking B0rnas +B0tched B14cksh4d0w +B1G BAD CHAD B1G8 +B1GARMS +B1GBOB +B1GGY B1G_D1ESEL +B1G_SW1GS B1RDUP +B1Te Me +B1ack Lotus B1ackJack B1asphemy B1gTTgothGF +B1gd0wg B1ink +B1mboBambi B1zrme B2AD +B2F +B2J3 B2bZeroSpecs +B2rad +B3A5T +B3CK B3NJAMOON +B3NT D0ver +B3TTERLUCK +B3Y0ND B3njamima +B3njamin +B3nny Blanco +B46 +B4RR +B4S B4llista +B6a +B6i +B70 B747B +B8 H8ER +BACKURBOI +BACONisgd4me BADASS BADDONE BADPanda2 BADUPDATES BAKED +BALD n DUMB BAMF BAMFnificent BARBARlANS BARRAGENOW BARRON24 +BASED DMM +BASEDQwert BATATUNGA BATH0RY BAYLlFE +BBBBBONKA BBGL +BBK BBKwenie +BBL RIZZY +BBQs +BBW Pizza BBarnzeyy +BBeepLettuce +BBoys Father BCBDestroyer +BCC YT BCDOJRP +BCK CigButts +BCMagoo BDCMatt BDrichard +BEANS9991119 BEARDED +BEAST ARCHER BEATONiT +BEBELAC28 +BEEA BEG0N BEG0NE BEINGMAXDSUX +BELSJ BEMBOU BEST +BET5 +BEYFYBOI +BFKay BHAFC +BIG ASHL3Y +BIG C0X LUVR +BIG HIT GOOD +BIG pontifex BIGBADWILLY +BIGD05 BIGDROPHUNTR +BIGFERGLOAD BIGHEADSMASH BIGJP BIGMINK +BIGSITUATI0N BIKlIllIlIIE +BILR0Y +BIOATED +BIRD SHlT BIRP BIRWAAA +BIS Monster BIaded BIadet BIapa +BIood Prince +BJ Applegate +BJ M +BJPlaysRS +BKBB +BKK BKLN +BL00DANG3L BL33DPL34S3 +BL3SS3D4LIFE +BLACK DOG NI BLASE BLGesus BLICKYSTIFFI +BLOBi WRX BLOOD BLUEPIZZAMAN BLXCKPINK +BLYATMOTOR BLlNKY +BMEUR +BMooldijk BMrgn BO0MERSO0NER +BOA Liger BOBGIGALUCKY BOBYAHMDGECI +BOER KAMIEL +BOGOBoneless +BOL0CKS BOLSOONARO BOMBAKLAP +BOMBU +BONITA 02 BOOPlNG BOOTTYBANDIT +BORDEN DAIRY BOSNIAQUE BOSS +BOSS OG +BOSSMANHOG BOlNEXTDOOR +BPNS Hawk +BPTheIronOne BPrimitive +BR0 BRO BR00KES BR0CE +BR0ER BR0THERS BR3AKABLE BRA71L @@ -1990,16 +4341,34 @@ BRAMMOSOVIC BRANDONSMlTH BRCH4 BREJCHA +BRUTAL BRabbit25 +BRlOCHE +BSB Howie BSNR +BST Steve +BSickler BSven-B BTCEUR +BTKMGJKL +BTMillz +BTW Beirre +BTW idgaf +BTW vs RNG BTWSaberlion +BTWsanta +BU1MER +BUHLL BUILD BUILD PONYS +BULLlSH BUNDHA BUNStheGOD +BUR13D AL1VE +BURYT0MORROW +BUSS IT BUTTB0NERED +BUTTCOlN BV Martyr BWheatZ BYEq @@ -2008,101 +4377,219 @@ Ba1i Ba5b0y Ba6y Ba77erY +Baa m +Baaaats +Baal157 BaalZvuv Baals Baalthas Baamf Baami +Baandos +Baarb Lahey Baas Baba +Baba Booey9 +Baba Borgar +BabaLovesKos +BabaSlaaf +BabaSleep BabaTarzan Babafasa +Babayaga-9 +Babayaga2121 +Baberrandrid +Babica Mraz Babiez +Babocat Baboosh +Babs221 BabsCox Baby +Baby Bear +Baby Bobby +Baby Boeing +Baby Daphne +Baby Houdini +Baby Motel +Baby1400 BabyB BabyDogeCoin BabyDogeFTW BabyGroot BabyHat +BabyHewy BabyHiggy BabyJIREN +BabyLinga BabyPandaa3 BabySas BabyTamer Babycerasaux Babydump +BabyfaceBill Babyjoker828 Bac0n BaccaM Bachelour Bachuras17 +Bachus67 Back +Back Jauer +Back Seater Back2Backk Back2ourdays BackInTime +BackToBambi +BackToOwn +BackelsOSRS Backinthe09RS Backlit +Backspace +Backsterr Backus Baco +Bacodie Bacoid1 Bacon +Bacon Glizzy +Bacon24717 BaconBlunts +Baconcannon +Baconist Baconlord100 Baconpancake Baconstrike Bacteria Bactomet Bacun +Bad Advice +Bad Ape +Bad Axe +Bad Day +Bad Fruits +Bad Kind +Bad Kuip +Bad Mario +Bad McNamey +Bad Mistake +Bad Name Now +Bad Robert +Bad Weather +Bad exp Bad2dabone30 BadAndB0ujee +BadAss_oO +BadChicken +BadChronic +BadDecision +BadIdeaDog BadIllusions +BadInIronMan +BadInNames +BadMamaJama +BadMannerz +BadSass +BadSpalling +Badaping Badass +Badass Fe +Badass Iron +Badass Rs +BadassIron +BadassPotato Badasseness Badaxx BaddieDragon Badgalriri +Badger Bush +BadgerInABag Badgerq97 Badical Badman +Badman ET +Badoodle Badora +Badora II +Baduk +Badvask +Bae-Kun8er Baeby Baekah +Bag it +Bag of Yay +BagFryLife Bagadigi +Bagan Bageera BagelSpanker Bagels Baggyshaw +Baghdad Baginga +Bagis +Bagokk Bags +Bags n Beer Bagsnacks +Bagz BahRitTanEe4 BahamutLimit Bahn Bahnzeen +Bahp +Bai Ningyang +Baib +Bail Jait Baileys Bailsby Bailz Baimonnnn +Baines +Baino BainoLad +Bainz Bairac +Baites +Baja Baja +Bajan Bajart Bajcolado +Bajela Bajere Bajhole +Bajista +Bajji Bajkami +Bajsanus Baju +Bak cornet +BakIron +Baka Mop +Baka Prase +BakaOsaka +BakaPho Bakazuro Baked +Baked Mage +Baked Saiyan BakedVandon Baked_Beagle Bakedd +Bakedmuffins BakeonBits +Baker Bully +Bakerloo +Bakers Baked Baketto +Bakhmut +Baki Hanma +Bakingsodah +Bakingsweets Bakkis Bakllava Bakron +Bakthawar Baku Bakuchiol Bakusho @@ -2110,49 +4597,100 @@ Bakuthedrunk BalIsackt1ts Baladend Balan +Balan Dinh Balar Balarezo +BalazinGIM +Balboa Park +Balbuli Bald +Bald Arab +Bald Male +Bald Shupa +Bald Sicknez +Bald To Bold +Bald Wookiee +Bald at 29 +BaldKnees +BaldMansKenn Baldfuc +BaldheadBill +Baldoc Baldorr +Baldrake Baldras Balerion Baliboosca Balkan +Balkenende Ball BallOfCotton Balla +Ballack342 +Ballade No 3 Ballcheck369 +Ballenaso Baller006 Ballerina Ballesekk +Ballet BallinBamf Ballotaa +Ballroom Bally +Ballztacular Ballzy Balmung311 Balneum Anas +Balob Balontra Balou +Balrogs Hell +Balthazar BL +Baltimohr +Baltimore MD Baltoth Baltramiejus +Baltzerboii Balu +Balu Titan +Baluk +Bam Achilles +Bam Grizz Bambi0326 BambiSlayer Bamboo +Bamboo James +BambooPandas +Bambuliuss Bamf BamfJoe Bamfbeav +Bamiman +Bamse Bamztile Banan Banefish Banerz +Banio +Bank Stander Bankaiz +Banki 1 1 Banzu +Bape +BapeNation +Bapelsin +BaphometsLov +Baptor Baqels +BarShake +BarSmash Baraek Barakoli +Baratheon +Barathrum BarbaariJuho +Barbatos Barbose Barbwiire Barbyte97 @@ -2160,60 +4698,105 @@ Barca Barca230492 Barcodes Barcoding +Barcy Bardimuss +BardleDooDoo Bare +Barely High +Barely Legal BarelyBusy BarelyDecent +Barfallonyou Barfs Barfy +Bargli Barhoor Bariviera49 +Bark sample Barkki BarnVo +Barncle Boy Barneey +Barnet Ftw Barney12370 +BarneyKing +Barneylover Barnez10 +Barnyard Boy Barnz BaroBro +Barometz Baron +Baron Iron +Baron Timmy BaronConey BaronGiraffe BaronVonGeeb +Baroondon Barqueefa +Barr +BarracuDro +Barraggah Barrako +Barrel Flop Barricade Barrowry Barry +Barry Koota +Barry0408 BarryBanaan BarsasBoom Barstukas Bart +Bart Heredit BartBelly BartSimps +BartenderBTW +Bartheez Bartholomeii Bartje +Bartol Bartycool Barzhal +Bas +Bas Waterpas Bas481 +Basant Base +Base 99s +Base Skills Based +Based Evan +Based Kyoko +Based Miyagi BasedGob BasedHeru BasedScape +Basement Dad +Baseplate86 Bash BashinBosses BashiraTHC +Bashram2 Bashx Basic +Basic Drop BasicBilbo BasicIronman Basil +Basil Hiiri Basileia +Basilikos12 Baska +Baskervilla Basket Basketballer Baskie Baskrans +Basmah +Bassaidai2 +Bassey +Basshu Fon Basshunter Bassilliaux Basstomouth @@ -2221,42 +4804,71 @@ BastasRemmen Bastermate Bastiao Bastidaz +Bastin101 Baszist +Bat Fastard +Bat Kills Bat418 +BatChatillon BatFat Batbot101 Bateau BatedUrGonna Batesanator Batgirl +Batin +Batistaisraw Batleris Batmain +Batmam Batman Batmanlul Bats BatsVsChina Batsborkair Batsmattie +BattDoode Batter +Batter Cake +Battle Jack Battousai +Battousai09 +Batu +Batussy +Batzch Batzz Bauchop +Baulsacky Bautista I +Bav Bavaria +Bawdlands Bawjaws Bawn +Bawwz DeadAf Bawz +Bax2 +Baxi 247 Baxxis +Bay +BaySeoul BayaniSenpai Bayern_pvm1 Bayes Baykr +Bayle Domon Bayleef180 Baylin Baylon +Baylon rs BaylyBoyBro Bayman +Bayou Bengal +Bayside +Baza +Baza NZ Bazilijus +Bazimga Bazmobile Bazoo415 Bazoomaster @@ -2265,45 +4877,96 @@ Bazukashrimp Bazz0r Bazzak BazzleB +BazzleBTW Bazzooi BbDontHurtMe +Bbase12 Bbaws Bboygainz +Bbubbsy +BcShane Bcgod +BchImaBus +BckOFFRTRD BckScratcher +Bcowzy Bdawg +Bdub187 +Be Aware +Be Rad +Be Strong +Be X Unlucky +Be as t +BeEzyMan2 BeFawn BeThankfulll BeTy BeaTee Beaan +Beak er +Beaken Beaky Beamesy77 +Beaming Beamo +Beams btw +Bean +Bean Bunn +Bean Slapper +Beanieus Beanis Beanisdead BeanozZ +Beanpot Beans1991 Bear +Bear Downn +Bear Finale +Bear IV +Bear Jake Bear Savage +Bear W Scarf Bear0nBeer Bear32 +BearByBlood BearByte +BearDown80 +BearGrillz BearImmunity Bearac +Beardboy Bearded +Bearded Hick +BeardedGummy +Beardedwick +Beardilizer +Bearing Down +Bearly Iron +Bearopenart +Bearrito42 BearsBeetsBG Bearsarefrie Bearsfan5455 Bearshot +Bearsome BearsyXL +Beary Beast +Beast Guard +Beast Things Beast1Range1 +Beasted Main Beastgriff +BeastieBoy31 Beastied +Beastley BeastlyGinge +BeastlyMuff Beastman642 +BeastyY69 Beat +Beat Tit +Beat my Wifi BeatboxRS Beatdown115 BeaterGod @@ -2311,34 +4974,73 @@ BeatmyJonson Beatrix Beatz Beau +Beau Ryan Beau1o Beaukaki +Beautifull Beaver +Beaver Booty +BeaverSweat +Beaverr +Beavers79 +Beavis 420 Beavis825 Bebbie Bebe BebeAtron Bebexx Bebo294 +BeboKAhnung Beckerr +Beckovas Beckyboo308 +Beckymikyu Become +Becoming Becton22 +Bed +BedByDaylite +BedForSale +Bedanc Bedeh +Beden Bedevil Bedni1 +BedtimeHERO +Bedwoods +Bee Butts +Bee Keep +BeebeM Beef +Beef Mince +Beef Squirt +BeefBottom Beefense Beefs Beefy +Beefy Potato +Beefy Treat +Beefycopter Beeg +Beeg splat +BeegeKhaos Beeghs +Beeks Beeky Beeline Beelzebubba Beelzebubby5 Been +Been Mobile +Been Poor +Beepern Beer +Beer Diet +Beer Is Cool +Beer Pong +Beer Tankard +Beer gut Jim +Beer n Blow BeerIsGood Beerchamp1 Beerfest @@ -2346,397 +5048,886 @@ Beerhana Beershits Beersmack Beerzinnss +Bees169 Beest Beestig Beeswakka BeetchAustin BeetchJeemmy Beetle +Beetus93 +BeeyondBeef BefJekyll Befkeuning +Before Snow BeforeDeth +Befsnor Ben +Begani +Begave +Beget Begin Beginning Beginnings +Beh +Behaarde lul Behemoth Behhnchod Beholder24 +Beibs Beidou +Beige Carpet +Beikengaut +Beikenost BeirH Beirreee +Beirut BekanTering +Bekfist +Bekk Bekkie Bekske Bekt Bekutma +Bel Lando +Belaa +Belamylinds Beleid Belenthir Belgarathion BelgianBeast Belgiium Belgique +Beliefofmine Belindaa BellWoods Bellatrix +BelleBeans +Bellebutties Bellerin Bellfontaine Bellicus BellsOfWar Bellum50 BellyEscobar +Bellzbellz +Belorca Beloved BelowAvrge +BelowMe +Belsassar Belsey Belsfyre Belsyn +Belt +Belt vs Kids +Beluge Belzebu Belzeddarr +Bemerson Bemke Bempii Bempire +Ben Babeslay +Ben Behling +Ben Bekkuman +Ben C H +Ben Jay +Ben Jones +Ben Kingsley +Ben Manolo +Ben Salden Ben Standish +Ben di +Ben1kzeker +BenDjammin BenDover321 +BenKuro +BenLevensMoe +BenQ Smasher BenRobbo06 BenShapirOwO BenWithJam Benarends +Benbooo Benching +BendOverBabe Bendak +Bendeguz BenderBuilda Bendigo +Bendigo Man Bendo +Bendos Hilt +Bendude +Bendyruler Bendzo Benedict Beneee Benefitz +Beneh Benelovent +BengtBreak +Bengtsson Beni +Beni Siitoin +Benifax +Benifird +Benis Hammer BenisTechTip +Benisbringer +Benja1666 Benji +Benji Stax +Benji189 Benjiii Benllech Benman Benmiester44 Benneta +Benni234 Benni2345 Bennie +Bennie Lam Bennis Bennny0_o +Benny Sins +Benny Stacks +Benny Yuh +Benny p mage Benny1175 BennyBeeee +BennyQ Bennybooom Bennyz Benocotch +Benoid +Benononono Benoragon +Benoxo Benqqu Benquad Bens +BensBent +Bensmom7 +Bensos Benstjohn Bentso Benvil Benyy +Benza Benzema1 Benzine +Benzoed +Benzylll Benzz +Beopia +Beozqt +BeppaPig BeppsaN +Ber Berada Beralf Bercuckle Berd +BerettaM9A4 +Berettapwnge BergJr +Bergheimen Bergkamp +Bergr Berk Berkan Berkis Berkven +Bermaitha Bermbommert Bernache +Bernie Mac +Berno +BernyMadoff +Beroepskech +Berraok BerrieFan Berrr Berry +Berry Carry +Berry Soap +BerryBus BerryInBooty Berrys Berrzerk Bersbillus +Berserk FTW +Bert Curtis +Bert NUFC +BertMacklin +Berta B0y Bertie Bertje Bertow +BerttDog Bervoets +Berzerking Berzurkah +Bes +Besin +Besseggen +BesselJ Bessone Best +Best Kio1 +Best Lolz +Best Murmeli +Best Nher yh +Best Realtor +Best Whilbur +BestCat BestDIzzyNA +BestFioraNA +BestHouse +BestInSloth BestJester BestMommyGF BestSenpai +BestVersion +Bestia Solus Besties +Bestkilnot Bestoladec Besty +Besynderligt +BethMeow +Bethany +Bethell Bethevoid Bethrezen +Betray Betta1009 BetterCookie +BettsISale1 Bettuh +BeugLizard Beuglord Beugsmate +BeunDeHaas Beunhaas053 Bevaun Bevelle +Bever Mama BeverGT +BeverlyPils Bevers +Beverst Bevruchter Bevvy Bewaker +Bewq Beyond +Beyond Max +Beyond Mind +BeyondKenzie +Beyond_blitz Beyta Bezbi +Bezdzwieczny Bezeo +BezosTezos +Bferg3 Bfh1master Bfood BgoinHAM +Bhaals Rage Bhakli Bhalobashi Bhangre Bhench0d +Bhillup Bhongk Bhoy Bhrad +Bht Bhuggy +Bi BiBi +BiGtOkEsBrUh BiZaAr3 BiZaM BiZoNaDe +BiasVariance Bibbit7 BibleThump BibleTrump +Bic +Biccins +Bice Bucket +Bicep Peak +Bicep Smooch +Biceps Btw +Bicho BichoFeote Bichslap +BickyChicken Bictim +Bid Whist +BidaFire Biddah +Biddiss Biden +Bidens Slow Bidensity +Bideo_Gabes BieBras +Biebje Biebop Bierliebe BierryTaudet Bierscape09 Biffo89 Biffsy +Bifking Bifta89 +Big Armpit Big Arms Ben +Big Ass Turd +Big Baby Boy +Big Bearius +Big Beets +Big Bennys Big Binotte +Big Boi Ian +Big Boone +Big Brane +Big Bruno +Big Buffalo +Big Buzz OG +Big Bwana +Big C +Big C A S H +Big Calquat +Big Cat Fan +Big Chest +Big Chugger +Big Crafter2 +Big Cypress +Big D Brasco +Big Dav II +Big Delts +Big Discus +Big Dog Sly +Big Donads +Big Dumb Zac +Big Eazy +Big Erecshun +Big Genitals +Big Geordie +Big Gol +Big Gorg Big Grorb +Big Guy Hax +Big Hingus +Big Hoss Lad +Big Jake +Big Jezza +Big Jim Hun +Big Johnnyy +Big Josher +Big Krukke +Big Latvian +Big Lesbo +Big Lew +Big Mag +Big Meth Big Mike +Big Mo +Big Mooch +Big N LilCox +Big O Wiener +Big Ole +Big Ole Dog +Big Pancaker +Big Poopa +Big Rek +Big Sage +Big Scoops +Big Seaweed +Big Shaq +Big Slick +Big Slurpp +Big Smi +Big Stivz +Big Suze +Big T +Big Tanz +Big Tripod +Big Zaff +Big mfkn Obi +Big ol Bag +Big rOve Big0neNC +BigBad Matt BigBadPurps BigBaller BigBallsagna BigBankTake +BigBantonio BigBaseNZ +BigBear675 BigBenPies +BigBinkus BigBlackCats +BigBlueCox +BigBoiiiBuzz +BigBoyBorri +BigBrad Wolf BigBrownMan +BigBtheOG BigBug +BigBulli BigBustUp BigBwanaCox +BigChinger +BigChuggen +BigChung99 +BigCitta BigCoatBrum +BigCruch +BigD Randy +BigDSurgery BigDaddyBomb BigDaddyEddy +BigDaddyMeme BigDaddyNate BigDaddyT85 +BigDick Q +BigDillz +BigDonKeedic BigDrizzleRS BigDug4U BigEar +BigEgoSmolPP +BigGreenEgg BigGuy +BigHatxLogan +BigHeadPG +BigHootyBoot +BigIronPanda +BigKusa BigLemonTree +BigLez71 +BigLiftsDad +BigLoadOfOof +BigLovve +BigLuke69420 +BigMainKilla BigMav0416 BigMilkerz BigMo +BigNerdd BigPapaDank +BigPapaya BigPepega +BigQuadric3p +BigRaph +BigRat17 BigRedDog +BigRedRodney +BigRickusTra BigRodger +BigSack2 +BigSammyD BigSappas +BigShot BTW BigSikTv BigSpook BigTastyy BigTimmy +BigUp01 BigX BigYitties +BigZikEnergy +Big_B0g +Big_Beard Bigballsac Bigbenslayer Bigborncrazy +Bigboy0007 +Bigboytec Bigcros Bigdaddy Bigdrongo +Bigex Bigg +BiggSackss +Bigga Boi +BiggerNDumbr Biggie +BiggieChonks +BiggieMcLarg +Biggieeeeeee +Biggiemollz +Biggies Maul +Biggums +Biggumz +Biggus +BiggusSnails Biggy BiggySauce Bighomiedebo +Bigjwood +Biglex GIM Bigmet +Bignoob80 +BignoseChris Bigred8616 Bigrigcoin +Bigripper420 Bigrobowskki +Bigtor Bigwildo010 Bigwilly597 +BigxFisch Bigz +Biic Biig +Biig Boobies BiigHatLogan +Biig_Boi +Biiggg B Biitter +BijnaMax +Bijstandswet Bijuzin +Biked Biku Bikura +Bil28 +Bilal Awan +Bilbalbek Bilbo +Bilbo Dabins +Bilbo55510 Bilboro Bilby Bilde Bilie +Bilk Mowl +Bilkos_Ices Bill +Bill Niah +Bill Smiff +Bill Ward +Bill Wigly Billa Billest +Billfish1 Billgodyz +Billie Boi +Billie Gin Billiee Billington Billjetti Billogbfd BillowyMilk +Bills Late +Bills PC BillsITIafia Billy +Billy Blunts +Billy Buchu +Billy H-M +Billy Joe77 +Billy Plates +Billy Tonka BillyBob546 +BillyBonka +BillyMav BillyNotRlly BillyRay Billyn +Billyreino +Billz xd BillzNSkillz +Bily Mavrick Bilybil +Bimmers Bingerss Bingo +Bingo Carla BingoBango Bingus +Bingy lol Binki2021 +Binkleborp +BinksBrew +Binky Stinky Binneli Binny +Bins78 +Binv +Bio s BioCosmic BioFrank +BioMasterZap Bioavailable Bioch +BiofieldMage Biohazard541 +Biomutant +Bionic Muse BionicKing +BionicalWolf Bionicle1395 Bionycle +Biosafety +Bioterrorism Bip0lar BipedalBear Bird +Bird Ibex +Bird Tickler BirdFactsr +BirdFarmRun Birdger +Birdie Train +Birdie713 +Birdies +Birdman +Birdman593 BirdmanZ17 +Birdmanjr07 Birdnesto Birds +BirdsNotRea1 +BirdyguyFe Birelis +Birjeldadge Birk +Birkenau88 +Birkenstock Birkenstocks +BiroGanda Birth +Birthfather +Bis +Biscape Bisccy +Biscuit +Biscuit Cats Biscuitnipz +Bishbasher +Bishbosch Bisher +Bishop Shane +Biskante +BiskyRizness Bison Bisq Bisse Bisun +Bit Bald BitScottish +Bitcoin Z Bitdefender1 Bitesized Bitey Bithc Bits +Bits Init +Bitsy Bee Bitte +Bitterkoekje +Bittles +Bitttimon +Bittykat +Bitwalker +Bitza BixT +Bizanovic +Bizarre Hobo +Bizk Bizness +Bizotic +Bizoune +Bizwald Bj0rnsson Bjarne +Bjarngrim +Bjergstroem +Bjonny +Bjorhn Bjorn +Bjorn XXVI +Bk BkGime Bkedlikelays +Bkeezy +Bken Bkguytd6 Bkostas Bl0od +Bl0od Legion +Bl0odFenix +BlG FROSTY +BlG MOBY +BlG POO +BlG WOODY +BlNA +BlSHNU BlZZ Blaaaaake Blaack +Blablibla101 Blac +Blaccy1 Black +Black Atlass +Black Bears +Black Bro +Black Caucus +Black Fuel +Black Hole +Black Hunlef +Black R X +Black Rifle +Black Upa +Black User +Black lv +BlackAliss BlackAwaken +BlackCawfee BlackCloud BlackHawkEye +BlackNuget BlackPyramid BlackTurtles BlackWidowM +Blacka +Blackasses3 Blackbandits Blackbelt Blackberry +BlackdRedHed Blackdahlia1 +Blacked Out Blackeye603 +Blackking435 BlacklMamba Blacklmp Blacknight +Blackout20XX +Blackpak Blackplosive Blacksamuri2 +Blackstar755 Blacktide1 Bladbro BladeTongue +BladeWizz24 +BladeXFury +Bladeboy6 Blademail +Bladenight69 +BladesEspada Bladypus Blaest +Blaf +Blag0 Blainyckz BlairChan +Blairzy Blaise +Blaise Green +Blaise22689 BlaiseDebest Blak +Blak Deshi +Blak Hawk987 BlakHammah BlakTooth Blakaraknid Blakdragon Blake +Blake Bar +BlakeOCE Blakeboi Blakeeeeee Blakely +Blakerke +Blakey3 Blakk Blakkeyy Blakpn0y Blame +Blame Blitz +Blame Dave Blamed +Blamer Blamp +Blank Face +BlankTree +Blanka1990 Blanke +Blankfillin +Blanks +Blap Blapa +BlaqBeard Blard +Blare Blaser Blasphemy Blast +Blast Bepe +Blastoise Blastrune995 Blaszczy16 Blattermans +Blauwe Brief Blaze +Blaze Hooker +Blaze King +BlazeBaked BlazeItDad +BlazeThatSht BlazeWhale Blazed +Blazed GIM BlazedRanarr +Blazed_Chip +Blazer Kdog +Blazer-31 +BlazerSven +Blazikenite +Blazin Blazinbiscut +BlazingGood BlazingREAPZ BlazingSid Blck +Bleak Days Bleak House Bleakair Bleasi BledReborn Bleekybleeky +BleepMyBloop Blehalol Bleidas +Bleiesnus Bleifuss +Bleken88 +BlendedWhale Blendon BlessOneTime BlessTime +Blessdd Blessed +Blessed IM +Blessed N L +Blessedcrypt Blessin +Blessnt +Bleuski Blevins33 Blew Bleyage @@ -2746,72 +5937,155 @@ Blije Blimey Blimpie33 Blimpysaur +Blind Yeti +Blind1 BlindByron BlindSamael Blindblinker +Blindeyy Blindspott +Blindwizard Blingblin255 Blinger Blink +Blinx337 +Blioneer +BlissWarrior BlissfulDrgn BlissfulIron Blisss +Blista Blistered Blithering +BlitzJuuls Blitze BlitzedBoss Blixtslag +BlizSukzNuts BlizZinski +Blizz of Ozz +Blizzard343 Blizzartd +Blk Kush Blkbeard22 Blkup Bloat Bloated +Bloats hard Blob Blobman1 Block +Block XD Blockhead +Blodmire +Blodreiina +Blodreina 1 +Blody Mess 2 +Bloedkuul +Bloeps +Blogmas +Bloke Kisser +Blom Blombo Blommer1 Blonde +Blonde Don Blonket +Bloo2 Blood +Blood Face09 +Blood Lotion +Blood Man478 +Blood Moor +Blood Rift +Blood Spawn +Blood Spawns +Blood Turd +Blood Vials +Blood Voss +Blood Wyrm +Blood0fhell Blood4Peace BloodWarzz Bloodfan0 Bloodgeon Bloodiedawn +Bloodlet +Bloodmaged +Bloodrave182 Bloodraven +Bloodredmano Bloodshade +Bloodsheds Bloodsportt +Bloody Scarf +BloodyMurder +Bloodyengine +Bloodyrootz Bloodytem BloomBouquet Blooms +Bloons TD +Blops Far +Blorgons +Blosh Blote Blouch +Blow Off +BlowBiden Blowd +Blowin Ohs +BlowingPipe Blown BlownbyTwins +Blowup Dolz BlowyBarry +Blu Ocean +Blu3Mink96 +Blu3print Blu3s BluAnimal +BluNightfall BluSPANKSyou BluSlidePark +Blubberboy BludMaul +Bludclat Bludgeoner0 +Bludime504 Blue +Blue 420 +Blue Atmos +Blue Fox 64 +Blue Inn +Blue Jay +Blue Legend +Blue Limes +Blue Nahuatl +Blue Osiris1 +Blue Oyster +Blue Summer +Blue Trex +Blue Winds +Blue Ziv +Blue305 BlueBoat +BlueBorough BlueCashews BlueDHYDshld BlueFrogsOP +BlueGwapes BlueHawkie +BlueHornet BlueJob BlueLegendzz BlueLemon66 BlueLine BlueLite +BlueMandril BlueMeanEyes BlueMuff1n +BlueOverkill BlueRanger BlueRose91 BlueTyphoon @@ -2820,61 +6094,108 @@ Bluebears Bluebury Bluecaps09 Blued +Blueface G Bluefir Blueluna Bluemoon97 Blueprint19 Blueqerry Bluerope14 +Blues 1 Clue Bluescreen +Blueshores +Bluestopher Bluetiger919 Bluewarthog +Bluexy +Blueyy04 Bluez Bluezaros Bluff Blufire +Blumkohl +Blump King Blunt +BluntBird +BluntSurgeon BluntTaster Bluntmore Bluntobject Bluntology Blunts +Blunty Pyro BluntzyMcGee +BlurOfLight +Blurr62 Blurred +Blurtt Blusaunders +Blut Arteiri Bluternite +Bluutooth +BluzCluzz +Blxke Blyat Blyckertski +Blyocr +Blze Bmart Bmaylor +Bmeg Bmoot +BmwNerd +BnB Bnaar +Bo Ed +Bo Selecta +Bo0nies +BoBgss126 +BoBoTaeTo Boa92 Boabs Boagrious Boang Boas +Boater lad +Boats N Hoos +Bob Blinger +Bob Burglar +Bob Kelso +Bob Lebowski +Bob Marley +Bob Shermon +Bob Violet Bob11790 Bob19922 Bob44th +BobBojangles +BobCatMilk BobGuenille BobNegao BobRossDied BobTheDaddy BobaNeZmogus Bobakadush +BobbaStanley Bobbanxd Bobbiie Bobby Bobby Brawl +Bobby JoJo +Bobby Lash7 BobbyBigLips +BobbyBonk +BobbyShurda BobbySmooth Bobbyccf Bobbydrake09 +Bobbysweggg BobbyyHill Bobi Bobicles2 Bobidabob +Bobite92 +Bobo Bagins Bobrobber Bobrusu Bobs @@ -2882,45 +6203,82 @@ Bobsaytoad Bobtunafish Bobvill Boca +Boca Raton +Bocaj +Boccle Bockovie +BockyOG Bodaajamies Bodacious Boddy +Bodhy BodisDelotis Bodjery Bodlamadrid Body +Body Kit +BodyRune +Bodyart +Bodybuilding Bodyguerdson +Boe f Boed Boeing Boeing MAX Boekanier +Boemsjaka Boer +Boer Salad +BoerJohannes Boerboel +Bofad Esnuts +Bofas Boff +Bog Jr BogDomen +Bogalog Bogart +Bogby Bogchamp +Bogged Dacks +Boggmonster +Boggsy III Bogi153 Boginskaya Bogmellon +Bogusbart +Bohachii Boham Bohejmen Bohn BohneSaw +BohnerSoup BoilerMakerr Boints +Boise Bronco +Bojaroff +BokOrmen +Bokbok9 Bokeva Bokje1 Boko BolaRobin Bold +Bold Smut +BoldPelipper BoldandBrash BoldupG Bolibomba BolleBenny +Bollieflex +Bologang +BolsaDeCoco +Bolsta +Boltagon +BoltonRamsay Boltzzzzz Bomb +Bomb Squad Bombergray Bomboy Bombu @@ -2928,76 +6286,171 @@ Bomer Bommerche Bommijn Bompa +Bompanero +Bompton Bomtastic +Bon Pa Tin +Bon fee BonQong Bonar +Bonar Slayer Bond +Bond Digger +Bond Huntin Bond09 +Bond4MyIron +BondBought BondedLiam +Bondegang Bonden +Bondi altti +Bondify Bondkyle +Bondoos Bonduwel Bondz +Bone Hawk +Bone Man +Bone Medic +Bone Thugsz Bone Ur Hole +BoneSurgeon Bonecrushing Bonedork Bonehead Bonen2 +BonerMachine Bones2Peach BonesNAltars +Bonez III +Bonfire1 +Bonfire237 +Bong Lenis +Bong Quixote +Bong Squats +BongAppetite +BongSec +BongTokez +BongZiller +Bonglefin +Bongletopper +Bonglewongle +BongoBasse78 BongoRider +Bongobodil +Bongotree +Bonh +Bonjwa1 +Bonk Weekly Bonkers517 +Bonkeykong Bonnaroovian BonnieTHICCC Bonnyjoy1 +Bonnyyyyy Bonus Bony +Boo Duh Boo5tn +BooBaker +Boob +Boob Dragon +Boob Slip +Boob zilla +Boobini Booblee +Boobs Mom BoobyHill +Booca +Booduh +Booffee +Boofqueefius Booger +Booger Ballz BoogerSnacks +Boogeyman Boogie +Boogie339 +BoogieCorgi Boogieman +BoogurBoy Booiesblazin Boojwazee +Book +Book of Sand +Bookie Killa Bookless +Booksmart BooleanSpl1t BoolinScape Booll05 Booloo +BoomBangPow8 +BoomFire69 BoomFloof +BoomMatt Boomdead123 +Boomerclicks +Boomfire2 Boomfire9 +Boomhakik +Boomjuice Boomstick08 Boondow +Booney Tunes Boookuh +BoopBeepBeep Booped +Boopette Boopie +Booping +BooptSnoot +Boorrie Boost Boost736 +Boosted six +BoostedBruh Boostedd Boostergold Boostify BoostyBetard Boot +Booteelick Boothanqq Boothyy BootieMix +Booties Bootihole Bootlesape Bootsjunge Booty +Booty Dishes +Booty Kev +Booty McDab +Booty Sensei +BootyCheek +BootyGod Zac +BootyNasty +Bootypixels Booxia3 +Booyeh Booze Boozem +Boozeobagins BoozyBirdy BoozyXander +Bop +Bopla +Bopoman +Boppin Bora Borads +Boramoss Borderguard Boreall Bored +Bored Idea +Bored Ingame BoredMalamut Bored_IRon5 BorenZo @@ -3006,8 +6459,13 @@ Borgiie Boriix BoringName Boris +Boris King7 +Boris sfisgh +Borkiin BorkingCorp Born +Born Abroad +Born to Hodl BornToTrade BornTwoGrind BornofDragon @@ -3017,203 +6475,421 @@ Boromer23 Borring Borsten Borussia +Bos Marmot Bos9 +BosNaes +Bosco Wong Bosh Boshby Bosif +Bosnald Bosnian Bosnier Boson Boss +Boss 4 Pet +Boss Biz +Boss Fights +Boss Mati +Boss Mei +Boss Rarkley +Boss Slayer Boss4Days BossMan BossMann +BossOnly100 +Bossalinie Bosses +Bossin x Bossk +BossmanJCriz Bossnian BossyEnemy +Bostin Loyd +Boston Tony Bosw8er +Bot Detected +Bot Matt +Bot Owen +Bot To 2277 +Bot for dayz +Bot player BotBooster Bothbalgone +Botman1431 Botsii Botsji Bottatrice +Bottlewin BottnScheise Bottrill +Botty Boub Bought +Bought Cape +Bought Gear +Boules Boulli120 Bouncy +Bound Books +Bounzy +Bourbon Time Bourgeoiis +Bournos Boutopia Bouwkundige +Bouwmans Boven Bovine +Bovyy +Bow And K0 Bow1ng Bow2ThyQueen +BowIads Bowbby Bower +Bowfa Barry +Bowfa Dees Bowin Bowinkle +Bowjacked Bowl BowlCutMonty BowlNutta BowlPacked +BowlResin Bowlcut +Bowleo +BowlerBTW +Bowlhead Bown Bownerator +Bowny20 +BowsB4Hose Bowse +Bowser Jr BowserSideB Bowwsy +Box Art +Box Office +Box T +Box Therapy +Box of Rain +BoxOGnomes Boxacle Boxedup +Boxer +Boxic BoxingNugget Boxio Boxse Boxxie Boxxy +Boxy +Boy Mobile +Boy Wonder +Boydem Boydie4427 +Boyfredaaa +BoylifeInNZ Boys +Boys Broke +Boys No Good +BoysAndGirls +BoysenBill Boysennn Boyyo Boze +Boze Worm +Boze mol BozeOog +Bozut +Br ay +Br ibb +Br yan Br00dje +Br0ox +Br0wnSpit Br3ws +Br4dders Br4w Br5andon BraBraBrad +Braaamps +Brabbss Brad +Brad RS +Brad T King +Brad017 BradIsAChad BradIsChad +BradKavanagh BraddahPoppz Braddahoodz +Braddict Brader +Bradj_55 +Bradlem95 +Bradleyussy +Bradlio Bradolai +Brads Wrath +BradyDezNutz +Bradycus Bragon Brah +Brahamus Brahu +Brain Drill +Brain Kancer +Brain Stim +Brain Sucker Brainiac2018 +BrainrotMaxx Brainsquash Braithy Brajen Brak Brakathor +Brakence +Brakke Bever +Brakza +Brallerx Bram +Bram91 BramDx Brambickle Brambo100 +Bramboo +Brammie +Bramxoo Bramziee Bran +Bran dont +BranDaMfMan +Branch Brand +BrandNewHere Branden +Brandi Rose Brandir Brandish Brando +Brando n +Brando23x Brandog Brandon +Brandon 126 +Brandon TJ Brandon9250 BrandonLarge Brandonp32 Brandonx188 Branflakez +Brannttt +Bransk +Bransterdamn Brap BrasilHemp +BraskaDad Brass +Brassy Bird BrastaSauce +BratusB +BraunBrains +BravSlav BraveBear +Bravenewfy +Braves Bravest Bravo +Bravo Eagle +Bravo Lv +Bravo8 Scav +Brawl Brawlers +BraxMax +Braxt +Bray0420 Braydons Braylor Brazden +Brazeheart Brazzerker +Brb Delivery Brblol +Brdy Breaches Bread +Bread Again +Bread Dead +Bread Init +Breadatour Breadhats +Break3r Gabe Breakchance Breakdownz +Breaver Brech Bredbeddle Breeann Breeno Breenus +Breeoh +Breez +Breezeeh Breezy +Breezy Jai +Breezy2277 +BreezyMT Breffest Breivig +BrejchaBoris Brejin +Brelinguette Bremen71 +Brendann Brendvn +Brenkku Brenkolovski +Brennzo +Brent Ham Brent4000100 Brentiscoool +Brenty88 +Breskvice +Bressjul Bret BretHammy24 Breth +Brett who +Brett6910 +BrettThePunk Brettdog Bretz +Brevetted Brew +Brew Dr +Brew IPA +BrewceWillis Brewdo +Brewin BrewskiGod Brewsley Brewsy +Brex Brexit Brezell Brezzy +Brian Dub Brian-senpai +Brian1234146 +Brian4755 Brian8781 Brianmyth +Brick Brick Brick49 Brickerino +Brickhead +Brickhooouse Brickhouse +Bridding +BridgeFourr +Bridger +Brie Larson +Brieenne Brief +Briefly +Briggs +Bright Day22 Brightest +Brightly +Brihyun +Briickzz Briidi Brin Brinfish Bring Bringtheheat +Brinkler Brinkosaurus Brinsgr Brisiinger +Briskly Brispy +Brist Brit Brite +British Diet +British bamf Britswelll Brittanys +Brittanyx Brlm BrntSausages +Bro bee +BroTwoTher BroUFailLol +Broad Broadboat +Broadday Brobasaurus Brobible +Brobin +Brocadillo Brocaine +BroccCheddar BroccoIi BroceanMan Brocepticon Brocknation +Brocolis +BroderPierre Brodie Brodie410 Brodie827 +Brodisi Jr +Brododore +Brodus Clay Brodymon316 +Broekloos +Broficiency +Broft Brogfrogdog +Broha +Brohard Broiled +Broken Cpu +Broken DPS +Broken Hip +Broken Top BrokenBones Brokenn +Brokensunset Brokk +BrokkMachine +BroknScrotum Broko Brolic Broly Bromagic +Bromatron Bromerly +Bromosapienn +Bromz +Bron Yr Aur2 Bronkey Bront +Bronxie Bronxy +Bronze Cow +Bronze Eel +Bronze Moose Bronze5 BronzeDong BronzeWorthy +Bronzegnu199 +Bronzr Bronzy BrooceWillis Broodje @@ -3221,38 +6897,69 @@ Broodoos Brooke Brookesbeast Brookfield +Brooksher +Brooksy BroomInBum Broomi Brootloops +Brootuss +Broque Bros Brosaph +Brosoul +Brossy Brotatochip +BrothaForest Brother BrotherChong +Brothermon Brotherr +Brotholomew Brown +Brown692 GIM +BrownTree +BrownTree63 Brownecakes Brownmo +Brrandon +Brrd +Brrinn +Brrr Zeus +Brthdy +Bru now Bruce +Bruce Jenr BruceJennder BruceNutty BruceWayme Bruceh +BruciebearTV Bruenor +Bruffell +Bruges Bruh Bruhh +BruhhChungus +Bruhs Bruiser Brumaks Brumle Brundeen Brundles +Brundo Brungoil +Bruno p15 Brus Brutal Brute Brutescape +Bruthar +Brutified +Brutzli Bruut +Bruva Eww Brvte +Brwny-mix Bryan BryanFury Bryannn @@ -3261,88 +6968,152 @@ Bryce Brychu Bryci Brycikins +Bryham0 +Brynildsen +Brynmor Brys Journey Bryson Brysons Brystreams Bryu Bryymstick +Bsakke Bsaunders0 +Bse +Bsgs Bsketbl +Bskli +Bslayer Bthomson Btownballer Btrec +Btv +Btw Das +Btw Guy +Btw tietjes +BtwBallo BtwImJoe BtwTradeMe +Bu nny Bu11itt +Bu11seye00 BuBszs +BuDzNBooze +BuLLeT PVM Bub Josh +Bub389 Bubba +Bubba Gage +Bubba Watson Bubbateux +Bubbba Bubble +Bubble Butts +Bubble Tea65 BubbleBlob BubbleJuice BubbleOLuke Bubbleguy Bubblenab BubblesGO +Bubblies Bubbo Bubbz +Bubgi moment +Bubo Bubu Bubzs +Bucc ees +Buccsta Buchu +Buchu Bong +Buchu Daddy Bucito Buck +Buck Oakly BuckNastyy Buckeye BuckinThanos +Buckjames Buckner1 Buckner20 +Bud Right BudLightBtls +BudSake Budabupbup Buddaball BuddahCheese +Buddeke BuddhaPuck +Buddweiiser Buddy +Buddy Waters BuddyGuy +BuddyJ Budgen Budget Budgets Budjeke Budpotamous +Buds Bud +Budsteel Budster Buduhhh +Budward Budwise +Budzinski Budzliteyear +Budzyn Buff +Buff Bezos +Buff Mafia +BuffFridge Buffel +Buffel x Bufubu +Bug V2 +Bugalado +Bugaloo Bugazzi +BugcatMoo Bugg +Bugg XO +Bugis x Bugs +Bugs c Bugsaur +Bugyo Bugyy +Bugziee Bugzy Buhe Buhnuhnuh Buhole Build3d Built +Built Dfrnt +BuiltToSpill BukesCarKey Bukkakey +Bukkele +Bukmyhr +BukseFar Bukt Bula +Bulb_a_saur Bulba BulbaThor Buli Bulk Bulkier BulkyTrout46 +Bull Shifter BullSharkBob Bulldawg1289 Bulldogs815 Bulldozer297 BulleT1017 +Bullerbyn Bullet135191 BulletBlitz Bullschiff @@ -3351,91 +7122,180 @@ Bulltill Buloz Bultac Bulte +Bum Hole +Bum Vinegar BumPounder Bumble +Bumblethumps Bumjam +Bummer +Bummholee +Bumveld +BunanaMuffin +BunanuhBread Bunceylad Bundle +Bundy red +Bungle Bug +Bungstar BungusBoi +Bunion Bunjamino +Bunni Pig Bunns +Bunns Dying Bunny +Bunny DeIron +BunnyRabb1t Bunnytrack +Bunq Bunryl Buns Buntian +Bunty Boy Bunzosteele +Buorin +Bup Buqqi Bur eye on +Bur y +Burak I Buray Burec Burens +Burensbd Bureze Burezz +Burg de Rott Burger +Burgerb0y Burgerr +Buri Burial Burials +Buried Mole Buriial Burkekey +Burkkle Burly +Burlyy +Burmecia Burn +Burn Herbal +Burn With Me +Burn t BurnMyChi1d +Burna Boy Burnardo Burnari Burnceller Burned BurnedIron Burners +Burnetplanet BurnhamAll12 Burnin +BurninOctane BurninStarIV BurningBrian +BurningCole BurningFight BurningKushh Burningdavid Burninghippo +Burnley Burns +Burns Green Burnstown Burnt +Burnt Bonez +Burnt Bunz +Burnt Senpai +Burnt cod +Burnt lol BurntT +Burntmeat BurnzWhnIPvp +Burnzaloree Burnzie Burp +Burpenen Burpsy Burrkle Burst +Bursts Burwell Bury BuryMeDeep Busch +Busch Ebba Buschhhhhhhh Buschhuscher +Buschkilla Bushbaby Bushido +Bushido Jack +BushidoNegro Bussch Bussino +Bussy Bwana Bust Bustard Busted +Busted Shaft +BusterBlader +Busternut +Bustinbibear +BustyBarry +BustySaurus +Busy Phat BusyDayToday BusyRightNow ButMuhMain +Buthole +Butinz +Butje Kef Butlergunner +Butt Clouds +Butt King +Butt Liquor +Butt Plog +ButtChugr +ButtExpert Butter +Butter Sock +ButterSirup ButterSyrup Butterbur Butters +Buttgers Butthxle ButtnCLICKER +Buttt Mud Butzaf06 +Buu Blat +Buuloki +Buurtpooier Buus Buutsika Buwuchu Buwuchuu +Buxi +Buxom Lady +Buy Wards +BuyBitcoin01 +BuyGF_1GP +Buyer Beware Buying +Buying IG GF +Buying gf 2M +Buying99rc BuyingRsGfs +Buzi Mi Daj +Buzi Syrom Buzin +Buzin Benji +Buzy BuzzL1teBeer BuzzMax Buzzard @@ -3444,121 +7304,294 @@ Buzzlghtbeer Buzzzwin Bvanana Bville23 +Bwan a Bwana +Bwana Brah +Bwana Haz +Bwana I +Bwana Rat +Bwana TK +Bwana Zander Bwana-senpai BwanaObama +BwanaOnDrugs +Bwanah Bwananabread Bwanario Bwanner +Bwarath Bwekfast Bwekfeest Bwie +Bwila Bwse +Bxao Bxdhi +Bxo +ByDesign +Bye Felicia +Bye Friend +Byles Bynguyen Byrd +Byrd Gang +Byrd Park Byrne ByteMeM8 Byzo Bztm +C 678 +C A M R O N +C A R S 0 N +C A R S O N +C Biologist +C D C D C D +C H l S E L +C H l Z E L +C HARL IE +C HlCKEN +C J 102 +C L A M P +C L U E 5 +C Maelstrom +C Mauricio +C OLLINGWOOD +C R I X US +C RoyMustang +C Rx +C S +C Sol +C Trafalgar +C W +C Wills +C a c h e +C a le b +C a r lo +C a sp er +C alvarium +C amel +C e v +C eej +C h a ds +C hap +C hristt +C ip +C itadel +C lhris +C nr +C ntmaster +C o r a l +C oach +C orgi +C osmos +C oty +C rafter +C raig +C raiig +C rux +C u r t +C x L C-11 +C-53 C-Dom +C-Stones +C-bat +C00RS Light C00per +C0Dl +C0LTE +C0MBAT 126 +C0RAZON +C0SA C0WK C0XG0BBLER +C0Xsuka4gp C0YS C0c0nUt +C0dy +C0dyssey C0nner C0wanz C11 H15 NO2 +C137 Beth +C18 C18H27NO3 +C204 C2okies +C3 P0 +C33 C3Pz +C4 RL C450 +C4L1BRE C4RL C4RNAG3 +C8M +C9rl CA5E CAKESNIFFER CALL +CAMBO D N CAMEL0T CANNONlNG +CANT SPEAK 0 CAPITALIST CAPSLOCK +CARAPlLS CARDlFF CARROLLYZER +CATS GO NYA CATsmoothies +CBC +CBD Brownie CBK20 CBMPeterson CBas CBomb +CC 9 +CC HM CCCCFF CCCO CCatt CChuk +CConnor CDKein +CDR +CDRMark +CDawg420 CEClL +CEL4L +CEO OF ARK +CEO of TOB +CF5 CFoodBisc CFour CG MindPr0 +CGA +CGC Zeus +CGR +CGoldy CH1P CH3CKMYSW4G +CH4OS_REIGNS CHATBN +CHEEFO CHIEF CHIEFWHALE +CHIEFxKEETH CHOF28 CHOLEZmusic CHOPSTlCKS CHOSENBYNAME +CHR187IAN +CHRlS PRATT +CHRlSDUDE +CHUCKDABANK +CHUDZY CHULIGAN911 +CHURNlN +CHlC-FlL-A CHlCKENS +CHlCKNUGGET CHlLDS +CIA GF PsyOp +CJ McCreery +CJ2K +CJB_01 +CJPez CJlivin11 CJoaboneP +CK the Wise +CK2 +CK9 CKSKEE CKVY171 +CL0wn Inc CL3O CLIIllIIllIT +CMBloodcraft CMER CMMCTk +CMU Chips CMoneyTwo CN77 CNC2112 +CNGT +CO maxplayer +CO0RS CO1N +COKECOKECOKE COLE COM3THAZINE +COM3TS CONFlDENT CORONA CORP +COTTONPlCK3R COVID +COX Kane +COYM COYMauves +CP8 +CPA Scape +CPR +CPT Morghun +CPT-Rock69 +CQJB +CR1SPY_BAC0N CR4ZYH34D +CR7 +CRAI9 CRLmastrFlex CRSSD CRTS CRUSlR CRY0GENIC +CRlMP CRlP CRlSTO CRlTIC CRtallboy +CS6 +CSG0 +CSchicky CT42 CTFU +CV90 CVID +CVTRILLOG +CYP 3A4 CYP450 +CZHANG +C_mrade +Ca 11 +Ca m el Ca3ino +CaL4MiiTYxX CaMOOflaged +CaPl Caam Caater +Cab Up +CabbageSeeds +Cabbi_cs +Cabdi Cabela Cabido +Cabinetsalt +Caboose121 Caboucha Cabouchi Cachalot Cache Cachet +CacklingMagi Cacoadragon Cactus Cactus9k +Cactusaak +Cadallic0 +CadmiumBird Cadov Caduzitcho Cadzie @@ -3567,143 +7600,290 @@ Caelanity Caelums013 Caerus Caesar +Caesar Pasta +Cafe Fraiche +CafeZing +Caffeine +Caffeine Use Caffeiner +Cages +Cagrets Cahors +Cahsmo Caide +Caifan Caifanes +Caillou hobo +Cailthun Cainie Cainn Caio +Cairn08 Cairo Caiser +Caitlin Rice Cajjj Cajun +Cajun Blood +Cajun Fox +Cajun Fries +CajunCrimson Cake +Cake qp CakeBoss1337 +Cakeboy 43 +Cakefound +Caketins +Cal Fong +Cal l u m +Cal-Mag +Cal_C +CalamityD Calamityy Calb_Potta Calbod Calcab Calcd +CalciferGIM +Calcio CalculateWhy Calcusource +Cald0g +Caldaris Caleb +Caleb o_o Caleeba Calf15 Caliace Calibeafall Calibrated +Calibur CaliburZ Calico +Calierazon +Califaa +Califauxeous +Califirona Calimandro Calipha Calipso +Calist0 Calisterie +Calivego Call +Call Me Milo +Call me Cham +Call me m16 +Call0 CallMeCletus CallMeGeorge CallMeJAS CallMeJoey CallMeKeen CallMeKeezy +CallMeLeger +CallMeNoLuck CallMeSofD CallMeStevo CallMeTipz +CallMeow +Call_Me_Arty +Callaghxn777 +Callboy Calliburr +Calliott Callisto +Callisto Cub CallmeZach Callmekee +CallumTM +Calluum +Calm Chris CalmCombo CalmDownSon CalmYourBits Calmoran +Calo3 +Calquat +CalquatFruit Calster26 Calu Calum Calvi +Calvi n Calvin +Calvin924 Calyxsys Calzano Calzone +CalzonePizza +Cam Jong-Fe +Cam Jong-Un +Cam Rebuild +CamAlot_iron CamDeezy CamLite +Camakie Camaniack +Camargue +Camb o Cambeezy +CamboSliice Cambridge +Cambulance +Camdenan +Camel Glue +Camel Neckit +CamelActive +Camelm8 +Camerocity CameronNeal Camhanaich +Camil Camion Camise Camm +Cammo545 Cammy +Cammy Ded Camosaur +CampBingBong Campbell Camperfish CampinOnline +Campisii +Campus Crew Camsdadddy Camshows Can Berry +Can Fe Spoon +Can U C Me7 +Can it Bozo +Can0fBeans CanChem CanMJ +CanManCannon +CanOManBFTM +CanUGetAway Canada CanadaBawd +CanadaGuuse +CanadaScape2 CanadasChief Canadian +Canadian Bc +CanadianDevi CanadianYeti Canadiehn Canadiian +Canan32 +Canberk +Cancel Cancelled +Cancelled It Cancer7 +Cancerfree24 Cancerios +Canderos +Candlebox Candrok Candy CandyFlipin CandyKing42 -Capt -Capt420 +CandyKing420 +CaneCorso Canear Canehdiann Canibalistic Caniz Cann +Cannabinoidz +Canned Beer +CannibalGK Cannibble CannoliBoi +Cannolis Cannondorf Cannot +Cannot Login +Cannrrrr +Canon Bob Canook Canowhoopazz Canserber0 Cant +Cant Quit 07 CantCasino +CantMaxBc69 +CantTalkPerm CantThink +CantTradeTho Canta Cantaloupe +Cantclik4sht Canter Cantina +Canting Cantu +Canyonranger CaoFasho +Caoe +Cap E Bara Cap10 +Capalotty +Cape Fear +Cape Max +Cape Seller CapeScape +CapeZer0 Caped Capez CapiiTano CapitalGainz Capitalise +Capitalist34 +Capn Wahle CapnCookd CapnMeliodas Capniq +Capnplant +Capo Yamato Capone Capos +Capotia Cappe Cappei +Cappie Greek +Capreol Capriccio Caprius Caprix Capsule +Capt +Capt Bobette +Capt Bumi +Capt Dave +Capt G +Capt Halo +Capt Hootler +Capt Kenway +Capt King +Capt Kumquat +Capt Murdoc +Capt Plank +Capt420 CaptHardon CaptLongJon +CaptPleb CaptZilyana Captain +Captain Jack +Captain Lev1 +Captain Tast +Captain Tec +Captain Y +Captain Zoso +CaptainAlice CaptainCanto CaptainCox CaptainDom @@ -3711,6 +7891,7 @@ CaptainDredd CaptainLuker CaptainOboy CaptainObvio +CaptainWalt Captainshiny Captainzzz Captan @@ -3718,44 +7899,62 @@ Captcha CaptinA CaptinKorasi Captinrandom +Captiva Captn +Captn Bear +Captn P00F +Captn Sqirk CaptnCommand CaptnMittens Capu Capussi +CapybaraDung +Car Ram Rod CarNalgas CaraDuraMC Caracal Carademlof CaramelSlice Caratheodory +Carbo Cat Carbolic Carbon +Carbon K CarbonCarbon Carbonist +Carbos Carbyne Carcinoid Carde +Carden CardiacKemba +Cardinxl1 +CardyZ Main Care Carea Caregiver Careo +Caricapaya Cariej CarimsEygon Carino +CarjUIM Carjacking Carked Carkis Carkosa Carl +Carl Sagan CarlBarker CarlDerp CarlSagan80 Carleton789 Carlin +Carlingue +Carlitos Carlo CarlosM +Carlosjavily Carlrip Carls453 Carly @@ -3763,6 +7962,7 @@ Carlyle Carmillae Carna Carnadova +CarneGrande Carnie CarnifexVeil Carnitas @@ -3771,93 +7971,188 @@ Carnzlo Carolan Carp3di3m13 Carpe +Carpenters Carpi Carr CarrickDaddy CarriedBayo +Carrot s +CarrotMilker +Carrs Pasty +Carrutt Carry +Carry Yak +Carry9otter Cars +Cars Suck Carsillas Carsten10 +Carterfish +Cartman Carton +Cartzy +Caru +Carvdogg17 +Carve +Caryopsis +Cas1a CasZeal +Casamigos +Casawn +Casc Casca +Cascola Casell +Caseten CaseyWaff Cash +Cash two Casherbob Cashewk +Cashlemke +Cashs Main Casino +Casino Sand CasinoProblm Casketball Casmatos +Cass Cath CassTheGod Cassarole Cassath Casserolio Cassidyy +Cassie Aram +Cassim +Cassirer Cast +Cast Rate +Cast Turbo Castello Castilla Castorly +Castrophany Casual +Casual Man CasualChris CasualGrinds CasualPete CasualStorm +Casualdkdk +Casualty +Casvel +Casviel +Cat Man1001 +Cat Nips +Cat Shaped +Cat Smuggler +Cat Snacks +Cat Soup +Cat Woman +Cat got out +Cat shirt +CatBoyInHeat +CatPandas CatPissRNG CatSaysMeow +Cataclysm55 +Catala Catalepsy +Catalyticx Catan CatchTheDrop Catchin +Catchin Pets Catchlove Catdog1280 +Catechism +Cater Champ +Catfish Rock Catflap +Catgirl Cafe +CatgirlSan Cath Catharina Cathays Cather Catherbae Cathuntdog +Catman31 Catnip +Catnip Cutie Catnor Catomic123 +Catra Meow +CatsnCars +Catsnarterrr +Catspeak +CattoKitty3 +CauZ Cauldrons Caulf1eld +CaulkPushUps +Caustic Cauterised Cavaleiro888 Cave +Cave Closer +Cave Horror Cavern +Cavern Freak Caves Cavos Cavs Cavsy +Cawrin +Cayk +Cayman 07 Cayrus +Caza Lowell +Cazaq2 Cazik Cazos Cazum Cazwise Cazzakin Cazzy +Cba Ofc +Cba To Carry +Cba To Share CbarDaKing +Cbetrs +Cbf Playing Cbot05 Cc17wo +Ccn Cd777 Cdale +Cdoc +Ceana +Ceb Ceber +Ceceil10 Cecetra CedIsMySon +Cedty +Cee real +Cee-Jay +Ceebooty CeeeHooo +Ceeege +Ceejaydjj +Ceekay Ceeon Ceeps Cefiro Ceio CekicGuc Celach +Celach 2 Celadus24 Celazer Celebio +Celer0 +Celesdel CelestialSun Celestive Celestron @@ -3865,130 +8160,229 @@ Celhon Celi0 Celiac Celikanen +Celine Dijon +Cell Saga +Cell Z Saga +CellaDwella Cellectric Celli Cellmate Celms +Celrisen CeltBrenny Celtic +Celtic Hero +Celtic555 Celtica Celyzh +Cemen Demon Cengkeh +Cennolink Cenpie +Censor +Centac +Centennials +Center Fit Centers +Centershot5 CentraIka +Central CentralC +Centrimag Centropy +Ceo of Timbs +Ceoe +Cephalopods Cepp Ceppt Irn +Cepxy Cera Ceraseus Cerati Cerb +Cerb R Us Cerberus Cerberus98 Cercle Cerea +CerealGuy +Cerebro +Cerenade CeriGG +Cerkev Cermi Cerpin +Cerpin Taxt +Cerpooch Cersey +Cersky +CertIronBoy +CertifiedPoS Cervantes18 CesarPalace Cesema Cetr Cets +Cev Cevap Cevarus Ceverie Cewl +Cewl Hwip +Cexilius Ceyyl +Cf b4 Cfretz244 +Ch i +Ch i c k en +Ch ief Ch00bies Ch0c0tac0 +Ch1cag0 Bear Ch1mes +Ch1noLf +Ch1zz Ch33sy Ch3ckMat3 Ch3spy +ChNPP +Cha rles Cha0ticAngel ChaCha +ChaCha Benny ChaSniff +Chaavez Chaboud Chaboul +Chacaliando +Chacksen Chad +Chad Draven +Chad Stride +Chad Vibes +Chad Wardn +Chad again +Chad isAFK +Chad okeefe +ChadBehavior +ChadCor ChadFratStar ChadMadLad34 +ChadR3333 Chadacas +ChadamantBar Chadders Chadding +Chaddyboy Chadga +Chadieus Chadkiller76 ChadronX +Chaehee Chael +Chaendo G Chaewon +Chaga Chai Chain +Chain Mace Chainn Chainsaw +Chainsaw Guy Chair ChairmanMao +Chaiyse Chaken +Chakleton +Chakra Monk +Chal1ce Chalba +ChaldeanGIM +Chalgrove +Chalk Bar Chalked Challandria Challll Chalva +Cham Cham +Chamariapero ChamberBeast Champ +Champ Ryan +ChampGaryOak Champagnole Champiion +Champion910 Championship Championz Chance2Skill +Chanced Chancellor +Chancie Chandler737 Chando ChangWu Change Changebear Changes +ChangrOfName Chanios +Channel +Channon Chaos +Chaos River ChaosBandego ChaosCleric +ChaosD00M +ChaosGIM +ChaosInbound ChaosJS ChaosedElf1 Chaosfish33 Chaoslynx +Chaotic Cole ChaoticH0B0 ChaoticMoott +ChaoticScorp Chaotixx Chaottic Chaoz Chap Zachman +Chap em up Chapels Chaplain +Chapo Chapoo Chapp +Chappe Chappers Chapter +Char Zeta +Char lang +CharL-32 Character252 Charamio Charatcur Charben Chardonn ChargeN +Chargez Chariot Chariten +Charitonin Charizardz Charlez +CharlieFTG CharlieFine CharlieOHair +CharlieScene CharlieTheIV CharlieWork Charlieb9 +CharliesFE Charloi Charlotta Charltonb +CharlyDarwin Charm Charmey Charmin12 @@ -3996,208 +8390,374 @@ Charmos Charmoul Charms Charmy +Charon0 Charor +Charred Yews Charrysteas +Chartart +Chase Goals +Chase Jones +Chase Riley ChaseGuy ChaseNBake ChasePP Chaser959 +Chasing Pets +ChasingDragz Chasmata +Chasse +Chastised Chatorbait Chattanugget Chatto +Chatty ChaukletZ +Chauman0819 Chauska Chavi Chaz +ChazMac Chazzdiddy ChazzedBangr Chazzy +Chazzy Paws +Cheah +Cheaps Check +Check Point CheckAndMate CheckRaiseHS CheckTheWiki CheckWikiPls +CheckWikiPlz +Checkaaa Checker +Checkley Checkmate CheddLaurent Chedda Chee CheecHnChong +Cheech Blaze +Cheeek Cheeks Cheeky +Cheeky Peeky +Cheeky Prawn +Cheeky Tom CheekyBanter +CheekyCheeto +CheekyDrop Cheelee Cheeno Cheepnis Cheermancy Cheers Cheese +Cheese Borgr +Cheese Slice +CheeseTax +Cheesebubby +Cheeseilton Cheeseit32 +Cheesteri Cheeswiz +CheesySweet Cheesyrice Cheetas CheetoRatFan +Cheetu +Cheex +Cheez-Zits +Cheeze Caper +Cheeze Nepz +CheezeOx Cheezer2000 Cheezewiz0 Cheezo +Cheezuz Cheezyboy25 Chef Chef Bu Fang +Chef Malone +Chef Mihali +Chef Peter +Chef Poopy +Chef Special ChefBoiRJay +Cheff Zaya Cheffrey Cheffy95 +Chefs Cheif Cheimuu +Cheiquispir ChelTheNelf +Chelate_D +Chello Sexy Chellyy +Chelsea Bot Chelsko +Chelsy Chelx Chemanelo ChemicalFade Chemleech +Chems +Chen Luu +Chendlar +Chenzooo +Cheomeister Cherba Cherisu Cherno-byl Chernoblyat +Chernobyl +Cherri Bomb Cherry +Cherry Field Cherrydown CherryxPiex Chesco +Cheshmate Chesscape420 ChessyQ18 Chessyboi Chestbrah +Chestbroh +Chester63 Chesterfi3ld Chestickles +Chestnut Chestodor Chet +Chet Baker +Chet Max +Chet Ripley +Chettos Chevaliers Chevrolet Chevron ChevyB1998 Chew +Chewbacca No Chewbakkaah Chewby +Chewby Lt Chewed Chewitt88 Chewy Chey +Chi hiro +Chi11 Wi11 ChiCity +ChiIdo ChiLongQua +ChiamataM +Chibattagreg +Chibber +Chiboubo Chicanery Chiccbacca ChichaBabby +Chichhuve ChickeNOG Chicken +Chicken Cat Chicken996 ChickenRings Chickenelli Chickenneth +Chickens Inc Chickensnout Chickerolies ChickinNugie +Chico Bean +Chico Cool5 Chicold Chicopee +Chid Heredit Chie ChieFbLuntz Chief +Chief Checka +Chief Gordy +Chief Herres +Chief Otaku +Chief Seneca +Chief Sound +Chief Toxine +Chief812 +ChiefBobert ChiefSmakaHo Chiefss ChieftonP Chiekz +Chiester 45 Chigginwangs Chiiiraq Chika +ChikenLiro Chikinbone Chiksan +Chikupa Chilasr Chilaxinman Chilcos +Childhoods Childish Chilgamesh Chili +Chili Pepper +Chili Popper ChiliTurd Chilibean ChilidogBank Chilidoger +Chilipupper Chill +Chill Mate +Chill Maxxy +Chill Zege ChillAssGuy +ChillHill +ChillScape Chillagalet Chilled Chilli +Chilli Ramen +Chilli dong +ChilliPesto +Chilliam +Chilliburns Chillmitch Chillrend +Chilly +ChillyPolarB ChimJongUn +Chimbilin Chimera364 Chimire Chimmy +Chimneybob +Chimp10n +Chimpywimp ChinStar China ChinaInBox17 ChinaSupaman Chinanumber1 Chinchompin +Chinchonkerz Chinchoopa +Chinegroe Chingie +ChingwaChing +Chinley Chinolc +Chinook Bram +Chinquila Chintan +Chio Bu +Chionophobia Chip Chip Black +Chip Skylark ChipRain +Chipotl e +ChippedHam ChipperyChip Chippy +Chippy btw +Chipsbok +Chipsncrackr Chipster321 Chipz Chisa Chisels +Chiswick +Chitus ChivZz +Chiw ChixDigTbows +Chiyo-Father +Chizle +Chizzet Chkn ChloeTragedy +Chloemacmate Chloes +Chloramine +Chnce +ChoNaWiadro +Chob Choccazz Choche +Choco Flex +ChocoMeteor Chocoblow Chocobo Chocobos Chocomalo14 +Chocys Chodemate +Chodless Chofl Chohanzzz Choi Choicess +Choke Daddy ChoklatMoose Chola Cholesterol Chom +Chombe +ChompVonDile Chompadile +Chompakilla Chompy +Chompy chick Chompys Chon ChonYee +ChonerScaper +Chongsy +ChonkPenguin Chonksta +Chonky Duck +Choobage Choobcob Choochy Chooks Choomb Choooooooch Chop +Chop Rips ChopSthix Chopemania Chopin ChopinDolphy +Chopinaway +Choppa ChoppinWork Chordes Choreboy Chornflakes Choronzon Chosen +Chosh Chow Chowfun +Choybin Choz ChozenGod +Chozen_Azn Chozo +Chozo Lite +Chozo Statue +Chqse +Chr Mck +Chr0nl Chranic ChrdyMcDenis Chri5ty @@ -4205,11 +8765,21 @@ Chriiiis Chriisgg Chrimon Chris +Chris F +Chris JR +Chris Luxon +Chris P3 +Chris Slays +Chris xD Chris0527 Chris1f +ChrisCleans +ChrisFate ChrisHanson ChrisKringle +ChrisOnTilt ChrisTheUnit +ChrisThomps Chrisadk Chrisha Chrishowe @@ -4217,67 +8787,122 @@ Chrisible Chrisjay Chriskies Chrisob +Chrisog Chrispy +Chrispy-sama Chriss +ChrissPIBass Chrissy +Chrissy Tooh +Chrissyx Christ +Christ an +ChristFarley Christiannnn Christidog Christlan +Christmonkee Christoffer Christoker +Christopher ChristyCloud +Chrisxmas Chriz +Chriz297 Chrizzoz95 Chrl Chroist Chromadorr Chromalox Chromatica +Chromi ChromieOX Chromixe Chronepsis Chroner Chronic +Chronic Jack +ChronicChris Chronicflame ChronnerBro +Chrono Aeon ChronoKiller Chronoburn Chronomancer Chronorage Chronos +Chronosanity +Chrostopher +Chrunndle +Chrusee +Chrysaorr Chrysaros Chryserys Chub Chub n Tuck +Chubafet Chubby +Chubby Nomad +Chubs-Magee +Chubsy Bubsy +Chuchin Chuck +Chuck Hoots +Chuck Prime +Chuck truck ChuckChan ChuckDogg +ChuckMeDaddy +ChuckNat ChuckSpedina ChuckTesta Chuckerberg +Chucky Chuda007 Chug +Chug Butt +Chug KoolAid ChugMyPPot +Chugg Jugg Chugga Chugger Chugging +ChumSlugger +Chuml +Chump +Chumpingham ChunchuloX +Chunetops Chung +Chung us +Chungus-kun ChungusClam Chunk +ChunkiCinni +ChunkmanFYB +Chunksy +Chunky Nan +ChunkyGimp Chunkybudda +Chupa Chupperino +Chur Broo +ChurchMouse ChurchTuring Churchfield +Churchieboy +Churd Churlrunone +Churnin Chuzyz +Chvrles Chxmpanzee Chyky Chyna +Chyurr CiSCii Ciamballer +Ciaphas Cain Ciastkowy Cic321 Cicely @@ -4286,241 +8911,447 @@ Cici Cicindelinae Cider CieloQueen +Cigarettez +Cigblaster +Cigggy +Ciggitybutts Cighan +Cignii Ciji +Cim Cimakas Cimelia +Cinamin Cincinnatus Cinderal Cinderhulker +Cindirty +Cinemaxxin CinmarRS +Cinoxe +Cinqoo Cinquain +Ciomsa +Circle Pea +CircleStrafe +Circleone58 +Circomcised +CircularSaw +Circumflexx +Circus Clown Circuz Ciresidnal +Cirexi Cirez Cirmit +Cirn0 +Ciroren +Cirrus Virus +Cirth Ciryatur Cisco +Cisk Cisplatin Ciszak1996 +Cit Funt Citadel +Citadel Wyrm +Citate CitizenTay CitrusLemons +CitrusTree City +City Limits +City Morgue +City Perks Civeo +Civz +Ciziin +Cizre Cizzakillss +Cjg117 Ckale +ClGARO +Cla ws Claca Claco Claiborne +ClairDeLune +ClaireFarr0n Clambam Clamburglar2 +Clan thmoker ClanChats +Clan_Daddy ClankImaTank Clannyy Clanworld Clap3d +ClapMaster4K ClapMeMommy +ClapYouDown +Claptrap ClarKah Clardy2 Clarisse +Clark C +Clarkey BTW Clarky +Clasherz Clasico Classic +Classic Max +ClassicTrap +ClassicVibes Classically Classickxd Classsikh ClassyCod +Claudenstein Claudiu Claw Clawdius Clawsie +Clay Nasty +ClayNugget Claymor Claypops +Clayters Claytn Clayton Clazerbeam Clean +Clean Dishes CleanSleeve CleanedBown Cleann Cleanst Clear +ClearEyes Clearly Cleatis Cleaver CleaverGreen Cledos Clefable +Clefairy +Cleg Drop Cleible Clem Clem585 ClemFandango CleoTehCat +Cleopatraa Cleoxc +Clerric Clethbery ClevelandOH Clevey ClexOrie +Cleyra +Clff Cliche Click +Click Bosses +Click hero ClickAndChil +ClickB8 Clickbait21 +Clicks0nMobs +Cliffder Clifferd +CliffyOG Cliffyy ClimateChang ClimberAZ +Clingy +Clinician +Clinicwater Clint Clinttt Cliphanger1 Clipper +ClipseZ Clipz +Clipz Ahoy CliveTrotter +Clix Cload +Clockwise Clod +Clogged King +Cloggin +Clogging Cloistered Clone +Clone bone ClosedLoop Closertohell Closeshots Closofy +Clotert ClottedCream Cloud +Cloud Cover +Cloud Els +Cloud Griega +Cloud Nine +Cloud Stairs +Cloud Surge +Cloud y +Cloud1K CloudSoftass CloudT +Cloudhill +Cloudi Boi Cloudjumper9 Clouds +Clouds_Song Cloudy +Cloudy Boi +Cloudy Sleep +Cloudy Wolf CloudyOcean Cloudys +Cloudystrife Clouted Clovie Clown +Clown Around Clown0164 +Clownfart7 +Clryy Clss CltRain ClubBruggeKV ClubWolf Clue +Clue More +Clue Relic +Clue box +Cluebringer +Clueful +CluelessKook +Cluer Clues +Clues Hunter +Clumpey Clumsy Clunker ClutchFlipsy Clutchy +Clyde Cooter ClydeBarrow Clynelish Clyps +Cmallz02 CmarBeast Cmndrcool222 +CmokinSrack Cmon +CmptrGmr +Cnbl +Cnhil +Cnr Cnwalker CoFlack +CoKMcGay +CoX Mentor +CoX ToB ToA CoXAcc Co_do_blyat +Coa Coach +Coach Trip +Coach Wright +CoachJal Coachsharter Coal +Coal ore Coan +Coat Throat +Cob Web Cobab Cobalt +CobaltKings CobbMorty +Cobba +Cobplacecow Cobra +CobraChickn Cobreu Cocajumba Coco +CocoGetsLoot +CocoaPuff +CocosLilSimp Codai Code +Code Mellow Codemax Coder00d Coderedcfc Coders Codfish808 Codi +Codonz Cody +Cody Smith +Cody The IM +Cody The M +CodyBeaumont +Codys Nuts Codyx Coeeyyy Coekebakker Coenzyme +Coex +Cofeni +Coffaholic +Coffe277 +Coffee Latte +Coffee Love +Coffee Robot Coffee94 CoffeeAndExp CoffeeBarrel CoffeeIsBest +CoffeeKitty +CoffeeMafia CoffeeQ888 CoffeeSlayer +Coffey +Coffin Cape +Cogelon +Coggle Cogstyle +Coheed Coherent Coilman Coin +CoinToss +Coinism Coipo CokeHogan CokeShoveler +Cokebro +CokedPepsi Col10 ColVolgin Cola +Cola Flesje +Colaboks +Colb y Cold +Cold Bowl +Cold Ham +Cold Hash +Cold Hemp +Cold War Cold1 ColdReign Coldhound591 Coldpiee +Coldraze +Coldrootbeer Coldshop Coldstream +Coldvayne Coldvepz +Coldwar +Coldwoods Cole10083 +ColeEleven ColeQuil +ColeVeryBakd +ColectionPog Colee +Colei Coleus Colgate777 Colienergia Colin Colins +Colivanov +Collan33 CollectMemes +CollectibIes CollectionIM Collectively +Collector of Collega +Collier Collierss +Colliflopter Collin892 CollioKey +Colom ColonColitis Colonel +Colonello ColonialDank Colonne +Color Climax ColorBlindHC ColorMeBlind +Coloradodo ColorfulE Colossus 5 Colossvs Colt +Colt trigger Coltan +Colthound +Coltn Coltrainz1 +Coltsfan1996 Columbot Columbus175 +ColumbusCrew +Colxman Colzy Coma Combaed +Combat Phase +CombatStudy Combatking13 Combibo Comboed Combos Come +Come Loads ComeAtMeBruh Comet Cometz +Comex Comfort Comfortably Comfy +Comfy Butt +Comfy hug Comic +Comic Books ComicalBust Comicmaster0 +ComingHome +Comiya Comlidor +Comm Zilyana +Command Grab +Commander Fh +Commander Ra Commas Commiefornia Common +Common Drop CommonGinger CommunistMoo CommunistPig CompactSugar Companiion Companion +Compd +Compe Compgeek Compilacion Complain @@ -4529,346 +9360,707 @@ CompleteAss CompleteSpud Completes Complex +ComplexTree +Complextro Composing +Composites Comptons Compy +Compyclon ComradSergey +Comrade Hanz +ComradeCas ComradeCovid +ComradeMule +ComradePugs +Con JD +Con Safos +Con ner ConKlave ConaAmadora +Conaizy +Conal +Conceited Concentrates Conchrist Concritus +Conductor4 Cone01 Conedinho +Confederacy Conficker Confidentiel Confined Conflamit +Conflate +Conflict Confuze +Congest Congetus +CongiestMunt Congra ConicLight Conjay +Conjo Conman Connavar +Connee Conner +Conner OK +Connerr Connerxx +Connor M +Connor xv +Connzy Conor +Conor J +Conor07 Conpetgrebe Conqo Conquar +Conquers Conquestions +Conquestti ConradK Consan Considerable Conskis +ConstantZero Constantnips Constellar +Constence Constricted Constructeur +Consumption Contaminate +Contant Geld Contendedd Conterfeit Contes +Contrabant +Contract M +Contradicted ControlAll +Controller Controlllers ControneX Contyy Conventicle +Converter Conway Conydriving +Conyon +Conzila +Conzine +Coob Lad Coogs Cooj Cook4everu Cookdaburra +Cooked Chook +Cooked Sinus Cookedvomit Cooki3Crumb CookiMan Cookie +Cookie Cake +Cookie O_o Cookie904 CookieKid00 +CookieSpec CookieV3 +Cookiers +Cookiezi Cool +Cool Raccoon +Cool Yak 496 +CoolAssName +CoolG WC +CoolNameBrah Coolair Coolavatar1 +Coolboy_cal Cooldesert2 +Coolest91 CoolestCow +Coolio +CoolmanCool Coolmanafo +Coolminer685 Coolmintoreo +Coolmonkey50 +Coolsniper Coolster Coolxkidz Coom Coomcoomber Coonrade Coopa +CoopaTroopah Cooper Cooperative +Coopr Coopsz +Coors Light +CoorsRight +Coorsy Coos +Cooter +Cooter Kick Cootie Coozn Copytopy Coquettish +CorCam Corabex +CoralCastlez CoralFire +CoralReef Corbi +Corbula CorbyTheLad +Cord Core OS Coree +Coreey +Coreling jr Corena Corey CoreyCarlaw CoreyNKH +Coreyeroc Corgi +Corgi Puppy +CorgisIron Corgo +Corinnna Corizz Corleone +Corley Corn +Corn x Holio Cornbuddy +Corndoq +CorneliusIII Cornflake Cornie Cornilius Cornish Cornwall94 Cornwalll +Corny10k Cornye +Corona +Corona Bat +Corona Case Coronad1 Corp -CorpToMax -Corpletics +Corp Scape Corp09RS +CorpLeecher +CorpRng +CorpSlapper +CorpToMax +Corping Bro +Corpletics +CorporalWolf +Correy Lahey +Correyyy CorruptDingo +Cors Corsa Corsetti Cortes +Cortex +Cortica Cortistatin +Cortsen +Corun +Corvanjer +Corvisquire Corvitolis +Corvo Corvoo Corvus +Corvus enca Corwin Corydonn Coryy Corzappy +Cos A Nostra Cosaintus Coseph Coshie Cosmic +Cosmic Booty +Cosmic Star +CosmicPebble +Cosmiclaws +Cosmog Costa +Costah +Costcutters +Costi CostlyOne Costom +Costs Cotched Cotopaxi Cotstini Cottee Cotton +Cottrell Cotty2Hotty +Couch Frame Couchie +Coughey +Count 2Three +Count Cranz +Count Dookie CountCuckula CountDrugula +Counterspell +Countries Country +Country Song Countwert Coupland Coures +CourtnyGears Couscous Cousin +Cousins Couzens90 +Cove Coventrians Coventry Covert +Covert Blade Covid +CovidFree Covvboyz +Cow Door Mat +Cow Foo +Cow Pet +Cow Tongue +Cow Wizard Cow395 CowChum +CowMr101 +CowSalsa +CowVein Cowa +Cowabungalow CowboyK1ller +CowboyKaroo +Cowdenbeath +Cowlories Cowrat +Cowsburp +Cowtongue CowzGoMoopy +Cox out boys +Coxed +CoyieGod +Coz +Cozmic_Raven Cozy Cozzza +Cpl Gaz +Cpl Seeder +Cpt Doobie +Cpt Fail +Cpt Kokopuff +Cpt Mellow +Cpt Mina +Cpt One Eye +Cpt Pepe +Cpt Pop Tart +Cpt Shanks +Cpt TRICKS +Cpt Unclutch +Cpt Wally CptAceRimmer CptKyle +CptRileyy CptRom CptSankara CptSmackAHo +CptUzu Cptainseveto Cptn +Cptn Jack +Cptn Murica +CptnBuckaroo +Cpunek +Cqrl +Cr o Cr0be Cr0codile Cr0z Cr4zyLuck +CrAzY HoRnEt Crab +Crab Emoji +Crab Jazlo +Crab Leaker Crabalt Crabby Crabcore Crader Craft +Craft Brew +Craft Guild +CraftPure +Crafter Jr Craftes Craftsmen Crafty +Crafty Man CraftyRunes +Cragyl Craig +Craig Dawson +Craighead18 +Craizy 8 Craizy9 +Crakced +Crammerr +CranadosX Cranberry Crangus Crank Cranke Cranky +Cranky Cows +CrankyScream +Crantock Crapicorn +Crapping +Crappy Luck CrappyScape Craqers Crash +Crash Crann +Crash Cymbal +Crash Site Crashbot Crashendo +Crashticles +Crateapa +Crateaqa +Cratzi Duhuh Crave +Crave Me +Crave to Max +Craw +Crawfish Crawl2033 Crawn +CrawsBow CrawsMain CrayQuaza Crayons +Crayy Crayzee +Craze NL +Crazed Man CrazedAfro CrazedAgain +Crazedmonkee +CraziKido Crazie Crazy +Crazy 859 +Crazy Canada +Crazy Sam +Crazy Skull +Crazy5115 CrazyDieMan +CrazyDryMan CrazyDutchy CrazyIron85 CrazyShrimp CrazyStuff Crazya0 +Crazyb00b Crazyhalo +Crazys Crazystevee Crazytree Crazzy +Crazzy Ivan +Crcsh CreaMiJeans Cream +Cream City +Cream Guzzle +Cream21 CreamFreesh CreamingPies Creamp +Creamy Fella +Creamy Whole +Creasential +Creat Steal Create CreateNoPain +Created +Creating6 Creator1212 Creator409 CreatorJR +Creaturree Creazy7 +Crecker Fux Crecket Credibility +Credito Creel Creenen Creepy Creezy CreightonRS +Crem Fraiche Cremator Crenoc Crescent +Cressp Cresss +Creston fftp Crevis6 CrewDoe Crewcial Crickets +Crickss Crier Crimewave Crimeway1991 Crimin +Criminaldmge +Criminalised Crimp Crimson CrimsonCow CrimsonDDS +CrimsonJayce CrimsonLily CrimsonRogu3 +CrimsonScape +CrimsonTide CrimsonU +CrimsonWic Crimsoncaim +Crimz0n Crinath +Cringy Grump +Cringy af +CrinsomX +Cripler Cripple Crippled +CrippledKev Crippler +Cripty Cris Crisby Crispiessss Crispy Crispy Bac0n +Crispy I CrispyBreast Crissco515 CristalAlken +Cristierra CriticalX +Criticizer +Critter J Crittx Crixos +Crixus x Criyosphinx Crna +Crna Gora CrnerMcGregr +Croc +CrocDanDee 1 Crocalu +Crocketeere +CrocoMaster Cromeh +Croney +Cronsus Cronus Cronz +Croogus +Crooked Path Crooklyn +Crooshtwoost +Crosby Alt +Crosem Cross +Cross Me Crosswinds Crouchling +Croupz Crow +Crow Mn CrowBox Crown +CrownConvict +CrownRoyal84 Crownescent Crowride1873 +Crowther Croww +Croxy Crozier CrspyPigeon Crudelismo Crudivore +Crue Xena Cruel Cub +Cruel Irony CruelVictory +Cruelcanary Cruixiote +Crumbledoor Crumblers +Crumbways +Crunch7O4 +Crus ty CrusH-HK +CrusadeKlr +Crusadershot +Crush Depth Crushed +Crushed Guam +Crusher9783 +CrusherWake Crushersun Crushertaco +Crushs noob +Crust Cape +Crustie +CrustyDuckr CrustyPusy Crustysnot Crux +Crux da King Crux25 Cruxyy Cruzty +Cruzzetbuzz +Crwfrd Crxk Crxsty +Cry +Cry0nman Cry4help +CryIs0gp +CrySupply Cryge +Crygechamp Crying +Crying Cutie +CrykeeOwO Crynceps Cryo +Cryo Chamber +Cryogen Cryogenica +Cryogenica 2 +Cryogenist +Cryorah Crypthead +CrypticSlays +Cryptling Crypto CryptoKitty CryptoMellow +Cryptonomico Crypzor +Crysaki +Crysh Crystal Crystian +Crzy Mia +Crzyarab +Csajka +Csk x +Cssy CtCandyRandy +Ctclaire999 +Cteel Ctep31 +Cth ulhu Cthrek Ctrengereid Ctrl +Ctrl F +CtrlAltDel x Cuada +Cuat3 Cuatche +Cuattr0 +Cub Will +Cuba Sybre Cubans +Cubed +Cubet +CuckChuck +CuckW +Cuckgex +Cucumbers CudddlyBear +Cujoh +Culemborg +Culinair Culls +Culltist +Cultured +Cultzy +CumHeretic43 +CumanderZili +Cummy Sigil +Cumpaska +Cunnavathing Cunny +Cuno Cuolua +Cuong +Cup Noodle +Cup Of Chai +Cupaplayer Cupcakess Cuppalimmy +Cuppi Cups Cupthebooty +Curaga +Curble +CurdTuttrboi +CuriPolymath Curll +Curls +Curlx Curly CurlyTwist Currancy Curry Storm +Curryleaf Curse +Curse on me Cursed +Cursed Chump +Cursed Nm +Cursed Side +Cursed ll +CursedOwned +CursedRNG oO Cursedpotato Curt +Curtis Jones Curve CurvedHorn CurvyChcken @@ -4876,32 +10068,64 @@ Curzonn Cushco Cushdy Custom +Custom Jr +Custom Sock +Custom TK CustomGFX Cute +Cute Cat lol +Cute Paunch +Cute n o o b CuteClit Cutegirluwu Cuti +Cuties +Cuttableedge +Cutting edge +Cuttl +Cuttle +Cutty Sarks Cuuap Cuult +Cuz im Jones +Cuz im jesus +Cuztom Cuzzo94 +Cuzzu +Cvanz Cvbgn Cvrkster Cvrsn Cw90 Cwab +Cwazza Cweavy Cwer Cwick Cwioktopus Cxffee +Cxld +Cxnnor CyaCyaCyaCya Cyal8rnub Cyan +Cyan Ablaze +Cyasoon +Cybb +Cyber Mind +Cyber n Bug +Cyber172 +CyberJesus CyberSlave18 Cybernautron Cybernike +Cyberpope +Cyborg Musk CyborgHex +Cybron Cycling +Cycling Road +Cyclogy Cyclone2498 Cycloner5 Cyclopedia @@ -4911,227 +10135,534 @@ Cygni CykPyk CykaNuggetzz Cyldan +Cym Cymatics +Cymbaline +CymricCat Cyn2k CynAcolyte Cyncess Cynda +Cyndaquiill +Cyndee +Cyndrakial Cynhi Cynic CynicalSilas Cynocephali +Cynognathus Cynosure +Cynox Cynthaen +Cyodot Cyous Cyox Cypersilver +Cypher Blue Cypress +Cypress H1ll Cyralx Cyrax Cyrax314 Cyriel +Cyrodiil22 Cyroenix Cyruscomrad +Cytain +Cytryn7 Cyuh +Cyum Czacha Czar +Czar I +Czar Trump +Czechmate +Czha Czivend123 +Czk +D A I J I N +D A L M +D A M 0 +D A M I A N D A M N L0L +D A N G I T +D A N N Yy +D A T H I T +D B A +D B Z pker +D Blo +D Clawz +D Darkblader D Derbles +D Dragon124 +D Duberstein +D E C O +D E F Q 0 N D E F Q O N +D E R P +D E V I N +D F U Q +D I Q +D I R T Y Ko +D J Diddles +D Long +D M T Elves +D O D +D O M M E L +D R 6 K E +D R l Z Z T +D Rack4 +D S A V +D Squarius +D U R K +D Wilson +D Wreck +D a mo +D a r t s +D a rk +D a u t o +D a v i d +D amz +D arking +D arkoz +D as +D ashing +D avey +D axe +D db +D ebol +D ecimate +D eej +D eek +D eep +D eez +D en +D enial +D entist +D i l a n +D i x o n +D i zz y +D iesel +D iizzy +D imi +D imitri +D onda +D onger +D oo l ey +D op e +D utchgr +D-D-A D00DLES +D00dey9 D0CTOR D0GS0NG D0NE0 D0NPABLO D0PESIC D0any +D0inkleberg +D0lNK +D0llynho +D0n vi0lad0r D0nte +D1APAM D1GG00BIITTI +D1P D1ZZY +D1ngu5555 D1no D24L D2Aina D2nzo +D2theOnTRUMP +D3 Washup D33L1N D3ATHBYPI3 +D3ATHVENG +D3C D3C0D3D D3DW8 +D3IVY OSRS +D3VONSHIRE +D3ZZi5 D3ceptions +D3precated D3thbyzebra D3v1lBoy D3vilmar3 D3zert D4H4EK3YSM +D4TE D4VO D4n0118 +D4ng3r Suca D4nkmemel0rd D4nnyy +D4rels D4rk D4rth D7NNY +D9 +D9R +DA ODB KIDZ +DA0IST +DA3N33RY DA4N +DAD563884271 +DADDYS IR0N +DAHDB +DARCHANG3L +DARGON DASH DAWGPOUND +DArtspret +DBA Paulo DBKangaroo +DBO90 +DBOL TRICEPS +DBUZZ +DC Flubber +DC-8-51 DCreek +DD 214 +DD G DD2I4 +DDAlt +DDSurWrists DDandy +DDoS Wildy +DDune DEFENDPOPPNK DEFlNE +DEM0L DEM0NIC +DEPRINS +DESPA SIITTO +DESPAlRGE DESTYLAT DFez +DG 00 DGAF +DH Marker +DH btw +DHIBZ DHSC4EVER +DHally +DHlN +DHoShow DIDZZ DIETDRDIET +DINGO SCUM +DIORITIC +DIORITlC DIRTYags DISAPRIN DITMANM0DE DIVINExNOVA DIY Ele +DIY Jamie +DIY Salsa +DIY Shane +DIY Steo DIYIronFeBTW DIYRevelled +DJ Bop +DJ Cranston +DJ Heiko +DJ Korsakoff +DJ Llama +DJ Pen is +DJEDDIK +DJK Daniel +DJKALLE ANKA DJKrampz DJMileyCyrus DJMuscleBram DJOFULLIN +DJSeinfeld +DKHo +DKJN +DMM 2 07 +DMM Gp Swap +DMM Ryu +DMT Dad +DMT Satan DMTDAN +DMTe +DMagnum 10 +DMessengerS +DN Atro DN20 +DNFL DNLD +DO0FY DOGELII DOITESTEVEN +DON FIJI +DON VALTEE +DONG GOLEM +DONJOHNNNY DOOMBRlNGER DOOMSlayer DOSNE DP776 +DPRNS +DPS Disciple DPvM +DPvM Jordan +DQ9 +DR PAAAK +DR VABA DRAEG0 DRANG3 DRD31 +DRFL DRGNSLAYER85 +DRIEST RNG DRL0NGD0NG +DRLGspace +DRNKN DWARF DROPPEDITALL DRTY +DRUNKIDIOT +DReelest DRegi +DS Stolen +DSHeavy +DStat DStroud DSyndrome +DT5 DUMB DUSKBLADEL0L +DV 8 DV trekpik +DVa +DW Gunthie DW24 DWlGHT DXQIWOL +DXXM DYao +DZ o_o +Da Bomb 149 +Da Buddah +Da Iron Jedi +Da Man 65 +Da Nils +Da Puppy13 +Da Sniper007 +Da T Virus +Da d dy +Da t +Da then bh +Da1ton DaBears DaBoz +DaBurg DaCatBeam DaCigarMan +DaCuddy +DaCult +DaDangus DaDude DaFatManDan +DaFeesh DaHumanClown +DaIronBooty DaIton DaJaVu DaLegendary +DaPootz DaRKi +DaSernet DaSheepFtClb DaTr3w +DaVinciss DaaChronic Daaak Daallas Daan +Daanilio Daanncorr +Daavos +Dab Thirty +Dab then pvm +Dab0mba DabANOMICS1 +DabDabDan DabInMyEye +DabTornado Dab_nScape Dabbadank +Dabbe +Dabbers +Dabbie Duck +Dabbin Dolo DabbinDonut +DabbingBrb Dabe +Dabe Sucks Dabei +Dabomb 13 Daborn92 +Dabos Weenie DabsWithDan Dabsmoke Dabsncats Dabsndogs Dabton +Dabvape420 +Dacaedia +Dacc +Dachowski Dackel Dackenerino Dacune +Dad Goals +Dad Sean +DadBurrys +DadGoingMad DadLeft DadRanarrWay DadamoSte +Dadbackward Dadbackwards +Dadbod4lyfe Daddaforce Daddy +Daddy Bundy +Daddy Chaman +Daddy Chubs +Daddy Danger +Daddy Dead +Daddy Drow +Daddy Oreo +Daddy Smit +Daddy Spoon +Daddy Yurdle +Daddy sandro +DaddyDeagz DaddyDylo DaddyLemming DaddyMac DaddyMars +DaddyNemmy +DaddyPig406 +DaddyTowel +Daddyfied Daddyplease DaddysLoad Daddyspie DaddyyDong Daddyz +Daddyz Clean Dadevil1616 +Dadfield Dadi415 +Dadorii Dadosaur Dads +Dadukez DadurQa +Dadyankee3 DaemonGunner DaemonTool Daemonmage +Daemonrds Daenrys Daep Daewolo +Daeyalt Ess +Daeye +Daez 95 +Daf Arch +Dafetsch Dafney Dafo322 +Daft Punked DaftDemon DaftPun Daftmoul DafttPunk +Dafuggg Dafy +Dag DaLooter +Dagannot hs Dagannuts +Daganoth Rex +Dagathar1 +Dagburn DaggaNOTKlNG Dagger93 +Dagggg +Daggou Daggz Dagin DagkingV2 +Dagnel Daddi Dago +DagobertTT +Dagoth Vemyn Dagsi27 +Daguar Dahlareen Dahls +Dahlson Dahmonkey Dahrmata +Dahrr Dahumbug Daijoukay +Daiju Spede Daiktusrenku Daileee Dailiana +Daily Grind +DailyDoomer DailyDosage +DailyPog DailyScape99 DailySharts +DailyShelfer Dailytoker Dain +Dainius Dains +Daipers Dairychuk DaisOfHavoc Daiseido Daiski +DaisukeJigen +DaisyApple DaivdFW +Daiw +Dajmkryss Daka +Daka Alt +Dakai +Dakay Daklozenkrat Dakotas Daksu Dal3y +Dalacks DalaiLlama +Dalanes +Dale Dobec +Dale G +Dale HC BTW +Dale Winton Dale1612 Daleferd Dalejamesw Dalek +Dalek Cookie Daley_Dose Dali176 Dalkz +Dalls Beep Dalmatian Daloomi Dalrak @@ -5139,6 +10670,7 @@ Dalski Daltficiency Dalton Dalton4theft +Dalton_LB Daltonnn Dalundo Dalzii @@ -5146,60 +10678,134 @@ DamDude DamSplash Damage Damarquis +Damberson +Damen +Dameon xd +Dami +Dami-an Damiaen +Damian_Xx Damish Damlotz DammitHarry +Damn Evil +Damn God +Damn its Sam DamnItHarlod +DamnSexy +DamnViton Damned +DamnedDaniel Damngoodsoup +Damni t Damo +Damo is free Damon +Damp Memes DampBucket DampForklift +DampMongot94 +Dampener Damzos +Dan Bad +Dan Craig +Dan Daoud +Dan Gleesac +Dan H +Dan M +Dan S +Dan xo Dan-Reijden Dan120201 +Dan452 +Dan646464 +Dan91 +DanAsker DanAye DanBTW +DanChan1101 +DanGreenFan DanGur27 DanMaxd DanMingo +DanO_o +DanOfCumelot +DanPJ DanRickshaw DanTheM4N +DanTofoo Danboy Dance +Dance for XP +Danced DancingForGP +DancingRa1n +Danco +Dandrikis +Dandwc88 +DaneDaddy99s Danea +Danelele DanernesLys Danex85 Dang +Dang It +DangFloopity +Dangelboi +Danger Swign +Danger Zoneh DangerBear DangerGlutes +Dangerouz Dangeruss +Dangerxi +Dangerz0ne15 DangitBobby Dani +Dani Cute +Dani D +Dani Dark 0p Dani007 Dania Danieelius Daniel +Daniel Mcc44 +Daniel OSRS Daniel12xx +DanielSumtin +Danielbisgod Daniella +DanielleOfc +Danielmcken1 Daniels +Danielwat +Daniiieell Danijenn1211 Danilin Danimal DanisH96 +DanishAdonis +DanishGoat Danje Dank +Dank Chef +Dank Exp +Dank Perp Dank Tigzy DankDylShpil +DankMagic +DankSavage99 +Dank_Mcnasty Dankdank96 Dankdog Dankdynasty +Dankey Dankfool +Dankleburgh Dankstanky Danktrees +DankumZ +Danky Kwuarm Danletics Danleypa Danlord3222 @@ -5214,32 +10820,51 @@ Dannk Dannny Dannum Danny +Danny Dubs +Danny Dvito +Danny Mate +Danny P +Danny Pudi +Danny m8 +Danny wtf DannyDeleto6 DannyK +DannyKjr DannyPho +Dannygem Dannytrol +Dannyw Danog Danoj Danol Danomeva +Danoontje Danozz +Danratty Danrow Danrue Dans Dansayeagle +Danski261 +DanteExitium Danxdoo1 DanykaNadeau Danzai Danzig Danzoler Daofather +Daoko X Dapper +Dapper Hades +Dapper Jr +Dappest Dappi DaquanNiguan Daquantaro Daquicker Darbinism Darc +Darc Sport Darcy Darda09 Daredevil595 @@ -5247,128 +10872,294 @@ Dareme95 Daremo Dareya2 DarfPlageus +DarfWantWarp +Darimy Dario +Dario hax +Darion Dariukas Dark +Dark X +Dark Alcyte +Dark Aloy +Dark Curtain +Dark Iron69 +Dark Jaxol +Dark Kadabra +Dark Kill L +Dark Knig ht +Dark Kurama +Dark Peter10 +Dark Rabbit +Dark Ranqe +Dark Side +Dark Specter +Dark Stone66 +Dark Tetrad +Dark Totem +Dark Uzi +Dark Veidar +Dark Wegener +Dark Xarpus +Dark kamui +Dark lll +Dark1437 +DarkAether DarkEater +DarkElf135 DarkEmerald DarkGraceful +DarkHoodGIM +DarkKn1ghts DarkMerliin DarkNut393 DarkOwI DarkReapingX DarkReign DarkWizard +DarkXiphles9 +DarkZulu97 Dark_Auth Darkang3lpkx Darkao Darkarmy40 +Darkbox777 Darkbright +Darke Hand Darkemissery +Darken Devil DarkenRahlrs Darker +Darkest Kind +Darkest Syde Darkfanime Darkflash15 +Darkgutz Darkhal +Darkine Darklord4211 Darknapster Darkp0wnz Darkphil007 Darkpupitar2 +Darkrai Darkraven368 +Darkscorpio5 Darkseance Darksense Darksim10 Darkskiller Darksn0w Darksnoweb +Darksocks1 Darkst4ar +Darkstar078 Darkturbo Darktyranno Darkwrath43 +Darkyr +Darlin g +Darling27 +Darma +Darnic Darquain Darras +Darren T Darrens Darriann Darse Darsehole +Dart Slayer Darth +Darth Kas +Darth Shiroo +Darth Spade +Darth Val DarthNevidia DarthSpliffs +DarthWrecker Darthe Darthodium Darthonion90 Darthrodger +Darthruneis Darthshea Darucell Darush Daryl Darz Darzix +Darzo5 +Das Idle +Das It Maine DasGoose DasReich88 +Daschiva +Dascrez Dasgirl09 +Daspwn +Dasrx RNG +Daszler +Dat Geezer Dat2eWoord +DatBoiNibbe DatBoyLoy DatGuySteve +DatIronNinja DatNeck +DatPeko DatYeet Data +Data Diddler +Data Privacy +DataDruid +Datadyne Datames Datcookie34 Date Dathx Datis +Datoxicate Dats Datskai +Datting +Dattos +Datuk +DaturaTrip Daubs +Daud +Daughters +Daunt Dauntless +DauntlessXav +Dauth Elda +DavSko Davaflav Davai +Davantage Dave +Dave From Au +Dave Hooray +Dave J +Dave Mario +Dave Senpai +Dave The Egg +DaveAckery DaveDaBeast +DaveDorvis DaveLister DaveYognaut Davebarbaren Davedication Davedude +Davej86 Daveken +Davesty +DaveyB0nes DaveyWSM David +David Co +David Duffs +David Ortiz +David R333 +David007 David2699 +David4755 +DavidKinaMan DavidPH Davidisiscoo Davidopathy +Davis751 Davlan Davo Davord +Davoron +Davros70 Davshing +Davu +Dawaj Lama +Dawg Dynasty +Dawkins Dawn +Dawn Destiny +Dawn Era +Dawn Summers +Dawn Wall +DawnEverbane +DawnSwanMon Dawna DawnfaIl Dawson141 Dawtrix +Daxidol Daxon +Daxx95 Daxxzy +Day Dreams Day Lightz +Day1ofNoFap Day6 DayJarVu DayOff2JOff +DayStar Turk +Daydrifta +Daye Xero Daykillz +Dayman_GIM Daymien +Daynnn +Daysi Darcey +Daystar +Daytrip +Dayuf +Dayveeeee +Dayz of Iron +Dayzd1 Daz3dnBlaz3d +Daze +DazedDave DazednConfsd +Dazzler95 Dazzz Dbeatz +Dbewt Dbmx +Dboot Dbricke +Dbz Pride +Dc B L I N D +Dc Hakkarn +Dc IronMan +Dcaravan Dced +Dced at Corp Dception +Dcing Dday6Jun1944 +Ddestroyer56 Ddos Dds4theko Ddsing +Ddsme +De Algerijn +De Baardman +De Eryngy +De Fishy Guy +De Gennaro +De Hoppert +De Jam +De Krikke +De La Trey +De Marco +De Max +De Mechelaar +De Pette 69 +De Redacteur +De Sam Matie +De monic De stoutsten +De us +De1icioso DeAndre DeDawgTubb DeEgis @@ -5387,38 +11178,80 @@ DeTrixzz De_lemme10 DeaTH Deacey +DeaconDrew Dead +Dead Baldy +Dead Bozo +Dead Chilli +Dead Easy +Dead HC Here +Dead Her0 +Dead I Guess +Dead Mum +Dead Policaj +Dead Psyc +Dead Sellout +Dead Ticks +Dead XVIII DeadDogRed +DeadGhost DeadKelly +DeadNowM9 DeadPickle DeadPower DeadPuto DeadRealSoon +DeadSanson DeadSlice +DeadSmog3 DeadWife Deadarrow99 Deadbeater +Deadblinx Deadened +Deadfallen Deadlft +Deadlift Deadliftjr Deadly +Deadly Pixel +DeadlyAnt DeadlyBatz DeadlyDJ +DeadlyDrinkr DeadlyHit DeadlyNecros +Deadman AF Deadness Deadpaal +Deadrango +Deadwool DeadxProof Deaf +Deaf Person +Deaf RScaper +Deafmau5 Deafs +Deakien Deakz +Deal or nah Dealing Dean +Dean Jac DeanRs Deandelouest Deantotheo +Deaptic Dear +Dear Deer +Dear no one Death +Death Devil +Death Eaters +Death Is Fun +Death Loco +Death Qweef +Death Rainer Death0fyou DeathByRC DeathPanda @@ -5427,133 +11260,224 @@ DeathSurgenc Deathbyclick Deathcloude Deathcore +Deathduspart Deathfuzz13 Deathhope666 +Deathism Deathit55 +Deathmast333 +Deathomen Deathrattle +DeathsCaII DeathsCoffer Deathscyt56 +Deathstarlol Deatths +Deb0wer Debauchery Debb Debdu +Debil Missed +Debiruss Deborla Debug Debwani Deca +Decapacitate +Decard Cain +Decarium Decath DecayedWrath Decca +Dece1ve Deceive +Decent Drugs Decerate Dechmann Decides Decim +Decim-san Decimate +Decius Maxim +Deckiez +Deckler Declare Declined +Deco Himself Decolonize Decomposed +Decon Decs +Dectron1 +Ded Ronald +Ded Smithy DedClicks DedVittu DedWilson DedZap DedZeppelin7 +Deddo92 Dedfin +Dedge Inside +Dediabl0 Dedicated +Dedicated XP Dedmoo Dedressing DedsetLegend Dedville Dedzone +Dee DeeLizard DeeLuxe DeeZe +Deedeedee630 Deedlebees Deeego DeeezNats Deejay +Deeku vaan DeelDoughs Deemush Deep +Deep Medi DeepInUrMum +Deepika P +Deepinuh Deepnsider Deerboy7I7 Deerhunt03 +Deerkl Deerski Deerskin18 Deevos Deeyja +Deez Mangoes +Deezima Deeztroyer Deezy44D Defaultbomb +Defaulted +Defeat Evil Defeater Defektas Defelorn Defence42 Defensief +Deffi +Defib +Define Pain Deflexus Defloat +Deflorator +DefoNotKiwi DefoNotSpoon Defog Deformedhell +Defqonlord Defrosted Deft Deftones Defund +Defunto2 +Defy Limit +Defyinq +Degak +Deganti Upe Degas +Degen Darra +Degen Ryu +DegenGod DegenRetard Degeneraatio +Degenic Neet +Degenomics Degradedd Deguns DeiAx Deidera Deifi Deimoss +Deinsmeins +Deiron Tyson Deitonus Deive +Deiviiz +Deivis +Dejagoo +Dekeyser +Dekkens Deko Dekul +Dekylex Del1nquent Del4no +DelaIron +Delaey Delay Delciotto Deldeen Dele +Deleted +DeleuzeSucks Deleven +Delga +Delger +Delhaize +DeliMeats Delic +Delicoffee Delimain +Delirah +Deliyria +Delkku2 Dell +Dell De Dul +DellaRS Dellamor Dellies +Delmar Delmaro Delonius Delphan09 Delskiii Delsym Delta +Delta 516 DeltaCloud DeltaEpsilon DeltaPapa Deltaskull +Deltawye Delten Delusional Delusionnn +Deluthul Deluvial +Deluxe5 +Delvex DemSkillsDoe Demander Demboo Demco Demerlay +Demetre130 Demi DemiGodKempo +DemigodI Demising +Demmegod Demolidor Demon +Demon Albarn +Demon Flare Demon Plate +Demon RS Demon1793 +DemonWolf1 +Demon_Matrix Demonaly +Demonas Demonboys1 Demoni Demonic @@ -5562,14 +11486,31 @@ Demonleader8 Demonslay335 Demonteats Demoted +Dempa Dempeanut +DempseyRoll +Dempsters Dempsu +Demyze +Den Bever +Den Duivel +Den Illya Den1z +DenZelouZ +Denaelc +Dench Bee Denclosure Denferok +Denfoe +Denggly Denglish +Denis Denix6 Denizenn +Denkil +Denkue +Denmarkian +Dennaro Denneny Dennis DennisWilbur @@ -5577,9 +11518,12 @@ Dennisfr Dennispanda1 Denny Dennyispro +Denonweff DenouncedGod Denoxite +DenseLayer Denssoni +Densu DentalMental Dented Dentistry @@ -5587,50 +11531,88 @@ Dently DenverHockey Deny Deny92345 +Denza93 Deon Deoo +Deoxy_19 Deoxys39 Depdada007 +DependaAF Depends +Depenenkal +Depletion Deplox +Deprimes Deprimir +Depster DepthStryder +Deputy Mibo DeputyDwigt +Der Hungrige +Der Panda DerPunkt31 Derbados DerbyDerbs Derdel DerekBarnett DerekEmKay +DerekWins Derenity Derick +Derived Hope +Derk Danger +Derkalurka +Derkila +Derkle Dermijiny +Dern Swan Derot +Deroy DerpCanin +DerpDeezy Derpmoil Derptimusbro DerpyClayDog Derrick +Derrick Rose +DerrickC145 Derrik Derrin +Dertay Des0rg +Desaus +Descendence +Desconectado +Desear Desert +DesertAmulet DesertEagle +DesertFlower Deserve Desi Desigium Desinger +Desinteresse Desire Desk Desley +Desn1q Desoroth Desper Desperados +Despitous Despot Dest +Destab Destinneh +Destiny Goat Destr0i +DestroyButts +Destructoid Desuelle +Desuhmate +Det BabyLegs +DetRedWings Detain Detects Detectum @@ -5639,219 +11621,416 @@ Detonati Detone Detriment Detrimental +DetroitSux Deugeniet Deunek +Deur NL Deurloos Deus +Deus Esq +Deus leto +Deus lux est DeusFerox DeusXQuinoa Deuxth +Dev Jacob +DevMoney420 +Devanar Devb0t Develinside9 +Develique +Devidedby0 +Devika Devil +Devil Horns +Devil lnside Devil9394 DevilOfheavn Devildog Devilege Devilgod +Devilish Deviljin583 Devilman Devils Devilsbkb0ne +Devilsjoker DevinTheDude Devinchi Devious +Devise Devizer Devo +Devon Btw +Devoted I Devotion Devoured Devourkitty +Devry Cain Devstated +Devv N +Devvy UwU Dewa +Dewarping Dewiro Dewitten420 Dewk Dewrunk Dewsk1 +Dewts Deww +Dex Territy Dexby Dexderp Dexlan Dexter +Dexter Lou Dexterity Dexxaz +Dexxtrouss +Dexy Fiend +DexyDean +Deykota Deyou Deyron Deysean +Deyvyejones Dezerthuntar Dezi-VII Dezkor2 +Dezmezz Deztroyer +Deztroyer 0 +Dezzu +Dflow023 +Dfs Sfs +Dfuks +Dfw Dgalaga Dgref DhRecka Dhae +Dhaegar57 Dhaeqriyil +Dhaggz +Dhalsim +Dharek +Dharma Van Dharoc Dharok +Dharok 0bama +Dharok Derek +DharokHardOn Dharokbomber +Dharoker +Dharokz Dhavy +Dhides +DhingusKhan Dhon +Dhon Do Dhor +Dhr Leon +Di vau vand DiaRelent DiaThresh +Diabalo DiabeticKid DiabeticPhuk +Diabetucus Diablo Diablo47741 Diablosis DiabolicDead Diade Diagnosis +Diagonlane +Diah Diako +Diako Gyan Dialga +Diam0nd01 +Diamega Diamond +Diamond D +Diamond Jozu Diamond2192 DiamondScott Diamondback Diamonds +Diamonds Jr Diamonds lit Diamoniakmep Diampromidi +Dianakins DianasaurEgg Diane Diapeetikko +Diaper Dan Diaresta Diariez Diarrhea +Diarrhea Tom Diart DiaryNoob +Dibbo Dibbu Dibidos Dibidus Dibsis +Dibsis CoMa DibtheLegend +Dibudabudi +Dibyel DiccEat +DicedBrotito +Dicer +Dicey ReRoll Dicio +Dick Jones +DickNBallin +Dickbruiser +Dickeroo +Dicko Diclo Force Dicnar +Dicso DictatorNL +Did You Fart +Did You Try DidAnalOnce DidISayWeast DidNotDieBtw DidUJustBgs +Didavoo +Diddle Drip Diddlybopp Diddmeister Didi Didnt +DidntAsk Didny +Didsome1say +Die hard 66 DieAntwoord +Dieby Died4Hides DiedRank DiedSierra +Diedrrr027 +Diegoide +Dien0 Dieno Diesel +Diesel Bill DieselPump +Diet Andy +DietSquid Dietch Dieu +Diffindo +Diffy +Dig Bick 420 DigBicker +DigDaniel DigOlBick Digestive Digger Diggernick32 Digghass +Diggy Digi1al DigiDestined DigiFlisp Digifreak045 Digironimo +Digit28 Digital +Digital Age +Digital day DigitalGamer DigitalKillz +Diglett Dave +Dignace Dignity +Digtalcarrot +Digweed Digyeety Dihl DiiK Diiferent DiiirtyDog +Diisphoria Dija +Dikk Mabbutt +Dikkaz Dikke +Dikke Hobbit +Dikke Kei Dikken +Dikuufd +Dil Pickle +Dilbo +DildoBagguns +Dilexro +Dilfslayer Dill0n +Dillaz Dillbert +DilleFlute Dillhen +Dillon DillontheFox +Dillusion94 Dilly Dillywacka Dillzpickl +Dilogos +Dilph Diluzion +Dilvy +Dilx +DimDimz DimJangle +DimKills Dimaa +Dimathys Dime +Dime Daddy +Dime Skate +Dimebag217 +DimitriosMVP Dimmu +Dimmy Dimples +Dimwits +DinFulaJavel +Dinamite +Dinari Dindu DinduNuffen Diner Ding-A-Ling2 +DingDong DingDongDell +Dingbat Rat Dinger30 +Dingle Eater Dingus +Dinh Dinho1 +Dinieras +DinkeLBerrG1 Dinkerwoltz +Dinkis DinkleBrrgh +Dinkleberg DinoBoy7 DinoSnore +Dinomite Dinoparrot91 +Dinorock +Dinosaur Dinostrong +DioBrandoXx Dioden +Diogaite Diogo Diogun +Dioor DiorTemplar +DiorTheGreat Dios +Dios world +Diosdado_12 Dioxins Dioxis6 Dipdadronan +Dipical Dipli Dipolio DippaDave Dippoldism +Dippy +DippyMcshit +Dipshit Dan Dipyo +Diq-In-A-Box +DirectDesire DirectDsLcul Direwolf Dirk +Dirk Stryker +DirkDigglr Dirkieflurky Dirol Dirt +Dirt Shark DirtAss +Dirtbag Dirtbikerpro Dirtboy345 Dirtjogger Dirty +Dirty Dozen +Dirty Nuts +Dirty Nwah +Dirty Pack +Dirty Tampon +Dirty Wit +Dirty Wookie +DirtyDoc DirtyGeUsers +DirtyGlock DirtyKo DirtyLube +DirtyOldMan +DirtyShire +DirtySouthNz +DirtySpade +DirtyWata +DirtyyIron Dirun +Disabled Disaster0 Disasterolgy +Disastro +Disbeliefs +Disc +Disc Chucker DiscGG +Disclosing +Disclosure Disco +Disco Mayhem +Disco PvM DiscoDarwin DiscoFever DiscoLars DiscoTronix +Discoburger Discodoris Disconnecct +Disconootje Discord Discordian +Discounted Discoveredx +Discretions Disentombed +Disfunctie Disgusting Diskmedel +Disloyal Disneyland +Disodrer +Disorderly Disperse Disprivilag +DissTeam Dissidence +DissyReborn +Distained Distanc3 Distard DistinctEvil @@ -5860,219 +12039,482 @@ Distroyer972 Disturbed Ditherman Ditt +Ditt o Ditto Ditty +DitzyGranny +Dive Ball DiveMedic Divertus +DividedSky +Dividends Divin3RS Divine +Divine Dream +Divine Fenix +Divine Furby +Divine Mass +Divine One +Divine Oodle +Divine Sveta +Divine Zaros +DivineLegend +DivineRhythm +DivineShine +Divinenro Divines +Divinethug Divinez Diving +Divinitii068 Divoky +Divorce +Divxd Dixed Dixie +Dixie Pixies +Dixin Yass +Dixon +Dixon Planks Dixxy +Dixy Wrecked +Diy Dan Diz_OG Dizho Dizk Dizstruxshon Dizza Dizzleslayer +DizzyDan +DizzyPanda DizzyRnG Dizzzzy +Dj Dimu +Dj Jordi +Dj Ju +DjNateP DjSpicyNuts DjWalkzz +Djacquays94 +Djagzar Djahat +Djauw +Djavul +Djbeejay99 DjenCraw DjentleSoul Djl0077 +Djmx1000 +Djoefer Djozztico +Djscoobsta_X Djunya +Djurfan +DjustinT DkPepper Dkafeine Dkzz DlAMONDS +DlBz +DlCKE DlCTATOR DlDS +DlE MACHlNE DlNNER DlONNE DlPLO DlRECT +DlSCONNECTED DlTTO +DlXON Dlghorner DliveMarc Dlorean +Dm8 +DmVinny Dmage Dmante Dmfs +Dmgp Dmhc Dmi11z +Dmitri333 +DmkFight DmkKilla Dmoe +Dmt Kitty +Dn Dayander +Dn Havoc +DnB Fanatic +Dnc +Dnv +Do It Best +Do It Myself +Do You Zerk +Do ur Best DoDArmy +DoHerbRuns DoKZx +DoMeDirty035 DoMeHardNan DoSoQi +DoUEvenTank +DoWerkSonn Doadles Doajiggi +Dob Au Dobar +Dobby Hanzo DobbyGotPwnd +DobbyTheSock Doboner Dobster +Doc Doc +Doc Heart +Doc k i n g +DocDingle DocGiggles +DocMonocle DocVamp +DocYouSign Doc_Hershey +Docere Docs Docta +Docta Mantis +Docta dean +Docter Brule Doctor +Doctor Ace +Doctor Bubca +Doctor Exx +Doctor Fail +Doctor Funk +Doctor Fuzz +Doctor Iron +Doctor Krieg +Doctor Luck +Doctor Oby +Doctor Swag +Doctor WormP +Doctor Wylie +Doctor Zeh DoctorCoc DoctorHeiter +DoctorKraft +DoctorNutz +DoctorRex +DoctorSquat DoctorTsom DoctuhDrew Docxm Doddi +Doddy8D Dodge +Dodge Feet +Dodger995 DodgersBRAH Dodgy +Dodgy Player +Dodgy Todge Dodjomaster +Dodko Dodo134 Dodokipje Dodol Dodose +DodusNet Doefos +Doeidoei +Doep Doesnt +Doesnt Trade +Doetje Dofric +Dog Fart Net +Dog Myrnac +Dog The Frog +Dog did Meow +DogDiet DogFlyRatCow DogLogNogJog +DogShitter1 DogSoldier29 +DogTag Dogan +Dogannn Dogax Doge +Doge Caravan Dogecoin100x +Dogesaurus +Dogessa Dogestyle +Dogfruit1555 Doggiezz Doggoat +Doggshit +Doglinsheran Doglips +Doglover7004 DogmanJones +Dogplaceman Dogpoop +Dogres Dogs +Dogsh1t Main +Dogshit Rick Dogslayer420 Dogsume +Dogtogod Dogzie Dohace +Dohdee Dohenus Dohero Dohski +Doin It Raw +DoinGodsWork Doiran99 Doiu +Doja Chompy +Doja Shark Dokarius Dokdo Doken +DokeyPickle +DokiToast +Dokter Klok +Doktor Onion DoktorBarber +Dokuganryu +Dokuhime +Dokusei +Dol +Dol Alt Dolce +Dolerkyo +Dolf Dolham Doll +Doll of Evil Dolle Dolly Dollynho +Dollynho Bro Dolomite Dolormight Dolph DolphinAnus DolphinPucci Dolphinl1ck +Dom R +Dom Wong DomGaz +DomLaurencio DomYouAll Domanater105 Domantass +Domatic +Domccas Domeeee +DomiGothMomi Domiinant Domika Domimic +Dominate Jr Dominaton +Dominator541 +Dominatorrz Dominikus67 Dominionmake DominoTerry Dominos +Dominus Omni Domiros +Domixin Domke25 +Domknit +Dommie 07 Dommy +DommyDucky +Domo Teddy +Domoralized Domoszlo Doms +Domstad Slay +Domtoren +Don Abuja +Don Bob +Don Boki +Don Con +Don DingDong +Don Dotta +Don Huono +Don Matt +Don Moochie +Don Persian +Don Robb +Don Sebitas +Don Serrano +Don callum +DonB0t +DonBroco DonDennis94 +DonMarcino +DonThinKill5 DonWonTon420 Donair +Donair Dude Donal Donald Donald Dumps +DonaldTrump Donaldfact DonaledTrump +Donantelo +Donate Here +DonateBond Donato Donchz +Donda +Dondd +Donderschok +Done enough +DoneThat Dong +Dong Chan +Dong em Down DongActual +DongCowboy DongEater +Donger Lord +Dongi Donging +Dongolark +Dongs Ahoy Dongsalad Dongu Dongus +Donkerblauw Donkere +Donkernick +Donkey Kink +Donkey Squid DonkeySocks +Donkhu DonldTrumpet Donnatello DonnieAssie Donnieduke Dono DonoWho +Donology +DonovanRS +Donsalt Dont +Dont Be Pked +Dont Heal +Dont Sir Me +Dont You Shy +Dont do dat +Dont skill +DontBeMad DontBotNerds +DontDieMase DontDieSans +DontEatMyDog DontGetDrops DontHoldBack +DontJump +DontPanicPlx DontSassMe1 +DontStarve2 DontTellFeds DontTouchIt DontUseFKeys +DontWarnMe +Dontneedbp Dontyoudoit1 Donuhtzz Donut +Donut181 +Donutman +Donvys Donwcpot +Doob Doobe +Doobie Doo +DoobleDecker +Doodle Bob73 DoodleCraver DoodleTheGod Doodled +Doodledash +Doodoolist Doodslag +Doof Doofe +Doofensmirtz Doofy +Doogl3 Doogyplumm Dookami Dookers Dookie Doom +Doom Bar +Doom Metal +Doom Raiser7 DoomDoomDoom DoomFruit Doomblade Doomed +Doomed Necro +DoomiShroomi +Doomkid50 +Doon +Doooooobie +DoorDshGamer +DoorTech +DoorToLight +Doorknob +Dooz 12 Dopamemes +DopaminAbxsr Dopamine +Dopamine OD Dopatopia Dope +Dope Doc +Dope Plugs +Dope Sweater +Dope btw DopeAnteater DopeCalippo +DopeDrop Dopedrift Dopeturtle Dopeules +Dopey Iron +Doppa +Doppleganger +Doppleladler DopplerDank +DoraDaExplra DoraSweetass +Dordin Dore +Dorede Dorg +DorgonPocket Dorgoroth Dorito Doritomancer Doriva Dorkydevil93 Dorman Jr +Dormant Evil DormiNdaExp Dorohedoro Doronn @@ -6080,39 +12522,126 @@ Dorp Dorse DorseHong Dorthea +DortyKil Dorvagten +Dory364 +Dos STI DosEquis DosKadenas +Dosia Dosk Doss +Dostalgia +Dot Death +Dot Head Dot_Tea Dothalican +DottySloth Douane Double +Double CupMc +Double Drop +Double Take +Double agent +DoubleBrowns DoubleDeezus Doubleb011 Doubledonk +Doubt +Doubt It +Doubted Pyro Doug +Doug Salt +Douglasotto2 Dougoboy Doujinmoe Douq +Douyin +Dova_sage Dovahkiintim Dovahkyng +Dovitello Dovydas54 Dowatnow Down +Down Vote Me +DowniePatrol Downland Downlifter Download Downtown +Downworld +Dowzi +Doxus Doxxme Doxy Doye +Doyer1 +Doyers Dozck Dozerdayne Dozerrrr DpittmanIM Dqzi +Dr 7 +Dr Atsum +Dr B3rry +Dr Bading +Dr Barty +Dr Butt Love +Dr Caccon +Dr Camembert +Dr Cremaster +Dr Crumpet +Dr DO0M +Dr Edy +Dr Flipflops +Dr Gainz +Dr Geyseks +Dr Glottis +Dr Gotham +Dr Gulak +Dr Harry Nut +Dr Ivar +Dr Jago +Dr Jerry +Dr Kenchi +Dr La Scotte +Dr Lulu +Dr Madv +Dr Mawffle +Dr Melons +Dr Mikey +Dr MonaLaser +Dr Mustacho +Dr Mustard +Dr NFC +Dr Neuron +Dr No Life +Dr Oreo +Dr PFAFF +Dr Phil +Dr Plebeian +Dr Quirke +Dr Rave +Dr Robster +Dr Rum +Dr Shook +Dr Siesta +Dr Slayer +Dr Stamps +Dr Swaggins +Dr T e a r +Dr Tanner +Dr Volts +Dr Will MM +Dr Yosty +Dr Zeby +Dr Zeh +Dr Zimm +Dr obZen +Dr smithing2 +DrAsTiiC DrBeast DrBen50 DrBigSang @@ -6121,96 +12650,197 @@ DrBlumpkinz DrBuckFitchs DrButter DrC0X +DrChezz DrCrunch13 DrDRespect +DrDankMan +DrDankRip +DrDemitrius +DrDennisMD +DrDitalini +DrDoddi DrDruen DrEfficient DrFlobe +DrFognog DrHugecookie +DrJoose +DrKayzed +DrKevinn DrKiloGram +DrKush69 DrKushBurner +DrLoomis +DrLuckyacorn DrMatthie +DrNachtiga DrOly +DrPeppermmm +DrPhil2019 +DrPhineas DrPoopstein DrProfess0r +DrRift DrRipStudwel DrSevens +DrSkunkPhD DrSquanto +DrSwag +DrWodahs DrWoolyNips Dr_Olusegun DraCoNiiaN +Draacaryss +DraakSeviper +Draakmonkey Drac0claws Dracarus Dracaryys +Draccnor +Drache104 Draciel Drack3d Drackon Draco +DracoGhost Draconian111 +Dracote Dracts Dracula50125 +Draeghem +Drag Puff +DragSyndrome Dragen +Dragen Ballz +Dragers DraggSlayaa Dragnarok Dragneel Dragnipurake +Dragnkllr233 Dragon +Dragon Boots +Dragon Brew +Dragon Gras +Dragon Imp +Dragon Not 6 +Dragon Sword +Dragon Those +Dragon7998 DragonAxePlx DragonDrew +DragonKlaas DragonPrime +DragonSpayer Dragonbait4 Dragonboy691 Dragoncore74 +Dragonfeir +Dragonflew Dragonheat46 +Dragonkin892 +Dragonkingiq Dragonlear1 +Dragonluv16 Dragonmake DragonoidA2 +Dragonrains Dragonrune16 +Dragons Iron +Dragonslayaz +Dragonstompy Dragonstone Dragonvirse Dragonz +Dragonz Fury Dragoon +Dragoon0517 Dragoonalfa Dragoonhuntz +Dragoonnoth +Dragoscale03 +Dragq +Dragster5300 Draind Drairi +Draiserman Draithus Drakanelf +Draken Feest +Drakenwold Drakesh12 Draket +Drakhom Drakken Draknar DrakoVamp01 Drakonid +DrakosWrath +Drakuonis Dramyre +Dranasty Dranty Drap Drapht +Draugadroid +Drava +Drawbridge72 Drawde Drawll +Drax +Drayden Drayheart +Draynth Drazar Drazart5 Drazhe +Drazzo Drbeto11 +Dre Skrila Dread +DreadPandora +Dreadfvl +Dreadnott173 +Dreadnough77 Dreadnoughtt Dreadnuts +Dreadzie Dreagation +Dreak Dream +Dream Deeply +Dream Delve +Dream Plan +Dream Queen +Dream Realm +Dream Tempo Dream-layer DreamJT Dreamboats +Dreamcatcher +Dreamchasr +Dreamfreeze Dreamguy +DreamingLily DreamingNote +DreamisBack +Dreams Real Dreamscape Dreamstain +Dreamvil +Dreamy Ku DreamzRs Drecy +Dredd Dreddshott Drednok Dreek +Dreepy885 +Dreeww +Dregulle Drei +Dremarial +Drenthe Drenz Drenzek Drenzi @@ -6221,78 +12851,167 @@ Drew89 DrewNG DrewToshiro Drewbber +Drewbob24464 +Drewby Drewcas Drewfuss32 DrewsHotMom +Dreww +Drex +DrezaR Drezilith Drfannypack Drgreenthumz +Drie Fristi +Dries D-P +Driesca Driest Driewieler Drift +DriftTap DriftVolvo Driftzzilla Driger +Driger F +Driink +Drijf Hout Drikett Drikon +Drill n Fill Drink +Drink 4 Loko +Drink Empty +Drink Local +DrinkCheap DrinkPapaMlk DrinkSlinger Drinkability +Drinker +Drinkin Pete Drip Drip To Hard +Dripping Gut DrippingSack +Drive Stick Driver193 Drivious +Drizzy Drake +DrkMstr89 Drkfirelord2 +Drlime +Dro0 +Dro268 +Droefto3ter Droge +Droge Dackel DrogeBanane +Droid Dromedario Drommy Dromy +Dron Iick +Drone Reed Drone347 +Droog Meneer Droom Droopy Drop +Drop Acid +Drop Brews +Drop Please +Drop Rates +DropGetter DropMeUrPet DropTheFlop DropTopWizop +Dropko +Dropletics Drops DropsWhen +Drosaire Droski +Drouter Drri Drrrrunk Drswole DrtyBusDr1vr +Drtyhnddog Drudenhaus +DrudgeMunkey Drug +Drug Test DrugProblems +Drugged Out Drugonaut +Drugs Hurt DrugsArBadMK DrugsHere +Drui +Druknr +Drukzah +Drum +DrumBarrel +Drumaz Drummaboy276 Drummadr Drummerz Drums +Drums X +Drums of War DrumsSpace +Drumz Drunk +Drunk CCTV +Drunk CJ Drunk Jake +Drunk Mimic +Drunk Pastor +Drunk gaming +Drunk n High DrunkAssassn DrunkDriving +DrunkGhillie +Drunken +Drunken Monk +Drunken org Drunkfam +Drunkiin +Drunkn Fun Drunknerd Drunknhiiiii +Drwe +Dry 4 Hilt +Dry Cleaner +Dry Dong +Dry Hard +Dry Hardcore +Dry Soup +Dry Trip +Dry on stuff +Dry so I Cry +Dry x DryBandit DryWolf Dryhump +DryksMuseum Dryness Drynx +Dryron Man Dryskies +Drysol +Dryune999 Dryya +DshubaMisfit +Dsk 10 DssHimself Dstroyed +Dt2m +DtWoofles Dtjenl Dton +Du +Du Old Pker +Du lourd Du5tin 3 DuTson DuYorick @@ -6300,231 +13019,489 @@ Dual Dualerz Dualicious Duaneker +Duarterecife Duathlon +Dub Season +DubC Brownie +Dubalonius Dubber +Dubbers15 +Dube Dubie +DubieDebies Dubios Dublestuffed Dublet Dubs +Dubs_789 Dubsake Dubspeck DubstepsDead Dubu +Dubu Dahyun Dubz4Dayz Dubzy91 +Duc +Ducid Lream Duck +Duck Sucker +Duck Tape +DuckEnte +DuckNoSmoke7 +DuckSick Duckcited +Ducklovesyou Duckreas +Ducks Quac +Ducks U +DucksFatha +Ducky HVIII +DuckyTears Duckzi11a Dudash Dude +Dude Come On Dude12677 Dude9103 DudeAtHome +DudeItsMikey +DudeWD40 +Dudefish64e Dudeimblazez +Dudenmi Dudetastic +Dudification DuelKing +Dufendr Duff +Duff Daddy +Duff Groupie +DuffHeavy Duffmanas Duffs Duffy234 +Duffybroh Dufwha +Duggie Dugong1338 +Dugsie +Duh Kota +Duhstin Duhul +DuiBuQiShaBi +Duindorp +Duk Me Hard +Duke Ag +Duke Babylon +Duke Roland +DukeBaird +Dukino +DukkuTzi Dularian +Dulezburg Duli +Dullskie Dulwich +Duly Ignored +Dum n dumr DumFuknIdiot Dumb +Dumb Alien +Dumb Ass Dan +Dumb Drunk DumbDog DumbMatt DumbThiccAss Dumbaneering Dumbass +Dumbass 69 +Dumbass Tony Dumbells Dumber Dumbfounded +Dumbfuk +Dumbi +Dumbledore +Dumesday80 +Dumfries Dumle +Dumoo Dumpert +Dumpsterjed1 Dumwood +DunJunn Dunadane +Dunbahn Dunc95 Duncan +Dunco Dundasso Dundefeated +DunderHonung +DunderLadd +Dundiez Dundonian Dundrift +Dundrturken +Dune dain +Dungfung Dunghead Dunizz Dunjins +Dunjun +Dunkbox Dunked +Dunked on +Dunkies +Dunkle Sonne +DunknDink +Dunkston Dunlaf DunneDone709 Dunnill Dunwall +Duo Armadyl +Duo Elysian Duodenum Duoing Duoli DupaMuck +Dupe Holder +Duper +Dupey Duplicated Dupy007 Duqq +DuraDoobs Duracell Duracell321 +Duradad Duradal Duradels Duradyl +Durag Pat +Duramax +Durant Duravillidad Durex Durexslayer +Duri Durial Durial32won Durialives Duriel1 Duriul321 Durkburt +Durlz Durni +Durns Main Durocher Durooo +Durtsa +Durtsamang +Durtstar Durty DurtyFish DurtyFish HC Durukah Durumfe Durumoomoo +Durza Spy +Dus1O Dusansss Duseballs2 +Dushie +Dusk Summers Duskel +Dust in Dusted Dusters Dustielle +Dustin Rush +Dustinant5 Dustinn +Dustins Main Dusto56 Dustpan18 Dustt +Dusturbia Dusty +Dusty cones +DustyNoods DustyPope +Dustyin Dutch +Dutch Diablo +Dutch Dog +Dutch GOdz +Dutch Genius +Dutch Quin +Dutch RvG +Dutch girls2 +DutchEnergy DutchStuff68 +Dutchbobby +Dutchie +Dutchtin DutchtinOS +DutchtinsAlt DutchyCrazy +Duty Dutzu Duuni-Pete Duva Duvel +Duvelicious Duvl +Duwua Lipa +Dux Sauce Duxt Duztin Dvaergen Dvdasa831 Dven Dvlkid +Dvn Dvsk Dvst +Dw b happy +Dw ight +Dwagos 2 Dwake +Dwaki Dwarf DwarfSailing Dwarfoox Dwarfson +Dwarve Mage Dwaybe +DwerlzCH Dwibbel +Dwight Sn00t +Dworf Dxnnis Dxscoveries +Dxue Dxukko Dydapynk +DyePhix +DyinToLive DykeMenace +Dyl Dough +Dyl Gates Dyla +Dyla n +Dylan Du Sol +Dylan Iron +DylanCarl DylanFx DylanWad Dylanimal Dylanm504 Dylann070 +Dylano Dylbots +DyldoGreat +Dylectable Dylian +Dyllann Dylrex +Dyls Dylshanwang +Dylson +Dylsy Dymass Dymenshun Dymosh DynamicDuo +Dynamicdan47 Dynasty Dynloris Dynomite +Dynstyreborn Dyonutborne +Dyonysos Dyoung116 Dypso Dyrakos +DyreBatt +Dyreshot Dyrus +Dysae +Dyslexic gg DyslexicTree +Dyslexual +Dysliexc +Dyspnea Dystopias +Dysturbed Dystxpian +Dyth +Dyvv +Dyy Dyzurah Dz03 +DzRz +Dzakar Dzangg Dzentelmenas +Dzievan +DzintarsOls +Dzoseris +DzsungelTyuk Dzud +Dzun +E D V +E Fox +E MB +E N I M A +E R A +E S H +E SL +E V 0 L +E Z I O +E hm C uee +E ldest +E lixor +E lvarg +E lves +E mni +E mpathy +E njoy +E nvy +E p i i +E r ic +E th +E thy +E volve +E-Dawg478 +E-Grill E-Hubble E-S-T19XX +E1K E4as EA888 EARLYoCUYLER EBDB EBIDABLAY +EBZ ECCIES ECH0 +EC_Legends +EDATERHG69 EDGV1L +EDM Erik +EDM Playlist +EDMJo +EF tanq +EFFlClENT +EFILLAICOSON +EFL EG6808 +EGA EGIRLSIMPER +EGirl Marcy EHPause EHstro EIGRP EIessar +EIithi +EJ 207 +EJackUL8 +EL BULLYS +EL GA TO +EL Guuapo ELCAMI ELFlikeaBOSS ELITE +ELJosh ELUSIVE +ELVD +EMIYA Alter +EMPER0R95 +EMaes32 +EN X ENAC ENimmo +EOCscape +EOD20 EODdv EPLS +EREXI0N ERGENEKON ERJ145XR +ERRIE HERRIE ERRORMONSTER +ESCEDDIE +ESP Zone ESPARG0 +ESPORZUL51 +ETE76 ETHIOPIAN ETurns +EV6A +EVA ELFlE EVE0 EVScape EWFPLMFRLRLF +EXECUT0RZ +EXTT EXpoZuR +EZ Tag +EZ Till Dead EZ-Y EZBar EZnvm +EaTZyourOReO Eac63 +Eadgars Ruse Eadles Eagel89 Eager Eagle +Eagle Eye EagleSafari EaglesWentz +Eairj Eajk +Eara1 +Earendil Earl131 EarlRico EarlVincent Earll +Earll Ragnar EarnNest +Earth rune +Earth1ing +Earthling Em +Earz +EasY FraG Ease East EastEnders EastSyde +EastWho +Eastlakeclub +Eastment Easy +Easy Girls Easy1 +EasyDingMike EasyFro +EasyJet EasyNoRoids EasyRNG EasyTurbo EasyWhale +Easycore +Easyskillszs +Eat Crow +Eat God +Eat Hot Chip +Eat Tacos XD +EatBrick Kid +EatMoreGlue EatNutsGegex EatSand EatSleepPlay @@ -6532,43 +13509,84 @@ EatULive_666 Eatbananana Eathan EatingBigD +Eatpiebro +Eats Pant EatsPoop Eatu Eazie Eazy +Eazy AF +Eazy Rat +EazyGam3 Eazy_Peazy16 +Eazymon +Eb Marah Ebbitten +Eberebus Ebgame2 Ebinki Ebisumaru +Ebola +Ebolapaska Ebp90 Ebrahem2004 +Eburr +Ec0 +Ecahs +Eccles Alt Ecdubs Echo +Echo GIM +Echo Justice +Echo Slam Echo4211 +Echo_XVII +Echte Belg +Ecko RS Eckou Eclardyne EclecticDern Ecnubsirhc EcoLite +EconPhD +Ecs Nick +Ectuu +Ecxes +Ed Dobalina +Ed Si +Ed Word EdEddndEddy +Edd Gein Eddapt Edde7000 +Eddi e +Eddie GGs +Eddie MLG Eddie1402 EddieMercury EddieTross EddieVanHalo +Eddoz +Ede n Edelbae Edelweise +Eden XD Edga64 +EdgaRyto Edgar EdgarAlnGrow EdgarKing +Edgarr Edge +Edge c dr +Edge51 Edgelord +Edgerunner Edgevill Edgeville Edghill +EdgingGod +Edgy Boss Edgykid Edgynald EdibleChickn @@ -6576,22 +13594,48 @@ EdibleSnow Edisx13 Ediverse Edje +Edjob +Edmund +Edn +Ednnnx0 EdoKirurari +Edocsil Domi +Edocsil IRON EdotheJew +Edoxa +Edpls_o Edson Edspresso Edsy +Eduardo II +Eduardoo II Eduasia1 +Edunu +Eduzey Edvin +Edvius GO Edvyno1 Edwald0 +Edward Teach +Edward phish Edwarriorx Edwin Ownz +Eeeeeeeman Eeeeera +Eeekpenguin +Eeepen +Eek The Nub Eeli EelsUpInside +Eem lekker EenViezeVent +Eend btw +Eendsaurus +Eernegem Eeveeeee +EeveyBee +Eexhausted +EfORya Efendi EfficientBot EffinNips @@ -6599,63 +13643,147 @@ Effril Efnie Eform Eftur +Egg Boy +Egg Olmlet +Egg rolls +EggInTheRain Egganator1 +Eggs 11 +Eggs N Bacey +EggscapeRoom +Eggspert +Eggssmells Eggsy Eggtooth1 Eggy +EggyChipz +Eggzotic +Eglerz +EgoTeri Egoistic Egoran Egorous Egotistixal +Eh MapleTree Ehdjsnd +EhhFK +Ehms Ehnra EhpEhbGrind +Ehpatrick +Ehpriori EhrenSpoon Ehunter +Ei hekille +Eidolonvool Eientei +Eigenspace +Eigenvalues Eight +Eight_10 +Eigma +Eihwaz EikAyZ Eikel +Eilfs Eimp +EinBerlin3r Einar Einaras +Eindbaas +EindelijkMax Eingebildet Einsteins +Einswarior3 +Einyel Einzelkampf Eirik Eisen +Eisengor Eitsei Ejiogbe88 Ejjj1000 Ejma +Ejx +Ekali Ekim +Ekim117 EkkoThresh Eklof +Eklofs Ekonomisti +Ekovo +Ekstra +Eksynet +Ekwendeni Ekxo +El Arana +El Blame +El Blaze +El Boppo +El Chip +El Ghost +El Guapo +El Panache +El PapiTrump +El Pato lOko +El Patolino +El Pinguino2 +El Podger +El Scouse +El Smurf 370 +El WarLord +El Znorro +El-Fahoum ElBlancooo91 +ElGatoGris +ElGreg_4 +ElHash ElJReezy ElJohnny ElOso ElPoyoSenpai +El_Pickle69 Elaedor +Elano Elasticy +Elatedscarab +ElationArrow +ElbartoOSRS +Elbowd +Elbows Out Elbs Elchapo24 +Eldasero +Elder Coal +Elder Siroj +Elder nerd +ElderMoth ElderRyu Eldereon Elderpliney +Eldest Sense Eldia +Eldin Eldonskii Eldoubleyou Eldrek +EldridPenny Eldritch +EldritchMage +Elduz Eleandil Elebit Electr0lysis Electric +Electric Mud +Electric cat +Electrics Electrike +Electro Beat +Elektr0nas Element +Element Sk8 Elementality Elementhur Elementiix @@ -6668,57 +13796,97 @@ Elendaro Elenion Elephanten Elephants +Elephantz Elephent Elev +ElevatedLife ElevatedSoul Eleven Elevennn Elevil Elevo Elexuitt +Eley Elezar Elf King Leg +Elf u +Elfess Elfie +Elfinsocks Elfire626 ElfsWordFly +Elgifted Elgingo1 +Elgstant +Eli Ancients +Eli Fe +Eli Junior Elia135792 Eliass +Elice +ElichikAyase Elicit Elif +Eligos +Elijah Who Elilia Eliot Elipson Eliptats +Elirond Eliseuh Elit +Elitaire Lul Elitarisme Elite +Elite HIT +Elite Moscu +Elite Sendi +Elite Slayer Elite387 +EliteJager ElitePvmer EliteScaper +Elite_Quests Eliteisftw ElitesEyes ElitesFinest +Elitist Weeb +Eliwin Eliwood225 Elixir Elizabethboy ElizeRyd Eljaaa +Elkanath Elkias +Ell1eScape +Ellakazam Elliebus23 Elliot727 Ellise29 Elliterate Ello +Ello Ron Ellphie +Elly Dog ElmSpringsTN Elmatron Elmo +Elmo Narca +Elmos Wrld +Elmswood +Elnaphant Elnuma +Elo Luigi +Elo Solo EloJimmini Elocuente +Elodie +Elon M9sk Elons +Elox7 +Elpis Elppu Elqnaattori Elrebririand @@ -6728,56 +13896,121 @@ ElroyFTW Elryeth Elsa Elshak +Elsheshima Elshi +Elshire +Elsworth +Eltader2 +Eltry Eltuu +Elucidation +ElunedsSong +Eluniel Elusive +Elusive Drop Elusive818 +ElusiveOne +Eluti Eluw Elve +Elve Alt1 +Elve Elve +Elveiq Elven +Elven Durant +Elven Lamp +Elvenborn Elverum Elvey Elviax +Elvin Elvis +Elvis Crespo +ElvisArt069 Elvs +Elvyn Frenzy +Elvz Elwood +Ely the Man +ElyGoulding Elyam +Elyaris +Elyas5 +Elyas_Max +Elycal +Elyes Elyixa Elynescence Elyphosani +Elyqs +Elyrion +Elysi Elysian +Elysian Brew +Elysian OSRS ElysianError ElysianOP ElysianTank Elysianlove Elysianz Elysion +Elysiuhm +ElysiumFalls Elyte Elyxr Elyysian +Elyzarin +Elz +Em il +EmTvLive +EmaBeast OS EmaRae +Emacity momo Emad EmagndiM11 +Emalexa Emanated +Emanuel Emasterone +Ember +Ember of Ash +Embereus Embossis Emce +Emeowtion Emerald +Emerald Jack +Emeraldx Emergency Emericanpkur +Emerickb93 +Emeritas EmeritusD +Emhrys Emiel +Emiel Btw EmielM +Emielos +Emiit Emilharen Emilis +Emilliio Emilozz +Emilozzz +Eminem Yupp +Emirdagli Emirdagliii Emithyst +Emiya Shiro Emma +Emma Main +Emma Q1 +Emma epsi Emma1020 EmmaCn132 EmmaEmma EmmaRose +Emma_RC Emmaes Emmalitarosa Emmet1156 @@ -6785,28 +14018,52 @@ Emmet20 Emnay Emnicious Emnitylol +Emo Bee +Emo King Emoness Emoticon Emoyy +Empathy TKE Empathys Emperor +Emperor Elo +Emperor Xau EmperorBuggy Empery Empire +Empire State +Empireglorth Empirix Empty +Empty Box EmptyB EmptyBox Emptyhalo +Emptynogin Empyre +Empyrius882 +Emriat +Emu War +Emumafia Emus +Emylia En Jernmand +En ki +En oo vajaa En99omgangen +EnCue Enabled Enabler Enanthat +Enbrel +EnbyDeal Encrypted +Encryptiron +Encyclopedie +End Goals +End Grind End1ess +EndOnDeath Endanged Endem1c EnderMart @@ -6814,134 +14071,266 @@ Enderofwar Endgame Endgameplzz Endgegner +EndingTime Endir Endl3ss EndlesNights Endless +Endless Dawn +Endless RNG +Endlingg Endo Endor +Endos +Enedal Enemez +Enemm Enemyboy +Energie +Energise Energy +Energy Dave +Enert Enes Enfers +Enfes +EnforcerOP Eng1and Engage +Engagement EngelNacht Engen Enginaer Enginarc Enginear +Engineer Sam +Engineered Enginerd09 +Engl1sh Englandwon Englebassen +Englehr English +English Sir +English noob Enhtitled EnigmaCoder EnigmaWR +EnigmaZen +Enis Kanter +Enivid Enjoi EnjoiAssault Enjoy +Enjoy It +Enjoy Today Enjoymydhide Enjoys +EnjoysQuests +Enk rypted Enkaidu Enkeltje +Enkh Enki +Enkidu260 EnlargedNads EnlargedNut +Enmios +Enora Nyx FE Enoran Enormous +Enormous Hog +Enos EnoughGfuel +Enoux Enphadei Enraged +Enraged Crow +Enraged Lamp +Enranged Enrich Enrico Enrik Enroza Ensanguined Enshu +Ensnare +Ensomhet +Ent Sapling +Ent1tle Enterprise +Enthusigasm Entitled +Entiy Envello +Envidius Envied Envil Envious Envvi +Envx Envx1 Envy +Envy S +Envy lul +Envyctus EnvyouS Envys Envyy +Enza Denino Enzed +Enzel Enziguru Enzyme EoMeri EocBlowsNuts Eodwyn +Eol Ivan +Eos Adamm +Eos Mark Eostrix +Eowy +Ep1breren +Epaah Eperz +EphermalGoat +Ephi +Ephrayim Epiales Epic +Epic Nom +Epic Popo77 +Epic Specz +Epic Star +EpicGamer69 EpicReefer +EpicSalmon +EpicSpasms EpicToaster9 +EpicTomato EpicX Epic_Knight0 +Epicderk Epicoz Epicurus4 Epicvoid Epicx +Epigraphs Epiixx +Epikki Episodic +Epitohm EpitomeSlay Epix_x Epiyon Epods WifeY +Epoinen9 Eponas Eposs +Eppdawg Eppers Epping +Epson Eco +Epyll +Eq Equilibria +Equilities Equnox Eqwity +Er0l +Er1kasss ErBr +Era EraJorma Erabeus +Erad Eradicus +Eragon 1 Eragondragen Erase +EraseThat Erased Eray +Erbal ErbearIV +Erbiboar Erbyss +Erdi Erebus +Erect Chair +Erect Diglet Erectus Croc +Eredln +Eren The Nub +Eren Yaegar Erg129 +EriUmi Eric +Eric Brad +Eric Dingus +Eric Hansen +Eric RS +Eric V +EricN7 EricPrydz +EricR95 EricTheNoob +Erica +Erican Maxos Ericc +Ericgone13 +Erichilles +Erics Pixels +Ericsurf6 Eriction Erie Erijk Erik +Erik Ryatal ErikProbably +Erikaugust Erikci Jr Erikito09 +Erikkert +Eriknau +Erikoisjouko Erikoiskahvi +Eriksen14 +Erikske89 +Erikv28 Erinus +Eriscord +Erixzo Erkki +Erkki Pers +Erkkiks Erlid +Erlin Erlksson +Erm its me +Ermac +Ermafrodita Erna +Ernestaiii Ernesto056 Ernsour +Ero Erock2828 Erocktastic +Erodel +Erodoris Erony Eros Erosei +Erotic Maid +Eroxtroy Error +Error 510 +Error X Life +Error812 +ErrorSeven Errric +Errrr Ersei Ershayz Ershayzz @@ -6950,29 +14339,44 @@ Ertsu Eruni Eruptingcat Erwin +Erwin J ErzaGames ErzaScarlet +Es0x Luc1us EsArTee +EsJayW EsXP EsbenViking Esbuh +Esc anor Escalation Escaline Escanor1994 EscapeMaIron +EscapeWhere Escension +Escha Escnirp Esconex +Escot +Esd +Ese Burrito Esham +Esheme Sen Esimies +Esinaahka Eskalt Eskandar Eskeleto9 Eskett +Eskett II +Eskib0y Eskii Eskild Eskimeme +Eskimo EskimoFro +Eskizzibur Esko79 Eslamm Eslihero @@ -6985,10 +14389,16 @@ Espuky Espyria Esquelito Essencehour +Essex RS Essylle +Est Bellum Estafeta1 +Estar +Estebaan +Esteedee Esteeme Esteet +Esteffano 3d EsterKai Esterbrook Estetica @@ -6996,20 +14406,33 @@ EstevamStain Estiem Estonia9184 Estra +Estrogenizer +Estus Estusin Esya +Etakenai Etardoron EtchaSketcho Eternal +Eternal Envy +Eternal Max +Eternal NEET +Eternal Orb +Eternal Qt +Eternal Riw EternalGuide EternalHeart EternalPants +EternalSin9 Eternalfury2 Eternalgod99 Eternity +EternityAP EternityEndz Ethaan Ethan +Ethan Hunt +EthanMcSexy Ethanol EthansMain Ethel @@ -7018,157 +14441,304 @@ EtherBunny Etherealist Ethereous Ethernet3 +Etherscan Ethoxide +Ethread Ethwin Etikk Etis Etkzera +Etna Etnie0 Etobicoke Etrengereid EttLitetHus Etuovi Etyaz +Etymology +Eu_Matheus Euanx Eucaryptus Eucleides Euclidian +Euclipenguin +Euf +Eugenepickl3 +Euhem +Euhh Eukaryote +Eukko +Eulers Eagle Eulogise Eunbiii +Eunie Xeno Eunx Euphael +Eupharu Euphorion9 +Euphoriotic Euro +Euro Centric +EuroGarden Eurovizija Eusheen Eusiriito Euthia +Euxii +Euxy Eva-O1 Evadari +Evaiv EvanWilliams +Evanz40 Evaptix Evawe +Evdog +Evelynn UwU Evemarner +Even2000 Even3518 EvenMater +Evenepoel Evening +Eventyrlig Ever +Ever so Dark +EverSingular Everglow +EveronSky Eversiction +Every Color +Every Word +EverybodyZuk +Evette Evictus Evil +Evil Buu +Evil H +Evil Inari +Evil Kenevil +Evil Lizard +Evil Lord +Evil Nishiki +Evil Oak +Evil Twin +Evil Zayne +Evil-Mind69 Evil3yez EvilCopycat +EvilDeeds +EvilDegen EvilFootLong EvilH4mmy +EvilOwen01 EvilTriumphs Evilaz +Evildoer +Evilegod2 +Evilelfy Evilhammer +Eviljay Evillized Evilstar34 Eviltroll648 +Eviran Evirane Evnn Evo9 +EvoSlacker EvoSlayz +EvoeHalt Evoke Evolooner Evolution Evolved +Evra Evrs Evrybodypays Evse Evuk +Evultion +Evylix Evytt Evzmac +Ew Its Mike Ewan BTW +Ewang +Ewiiyar Ewmu Ewos +Ewya +Ex Files +Ex Holy +Ex Port +Ex Sythe +Ex-Slur Ex3rt Ex903 +ExHCMangy +ExMachina +ExMaxxed ExPro +ExPsi +ExQ Bratan +ExSevenTech +ExShiron +Exa OuO +Exaalt ExamJ +Exambient +ExamineCoins +Examines Examon +Exanso Exarch +Exasperation Exaz +Exbos Excadrill Excaria ExcelIsLife Excellarate +Excellini +Excinium Excise Exclude Exclusic Excody0 ExcuseMyStr +Execrating +Executed Executes Exella +Exem Exercise Exero Exflacto Exhaust Exhibition Exhumed +Exi Sisukas Exia Exile +Exile Vision Exile88 +Exile_vChad +Exiled Noobi +Exiled Peon +Exiled Slay Exileeeee Exiquio Exiriam Exiting Exmigrant1 +Exminer +Exo25 +Exoden Exodusty Exonaut Exor +Exorcism Exoristic Exorzist Exotic +Exotic Xenon +Exotica +Exotick +Exp Jesus +Exp Nerd +Exp Wasting Expaaja Expansa Expansce Expanse Expensive +Experience +Expl0it Explaindeath ExplicitPvm +Explodet +Explodium +Exploiter +Explorer +Explorifice Explosia Expochan Export +Expozz Expressed +Expressen Exright +Extella +Extended C Extile Extinct +ExtinctDecay +Extinguished +Extortionate Extra +Extra Chance +Extra Droog +Extra Poor +Extra Shadow +ExtraRNG ExtractorX +Extranjer0 Extreemkills Extrem3b0y Extreme +Extreme JeJe +Extreme Moo +Extreme Pray ExtremeJokeR +Extremterms Extrim Extrovert Extubation +Exty Exultia Exume Exus +Exus De Exustron Exwalker +Exxtinct Exynyt +Exz0 +Exzacly +Exzactly Exzakly +Exzian +Ey1 +Eye patch +EyeBDaMan EyeHaveToPoo EyeMakeYouQQ Eyeless +Eyelessjohn Eyeofdeath3 +Eyeron E +Eyes Inside EyesLowIndo EyezClosed +Eyggs Eylix Eymox +Eyrfire +Eyup xD +Ez Klaplong Ez4Peru +Ez4u2nv Ez4u2say EzBot EzCashin +EzSlayEzLife EzUnReal Ezarc HCIM Ezdrae +Ezero Br Ezey +Ezfart Ezia Ezra Ezup @@ -7177,117 +14747,299 @@ Ezxkiel Ezzardx300 Ezzi Ezzuna +F 3 4 R +F 0 N Z Y +F 3 R R U M +F A C E +F A N TT I +F E L I CYA +F For Flash +F I X E D +F L A T +F M L +F R A T +F Ribeiro F SONY +F T P +F You Bro +F athlete +F e e b s +F l a n Cat +F l u f fy +F l ux +F loofy +F reeze +F u z Z e +F ury +F-16 +F-ck Bleed +F-ck Irons +F00DCOMA F00F +F0O F0REIN F0ZI +F0cus Me F0rtune +F0sta F0xBr34d +F1 Mercedes +F104 +F19 +F1REWIND F1nalHour F1rst F1skemann +F1tn3ssWorld F20z F23A +F2L +F2P Waifu 07 +F34r My B0w F34rcr4ds +F3ARCE +F3LINA +F3lo F4DE +F4TB0Y +F4ust F4xi F7VD3 F80Scott +F8n +F8tz +F9 +FA DY +FA Joel FA1Z +FAILED ZEUS FALLGIRL1029 +FASHl0NSCAPE +FASTBLAST I FATHER +FATHER RANCH +FAlRWEATHER +FBD +FBGM 101 +FBGMi +FC Groningen +FC Liverpool FD3S_RX7 +FE AltScape +FE Cerise +FE Endeavor +FE Mazoku +FE Smore +FE wretch +FE80 Seal +FEJMBE FER00 FER0C1OUS +FERTILE DONG FEstlkerpeng +FFA +FFA Everyday +FFASplit +FFIE go brr +FFS Sam +FFV FF_GhiLLi +FFelidae FGHTIN +FHV +FIDADDY +FIFG Phat FILLME +FIRE 0F FIRESlTE +FISHY au +FIV3 FIying +FJ +FK Corp +FK TURAEL +FK1 +FKGlory +FKN RAPTOR +FKRA +FL jit +FL0CK FL3XPL3K +FLAPPYLIPZ +FLC L FLOR1DA FLYGOD FLYING FLgoob FLoKii +FM DOOM FMGbrien FMLshes17 FMarshalBill FN2187 +FNA +FNEkkGKGKWGK FO-LAZY FOES FOLEM FONKY +FONNlX FOSTEEEEZY FOUO +FOUR GATSU +FOV90slider +FOXBRAH +FP Engineer FR1T +FRAQSTAR +FREEBABA +FREED0M 1776 +FRIENDSHRIMP +FROM ICELAND FROO00OO0ZEN +FROSTYFOO +FROXBURG +FSK Gaz +FSW 2JZGTE +FTI FULGRlM FUTRHNDRX FUTillIFU FVChallenger +FW HYPZ +FX Teddy FXF_Tails +FYXON +Fa k er FaBeSCa FaNNtastix FaZe Faabio +Faalk Faardo +Faarihn +Faathos Fabario FabiScape Fabiann +Fabidjann Fabiiano +Fabioso Fabled +Fabled Fox Fabreezy +Fabretzio +Fabsitz +Fabulence Face +Face Seat Face9 FaceHook FaceTheFacts +FacedBased Facehuntter FacelessBoyd +Facilis Facing +Fack Russia +Factual +FadaPDF Fade +Fade Lightly +Fade zz +Faded Crook +Faded Focus +Faded Lungz +Faded Past +FadedFace Faderfouras Fadfats Fadge7 Fading +Faeanaro Faebat Faeles Faerindel +Faested +Faf FahQ +Fahim +Fahrbot +Fai1ure +Fail Be Dont +Fail Friday +Fail Stacks FailFish +FailedNoob FailedZerk Failing +Failocity +FailsauceFTW Faint +Faint Dallas Fair +Fair Folk +Fair Luck +Fairburn Fairex FairiesBane +FairlyDecent Fairr Fairr Enough Fairwolf139I FairyFeind FairyVeiler +Faith xd Faiu Fakdo Fake +Fake Chad +Fake Dylan +Fake ID +Fake Levi +Fake Pobble +Fake Suffer +Fake Will FakeBallots FakeGirl +FakeNews_CNN FakeNudez Fakent +Fakeppa +Fakez07 Fakezgen +Fakn Oats Faknius +Faku +Fal +Fal7 Falabar247 Faladalore Faladas +Faladoor +Falador +Falador Sq FaladorabIe +Faladorks +Faladussy Falcon Falconf1 Falconr Falconz420 +Faleis +Falestine Falkenberg +Falkuntpunch +FallOfSolace +Fallacy i Falladis +Falldyl Fallen +Fallen Dark +Fallen Hopes +Fallen Noble FallenBlur +FallenGods FallenOutlaw FallenSlayer Fallenchamps @@ -7300,152 +15052,395 @@ Fallon Fallout Falloutah Falls +Falls Apart +Falls Road +FallyFiddler +Fals FalscherHase False +False 9 +False Boon +False Divine +False News FalseGod Falsemorels Falubo +Fam Fam Chi Boy +FamIso Famcloth +Fame is +Famexx1337 +Famfrit +Familia +Familie +Family Crest +Famished Fe +FamishedNine Famjam +Famoose +FamousBowl +FamousBowls FamousPixels Famuel +Fan +Fanatic Est Fanatiker Fanawr Fancys +Fandri Fanfiction +Fang Dude +FangJo Fangsie +FannyPaca +Fantasieloos +Fantastik Fantikerz Fantomine +Fantrix +Fany Pack Fanzi Fappel +Fapperd +Fapple Bees Fapricorn Fapworthy +Far Alone FarAke FarFarOut +FarNear +Farages Army +Faragi Farah +Faramir +Faran Farao Faraolorddd +Farcry25 Fareham Farelinho +Farenru +Farewell Bud Farfire +Farid Lord +Faridi +Farigno Farkle +Farkwar +Farlz Farm +Farm Strong Farm11m Farm3r +FarmHub +FarmRun Clay Farmance Farmay7 +Farmelot +Farmenheimer Farmer +Farmer Blake +Farmer Crab +Farmer Dannn +Farmer Jayy +Farmer Johnx +Farmer McGee +Farmer Santa +Farmer Wull +Farmer Zach +Farmer Zratz Farmers Farmertree Farmin Farming +Farming Arma +Farming Moe Farmingfoxx +Farmingtool Faroeallstar Faron Faror Farpoint09 Farqin Farrier +Farron08 Farseer +Farseer Hat Farsight2 +Farssi +Fart Gunner +Fart Shartly +Fart Weed +FartInMyGob +FartSymphony +Farthinder +FartinLKing +FartinLuther +Farting cat +FartingCow +Farts2Wet +Farty Ass +Farukoo FasTLT Fascia +FashionBTW +FashionPig +Fashy Fast +Fast AF +Fastenal Fasterman Fastly Fastmagepk1 Fastmaniac +Fat Bawlsack +Fat Boobs +Fat Cat +Fat Clouds +Fat Fur +Fat Goombah +Fat Jonny +Fat Leb +Fat Maniac +Fat Mat +Fat Moron +Fat Nymph +Fat Rackz +Fat Ralph +Fat Salesman +Fat Swaggy +Fat Thin +Fat Waddle +Fat Wirgin +Fat Wrath FatBIunt +FatBurgerKid FatClock FatGreasyGuy FatITguy +FatKidBrett FatKidHougie +FatMonkey +FatSlutKing +Fatafeat Fatal +Fatal Cazual +Fatal Psycho FatalReign FatalXfire FatalisRS +Fatalsystem Fataltorment FatboislimOS Fatboy6 +Fatcav2 +Fatceps +Fatdogs11 +Fate Gu +Fate Z +Fateq FatesWarning +Fatez Fathead Father +Father Of 3 +Father Of M0 +Father Tacos Fathey +Fatmat +Fatrekt Fatso Fatstinkysak +Fatterhead +Fatty Combat +Fatty Park +Fatty-Kent Fatty380 +FattyIsntFat FattySupreme FattyTerps +Fattybanger Fattymcdugal Faultty +Fausto +FautleferQC +Fave +Favelador +Favion +Faw OCE Fawka516 Fawn1460 Fawz Faxon +Fayl Fayumi +Fayv FazTazTic +FazeQ FazeSkilling Fazebook +Fazed Imp Fazist +Fcape +Fcbfan1994 Fctillidie +Fe AND17 +Fe Adex +Fe Alma +Fe Anakin +Fe Bastok +Fe Carter +Fe Clay8 +Fe Cremator +Fe Dahje +Fe Dentyste +Fe Dopamine +Fe Gangsta +Fe Grom +Fe Gson Fe Hellpuppy +Fe Hucks Fe KabooM +Fe Kiteman +Fe Kyle Fe +Fe Liquid +Fe Logix +Fe Louis +Fe MGTOW +Fe MagicMan +Fe Monkey +Fe Mulks Fe Nail +Fe Nini +Fe Nocturne +Fe Nylost +Fe Oakdice +Fe Papi +Fe Patrick +Fe Pvm Tiger Fe Pyles XV Fe Qlimax +Fe Republic +Fe Rex +Fe Sav +Fe Setting +Fe Skilleye +Fe Slammed +Fe Sten +Fe Tentacles +Fe Thijssie +Fe Tomie +Fe UIM +Fe Viraxic +Fe Werty +Fe X AE A-Xi +Fe Xems +Fe Zachpnnc +Fe Zelta +Fe anor +Fe ared +Fe depressed +Fe elsBadMan +Fe il +Fe lonies +Fe lungs +Fe lyne +Fe r a t +Fe rgy +Fe ta Fe-ranator +Fe0xi +Fe20 Fe26 FeBriZley +FeCapedSloth FeCrab FeDestroyed +FeDyl FeFiFoFum FeFireTruck FeFlo FeFox FeIII +FeJonnisjoen FeKyle +FeMale Chris FeManlett FeMattsheets +FeMoist +FeMudcheck +FeNote FeOx +FePash FePinkviini FeReelix FeShane FeSolaris FeSynical FeTaeliah +FeUIMIronBTW +Fe_Schrute69 +Fe_StarLord +Fe_Symphony FeaR +Feaaar FeagMeister Fear +Fear Fun FearKev FearMyTM26 +FearOfChange +FearSamurai FearTheBear FearThyDoom FearTurkey Fearengineer Feargasm FearlessFudu +Fearmaker1 Fearrod +FearzebuJr Feasted Feather +Feather Bug Feathereli Febelz +Febreezio +FecesFyrHose +Fedad Feded Federal +Federated +Fedji Fedo +Fedooool Fedt Feebee Feebz +Feed Forward FeedMePlants Feedback +Feedle +Feek 1 +Feel Ya +FeelMyBirdie Feelin +Feelin Fine +Feelin It FeelinHappy Feelmygame Feelosophy Feels +Feels Lonely +FeelsDuBis Feelsbadkapp FeelslronMan +FeelzBradMan Feen +Feet Watcher +FeetFunGoose +FeetPic Fefe3 FefilleLaDur Fegicaly @@ -7453,48 +15448,96 @@ Fegoob Feint Feisty Feit +Feitn +Fel FelNeck Felamar Feldip Felgenhauer +Feli-Marie +Feliaff Felinaed +Feline Feast Felix2 Felixa +Felixigor6 +Feller Crus +Feller Magic Fellow +Fellow Fool +Fellowships Felmyst +Felon +Felonies +FeloniousHam +Felony Ruler +Felted Female Henry +Fembo y Femboi +FemboyFloppa +Fementedspaq Femister Femke +Femke Bol Femkekos +Femstiq Fencig5 +Fencingduck Fencko Fender +Fender Axes +Fender Twin +Fendoral +Fene77 Fenerbahce +Feniks366 Fenix Downs +Fenix Kiros Fenkat Fennikel +Fenny RS Fenomeno11 +Fenothiazine +FenrilasIM +Fenrirs Fall Fens +Fenshy Fenske +Fented +Fentenal Fenternal Fenyxgreen +Fenzo +Fenzy +Fequites +Feral Wiki FeralFiddler Feralblood +Feraligatr44 Feraliigatr Feratzu +Ferbzy FerdaWoox Ferduh Fereekelor Ferlide +Fermeon Fernando-Frv Fernie Ferny Fero217 +Ferocious J Ferocious97 +Feroos +Ferousity +Ferpderp Ferrarezi +Ferrari +Ferrari Enzo FerrariScape Ferrat +Ferrdie Ferreklop FerretFoSho Ferrex @@ -7504,117 +15547,216 @@ Ferring Ferrinheight Ferro FerroPlanty +Ferrothan Ferrothorn Ferrous +Ferrous Frog +Ferrous Hugs +FerrousBoii FerrousWheel FerrousWolfe +Ferrowing Ferrum +Ferrum-56 FerrumPawn Fersapian Ferus +Ferus Ferrum +Fervor Ferynys Fesan Fesenko Festis Festiva +Fesu FetaCheese Fetetchie Fettisdagen Fetus Feudal +Feunk Fewest Fewideahide +Feydx Feyenoord36 Feylon Ffilteg FfsImAfk FgtBob +Fhil +Fhk Trudeau Fhpure1 +Fibeer Fiberlight +Fibix +Ficc Ficomon +FiddlesLeaf +Fidds93 +Fiddy Ate +Fiddy Bag +FiddyDuddy FiddySweens +Fidelity Fiege +Fielacius +Fielde FiendinLo +FiendsDream +Fierce some Fiero Fiets +Fiets opa Fietsen +Fiff Fifflaren Fifth +Fifth Herald Fifty +Fifty 5O +Fifty Cent FiftyFive FiftyForty +FiftyStones FigaroTheCat Figes Fight +Fight 04 +Fight For Jk +FightMiIk +Fighta +FighterSE +Figless Duck Figmentx +Fignootters Figpig +FigsNotPigs +Figure +Figwit Fiiderino Fiiggy +Filed +FiletOFlesh FilipTelford Filippia Fillifjonken FillyFilly12 Fillzu Filmmaker +Filo Legolas +Filofteia +Filoksenia +Filson-zzZ +Filth0 Filthy Filthy Bird +Filthy Clam +Filthy Pesnt +Filthy Santa +Filthy Weeb +Filthy istik Filthy4Iron +FilthyDane FilthyDingus FilthyOreo FilthyPixels +Filthy_Stew Filz +FimiWolf +Fin Vader +Fin219 FinPunisher Final +Final Redux +Final playz +FinalBossUIM +Finalbang Finalbrk +FinalityB Finally Finaltank Finch +Fincher Find +Find mucking +FindTheTeemo Findawg56 +Finding Dory +FindingDory Finding_Mary +Fineillspoon +Finem Mundo Finesse Finessing +Finex FingerBang +Fingerdfilly Fingered Fingerlegs FinickySquid Finish +Finish Flash FinishedLog Finishzuelan Finklestein FinkyFink Finlan Finland +Finlanderi Finlanders Finlays +Finlunch Finn1309 FinnelCake Finnica Finnish +Finnkie Finral Finrufa Finsho Finsta +Fintiaani +Finutty Fiola34 Fiora +Fipimuesi +Fir3bird85 Firat +Firben Fire +Fire 745 +Fire 961 +Fire Captain +Fire Chiller Fire Fiesta +Fire Fist +Fire Galley Fire Kaped +Fire Kittie +Fire Tune Fire10910 FireAF FireAnRescue FireGiant +FireKornez FireOnRs +FireReid FireTruckBoy FireWolf28r +Firebelly Firebuggy +Firebwans +Firecan1314 Firedevil +Firedrake658 +Firehawk746 Fireheart470 +Firein12 Firejax Firekirbyeli Fireknight0 Fireman +Fireman Sam Firemanzz Firemutt Firenova @@ -7624,116 +15766,201 @@ Firesmash Firestarone FireyDragons Firko +Firnik First +First Bozz +First Up FirstBathTub +FirstBlood 1 +FirstIron +FirstRS First_Viking Firulay18 Fish +Fish Cow Dog +Fish Keeper +Fish Nibba FishFingerz FishGoBlub +Fishcake +FishesBmine Fishey Fishie +Fishling Fishoii Fishstickz +Fishsword389 +Fishvaulter FishyBrines Fisicas Fiskhue +Fisna Fist FistMeTrump Fister +Fit Jr +FitAid +Fitdis Fitis Fitsit +Fitty Tyson FittyBuck +Fitzy Smalls Fiurio Five +Five Demands FiveManSplit FivePaws FivePointOh +Fivee Skinn Fiveskin FixationRS Fixed FixedCost FixedMain +FixedWing Fixions Fizbin +Fizg0d +Fizz Khalifa Fizzed Fizzle +Fizzy Glizzy FizzyGuzzler Fjala +Fjala r Fjalee Fjollan Fjolle Mate Fjomp +Fk RS L +Fk W Da Woo +FkingEzeKial Fkking Fklostmybnk Fkluzac +FknDeece FknDreaming +Fkuru +Fl y FlLTHYYYYYY FlNARKY FlTTY +FlTZ +Flabaghast +Flaboogles Flaccid +Flaccid RNG +Flaccid Semi FlaccidWhip Flacid +Flackyo Fladra +Flagaria Flagrance +Flagrant 2 +Flagz Flaherdog FlailTheKing +FlailingGoat +Flair Viper +Flakelar Flakey Flakeydude Flakkas Flakto Flamaha +Flame Lurker +Flame0fUdun Flame29 +FlameSoother Flamed Flamel Flamepistol +Flames4 Ever +FlamingPee +Flamingfox +Flamingguy7 +Flaminold Flaminrofls +Flammbam Flanelli Flap +Flap Slap +FlapJackals +Flapple +Flapsian Flare Flare Grylls +Flareblade Flareon +Flarez Flarp Flash +Flash Voyage Flash3200 FlashYellow Flashbane +Flashbang +Flashcards Flaskepost Flat +Flat Eric FlatAssPlank FlatEarth361 +Flatback Flatlandah Flatorbrush +Flaunt Flava Flavaaaaa +Flavie +Flavour Trip +FlavouredDav Flavur Flaw Flawd Flawed +Flawed Bliss +Flawless xD Flawnn Flawzin +Flax Pickerr Flaze Fleaa +Fled 6ymcric Fleeson +Flemingo Flemino Fleruas +Flesh Forest Fleshgod +Fleshpound Fletch2 Fletched0 Fletcher +Fletcher 06 Fleton +Fletz Fleurescent Flex +Flex Wayne +Flex1bel Flex1bility +FlexCity FlexSeal Flexatronic +Fli pper FliPancakes Flick FlickMyTick +Flicker 06 FlidFlop Fliga Flight FlikAwrist +Flikker +Flikker Kind Flimmyflan Flimpy Flimsywhale @@ -7741,83 +15968,147 @@ Flimzy Flinch FlingDragon Flint +Flint RS +Flint Water Flintstoned +FlipDaddy +Flipdam +Flipje Flipman24 Flippa +Flippa Monk Flippar Flippers Flippin +Flipsen07 Flipzzzup Flirz +Flit Wick Flix +Flixy +Flizz xd Flllllllllll Float +Floater eyes Floatzelo Floballer Flock43 +Floeter +Floki loki +Flomple Flonza +Floody +FloofyTurtle +FloofyWaffle Flooga Floogan33 +Floopyboople FloorTwenty Floot +FlopLord Flopez Floppy +Floppy Boaby +FloppyDongle Florian +Florian_1988 Florida Floth +Flover Flow +Flow it +FlowMafiaaa FlowState Flowah Flowen0ne Flower +Flowerm00se Flowiey +FlownbyFire Flowtown +Floww-Grown +Flowwar Flowzyy +Floxin Floy Floyx +Fludds Fluenz +FluffPandaSr Fluffafluff Fluffalo +FlufflePluff Fluffy Fluffy chair FluffyTater Fluffypony44 Fluga +Flugel25 Flugenhorn1 Fluid +Fluid Voyage +Fluke Jr Fluminense Flunc +Flunt Capz Fluoroform Fluorophore Flupbaster Flurius +Flushiz FluteBand Flutlicht Fluxee Fluxery +Fluxion +Fluxx V2 +Flvd +Fly an High +Fly by N7ght +Fly in Fly-TheW +FlyLo FlyPap3rr FlySunyQuest Flyboyray Flydol +FlydonWayno Flyeeee Flyer +Flyer TMT +Flyer W Iron Flyern Flyguy13 Flyhll Flying +Flying Pingu +Flying Swede FlyingBobcat +FlyingIrish +Flymi +Flymzy Flynny Flynsquirrel Flynt Flyonwings Flyvende +Flyvende Ged Flyy +Fml IronMan +Fml Its Adam +Fml Its Carl FnHit +Fnatic +Fo Ring +FoRbeZiiLLa +Foam Corner FoamDemon1 +FoamShrimp +Foamshire Fobu Fockwolf1 Focus +Focus73 FocusOnGoals Focusor Foen @@ -7830,78 +16121,157 @@ Fohmie FoiledSoftly Fojin Fokjefe +Folayyy +Foleshill +Folesy Folfox +Folieo Folk Folk169 Folktale FollieRS +Followed +Follower Folwifuswalo +Folxi Fomble +Fomid +Fondle FondleMyIron Fong Fonnis FonsJ +Foo I I Food +Food For Me FoodEmperor +FoodStampGod +Foodfoodfood Foodie +Foofickle FookOffNerd Fool +Fool Senpai +FoolFighters +Foolery +Foolingling +FoolishHeart +Fools +Fools Errand +Fools Switch +FoolsErrand +Foomp Foooman Foose Foosyy Foot +Foot Ball242 +Foot Magnet FootLocker Footaphiliac +FootlongMike Fooz +For +For Karamja +For My Block ForDaScratch ForKrimpt ForNostalgi +ForRusssia ForWeAreMany Forbe ForbiddenCry Forbids Force +ForceZero +Forcekid Ford +Ford Racing Forearm ForeignBoris Foreigner Foreknown +Forerunner40 +Forescimitar Forest93 +Foresteris +Foreva +Forever Wild +ForeverAlone +ForeverClone ForeverLost Foreverett ForgetThePet Forgetmenot Forgetpluto +Forgettios Forgive Forgived +ForgottedPin Forgotten ForgottenRNG Forkbomb +Forkers +Forkmitt +Forks Out Forma +Formal +FormalPotato Formaldhyde +Former Nub FormerDivine +FormerSeaman +FormuIa 1 +Fornuis +Forresten Forsaken +Forsakens Forsworn66 Fort +Fort Chronos Forteh +Fortified +Fortify +Fortimuss +Fortis Lupus +Fortjent FortniteRox Fortran95 Fortress +Fortunes Forty Forza +Forza Derby ForzaGTx +Forzam Fosh +Fossa69 +Fossil Rock +Fossul +FosterXP Fostret +Fosz +Foton Fotty Foumy6 +Found Prawn +Found3Groots FoundABaby Four +Four One 6 +FourB FourZone3 Fourlifee Fourloco Fournlock FourthChance +FourthPower +Fowke +Fox Enjoyer +Fox Two +Fox Zebra Fox243 +FoxStevenson FoxTherapy FoxVex Foxall @@ -7909,152 +16279,281 @@ Foxe Foxerx1 Foxes Foxfiend +Foxfire +FoxfireStyle Foxi +Foxing Foxtrot +Foxx OG +Foxxwild Foxy +Foxy Rebel +Foxy SuzyQ FoxyBaphomet FoxySquid +FoxzHound +Fozbert64 +Fozie FpsMichael +FpsWheelerrx +Fpsallday +Fr anci5 +Fr ankie +Fr0 Fr0zEn111 Fr1ckingH3ck Fr1xioN +Fr3Pa1est1n4 FraJoLaaa Fraagile +Fraazzy Frab Frac +Fractals Son Fractuality +Fraggiux Fraggle FragmentedX +Fragmentizer +Frags Frail Framed FramedJunior +Frames Wolfe Framingham +Franche420 Francine1225 +Francis0wns FranciscoJ32 Franco Franf10 Frank +Frank Donner +Frank Is Dog +Frank Lin +Frank White +Frank-sama +Frank836 +FrankMadeME FrankTTank Frankers FrankieFlow FrankiesAlt +Frankinspank Franklin1O1 FrankyFish32 +FrankyLeGros FrankyyTanky +Frantiks +Franwan Frappacholo +Fraqqer +Fraser +Fraser840 +Fraspex Frassboss Frate Fratto +Fratty Flows +Fraud Thurgo Fraught Frauwz +Fravvomaxx +Frayhalla +Frazer Frazia Frazoo Frazze +Frazzlewagg Frazzt +Frchtzcker +Fre d Freaak Freak +Freako07 Freako09 Freaks Freakxx +Freaky Peet +FreakyWajt Fred +Fred J Frost +Fred Solo FredMaxedALT +Fredagsgroda Fredanbetty +Freddi Freddiep +Freddy CEO +Freddy ownz Fredericton +Frederizz Fredla +Fredleif +Fredo Fredric Fredriks +Freds Breads Free +Free Dumb +Free Minded +Free O9 +Free lootkey FreeFreeze FreeFryBread FreeHongK0ng FreeKoolaid FreeTeli2Lum +FreeTheRealD Freebooterv +Freeczs BTC Freedom Freedom06 FreedomJust FreedomUP Freee +Freehky +Freek 1 FreekALeak Freekkittens Freelo Freeman Freepop +Freerangee Freesoul Freest Freestylin Freeway Freeze +Freeze M E Freezes Fregion Frei Frekitohh +Freky FrenchBuoy +Frendi Frenek +Frenkurt Frenmiir Frenzy +Frenzy4 FreonPays Fresaaaa Fresco Fresh +Fresh Cheese +Fresh Pesto FreshPotsRS +Freshly +Freshske +Frette Freud Frevlin Frewdy Freya +Freyas BTW +Frez +FriarNation Fricas +FriccKip Frickin +Frickn Playa FridayDa13th +FridayMojito Fridelis Fridfoolium Fried +Fried Erik +FriedRicePls +Friede Friend FriendlyJo Frietjecurry Frieza +Frieza Saga +Friezerik Friggn +Friggy +Friibag Friisby +Fris +Frisdrank Jr +Frisky Lime FriskyBiznaz FriskyJews FriskyPainal +Fritszon +Frittered Fritz Fritz94 +Frix Frkn +Frnds FrobroSwagin +FrochioRS Frodo +Frodo Bagger +FrodoBagGims FrodoBogues FrodoRS Frodoinc1 Froemelke +Froemeltchhh Froez66 Frofuzz Frog +Frog Blog +FrogBoySlim +Frogfish12 +Froggiefish Froggit +FroggyBro Frogmann +Frogone9 +Frogsforjp Frogtelllow +Frogtoken Frogwork Frohobo Froloox From +From Beyond +From Florida +From Lebanon +From Nz FromNewWorld FromSoftware Front +FrontBottoms FrontierEdge +Froobing Frooby Froomey +Froot Loop +Frootata +Froppy Tsuyu Fropul Frosht42 Frost +Frost JTS +Frost Mages Frost_PvM Frostbiter +FrostedBow Frostmourne Frostserpent Frosty +Frosty E +Frosty Gay +Frosty6570 Frosty751 +FrostyPea43 +FrostySurge Frostyfuz Frostyyyman Frote +Frotzr Frovz Froya Froyotech @@ -8062,221 +16561,604 @@ Froz09 Frozah Froze Frozen +Frozen Crown +Frozen Rain +FrozenKing FrozenMosin +Frozencarrot +Frozenn Dusk +Frozty Feet Frrrozn +Fru Katt Frufoo Frugal Frugtmanden Fruh +Fruit Crepes +Fruit Dryer +Fruit Munch Fruit1824 Fruit1846 Fruitbooting +Fruitje +Fruitloop8 Frunk FrunktheHunk Fruta Frvnk +Frxnk White +Frxnklin +Fry B +Frysta +Fsaze +FtheFrench73 FuAiluue FuNrikoz FuRiouSs +Fubar3d Fubini +Fubuki +Fubuking +FucDPS FucNorfKorea FucaDuc +Fuchsiger +Fudeath36 +Fudg3 Fudge Fudgie Fuel +Fuel Ex Fuertote_17 +Fuetakishi +Fug +Fug sakes FugDisFugDat Fugedit FuggleQuest +FuggnIronBtw Fuglemanden +Fuhni Fuhrerengel Fujimator Fujk FukYeaChina Fukahi Fukinnnn +Fukn Lit Btw Fuktflekken FulMomentum Fula FulgBTW Fuliss Full +Full 3A +Full AD Vlad +Full Boost +Full Moot +Full Tilt +Full of Wo0d +FullGraceful +FullSendGoat +FullTimeNerd FullTryHard FullerACL Fully +Fully Mobile Fullysick Fulsh +Fumblebag Fumblers Fumbles12 Fumin +FumingMe +Fun Guy mhm +Fun Sucker +Fun1nFunera1 +FunHotBox +FunWithFire +FunctioningC Fund Fundo +FuneRune Funeral Fungi Funk +Funk Head +Funk Mastah FunkBondMule Funkafiend FunkiPorcini Funkiboy2 FunkiiMonkii +FunkleRoy +Funkr +Funkshun +Funkworks Funky FunkyBoy4444 FunkyDiddler +FunkySlut +Funky_Hunk +FunnelCaker Funni +Funni Memmer Funny Funny-King03 +FunnyAndEpic +Funobu Funpeople5 +Funrun123456 Funzip Fuorra +FuqAgility FurMissile Furbs +Furi Potion Furious FuriousPower Furiri Furo Furox Furry +Furry Wall FurryDemon Furrykins +FursonaHaver +FurtleTurtle Furty Furu Fury +Fury DnT +Fury Lord FuryJunky FuryL0rd Furyian Fuse +Fuse is HARD +Fuseton Fusiiion +Fusion Aurum +FusionGT2 +FussyParter FutchDuck Futex +Futss Future +Future Tense FutureKaiden +Futureruler9 Futures +Futures Main +Fuuwah +Fuxley Fuze +Fuzz Man FuzzManPeach Fuzzums4 Fuzzy +Fuzzy Sox +Fuzzy jaw95 Fuzzy1918 FuzzyDonkey FuzzyFudgey FuzzyMWV +Fuzzy_NooT +Fuzzybear65 Fuzzyblaa56 +Fuzzykitty Fuzzzy +FvS Love +Fweafwe +Fxked +Fxob +Fxrgus +Fxrret +Fy six Fy12e +Fyitz +Fylberg +Fylize +Fym +Fyr +Fyr Tornado Fyresam Fyri +Fyrinlight Fyron +Fyrox Fyszz +Fyzio Fziaa +G +G 0 A T +G A F F E R +G Alfansos G Ape +G B S +G Check +G Fruit +G GAMER GIRL +G I M Danny +G Iron Adam +G L E N +G L H F +G M B +G Man9822 +G Mauls +G O O D +G O O S E Y +G R E E N +G R I L L O +G Terminator +G X F C +G Zus +G a r t y +G a t t s u +G ante +G dot C +G e n g a r +G eeb +G i j s +G i l b o +G iG +G imme +G iooo +G l R L +G laze +G no Z +G o G G u +G o h o +G o th i X +G oldd +G oldeen +G oofy +G ozz +G rafix +G ronimo27 +G root +G shi +G u a m s +G unitt +G-F-M +G-IronKuv +G-Thug Nasty +G-knowww +G-rivs +G0AT JUICE G0ATWRANGLER G0D Richard G0DLY +G0LD3N G0LDC0AST G0LDEN G0LDENB0Y G0LDIE G0NGSHOW +G0OEY +G0TH ANGEL G0TTY +G0blineer +G0dr3kt G0dsnotdead G0ing G0ld G0lden +G0rillaJesus +G1C G1adiador G1edrelis +G1izzyGob1in +G1m Luna G3RM4N +G3ZZ4 G4M3OV3R G4RG0YLE G4UAFS +G4YLORD G4de +G59 Awol +G6E Turbo G6Wizard +G80 +G99s GAAANNNGGG GABBAGE +GAGGED N +GALO9A GAMMAGARD +GAMxJAKE +GANONd0rf GAOKNMxzHR +GARDlNER GATECRASH GATTl +GB Amarok GBJackieWang GCMidlane +GDG +GE IS WACK +GE to FE GE0RG3WKUSH GEARLE55 GEEZ0 +GERANUS GERRIEE GEtItFrAnK GFHL +GFe Aang GFmanbearpig +GG Joe +GG McCloud +GG Sofie +GG Wped GG6HJA9KSDHJ +GGGGGGGGanta GGGskywalker +GGW Limit +GGW Thanos +GGf1cation GGtoHH +GH05T DUDE GH95 GHEEESE GHETT0JESUS +GI GN +GI Lewis +GI Lucky +GI Madras +GI Mohib +GIANT HENO +GIANT ITIOLE GIBBO96 +GIGAGUMPH +GIM 0dawg +GIM 2nd +GIM Albin +GIM Aperture +GIM BTWW +GIM Ballak +GIM Barkless +GIM Baylis +GIM Bunzz +GIM C J +GIM CgIsEasy +GIM Composer +GIM Conkers +GIM Cris +GIM DareDev +GIM Derek +GIM Dionysus +GIM Dormant +GIM FatLion +GIM Fearzzy +GIM Ganther +GIM Glalie +GIM Globi +GIM Hatred +GIM Hola +GIM LWE +GIM Lasse_ju +GIM Lazyguy +GIM Lxxs +GIM Lyon +GIM M0FFY +GIM Madac +GIM Magneto +GIM Malphite +GIM Martas +GIM Maudi +GIM Mcstro +GIM Me STD +GIM Mecknon +GIM Mirasei +GIM Moith +GIM NewPorts +GIM OatBloke +GIM Obvious +GIM Or715 +GIM Philrat +GIM Player02 +GIM Qtpie +GIM RWT +GIM RagePope +GIM Raheem +GIM Rakfaan +GIM Ring +GIM Schaa +GIM Schnei +GIM Sense +GIM Snax +GIM Stinks +GIM Stompy +GIM Stoney P +GIM Stu +GIM TUT +GIM Terix +GIM Tman +GIM Tobi +GIM Toe Pic +GIM Ved +GIM Viklok +GIM Wife +GIM Zork +GIM atomic +GIM bad ass +GIM brutal +GIM kerma +GIM poy249 +GIM sabinsky +GIM xDist +GIM-Topsu +GIMAerospace +GIMButcha +GIMFunder +GIMME CLAW +GIMP Crystal +GIMP DlCC +GIMP Jonny +GIMP Suns +GIMP Toast +GIMP Will +GIMP Zuzu +GIMPlayer69 +GIMRavenKing +GIMTruck-Kun +GIM_NoRun +GIM_Snowwolf +GIM_tybraun +GIMcardellio +GIMonster +GIMp Brian +GIMpope +GIMxH2 +GIRTH CHECK +GIronPPVibes +GL GETTIN IN +GL on Purple GL78 +GLG Sophie +GLUS GLWeAllDie +GM Matt +GMEMan8000 +GMKirby GNULinux +GOAT GOD 420 +GODSOMBRA SL GOGETA +GOLOVOLOMKEE GOOD +GOODGAMEGGXD +GOP DUMB +GOZERGAST +GPA +GPWASTER +GR0OOT +GR1NCH GRAB-A-LEAF GRAN1T3 GRAND GRAPHISTED GREIV0US GREIVUOS +GRIMSTAIN +GRINGOLAO GRUMPY +GRUMPY BSTRD +GRUMPY PAPAW +GRYGRY GRiMZ +GRiMZ 420 GRlM +GRlM REEFER +GS Concerta GSAileen +GSW Shane GSwolls GT350 +GTA SA GTFOIMGAMIN +GTOD +GTRKingZilla GUBIDUBII +GUCCI SENPAI +GUCCl GUJ0 GUNMAN683 GURRD GUUMBY GVSU GYDtown +GZA +GZX +Ga l +Ga7ba Gaara +Gaara Sand Gaarden +Gaarise +Gaarlokiin +Gab al Ghul +Gabagoo l Gabber +Gabber NaTaN +Gabberboy15 +Gabble Gabe Gabe020 +Gaberoks Gabesz Gabo +Gabrahanth Gabriel61198 +Gabris +GabyabyxD Gacha Gaddgedlar Gadrak Gadwall Gaerolth +GaethjeKO Gaga +Gaga Iron +Gage S +Gageks +Gagne Gahbo Gain +Gain exp +Gainful Grey +Gains alot +Gainz Godz +Gainz Nerd GainzForHire +GainzWorld +Gainzvill +Gainzville GaiusTavi Gaiy GalGadotsBF +Gala G Galamx Galandorf1 Galar Galava Galaxia +Galaxiancam +Galaxies Galaxy +Galbazeek1 +Galderr Galdysa Galgenbrok +GalickGunner Galilee Gallaxy Galleta Gallium +Gallowbell +Galon Garuda Galon1 Galow +Galton +Galvatron Galwic +Galyam +Galzuk +Gam Gamaiiron Gambinahuora Gambino +Gambit +Gambit Ghoul Gamblerz Gamboy46 Game +Game Clown +Game Name +Game Of Olms +GameBot2000 GameOvaries3 GameStop Gameboto4 @@ -8284,36 +17166,63 @@ Gameboyjr Gameboylight Gamelia7 Gamer +Gamer Log +Gamer Worded Gamer4Life +Gamercutie Gamerplaya7 +Gamerr +Games Rigged +Gameshow Gamesman Gaming GamingLover GamingVapor +Gamir Von Gammel Mand Gamorrah +Ganandalf +GandaIfNoir Gandalf +Gandalf staf +Gandalf3602 GandalfBalle Gandhi Gandolf +Gandolpeeni +Gandon12840 Gandulff Gane Gang +Gang Goblin +Gangbarang Gangnrad +Gangris +Gangry Gangsta Ganj GanjaGhandhi GanjaRhino +Ganjoefarner +Gankicus Gankster +Ganna Bogdan Gannicus Gannicusproo +Gannnon Ganooch Gansa0 Ganta Gantee Ganthorains +Gantre +Ganzaxuz +Ganze +Gap Tester Gaping +GaratholX7 Garbage +Garbage Life GarbageEater Garbagexd Garbar6 @@ -8321,140 +17230,268 @@ Garboh Garbug Gardenia Gardening +Garder1968 Garena Garfinator Gargano Gargaspoon Gargath286 +Gargathon Gargleon Gargoyle79 Garlic +Garnat Garnet Gust Garp +Garrb3ar Garrett Garretttt Garrothis Garthoon +Garvis Gary +Gary Ghee +Gary Giggles +Gary Guldske +Gary Linkov +Gary Wang GaryDabs GarySmokeOak Garyjayjay Garysson +Gasconade x +Gash Pasher GashBndicoot Gashtag Gassy +Gassy Cat GassyFlaps +Gastje888 Gastronaught Gate Gateofdoom +Gath Gato GatoNaranja Gator +Gator Bob Jr +Gator Papi Gatorboiz +Gatorian176 Gatorslyr GatrsNTaters +Gau Cho Gauntlet +Gaur Gura Gauss Gautstafer +Gauvin Gavbin Gavi Gavii +Gavin Dance Gavita Gavrot +Gavussy Gavy +Gawd Bodi +Gawk5000 Gawkes Gawkss Gawt Gawz +Gay 4 Guthix +Gay Bear +Gay Bear Cub +Gay Farm +Gay Fieri +Gay Framed +Gay Holly +Gay Impling +Gay Liam +Gay Male69 +Gay Snake +Gay Squire +Gay Vermin +Gay Wimp +Gay450Bucks +Gay4Ketchup +GayWalrus69 +Gayblade +GaylsHaram +Gayme Gayron +Gaz GazGoon Gazaman11 +Gazan Gazd Gazed +Gazed Soul Gazelle2211 +Gazru Gaztok +Gazza +Gbax-Style Gdey56 Ge0rgeburton GeChallengeM GeTLeFt1 +Gear 5th +Gear Carried +Gear Fear +Gear Sekando Geard +Gearfried +GearheadSTL GebwellB +Geck-o +GeckoDad +Gedmuush GeeHasMews GeeStreet Geeb Geebs Geebster +Geef Bier +Geegles Geeker Geeknip +Geeman125 Geen +GeenBankMan Geera +Geerin GeertWillows Geestig +Geeving +Geewd +Gefilus666 GeggaMoya Gehrman GeilTijgerke GeilePaddo Geitekaas +Gekido GekkaJohn Gekke +Gekke Farmer +Gekke Nelis +Gekke Pablo +Gekkesmurf +Gekoh Gekolian +Geks GelZmog Gelawynn +Gelderlander Gelezis +Gelijk Geliquideerd Gellaitry +Geller Bing Gelly +Gelmar +Gem +GeminitusII +Gemlingur Gemsbok +Gen Kokopuff +Gen4 UU GenCoupeGang +GenFormat GenGraardor +Genabackis +Genann +Genaron +Gene Shorts GeneBelcher GeneDenim GeneKapp +GeneVieve_SL General +General Aldo +General Bigi +General Iroh +General NL GeneralJosh GeneralLudd +GeneralMcRib GeneralMo GeneralMoist +GeneralNASTY GeneralPlay +GeneralStore Generall +Generall_xx Generalton Generol55 +Genesee +Genesis G80 Genetiics Genetiz +Genga r Gengahr Gengerbred Geni +Genie Magick +Genie Talls GenitalsHead +Genix +Genjitatsumi +Genk Gennie +Gennu Geno +Geno RS Geno2566 GenoJuice Genocidal +Genovan Genres Gensha Gentileza Gentle +Gentle Death +Gentle Gypsy +Gentleman J Gentlemanfox Genty Genuine GenuineDude Genus +Geo Fe Geo Geo GeoDuuud Geoculus Geodark +Geof Sux 666 +Geoff_Failed Geoffry +Geography +Geoguess +Geoide Geologistic +Geomatic Geonde Geordie George +George 36th +George F M +George Vv GeorgeJx93 +GeorgeVork Georgey +Georgezs +Georgies +Georgopol Geoso Geotechnicki Geovanna GerAmi +Gera1d Gerald +Geralt Nivea Gerb3 Gerberos Gerbs @@ -8465,47 +17502,92 @@ Gerjan GerksShirts Germaan GermanGuyRS +Germanic Germaphobic Germimo Gernoazi +Gero Geroko +Gerr Hurka +Gerrido Gerrmz +Gerroes4GIM +Gerry Bud Gershboon Gert GertrudeSimp +GertrudesAss +Gery07 +Gesaldo +Gese Geskar +Geso Gesoz +Gestetner +Get A Life B +Get Back Son +Get Bread +Get Exp +Get Fukt Pal +Get Pho +Get Syked Get0nMyHorse GetABeerInYa GetATapWater +GetAclick GetDatXp GetMeOut +GetMeOutHere GetOnMyLvl +GetOverlt +GetPets12 GetSkipped GetThatUpYaa +GetTogether GetUrWild0n GetWreckdSon Getdolphined Getfck3dup Getlow777 +Getplayed Getsu +GettinTanked +GeudensK +Geuld +Gew Gewoon Gezang +Gf g Gf4tiuser GfRsIGotAGf +Gfi +Gh3t0 T4nk3r Ghafir +Ghaipol +Ghandizy Ghandy +Gharron Ghckkrchkrer Ghda +Gheed Ghentiana Gherkins Ghetto +Ghhjgwsr +Ghidorah +Ghillie Suit Ghilly Ghoosted Ghorraka Ghosnik Ghost +Ghost 311 +Ghost Ahoy +Ghost Fe +Ghost XP +GhostAralein GhostHouse +GhostOrchids GhostTank GhostXD Ghostas @@ -8513,305 +17595,675 @@ Ghosteeen Ghostfice Ghostflips Ghostiami1 +Ghostly Taco GhostlyFetus Ghostlyowns Ghostninja +Ghostsu +Ghostwish492 +Ghosty +Ghoul Intent +Ghoul Zeta +Ghraz Ghruntsarokk Ght179 +Ghurrix +Ghuuneiboi Ghxul +Gi Lew +Gi bbo Giallo Giant +Giant Ego +Giant Gochu +Giant Killer +Giant Peenos +Giant Plums +Giant Trunks +Giant spider +Giant235 GiantConker +GiantShafty Giantesss +Giaveri +Gib bread GibMePets Gibbed Gibberish Gibboh +Gibbon +Gibbon girl Gibby Gibby0712 Gibbzzy +Gibsky +Gibsmeister +Gibson Giddy Cowboy +Giddy Yup Gidejong Gidionn +Gidle Soyeon Giebu Gielinor +GielinorGage +GielinorGoat +Gielinord +Gielinored +Giena +Gier Gierige +Giffaro +Gifgas GigaBinary +GigaChad FSW Gigabit +Gigantic Nut +Gigantic Owl +Giganttio +Gigashit +Giggel Boy +Giggety +GiggleFailer +GiggySmalls Gigi Gigime +Gigs x0rz +Giiblo Giiirtz +Giitzy Gijoe184 Gijs Gijsbart +Gilan Gilbert +Gildart_91 Gilded +Gilded Gucci +Gilded Robes +GildeddGuppy +Gilenoth +Gilesy_93 GilgaGaming Gilindur +Giljom Gillan +Gillbert44 Gille Gilles Gillisuit +GillyChop +Gillyjj Gilnash Gils +Gilthresa +Gim Balboa +Gim Mayhem +Gim Reaper x +Gim Rickard +Gim Sahara +Gim Synicals +Gim ckn +GimJonel Gimaralas Gimbli102 Gimlocke Gimme +Gimme Pixels GimmeDatWAP +Gimmie 500k +Gimp Charona +Gimp George +Gimp Rachau +Gimp Sage +Gimperfect +Gimpgas +Gimptan +Gin N pizza +Gin Theory GinValid +Gina Linetti Ginekologs +GingaFe +Gingaaaaa Ginger +Ginger Elvis +Ginger KG GingerUnit49 GingerZ94 +Ginger_Men Gingerbeefe Gingr +Gingr Snaps +Gingyge +Ginjaah +Ginko Sora Ginny Ginobeatsss Ginyuu +Gioarcher69 Giorgi +Gios Ladder +Giovanniam Giovano +Gippeo Gipson Giraaa GiraffeLord Giraffeneck Girl +Girl10116 +GirlGamer420 +GirlLoveeee Girls +Girls Add Me Girlygurl420 Giroza +GirrthBrooks +Girsu Girth +GirthBeast +GirthMatters +Girthy Bunny GirthyBaboon +GirthySack Giskardr +Gist Gistermorgen +Git Ducked +Git Gudr +Gity +Giuuntas Give +Give Advice +Give Er +Give Me Kith +Give Purple +Give Purples GiveMeMaul +GiveMutagen Given +Given Bow +Given Power Givera0 +Giville Giving Givox +Gixerikan Gizmoed +Gizmoo247 +Gizzy-71 +Gj nice Gjerloew Gjonunaki +Gktr +Gl Bob +Gl Im Dandy +Gl Im Tom +Gl0ver GlLDARTS +GlM Sam +GlMKingCole +GlMP Blue +GlMP Purpp GlNGER GlRLS +GlTHUB +GlUwUten +Glaceon Girl Glacial +Glacial Fog GlacieredTig GladePlugIns +GladeSONPT Gladeandy +Gladiator12 Gladiator4x4 Gladpootje Glads +Glaeser +Glaive Rush Glance +GlasMelk +Glasabarn Glashute +Glass talons +GlassIsrael +Glassblow Me +Glassface +Glava Glavas Glazmeister Glazyy +Glcnn +Glcnn II +Glcsw Gleassi Gleddified Gleep +Gleesoman123 +Glejak Glen +Glen Cook +Glen Kenobi +Glen OSRS +Glenbobis +Glennis25 Glennitals Glennyboy Glennzii +GleuberBTW Gleythar +Glicky +GlideWho Glierieman GlitCH1221 Glitchfather +Glitchxd +Glitterix Glo-Tank +Glob Master +Globalled +Globby Gloc Glock +Glock 48 GlockHoliday Glockford +Glocktopuss +Glocs +Glod Gloei +Gloei Lampje Gloom Gloop Glopyz Glorfsaus Glories Glorious +Glorpy Glorp Glossay Glou +Glough Gloves Glow +Glowing Dawn +Glowing Ray Gloyer +GlucGluc9000 Glue +Glue Scroll GlueInhaler Gluehbirne Gluest1ck +Gluhh Glukoosi Gluons Glut +Gluteeni +Gluten ei Gluteous +GluteyIM +Gluurbuur Gluurder +Glyn Glyo Glys +Gmackie1 +Gmic +Gnaes +GnakNim Gnarkotics +Gnarled Gnarles +Gnarly Nacho Gnarlysurfer Gnarmato Gnarnold Gnarwhal +Gnashit GngBng +Gnge Gnight Gniles Gnoblin Gnome +Gnome Ass +Gnome nz GnomeBustas +GnomeChompii +GnomeFN +GnomeScarf +Gnome_child1 +Gnomeball Me +Gnomedalf Gnomeopathy Gnomeplay Gnomeputone Gnomerex Gnomeruno +Gnomes +GnomesDid911 +Gnomonkey +Gnomuim Gnon +Gnoob Gnorc Gnos +Gnosticz Gnotyep +Go Already +Go Die Go Do One M8 +Go FF +Go Outdoors +Go Outside +Go Rogue +Go Thanos +Go ask Wiki Go2HornyJail +Go4Stonks GoBearsUA GoBiiii +GoDhAsCoMe2u GoDrinkWater GoGGu +GoIsland GoPlayOutsid +GoSlow +GoToBedKids Goads Goal2Max Goalkeeper54 Goals Goat +Goat Toes Goat512 GoatAteMySon GoatOfWar GoatStank +Goatbigboy Goaticorn +Goatman Goatmelk +Goatrilla Goats +Goatsky Gobbie Gobiasinds Goblin +GoblinBreath GoblinLevel1 Goblinborn +Goblinchips +GoblinxSlaya Goby358 +Gochancee1 +God 126 +God Aye +God Bjorn +God Flax +God Forgave +God Like PvM +God Of Chips +God Of Hatrd +God Of Lamps +God Of W13 +God Pandora +God Ranger90 +God Tier +God of Walls GodDmntNappa GodEmpsTrump GodHalfMercy GodKingJT +GodLovesG4ys GodM GodOfCats GodOfTorment GodSaidGrind +GodSaved GodSlayer422 +GodSoge +GodTierAcct GodTormentor Godaa Godcody +Godd Is Good Goddaz GoddessAmaya GoddessHylia Godenzonen +Godeso Godezzo +Godfather TR +Godfather9 4 Godis Godkip +Godleh +Godlike Nico GodlikeOne Godly GodlyLeecher GodlyThug GodofAhri +Godofore +Godofyou2 +Godric777 +Godrics Gods +Gods Madness +Gods Plans +Gods Savage +Gods Taint +Gods Word GodsBlackSon GodsSniper GodsSyndrome GodsZombies +Godsent GodsentHero Godspade +Godss Angel Godstrong +Godswordl +Godte +Godumas +Godvermomme +Godwar Godwise GodzFear Godzilla Godzilla1282 Goed +Goed Gedaan +Goed stinke Goedaardig +Goedgemutst +Goeie Sannie Goeman Goes +Goggoli Gogo +Gogo Gorilla Gogojuice +Gogoud GogurtYogurt Gohan +Gohanski Gohler +Goiij +Goilin +Goin +Goin Slayin +Going HCIm +GoingBald130 +GoingBonkers Goinvokeman Gojam +Goji Berries +Gok Wan k +Gokillanoob Goku +Goku 420 +Goku Nazz Goku3ss3 Gokuu +Gol +Gol D Stocks Gold +Gold Avocado +Gold Devill +Gold Golem +Gold Lynx Gold M40A3 +Gold N Mole +Gold Ox +Gold Ruler +Gold Scrap Gold-Ileana GoldTracerR1 Goldclaw30 Golden +Golden Acorn +Golden Dunes +Golden Eyes +Golden Power Golden2Aim GoldenArm647 +GoldenGoose GoldenMinion Goldendev +Goldendrgn GoldfarmCOX Goldie47 GoldieOne +Goldiekk +Goldkimono Goldless Goldmagic +Goldmagic 1 +Goldminer727 Golds +Golduck +Goldy Mox Golem +GolemDuoGIM Golemantium Golembaby Golf +Golf Socks Golfcarts Golfje92 Golfkarton Gollito GolloSam +Gomardo Gomham Gomsugo Gomut +Gon GonDaba GonFeeshin +Gonah Gonapappa Gondort Gone +Gone9237 +GoneClear +GoneFarming +GoneInDry GonjaMon +GonnaSleep +Gont Gontr +Gonyor Gonzalo +Gonzra +Goo Cat GooMoonRyong +Gooball Gooberz +GoobledeGoop +Goobtacular Good +Good Bull +Good Creb +Good Cuxt +Good Darts +Good Duck +Good Ironman +Good Natured +Good Pho You +Good Potato +Good Voodoo +Good lol GoodFight +GoodGuyJosh +GoodOlBussy +GoodSkinCare +GoodTalk BRO +GoodVibes Gooda4 +Gooday2uall Goodbye +Goodbye game Goodcents +Gooddeerend +Gooder Album GooderB0aty +GoodlyKi56 Goodole Goodpizza +Goodriver Goodwin +Gooey Bace Goofy +Goofy Kitten GoofyLlama +Goofzillers Googagoogago Googie +Googii Google +Google Plus +Google THC Googleisme Googs +Gool164 +Goomy +Goon Galore +Goon Rangoon +GoonRNG +Gooner Gooneshy Gooney Goooby Gooochy +GooodO1dDays Goopbandit +GoopityGlorp Goose +Goose Field +Goose World GooseAlmyti +Goosebottle +Goosecaboos Goosely Gooshus +Goostafus Goothan Gooziky Goph +Gopherpaws Gophurr Gopuppy +GorGorDon16 Goragtong +Gorak +Gorastaman Gorathe +Gorazdus Gord Gordi Gords @@ -8819,68 +18271,133 @@ Gore GoreTexZ Gorehogthot Gorg +Gorge002 Gorgeous +Gorgeous Vin +Gorgeous War Gorgonsolo +Gorgoth +Gorila911 +GorillaChad +GorillaHaze GorillaMon3Y +Gorillr Goriox GorkOfGuthix GorogSmash Goromi +Gorons Ruby +Gorpie GorskiGG +Gortron +Gory Figment +Gorzie Goshiki Gossip Gosustyle +Got Mad Nugz Got my DD214 GotABigDoing +GotItAll99 GotTheTool GotchuXL +Goteborg Gotemburgo +Gotham Steel +Gothic Mommy GothicHippy Gothicking70 +Gotta +Gotta B Dope GottaSlay Gottaburn +Gougar Gountor5 +Gourd GourdPicker +Gourmandise Gourmet +Goy Boyy +Goy Oy Vey Gozi Gozpot Gp On Me +Gr OO7 Gr0tti +Gr33n +Gr3at Noob +Gr4naat Gr4p3 +Gr4y W0lf Gr8Legacy +Graaf Mol +Graardad +Graarp +Graav Grab +GrabAColdOne GrabbaLeaff +GrabsPeePee +GraciousHeeb +Grade I Graenmeti Grafie Grafvs Graham47 Grain +GrainUhSalt Grainfedbeef Grainter +Grainwave Grainys +Grakron +Grallham +Gram othy Gram0fDabs Gramalio +Grambo Grammar +Grammar Naxi GrampaDreami GramsaySan Gran +Gran t +Granbyboys69 Grand +Grand Magpie +Grand Tree +Grand mb GrandErected +GrandSlash +GrandVince +GrandWizard +Grander LTU Grandikid GrandmasterT Grandmasters Grandpa +Grandpa H Grandwarlike Graniitti +Granite God1 +Grannolegs Grannt +Granny Tiddy +Grant +Grant LLKL Granto +Grantos1993 Grants2004 Grape Graphics +Grappleshot Graros Grasp +Grass Nugs Grasshoppper +Grassman-420 Grast1z +GratefulFunk Gratefulfunk Gratis Gratitheode @@ -8888,178 +18405,360 @@ Graudalin GraveDiqqer Gravecan Graveless +Gravenor Graves Gravew0rm +Gravitrons +GravityPulse GravyTugboat Grawgar Gray +Gray Goo +Gray Icon +Gray Nine +Gray Owl +Gray Sails +Gray Wolf31 +GrayXOF +Graybanns Grayden Graymarrow Grayola +Grays Peak +Grayv +Grazalay +Grazia Grazing +Grazing Goat Grazium +Grazuz +Grazy Killah Grazzak GrcRevisited +GreFunky Greally +GreasierNerd Greasy +Greasy Nerds GreasyGoose +GreasyKnucks Great +Great Catto +Great Dain +Great Sneeze GreatBritain +GreatFambino GreatGinGin7 GreatNorth +Greater Rev Greaterbeing +Greaterwang Greatgranpa Greatwhite62 +Gredhkj GreedyFox GreedyG4me Greek +Greek Bust Green +Green Floyd +Green Gh0st Green Guru42 +Green HoHo +Green Molly +Green O +Green Theory +Green noob Green0ktober GreenBstard GreenClue GreenCortex +GreenMonstah Green_Matcha Greenbeens1 +Greenboy638 Greendor +Greene Daeye Greenikulas Greenmusiq Greenwell Greenwolf666 Greg +Greg Bcknghm +Greg M +Greg NM +Greg Why +GregLAD Greggery Gregley +Gregobeast69 +Gregor itos GregorJesk Gregornaut Gregory +Gregoryxo +Gregreg Gregsdabomb +Gregy165 +Greh Greig +Greil +Grej Gremlin Gremoir GremorysPawn Grena +Grenadev Grenenjah Grenthyl Gressaker +Grewal +Grey Beards +GreyCheddar GreyStation2 +Greyballes +Greybird +Greyhound00 +Greyhounds Greyhunter +Greytness Grider Griefstonks Grieve +Grieve r Grieve4Nieve Grievedd +GrifSpice +Griff Please +Griff6GG Griffey +Griffeydor Griffimano Griffin +Griffin 900 +Griffinerr Griffoo Grifo Grifwin Grihm Grilby +Grill Bears +GrilledNem Grim +Grim Lahey +Grim Reapr Grim1O GrimR GrimRequiem Grimalkinn +Grimby +Grimescene +Grimey17 +Grimfeather +Grimhimblem Grimm +Grimm Malice Grimmauld Grimmij0w +Grimmjaww +GrimmjowCero Grimmlie Grimms Baane Grimnitro Grimple Grimreaper22 Grimsomebody +Grimwar Grimwold2 Grimy +Grimy Elf +Grimy Ghetto +Grimy Guamay Grimy H +Grimy Lord +Grimy Oppie GrimyBuchu GrimyIndica +GrimyPussy +GrimyRanarrs +GrimyTorstol +Grimz Reapin +Grimzyy +Grinchy Grind +Grind 4 Max +Grind 4 Pets +Grind Time Grind4Life2k +GrindAndGame +GrindFaster Grindcorr Grinder GrinderTo99s Grindin Grindinator Grindius +Grindlwald Gringotts GrinoReigns +Gripzakje +Gris Moor +GrittyTacos +Griz mobile +Grizmatik Grizzley +Grizzly +Grizzly Rock GrizzlyCares GrmblGrombl +GrnBstrdO_o Grobar20 Grobaz Grod15 +Groen Hertje Groene Groesbeek Groet +Grog Dog +Grog Mobster Grogget Groleo +Gromada GrommrUK +Gromp Love Gronk Gronky +Gronky Mutt +GroogeN +Groot Lol GrootPool GropeJelly Gros Gross +Grosyte +Grot I +Grotegelds Grotkop Grotty Grottyzilla +Ground Hawk +GroundGolem GroundRanarr +Group Int +Group Pig +GroupAaronMn +GroupSexyMan +Groupie Hugs +GroupieSheep +GroupyCoopy +Groves +Grow Culture +Grow a Set Grown +Grown ups 2 Grrh Grrim Grrr17 Grrrrrrr +Grubbfoot +GrubbyNative +Grubumas Grucci Gruglio Grum Grummmy Grumpy +Grumpy Bear +Grumpy Dane +Grumpy Rock +GrumpyBean GrumpyFire +GrumpySkelly +Grumtuque +Grunderzz +Gruntlife +Grupopo Grwl Gryff1n Gryygeri Grze100 Gschlez Gsef +Gsuz +Gtacoolz GtheSquid Gtotgt +Gtr Zilla +GuBoki Guadanha Guak Guaka Guala Guam +Guam Autism +Guam Extract +Guam Leaf +GuamFarm +Guapify Guar GuardMaze Guardhakan +Guardiaaan Guardian726 Guardy Guason Guataca Gubrew Gucci +Gucci Print +Gucci Sheets +Gucci Skrrt Gucci0 GucciBalboa +GucciChris GucciDang GucciDragon +GucciMedHelm +Guccifi GucciiFlops Guckle Gudfad3rn +Gudge +Gudginator +Gudmen Gudomligt +Gudown Guelph +Guernicaa +Guess I Did Guest738 Gufix +Gugaru Guidebook +Guidefox +Guido Bwana +Guildboss Guile Guilles123 Guillotine Guilou3 +Guimmy GuineaHorn +Guizompaz +Gukkiman GulTraktor Gulf +Gulmek +Gulpar +Gulpin Gulsaft +Gum +GumGum B +GumGum Fruit +Gumat0s +GumbleChunky +Gumbygerkin +Gumbygrump Gumbygusher Gumbypriest Gumiibear @@ -9068,12 +18767,19 @@ Gummibier Gummy GummyTushie Gumpie123 +Gumri +Gun +Gunalations Gunci +GundarV Gundrace Gundrak Gune Gunfire +Gunite Gunjamini +Gunman +Gunn a Gunna Gunnar Gunnawr @@ -9082,156 +18788,351 @@ Gunnolvur Gunny GunnyGuest Guns +Guns Up +Guns n Rozes GunsN +GunsN Roses +Gunshow36 Gunsmith95 GunsonGurner +Gunta Guy +Gunter +Gunteria GuntherNese Gunthie Guntilius +Guntown Gunvald1 Gunwil +Gunzyx +Gupto3 Gura GuraChan +Gurahka Gurchh Guren +Gurenz Guri77 Gurilovich +Gurke Gurkes Gurnie GurningPills +Gurnsy Gurog Gurp +Gurp Gork +Guru Pathik GuruOz3 +Gus is Maxed GusMagnel +Gushing Thot +Gusmamba Gustav +Gusten +Gustice Gustie1 +Gusty x GuteArbeiter Gutenberg +Gutenberger +Guthiccz Guthix +Guthix Boss +Guthix San GuthixIsBae GuthixVapes Gutti +Guuds +Guus geIuk +Guusman Guwaap Guwapp +Guwgle +Guy Shishioh +Guy Williams +Guy in hat +GuyBillNye GuyDude GuyHarvey GuyInReaLife GuyOnaBear +GuyOverThere +GuyintheChat Guyringo Guzas +Guzman Loer Guzzle Gvjordan Gvzy +Gwaas Gwas +Gwas BE GwasMaster Gwasha Gwasing +Gwaztec +Gwd Gweav +Gween Dwagon +Gweeve Gwellz +Gwenchanaa +GwigMate Gwimer Gwinter225 +Gws 2 +Gwu +Gwublin Gwyn +Gwynrwyn +Gxhost Gyanzin +Gyga +Gym Beam +Gym Is Home +Gym then GYG +Gymming Gymp +Gynn GypsyAvenger +GypsyTears Gyrati0n Gyro Gyroman +Gyrotta Gytisr9 Gyts Gyym +Gz Parsec GzBs +H 0 U N D +H 2R +H A D O +H A I D E S +H A M M E H +H A R L S +H A T +H A X 3 D +H D M P +H E A L E R +H E J S A N +H O L M E +H Q Killer +H S 7 +H U L K K +H U N A J A +H a t y +H a w n y +H abs +H alo +H ch +H e c t o r +H i n e s +H l E U +H obbs +H owl +H rekDoer +H uff +H-tsi H0B0 H0DEX +H0LINESS H0NA H0PES H0RSEFIGHTER +H0RY +H0j +H0j00 H0lyPorkyPig H0me H0nestyBe +H0wy H1GH H1gh H1gs +H2 Ninja +H20magic H22a H2GivesUTheD H2HO H2Okt +H2R H3AV3N1Y H3NNESSEY H3adBurner H3ll H3llg0g H3nti3 +H3rbMaXD H3rbs H3rioon H3xxar +H4RD P V M +H4RD PVM +H4wk0n H501 +H61 +H8 +H8rry +HA RD HACHE HADYYY +HAHA CODY +HAMMY P0TTER HANDLEwCARE HAOKIMALONE HARVEY HARYWERNASDA HASA +HAUS DOSAN +HAWGCRANKD HAXERFUGGv2 +HBPandox +HBTFD HBTurpin +HC DVS HC Diego +HC H R A D +HC HALLEN +HC J P +HC Lone +HC Nitsy +HC PogChamp +HC Tehl0rd +HCByTheWay HCIM +HCIM Freak +HCIM Telmo HCJynkky +HCTHB HCaturbate HCgenes HClM HCmunchies +HCquitter HCsndr HDGamerLewis HDarcticus +HEAD TURNER HEADLlNES HEAVYWElGHT HEAVYWORK +HEJNUS HEL1O HELPPOLOTO HELl0S +HEMSKl +HEMULIN LADA +HER0X HEREforPETS HERYWERNASDA +HEWlTT +HEXISZ7N510Q +HElMDALLR HElSENBERG +HFH141 HGCduke +HGHZ HHans +HI IM XP +HI lM SATAN HICK +HIHIHl HIKI VALUU +HIM thespaz +HINEaKIN +HKD HL8ight +HM-O3 +HM01Urself +HM01Yourself HM02YouFools HM05 +HM05 Flash +HMFIC +HMR9 +HMS Bass +HMTickle +HMWRCKR +HO NK +HOB0MAN +HODL DEEZ HOKIE +HOKIE II +HOLDUP +HOMIE_SNKE HOOD HOOLlGANS +HQ140 +HR palvelut HRVRD +HRZ Dennys +HReversti HT Hero King +HTKBeast HTTP HUGE +HULKSMASH +HULL CI TY +HUMANATSIGHT HUMPING HUUTIS HVSG73373535 +HWFG Jordan +HYDROPWN1C +HYPEHYPEHYPE +HYPERTAIKURI +HYPOXD +Ha Tsjoe +Ha Yea +Ha rrison +Ha ss +Ha1fWayCrook HaClintondix +HaKooro +HaMwise HaNamer Haaaa Haaaydeez +Haar-Xil-MES +Haarde Knud Haardest Haarvey Haavard Habaneros +Habbitatt Habib HabsWin2021 Hachune +Hacien +Hacis Zone +Hack McGraw +Hackdash Hacked +Hacked Here +HackermanJS Hackett116 Hackie +Hacklang Hacky +Had Enough +Had2hurt HaddlingOS Hadeees Hadei Haderlumpen Hades +Hades m8 +Hadezzz +Hadhod46 +Hadies205 +Hads MC +Hadyen +Haelendur Haell +Haemogobl1n +Haertyew Hafco Hafcos Hafdis @@ -9241,22 +19142,42 @@ Hagaar Hagedis Hagenau Hagenees +HaggisChaser +HagridGone Haha +Haha noob +HahaBonk HahaYes Hahaha +Hahalord888 HahnSuperDry +Hahru HaiDiLao +HaiHai +HaiIZaros Haiaf +Haiden Haidynn Haighy +Hail to Pitt HailYeah +Hailie Jade +Haio Hair +HairNotFound Hairy +Hairy Batman +Hairy Dery +Hairy Hobbit +Hairy Hooker HairyCave +HairyGirls76 HairyLatino +HairysJungle Hairyteddybe Haissi20 Haiten +Haitze Haizet Hajduk Hajj12 @@ -9265,28 +19186,49 @@ HakGwai Hakafissken Hakala HakanTheMad +Hakaniemi +Hakattu Hakeem +Hakkie Hakkua Hakozuka HakunaWHO Hakuryuu Hakwai +Hal Jordan +Halaal +Halal 1 +Halal Burger HalalSnakbar +Halaladdin +Halbrand3791 +Halcony +Halcyon Myth +Haldir Haldir140 +Haldun Hale +Hale End Half Half Volley +Half a Cig +Half a Worm HalfAb0rted HalfHawaiian Halfdanger Halfway HalfwayOkay Haliax +Halibelle Halipatsuife Hall +Hall of Fame Halla +Halla-Aho +Hallako Halleloja Haller +Hallo Knul Halloman71 Hallonkram Hallowed @@ -9294,74 +19236,156 @@ Halls104 Hallucianter Hallucin8d Hallufauz +Halluusio Halo HaloMisfit +Haloboy Haloking176 +Haloman6666 Haloo +Halseys Halsly +Haltrek +Halujo HaluunKeksin Halve +Ham Dip +Ham Turkey +Ham r +HamHawk +Hamada +Hamaken Hamataka +Hambam55 +Hamburgor Hambzy +Hamdulilah Hamed +Hamel Hamii Hamis +Hamlee +Hamm HammZ Hammer +Hammer Man +Hammer4422 Hammer4442 HammerTime29 HammerTine Hammered +Hammertime Hammond +HamnerTime HamoodyShimy Hamp +Hampton Bays Hampus +Hamrocks Hamsterslayr +Hamstr Hamudiy +Hana the Mad +Hanashee +Hand Puppet HandTuggy Hande +Handecapped +Handex Handicap HandleofJD Handley Handphone Hands +Hands Hurt Handsome +Handsome Rob Haneet +HanekawaSimp +Hanfkeks Hang +Hang em high HangChowCrow Hangin +Hangin Nuts Hanhen +Hani +Hanikala +Hank Jackson +Hank T Tank +Hank343 HankTheTank2 Hankanini +Hankk1 HankoAJ Hankzilla Hannah Hannh +Hannibal GIM Hanno Hannu +Hannu Poika +Hannu Soolo +Hannys Main Hanoi +Hanoi Rocks +Hanoob btw +Hans Glans Hansen Hansern +Hansiil +Hansonville Hansy +Hantarded Hanwi Hanzino HaochengZ Haole Haphazzardz Hapoton +Happ1RS Happier Happiikhat Happy +Happy Beluga +Happy Dappy +Happy Feet +Happy Josh +Happy Kitty +Happy Light +Happy Meal +Happy Prayer +Happy Tj +Happy exSlut +Happy2DaCore Happy420 +HappyMeds HappyPandaXD +HappyScape +Happydud01 Happyfrog12 Happyguy +Happypurpday +Happytoy +HapuApu Hapukurk HarKieren +HarO Reborn +Hara YEP +HaraHachiBu Haraket Harambe +Harambro +Harash Harassment +Harbix +Harbor 2567 Hard +Hard Chance +Hard Clue +Hard Filmur1 +Hard Kinta +Hard TeK HardFormed HardHatJeffy HardMannetje @@ -9370,13 +19394,22 @@ HardRazer Hardc0reBruh Hardcome Hardcrux +Harde Pik HardeSnikkel +HardenedRS Harder +Hardeschijf Hardie +Harding Hardlesas Hardman +Hardon hank +Hardstyle IM Hardwaring +Hardwell420 +Hare Danger Harem +Harem Isekai Harja Hark Harka @@ -9384,122 +19417,236 @@ Harken Harken21 Harlekin Harlem +Harley Nz +Harleyrox +Harlie Harlot Harm Harmala Harmentje +Harming +HarmlessTwig +Harmon Angel +Harmoniiumm Harmonized Harmony +Harmonyy Harmr Harms +Harms Whey +HarpoonMyAss Harraggen Harresvela Harri +Harri Hylje Harriganrace +Harris0n Harrithon +Harrods Fish Harrris Harry +Harry Bucket +Harry Dirty +Harry Hendo +HarryPoppa HarryTheGOD HarryTheWhte Harryalt +Harryguy23 Harryindacut +Harses +Harsh Times Harshhashh HartIess Hartlepool Hartree +Hartreni Haruka +Haruka Meh +Harus Ranger Harutikk Haruto Haruuki +Harvahammas Harvardista +Harveytec +HaryLongWood Has2BeSHiFtY +Has547 +HasOneLegacy Hasballa +Hasbullah Hasek Hash +Hash Browns +Hash House +Hash One M8 +Hash katchum Hash1 +Hash1Aussie HashHund HashIsLife +HashTagYou Hashh Hashiiirama +Hashimm +Hashlife HashtagGOALS Hashtagz +Hasloa Hass +Hassall Hassis Hassoni Hastings +Hasty Power +Hat i +Hat tis HatchetOG +Hatchkat17 +Hatchling +Hathrow HatiFnattt +HatoradeJack Hats Hatsune +Hatsune Miko Hatsy Hatte +Hattibagen Hattnn +Hattuviilari +Haubjerg +Hauki0nFive Hauki0nIron Hauki0nNoob Hauki0nUlti +Haunleff Hauntedfury Haunter12123 Hauntya HausHausHaus +HautaNorsu +Hautboi HavU Havac +Havard Havawk Have +Have Sabr HaveAGood1 +Havendare +Havenmeester +Havi k +Havidex +Havoc Grasp HavocRavage +HavocThomas Havok Havottaja Havzzter +Haw +Haw4iiankush Hawaii +Hawaii Ice HawaiiMadez Hawaiian +Hawasser Hawes Hawk +Hawk Driver Hawkear Hawkes +Hawkeye87 Hawkit HawkofLight HawksMama +Hawkwound +Hawkzrael +Hawler12 Hawolt +HaxY Haxd +Haxident +Haxixero Haxn Haxnoiz Haxoonie +HayHay Hayabusa +Hayaku Hayasaca +Hayasaka +Hayboo Hayden3 +Haydenl451 Haydenss +Haydern Haydor Hayesy Hayfield Hayirlisi HayleyQuinn Haylz +Haynex +Haz-zy +Haz2 HazDownz Hazade +Hazardless Hazardouss +Hazards Haze +HazeCloud99 +HazeMePls HazePGN Hazed Hazeley +Hazezor Hazla Hazsle +Haztraz +Hazy Sparrow HazyHerb +HazyHour HazzFive +Hc OffWhite +Hc Oh Wait +Hc Toleranss +HcFlex004 v4 HcGon HcHoopla +HcimTironade Hcimdiot +Hckie +He Adventure +He Pinky +He Who Iron +He nry He11zdone HeSmokesBud Head +HeadBear HeadHuunter +HeadOverseer +Headache +Headenforcer Headgaskets Headleya +Headliners Headlines +Headlines v2 +Headshotting +Headwipe +Healers +Healty Heard +Heard Chef Hearne +Heart Please Hearted Hearten +Heartlesdude Heartlock Heartquake Hearts @@ -9511,154 +19658,299 @@ Heat HeatSeekr187 Heated Heathen +Heathenry +Heather x3 +Heatmyser HeatproofIM Heav7n +HeavenIyGard +Heavener HeavenlyBlue Heavens +Heavens Feel +HeavensDao HeavensStorm +Heavensdown Heavy +HeavyBowGun +Heb0 +Hecec Heck +HeckIron +HeckinWoofer HeckingFish HecklerNKoch Heckstrm Hecote Hectic Mic HecticGinger +HecticHerb +HecticKebab +Hectoplasma Hedge +Hedralx +Heebie Heedbo Heee Heel Heeling +Hefboom HeftyFlan HegXDXD Hege Hegelund Hegert Hegesy +HehtoLerssi +Hei Kneuter +Heifetz +Heightenings HeikkiPentti Heil +Heil Pierce +Heil Vegeta Heilios +Heill Ragnar Heinamies Heineken +Heinekentje Heinered Heinzzel +Heir HeirHead +Heisbergh HeisenBergW +Heisenber9 +Heist Music +Hej hej +HejBrink +Hekaton Hekdik Hekla Heko +HektikBadger +Helbur Heldip HelemaalMooi Helesta Helium +Hell ETC +Hell Kitt3n +Hell ffa HellAngel HellBlood Hella +Hella Tonk +Hellabad Hellafly +Hellagaybtw Hellandnz Helldog946 Hellhaunted +HellianV Hellinferno4 Hellixx64 Helllblazer +Hellmo Hello +Hello Bel +Hello Friend Hello6 +HelloBot9000 HelloDottie Hellodummy Hellrain962 Hellrazorr +Hells 10hp +Hells Chance +Hells Godz +Hells Puppet +Hells doggy HellsRavage2 +Hellwater Hellz Helm Helmoid Help +Help Autism +Help I Gay +Help my +HelpImStupid HelpLahh HelpTheNoobs +Helpful +Helpless Ale Helson Ryse Helta9 Helx +Hema roids Heme2 HemeSupreme Hemiptera Hemmii +Hemmur HemoGlobe Hemp +Hemp product Hempopotamus +Hempwick Hems +Hen Ac Araf HenaQ +Henbane0 +Hendo Au Henee +Henehh Henfami Hengecobdig +Hengggga +HenkDeTanker HenkdePlank +Henkka Btw +Henkka242 Henkkawaa +Henkkiiieee Henkleebruce +Henko420GB +Henktius Henlo +Henners Hennesssssy Henni Henniejj Hennssey +Henri380 +Henry Fe +Henry XV Henry14 Hens +Hensel +Henslord +Hensta +Hentai Daddy +HentaiMaiden +HentaiMusic +Henu OG +Henzone58 Heolis HephaestusRS +Her Gay HerAddiction Herb +Herb Deen +Herb Lab +Herb Quest +Herb box Herbacist Herbaderb HerbalKing +HerbalTeabag Herbalicious Herbby Herbilicious Herblore Herbmania +Herbmatic Herbogus +HerbsnSpicer HerculesLoyd Herculooo HereFor_Beer HereSomeDank HeresALlama HeresySlayer +Herk +Herkko +Herkko32 +Herkku Herkules Herky Herm10ne Hermanni +Hermaus M +Herminator0 Hermit +Hermit Jack Hermitian +Hermot palaa Hero +Hero Extreme +Hero Kid +Hero vs Hero HeroDan +HeroGenos HeroToby Heroed Heroic +Heroic Hope HeroicDesire +Heroicjesus +Heron Bird Herp Herpa HerpaGnome HerpesNo Herpesbek +Herpito +Herr Gaucho +Herr Luc Herra +Herra Beavis +HerraBonsai Herrits +Herrmann3 Hesienberg Hesitation +Hesmander Hespori +Hespori KC +HesporisSeed Hespus Hess Hesson +Het Heden +Hetdorp +Hetebeir +Hetehaan +Hetimos +Hets Capture Hetumo Hetzer Heuha Heur +Heuro Heuvel +Heuvel Reus +Hevenlys Hever Hevi +Hevi Luumu Heving +Hewy +Hex Is Maxed HexMage Hexadecimal +Hexagon Heat +Hexalogy +Hexedited Hexenblut666 Hexenkonig Hexication +Hexos +Hexphase Hextale Hextra0rdnry +Hexun Hexxjan +Hey B +Hey Bob +Hey Cheems +Hey Im Dylan +Hey J +Hey Jase +Hey Jay +Hey Mills +Hey Ubba HeyBeautiful HeyBigDaddy HeyDaddy @@ -9666,42 +19958,103 @@ HeyFonte HeyHowsLife HeyItsLachy HeyLuffy +HeyThatsLife +Hey_Duck +Heymakerz +Heyman Heywaddup +HeyyDivine Hezbullah +Hezr HgH2 Hhmm45 Hhmora +Hi Cookie +Hi Im Ant +Hi Im Duncan +Hi Im Emil +Hi Im Frak +Hi Im Ken +Hi Im Pei +Hi Im Stevo +Hi Im Vitao +Hi ImMrRight +Hi Lvl Ideas +Hi Opal +Hi Ren +Hi Suka xD +Hi Tyler +Hi im Gin +Hi im Savage +Hi im Shar +Hi im Yellow +Hi lm Paul +Hi lm Sk8 HiDyvvnBDP HiGHFLYER +HiHowAreYa HiImIronRick HiImNicole HiLumbridge +HiNebb +Hiarii Hibbetts +Hiben75 +Hibernal Hick Hickery Hicu Hidaki Hidden +Hidden Power +Hidden one1 Hiddenn +Hiddinkiwi Hiddy +Hide Ink 207 +Hide Ya Kids Hidekia Hideo +Hideopenend Hider HidesHerEyes +Hidi Hidole555 +Hidro Hieroglyfic Hifter13 Higgens High +High Content +High Halpert +High Hustler +High Knight +High On Cake +High Ronnie +High Score +High Snorse +High Up Here +High as Zuk +High lm hi +HighAsF Bruh +HighBacon HighMeatloaf HighNoobJhin HighOnPlants HighProbably +Highbury Highcaalibex +Higher Force +Highestarchy Highfish +Highjacurmum +Highland +Highly Faded Highmountain HighonCash +Highs Highscaping +Hightierian Highway2Hell Higlen Higu @@ -9709,144 +20062,253 @@ HihoMenono HiiddeN HiipFiire Hiisgreat +Hiit Man Hiiz Hijack778 Hijsbergh Hiker Hikiukko Hikizato +Hikmet +Hilarious +Hilda Garde Hildr Hilge +Hilipilli Hilk +Hillhouse +Hillpker Hillram HilltopHick Hillzy Hilo +Himala +Himaster +Himtheguy +Himy +Hina Suguta Hincarpie +Hinchers Hindi +Hindle Hinny +Hinss +Hintai +Hiopeer +Hip Fe Hop +HipSlippers Hiphop Hiphopdude0 +Hipiguy48 Hipnodiskd4 Hippeis HippieLexy Hippies +HippoBlunts +Hiqhlife Hirae2 Hiria1 Hiroble Hiroticus +Hiroyuki Isa +His Faithful HisHungness HisLordship Hisano Hisblore +Hislordship HisokaX4 Hispeeed +Hissi-Timo +Hit U +Hit By Car +Hit Points +Hit The Guam HitMyVape HitOnRun +HitOrange HitTheVape Hitagi Hitch350 Hitchens HitherDither +Hitman Style +Hitme8 Hitmonchans Hits +Hits Like +Hitsumaru Hittimestari Hitz +Hiva Hiya Hiyouri HjaIIe Hjaldr HlKEN HlMBO +Hladomor +Hlata Hldz Hlep +Hlnub +Hm +Hmg Hawj Str +Hmm Fr Bruh HmmKoekjes +Hmmer Hmmingbird Hmmk Hmmm +HntrBTW +Ho Chi Meme +Ho LiFuk HoButter HoFkee +HoLeShite HoarderMan +Hob Ralford +Hobbi Hobbit +Hobby Jogger Hobex +Hobgoblinas Hobgoblins +Hobgrot Hobie Hobo Hobodude67 Hock Baws +Hockney Hocky Hocpuck +Hocus POTUS HodamOS +Hodeman +Hodge5 Hodgetwin Hodort Hodvik Hoffmanator +Hoffmanbmx Hoffsworth Hofus +Hog HogPacker70 +Hogboar +Hogboy Hoggormen +Hognag Hogo Hogsen Hogskoleprov Hoheti Hohhenheim Hohhoijjaaaa +Hoi An Hoilam Hoix Hokage +Hokage Jason +Hokage Sama HokageRS Hokiako Hokie +Hokiee +Hokies HolaBurrito Holbox Hold +Hold My Gun Hold3nMc +HoldMyShrimp +HoldenBallz Hole HoleNewName Holenes Holidayz Holiest +Holiidayyy Holisti Hollar Hollington24 Hollow +Hollowed One Hollsyy +HoloTheDrunk +HololiveOS +Holoubek Holsey3126 +Holt +Holtsen +Holunder Holy +Holy Cheetos +Holy Druid +Holy Jandals +Holy Moses +Holy Plan +Holy Rock91 +Holy Scrotum +HolyChrist HolyColt HolyDugong HolyElixir HolyEntity HolyGhost HolyHerb +HolyIronmoly +HolyMould HolySaints HolySkittles +Holycaw Holyfuk Holyhonza Holykana Holyshmokez Holyzuk Hom3r +Homage48 +Hombelkei Home +Home Boy +Home Office +Home Page +Home Plug +Home Run26 Homealot +Homebad Homefront Homegrowe Homelander Homeless +Homeowner Homepage Homer +Homer LT 0_o Homestead Homicide Homie Homies556 Homingstats Homlepung +HommeDeFer Hompski +Hon Honaz +Honchcrow +Honda Coupes +HondaXJ20 Honden +Hondo Ohnaka +Hondsvot Honest +HonestThomas +Honestben +Honestidade Honeytrippin Honeyyyy +HonkMyClussy Honket HonkeyKong Honkitonki1 @@ -9854,37 +20316,61 @@ Honko Honkys Honler Honor +Honourable Honq +Honra Honru +Hoo Lee Fook Hooba Hoobloob HoochFighter Hood +Hood Rat HoodBrothers Hoodaloo Hooded HoodedDeath Hooder Hoodieninja +Hoodless +Hoodlum_94 Hoodrat +Hoodstar XD HoofHarted +Hoogbegaafd +Hook +Hook N Arrow +Hookah Hookin4GP Hookr Hooleys +Hooofer Hoooka HooorrraaaH +Hoop Snake HoopaLoopz +Hoopdie Hoopre Hoopti Hoorus +Hooryu +Hootenanny +Hooyaah Hoozio +Hop Loser HopNowNoob Hope Hope4TheBest HopeSmolDicc +Hopeless +Hopelite22 Hopeu98 Hopian Hoppa +Hopper727 +Hoppip +Hoppipolla +Hops World Hoptilicus Horatio Horatioat @@ -9893,27 +20379,60 @@ Hord Hordalad Hordaland Hordelli +Horey Horizons +Horizons End +Horme HornDawgx Horned Horns +Horny4Herbs Horny4Hunlef Horozu +Horppi +Horrific Rat Horriser +Horros +Horrow jr Horse +Horse Tock Horselord +Horshock +Horsley park Horstel +Horus Hyla Horus1701 HoshizoraRin Hospitaliano +Hoss +Host Proctix HostileBoss +HostileCloud HostileEx Hostiles +Hot Ass +Hot Box +Hot Boxing +Hot Chesters +Hot Choco +Hot Chook +Hot Dog 1995 +Hot Dog Guy +Hot Girl +Hot Glacier +Hot Gril +Hot Hemp +Hot Shop +Hot Since 82 +Hot Stepdadi +Hot nurse HotAsianCuti HotCocoa HotCuppaT HotDoggWater HotManCurry +HotMaxCurry +HotPud HotSaus123 HotSnow Hot_Farmer99 @@ -9922,180 +20441,342 @@ Hotandhorny Hotbox Hotchelli Hotdog +Hotdog Timmy Hotdog655 +HotdogGravy Hotdogit101 +Hotdogwhat Hotel Hotkeys +Hotline Juan +Hotot +Hotputhy +Hotrod-Matt Hots Hotsbo +Hotse Hotsgonwild +Hotshotz61 Hotslol Hotwheeler Houdinski Houndstooth Houowvwvv +Hourlies +HoursAndy Hous House House748 +HouseSniffer +Houseman45 Housewife +Houston713 Houstoned Houstonnnn +Hovis +Hovland Hovmeizter +How do I win +How to quit How2Boss How2username HowAboutNeup +HowDoDisWork +HowHardd HowIing HowIsDis +HowLee +HowSway Howde Howland +Howler Head Howlini Hows +Howsey Howson HoySmallFry +Hoya Markus +Hoyaa Hoyah Hoyiron Hoyryjyra +Hoyyehh +Hp x Hp +Hr Banan4 +Hr CarlSmart +Hr KalZ +Hr Pagulane +HrMystik +Hra Amiraali +HraMajuri +Hraerekkrr Hrafnagud +Hrag +Hrathix +Hrry Hrungiir HshBrwnClwn +Hsilver +Hska HsrFresh +Htine +HuZ Ryan Huaraaturpaa Huard Hub3r +Hubbbzzzz Hubbz216 Hubeeb +Hubie +Hubiera Hubli +Huby K +Huck HuckUsAHandy +Huckn +Huddo Hudsy Huebs Huevote +HueycoatI +Huffers +Hug A Mudkip +Hug my cat Hugakitty Huge +Huge Coom +Huge Mudkip +HugeCcret +HugeCox HugeMike HugeSimp HugeSquanch +HugeXpWaste Hugh +Hugh Jannob +Hugh Jas Hugh Jazzhol HughAreMyron +HughJastle +Hughes Luck Hugo +Hugo Baws +Hugo C Hugodzilla Hugos Hugoslavic Hugotec +HugsTibbers Hugsi +Hugsqtface +Huhi Huhm0ng +Huhmble Huhu +Huidskleur Huiliuiliu Huilu Huinca Hukka Hulagu +Huler Hulk +HulkSmaash Hullcity25 Hulll Hulluke Hulluurpo Hulvatonta +Hulyx Human +Human Knight +Human Reject +HumanMalware +HumanSl0th HumanToxin Humanist Humanityy +Humanoid Rat Humb +Humb RS Humbabe Humberto Humble +Humble Finn +Humble Husk +Humble Otter +Humble RNG +Humble lp HumbleCanoe HumbleCrab +HumbleFarmer HumblePlayer Humblee +Humblegods Humbleherb Humblenobody Humbles +Humboy +Humgrump Humid +Humiliation +Humji +Hummbinger Hummerspeck HumperT Humpletics Humu +Hunajamurune Hundjagare Hundred +Hung Opossum +Hung Pikachu +Hung Zebra HungLow30 HungSolo45 Hungarry208 HungerRee Hungry +Hungry as fk +HungrySponge HuniePop Hunkadoris +Hunks +Hunlef Hunnets +Hunni Bunny Hunpy +Hunt Mike Huntarr Huntdragimps Hunter Hunter10139 +Hunter4056 HunterPandav +HunterXosrs +Hunters Main Hunterz +Huntin Specs Huntindawg Huntindawg2 Hunting HuntingGirls +HuntingPets +Huntinrox Huntinthotts +Huntiritox HuntnCoFTB +Huntsin Hunyadi +Huolon +Hupipelaaja Huppis +HuppuKostaja +Hups +Huqin Hurlae Hurleee Hurley +Hurley24 Hurlock Hurri +Hurrisun +Hurs 1 Hurtigmads HurtsDonut Huscarl +HuseG00SE Huseyin31 +Hush Pup Husker HuskerFool Huskieeeee +Huskvarna +Huskyskills +Hussar1683 Husse1n Husseinn Hustensaft Hustlez Huswan Hutchens +HutchoAU Hutros Huts +Hutula +HuugeCok Huugo35 Huule Huumepoliisl +Huupee Huutiset Huutoripuli Huviksee +Huxley King +Huxtap Huxtin Huzeyfe +HvH Hvale +Hvale Kongen Hvorfor +Hwael +HwbrangerUIM +Hwzrs +Hxc Ironfail Hxcmackin +Hxpeful Hxrm Hxrsey +Hy a Hy3RoGeN +Hy3RoGeN x HyAcey +HyDrooo +Hybernated Hybrid +Hybrid Hurtz +Hybrid Mech HybridSanta HydG +Hydi Hydr0nate +Hydr0p0nic Hydra +Hydra Ikkle +Hydra Spoon +Hydra_Lerna Hydraletics +Hydras Daddy Hydration Hydraxon +Hydrerion +Hydro-Quebec HydroBlast +HydroC +HydroGasMask +Hydrocarbons Hydroe Hydrofiner HydrosFather +Hyemi +Hyfens HyggeDansker +Hyi Hykd HylianLink +HylianLombax Hylkon +Hymy Hyosin +Hyough HypaDunkTv +HypaZestay HypeLad Hyper +Hyper 212 +Hyper Threat +Hyper autist Hypercam Hypercoaster Hyperdeath20 @@ -10103,68 +20784,310 @@ Hypermole Hypersomnia Hypertroll Hypertrophic +Hyperversity Hyphen +Hyphen-ated Hyphenated +HyphyRS Hypn0se Hypocriet +Hyprocrisy Hysteric4l +Hytrogod Hyttynen +Hyula Hyun Hyxbe +Hyze +Hyzers +I M +I Skilz I +I ADK I +I AM DROID +I Am Arun +I Am Calysto +I Am Cicu +I Am Dave +I Am Eugene +I Am Malenia +I Am Mathew +I Am Mizzou +I Am Times +I Am Vexed I Am Vono +I Am Xyience +I Am Yesac +I B Z +I Barak I +I Be Vibing +I Be Yankin +I BenJammin +I Bryce I +I Buy Bulks +I Buy You +I Call BS +I Cant Veng +I Chance I +I Chaz I +I Chop Life +I DanWise I +I Daniella I +I Decimate +I Descend I Dizz I +I Dont Gnome +I Dont Know +I Dont Weld +I Drink T +I Drive Jeep +I Duck Hunt +I ELITE I +I ET BBIES +I Emu I +I Enjoy Butt +I Faheem I +I Farm On Rs +I Flick Bean +I Gem I +I Genesis l +I Go Noob +I Gribben I +I Grind it +I H8 GAGEX +I Haros I +I Have Quit +I Heal DHers +I Herb Store +I Hunt Pets +I Hustle Man +I I ODIN I I +I JANGLER I +I Judgee Yu +I Ko U Lol 1 +I L0ve Lolis I LOTR I +I LV PEACH X +I Lezima I +I Liam I +I Liamz6 I +I Like Cake +I Like JoJo +I Lov3 Pho +I Love DILFS +I Love Kinja +I Love Kylie +I Love Muff +I Love You +I Luci +I Luhhya +I Luv tSwift +I M A P nuss +I M Ket +I M S N Y +I M The Weed +I Maxed OSRS +I May Be +I MoF +I Mograine I +I N D I A N +I Need 1M +I Need Succ +I Need aBeer +I Novato I +I Nz I +I OX +I Owning +I P0 +I P0KY I +I Parry I +I Peter I +I Pk In Monk +I Plank Lots +I Play Aegis +I Pod335 +I Pri +I Q T I R N B R U +I R O N 99 +I RC +I ReAPeR I +I Roll Need +I Ron Man +I Ruin Holes +I Run East 2 +I Sharted +I Smiteyou I +I Smoke Irit +I Specs I +I Spied +I Strangles +I Taka Hujit +I Teller I +I Tricky I +I Tuscan I +I Valknir +I Vape Lube +I Viral +I Want 60FPS +I Would Rage +I Xl I I Xl +I Yogurt +I Yoshitsune +I am Anyone +I am Bryce +I am DB o +I am Elk +I am Future +I am Glynny +I am J M E +I am Jedi +I am Niels +I am Nyheim +I am Olive +I am Ry +I am SHODAN +I am Saitama +I am Shiny +I am Shogun +I am Simon +I am Soloing +I am Twiggy +I am a wreck +I am aRusher +I am drunk +I am groot B +I ate +I bad Guy +I desire God +I do be Iron +I eat peach I h8 farming +I have egg73 +I have poon +I have rng I ll fail +I love me +I love yew +I m Dave +I m Kenpachi +I m an Angel +I matchjad I +I mpling +I n b 4 +I ndy +I nspire +I rvin +I sak +I smell zaza +I so pale +I squared R +I t I s +I tbag cows +I used to Rc +I van +I vinrox +I w4ste xp +I wana dew u +I want wings +I-IEEBO +I-Iulk I00OOOOOO00I +I2P +I2m +I2ocky +I2ossiiii I3ET I3WANA +I3ooth +I3ow Hunter I3ox I3rucex I3ubbles I3ulow +I7I +I7iablo +IA +IAM HARDWELL IAMBillNye +IAMDouble_C IAlwaysBurn IAmArcade +IAmArchangel IAmBabyYoda IAmBinx IAmEcliptic IAmFaptastic IAmFrampt +IAmJacbo IAmRichard IAmSlimShady IAmStyle IAmUnbound IAmWaffles +IAreTazz IB4I +IBB Baskani IBIoodRose IBaIance IBangBBWs +IBardak +IBloodwing +IBuyGF5GP IBuyPowers +IBuyShungite +IC 3 +IC XC NI KA +ICE GIANTT ICNK +ICantFit ICauseRage ICrazyCamelI +ID theft IDEPOCIEBIE +IDSilver IDark IDiabloI +IDustie IEATEDYU +IFALL3NI +IFIFIFFIFIF +IFIFIFIFFIFI +IFIFIFlFIFIF IFRC IFRS +IFRS 9 +IFartOnBaby IFernedYou +IFistBonk +IFoundABaby +IG0R II IGN0RANCE IGoogledIt IHQueenI IHasHips IHaveAWifey +IHaveChicken IHaveToQuit +IHobbitI +IHopFromNo1 IHuntKoalas +II Fabi II +II Gots Piie +II Mossy II +II TurMz II0III II0v0II IICyRaNoKII +III SLAYR +III jin III IIIBCIII +IIIGavinIII IIIel0n +IIIusionist +IIKOO +IIPAC IIPrincesaII +II_Blanx_II IIamaman IIflemishII IIlIIAS @@ -10174,24 +21097,119 @@ IIlllllIIlIl IIndy IIrisviel IIsaac +IIx Link xII +IJzer Bal IJzermeneer +IK Pegasi +IKotol +ILL M1ND ILLUMINATIS ILOVEUBRAT +ILVI ILike +ILikeKorone +ILizzy +ILostMySock ILostMyTalk ILoveAstolfo ILoveSeaFood +ILoveYasmin +IM Adept +IM BLUE DA +IM Bazlish +IM Big N8 +IM Bjakke +IM Bolle Ui +IM Butchh +IM CSI +IM Cares +IM Chaudoin +IM Corita +IM Dahak +IM Damy +IM Dislectyc +IM Dodgypig +IM Donactro +IM Drone +IM Dutch Dog +IM EL Clawo +IM Envy +IM Erlandu +IM GERRALD95 +IM Gekko +IM Gokhan +IM Guilty +IM HEEM +IM Hail +IM Harnas +IM Hulkeen +IM Infested +IM Jap1 +IM Jenskee +IM Kat +IM Knol +IM Krona +IM M U R P H +IM M0A IM Maccy +IM MajorRat +IM Maninja +IM Musa +IM Noddy +IM Ong Gia +IM PVM BRADY +IM PapaBless +IM Peach Man +IM Pepekage +IM Perp +IM Phubz +IM RICO_1 +IM Rampage +IM Reiner +IM Rome +IM Ruinscape +IM ST0NED +IM Sebaa +IM Soad +IM Spooned +IM Super AFK +IM Tank OG +IM ThiccSucc +IM Tipz +IM Toon +IM Walnut +IM Witt +IM Zoot +IM a pansy +IM blazeit +IM sanderve +IM spen IMAEATURASSS IMAFKNNONCE2 +IMGOKU1 IMHankey +IMHky IMJeffG +IMMezzo +IMNagrom +IMPERlO IMPlayerjohn +IMSOULSHADOW IMSausjegeit +IMSkrt +IMWaldorf IM_Queeffing +IMa Planker +IMakeUBegKid +IMevil yoshi IMissNieve IMnotQ +IMoIVoxide IMsmits +IN A FERRARI +IN PRISON RN +INCEL839273 +INCreate INDICA INFERNALMAX INTERMEDlATE @@ -10199,130 +21217,249 @@ INYOURTS INeedABump IOHBOY IOnSpacezI +IP6 IPkdYourBank IPlayForPeso +IPlayHigh +IPonderosa +IProGuthix IPumpkin IPunchKids IQUICA +IR Hardcore IR0N +IR0N BOND IR0NMAN +IR4Q +IRL Femboy IRON +IRON BYRNS +IRON DlOXlDE +IRON NOT HOT +IRONMAN ADO IReallyLikeU +IRework IRidePipe +IRoldy IRowForUCD +ISAPOOPS +ISO PEE ISOMAN12 +ISQQC +ISTRA +IScottGame +ISjorsI +ISmokeABit ISoLaTIIon +ISquirk ISuperStarI +ITIB +ITIISERY ITIOMME ITIace ITIaddy ITIagicka +ITIagicks ITIajestic ITIallen ITIasKilleR ITIassacre +ITIatty ITIerica +ITIiko ITIoj +ITIoosh ITIr ITSPYRO +ITV X +IThe Beatles +ITrimGlories +ITurtlekunI +IUR IUntradeable +IV Luke +IV skin +IVI V G IVICMXCIII IVIE0W IVIacaroni IVIaybe +IVIeesa +IVIeredith IVIesprit IVIetallica IVIidget IVIooN +IVIowlzy IVIugginz +IVIush IVIxtthew +IVOXYGEN +IW Lytheria IW4M +IWGP +I_Eat_Ass04 +I_Inity +I_Noodz_I +I_make_noise +Iacto Iaculch Iaffan Iafs +IaintHiding +Iam Bald IamAjewBoy +IamBowser IamBruun IamCheesin IamCrazyBoy IamDuru IamErect +IamGossip IamNSFW +IamNotHodor IamRichPutin IamSneak +IamThaLiquor IamXerxes +Iambadhaha +Iamcruxer Iamearth Iaminsanemax Iammooted Iammtpjr Iamstepbro +Iamthestars +IamthyPope +Ian Beale +Ian Rush IanT Ianalan Ianbird Ianeke Ianeke4 Ianyaboii +Ianz +Iax 2000 +Ibanezx88 Ibanezx99 +Ibans Fe nix IbansPheonix Ibarbo +Ibb +Iberis +Ibexleave Iblushh +Ibn Ibrahimovic +Ibugppl Jr +Ibunaaru Ibuprofen800 IcEyCuDa Icanfixthat +Icarius Fell IcarusRS +Icaruz Icculus1 +Ice 9112 +Ice Barrage +Ice Caves +Ice Gengar +Ice King +Ice clap +Ice giants Ice in Vodka +Ice5566 IceBarraging IceCreamDog IceDesert +IceFoxZero IceH +IceIceChooky IceNineKiIIs +IcePrisonMe IceSparro Iceb Icebeam Iceburghomie +Iced 0ut IcedOutDrip IcedScrabble Icedeadmage Icee +Icefirezz Icehound Iceid +Icejawa Iceland Iceleag Iceliang530 +Icelight +Icemonkeyz +Icen Icesoze Icespeller +Icetaylor Icey +Icey Lunaris +Icey M Iceyou90 Ichi Ichi6an +IchikaNagano Iciclez +Ico n +IconPara +Iconiic Iconoclasmic Icookpeople Icoz IctCat +Icy Duck +Icy Tires IcyFear IcyTowerr Icyene +Icyene Hero Icyenicblood Icyflowers Icyx +Id Tele Too +Id Yak It Idaho IdahoTaters IdcGoAway Idea +Ideaal Ideekay Idepredad0rI Ididit33 Idk Why PvM +Idle GE +Idle Melvor IdleWhale +Idlibi +Idna Supafly +Idol Namib Idont Idontlikejad +Idopk1 Iecysoda +Ienjoisk8ing +Iern +If I Succeed +Iffi Iffyz +Igge +Iggy Ontario Igloocold +Igmere Ignace Ignent +IgnisDraco22 +IgnobleSolid +Igor Krutoy +Igor Mang IgorBogdanof Igorr Igot99failin @@ -10332,87 +21469,261 @@ Ih8myRNG Ihan Ihana Ihme +Ihnigmakage IiIy +IidaEmilia +Iilqtforeva Iimel IisRaDiO Iisalmi Iivomon Ijoinedthis Ijusheadshot +Ijzer tekort +Ik Geef Bon +IkBenWeg Ikastovizski IkeJEI Ikhela Ikillu247 IkkeVincent Ikkle +Ikkle Hydra +Ikkle v Pain +IkkleHorvick +Il +Il MrBorn lI IlIBooieIlI IlIIIIlIIIlI +IlIIlIllIlII IlINard0IlI IlRisklIlIlI +Ileeze Ilija +Ill Ko You +Ill Effect Illbeyourcat +Illegal Char Illest Logic +IllestEver +Illinoisan +Illithid69 +Illlenium Illumee +Illuminatorr IlluminumRS +Illusionist +Illusivezz +Illustration +Illuzory Illwil +Illyrian +Illyrian TSX +Ilmer +Ilotalo Ilovethewar IlpOG Ilsaldur Iltasanomat Iluredyou +Ilusm +Iluvahrim +Iluvitar Eru IlyrianBlood +Im 85 Slayr +Im A Saint +Im Am Die +Im Ami +Im Anabolic +Im Annoyed +Im Artur +Im Ash +Im Asking +Im Aspergers +Im BackUp +Im Baguette +Im Barlow +Im Behemoth +Im Benough +Im Blastoise +Im Bo Licht +Im Brills +Im Burnz +Im CG +Im Chal Im Christian +Im Courage +Im Dino +Im Dr Afk +Im Easy Exp +Im Elyk +Im Excalibur +Im Fabulous +Im Fever +Im Finnish +Im From Jax +Im Gainz +Im Gippin +Im Gutfor +Im Hawkward +Im Hersh +Im Huck +Im Hunta +Im Hussain +Im Iron +Im Iron WTF +Im Jags +Im Jokers +Im Jovian +Im Just Ken +Im Krazy +Im Kyle Btw +Im Legacy XD +Im Los t +Im Luis +Im McGriddle +Im Missle +Im Moist 4 U +Im Moore +Im Mr Josh +Im Nate +Im Nathan x +Im Newschool +Im Nice +Im Not Okayy +Im Not lron +Im Nzmitch +Im Off Chops +Im On A Yak +Im On Point +Im Only +Im Orc Im Outt +Im Pogi +Im Qwerty +Im Ron BTW +Im Rus +Im Savage +Im Scoob +Im Secor +Im Shambles +Im Single AF +Im Smexy +Im Snake A +Im So Vayne +Im Songs +Im Spirit +Im Stig +Im Symb +Im Tainted +Im Tame +Im Temppay +Im Terrible5 +Im TheBeast Im Tibbz +Im Travis +Im Triz +Im Tyl3rr +Im Whey +Im Ya Papa +Im Your Babe +Im Your Dad +Im Yui +Im Zac +Im Ze 0wner +Im alex btw +Im just Cody +Im not FBI Im relapsing +Im tall af +Im wokeup +Im-Crowess +Im2good4u2nv Im45defence +Im4everlucky ImALilPigBoy ImASthhnake ImAdam ImAl0ne +ImAn Ironman +ImBaldFromRS +ImBobbyrayj +ImBoredOfRS ImCharky +ImChuckBass +ImClueless ImCravenTim ImDaPlugLLC ImDaved ImDaviie +ImDirtyyyDan +ImDoctaWhom +ImEmilyy +ImEric ImFinnish ImFlizz ImGodd +ImGodly ImHavingFun +ImHellaHigh ImHighASFSry +ImHisBae ImIncredible +ImInfested ImIntense +ImJames ImJebbe +ImJinxed +ImJordon ImJustLonely +ImJustRaging +ImKnotSimple ImLarge ImLeeuwarden ImLikeRlyBad +ImLukesGIM ImMAdBro +ImMajesticAf ImMaxy ImMilky ImNewbie +ImNoobK ImNooblit ImNotAfriend ImNotHumble +ImNotLost ImNotThien ImPaul +ImPaulAlt ImQuorra ImRyboy ImSane ImShreksDad ImSixteen +ImSnoop +ImSoGodlyy ImSoVulgar +ImStormmm ImTaegan ImTayla ImTinyRiick ImTipsy ImToFrosty +ImTooGucci +ImTwisted +ImVanilla +ImWargasm ImWilbur ImYourDadBTW ImZe ImZorp Im_Stone126 +Ima Guy +Ima Lil High +Ima Maleman +Ima lron man ImaCraftHor +ImaMainbtw Imack Imafore Images @@ -10420,17 +21731,33 @@ Imaginable Imagine Imakuni0 Imalooter +Iman103 +Imangry20 +Imanity +Imanubnoob +Imar Pussay Imasecretspy Imasin Imawizardm8 Imazamox Imblim +Imblim Area +Imbrochavel +Imbue +Imbued Haert +Imbuing Imbune ImeshuggahI Imgrazy +Imhellgracio Imidril +ImiteBurDADY +Imitus +Imm Mad Bro Imma ImmaSkill +Immaturity +Immediate W Immense Immer Immerseus @@ -10439,60 +21766,126 @@ Immortal Immortal91 ImmortalK12 ImmortanMike +Immxnse Immys +Imnotahuman +ImoChase +Imoinu +Imotay ImpactPewPew +Impale Her Impared Impeccable Impedance +Impede Imperdoavel +Imperfecto Imperfekt ImperialQc +Impietyy Impish +Impish Ace Impishhh +Imposter Jon Impriznd Improv +Improved +Impulsive Au Impurest678 +Imql +Imre Imsain Imsokwtie +Imstratss +ImtheAnimal +ImtheCARRYv2 Imthick Imtoob +Imuhbeast +Imuripieru Imus +ImxricK +In Game Life +In Menu Andy +In My Shadow +In Recovery +In Seconds +In2cept InCyson +InDaCouch666 InDespair +InFemous InGameLies +InTawlerable InThe90s +InTolu +InTransition +InXanee +InYaShower Inaaya +Inactive Acc +Inar +Inavi Jones Inbow Inbred Incapacitory +Incendia Vir +Inch95 +Incin3rat3 X +Inco-san +Incoherent +Incomes IncomingBeef IncomingGank IncredblDino +Incumbency Indain IndawrCrwdad Indcsn +Indecisive +IndecisiveO +Indemise +IndexNull Indian +Indian Chad IndianCat +IndianaJones Indicates Indicator IndieGarri Indigo640 +Indivi2you +Indoktrinera +IndominusAsh +Indoor Scape +IndoorsOnly Indoorsman Indrado Indrias1 +Indy Ju +Inecstatic Inedibles IneedPVM +Ineff icient Inefishient +IneptEwok +InfIuenced Infaam +InfallibleX2 InfamousAB InfamousEvil InfamousMain +Infantiel Infase Infectus +InferiorRNG InfernOwl Infernadev Infernal +InfernalChef +InfernalLuke InfernalPRO +Infernax Inferneo Inferno InfernoTism @@ -10501,18 +21894,23 @@ Infero Infesmati Infin1te Infinate +Infininth InfinitiGX InfinityMan Infirme Inflnite Influx Info +Info Kiosk Infobese Infor +Infora Treat Ingeb +Ingenious G Ingeniously Inglane Ingleburn +Inhaled Inhinyero InhumanBTW Inimene1020 @@ -10522,38 +21920,68 @@ Iniys Inkedsaint Inko Inksane95 +Inkwes Inner +Inner Brian +Inner Peace InnerBliss Innvision +Inori xdd +Inosuke xo Inqku Inri Insafi Insane +Insane Iron InsaneBobbie InsaneFruit InsaneIee InsanePerson +Insaneglutes +Insanewolfy Insaniack InsanityNix +Insanityx Insanityzz +Insatiate +Inse +Insectkoala InsertGold Insluiper +Insomnia Insomnist +Insomnity Inspection +Insta +Insta Spec +Instabox +Instagrem Instail Instinct Instro +Instruct Insucc +Insulter Insulting +Insulting MF +Insurgence Insurgent Int0mieli Integration +Intel6 +IntelManiac +Intellectual +Intendi IntenseRage Intensifyyy Intentional Intercept0r +Interchange +Intermk Internal +Internal Max Interracial +Interronator Intherial Intier Intimate @@ -10563,154 +21991,542 @@ IntoInfinite Intoner Intresant Intresenting +Ints Intubator Intuition Inubashiri +Inukal +Inuouk +Inur InvTagsBad Inva Invalar Invercargill +Inversives Invert +InvertedDuck Inveterate +Invierno Invisa +Invisa Scav +Invisa-Veng Invisioners Invokers Involk Invoryy +Inwu Inxo +Io Jah Pul +IoB Sylarke +Iock +Ion Benny Ion870 IonCannon Ionia Iono +Iou689 Iowa146 IowaCJ Ioyal +Ioyins IpeeInTheSea Ipod5412 +IpotYouDrop Ippe +Ipswich +Ipswich FC +Ir JWWillem +Ir Phys Ir0n +Ir0n Bot +Ir0n b00k Ir0nMammoth +Ir0nMish +Ir0nPuma Ir0nRagnarok IrDA Iralimir +Irha Irid Irie IrieLuDa Irinichina +Iris Elea Iris_1904 Irish Irithe +Irl Baller Irmak Irobar +Iroff SGDaht +Iroflman +Iroh Bison Iroha +Iroic +Irom Typo Iron +Iron 0lm +Iron 2DWaifu +Iron 2b +Iron 3 Hit +Iron ABC +Iron Achael +Iron Aeolian Iron Alabama +Iron Alee +Iron Aloha +Iron Aloodum +Iron Alpha +Iron Anaerob +Iron Anno +Iron Anonymo +Iron Antzz +Iron Areolas +Iron Arnoud +Iron Arzka +Iron Assface +Iron Ayen +Iron Azshara +Iron B O D Y +Iron B0yx +Iron BDN +Iron BLK +Iron Bart +Iron Baskan +Iron Bawlsak +Iron Bawsss Iron Beabs +Iron Beeto +Iron Besu +Iron Bezem +Iron Bibi2u +Iron Biddys +Iron Bloke +Iron Bogmac +Iron Book +Iron Boomz +Iron Booxia +Iron Borrrby +Iron Brevik Iron Brinnie +Iron BryconC +Iron Buchu +Iron Buckc +Iron Bucket +Iron Bumko +Iron Burrata +Iron Buuh +Iron Byld +Iron Caddy +Iron Calquat +Iron Carly +Iron Carmine +Iron Carna +Iron Cerv +Iron Cevol +Iron Chaw +Iron Chemic +Iron Chkn +Iron Cholby Iron Chombre +Iron Chrisss +Iron Coby +Iron Coin +Iron Coronao +Iron D Luffy +Iron D Natsu +Iron D Zeref +Iron DJG +Iron DSC +Iron DVS +Iron Daniel +Iron Deku +Iron Denal +Iron Deroq +Iron Destr0i +Iron Dipshit +Iron Dixi +Iron Domi +Iron Dumontx +Iron Dylnn +Iron E +Iron E s s +Iron E z +Iron Eagah Iron Elyon +Iron Equity +Iron Excal +Iron Exed +Iron FFrenzy +Iron Fe +Iron Ferro +Iron Filthy +Iron Fioxxu +Iron Fire 75 +Iron Floor +Iron Forgy Iron Franchi +Iron Frodo +Iron Fuhrers +Iron G Zeus Iron G0liath +Iron Gaan +Iron Gamerz +Iron Gayness Iron Ghosgar +Iron Ginto Iron Gios +Iron Go +Iron Godley +Iron Godly +Iron Golem +Iron Goth GF +Iron Greg +Iron Grind20 Iron Griss +Iron Haze +Iron Hoiy +Iron Holub +Iron Hookkan +Iron Hossi Iron Hroth Iron Hugge +Iron Husky +Iron Hydros +Iron Hyger +Iron IPvMI +Iron Insaneo +Iron Jakob +Iron Jal-Nib +Iron Jeref +Iron Jeroen +Iron JimmyJ +Iron Jizzman +Iron Jss +Iron K3 Iron Kalde +Iron Kaleef +Iron Kapkeik +Iron Karjala +Iron Katniss +Iron Kayn +Iron Keagan +Iron Kess +Iron Kiwii Iron Kngs +Iron Knipp +Iron Kodie +Iron Koopsy +Iron Kornie +Iron Krijn +Iron Krikke Iron Laplace +Iron Laxor +Iron Layfe +Iron Letski +Iron Liber +Iron Lose +Iron Lungsta Iron MTUT +Iron Mamma +Iron Mammal Iron Mapes +Iron Marie26 +Iron Marlon +Iron Martin +Iron Masori Iron Masuli +Iron Matt +Iron Matt NZ +Iron Matty W +Iron Matuba +Iron Maxvoid +Iron Mellem Iron Meydon +Iron Miguel +Iron Mikebor +Iron Milan +Iron Mimal +Iron Miro +Iron Moardus +Iron Mossyy +Iron Munted +Iron Mystip +Iron Nap Iron Nellz +Iron Nick +Iron Nimmi +Iron Nobody +Iron Noe +Iron Nuclide +Iron Nuuby +Iron O Brien +Iron Oak +Iron Oboi Iron Ohboy +Iron Ohgodno +Iron Ohzone +Iron Opto +Iron Ores +Iron Pandis +Iron Parkour +Iron Patches Iron Peg +Iron Pge +Iron Phe +Iron Plague +Iron Pothead +Iron Praxis +Iron Pwn +Iron Pwnstar +Iron RNGsus +Iron Rakey +Iron Randall +Iron Randyy +Iron Reedzy +Iron Rekkr Iron Request Iron Rikyu +Iron Rind +Iron Roach +Iron Robsham +Iron Roof +Iron Rushy +Iron Rybread +Iron Ryuuga +Iron S t i g +Iron SSJ +Iron Sagga +Iron Samiria +Iron Santus Iron Scolew +Iron Scran Iron Seagz +Iron Seha +Iron Shaikh +Iron Shavers +Iron Sheik +Iron Shnapi +Iron Siem +Iron Sioux +Iron Skoptsy +Iron Skroten +Iron Skydive +Iron Slash +Iron Slaya83 +Iron Sloth 2 +Iron Smoke94 +Iron Snypah +Iron Sparco +Iron Splat +Iron Stannrs Iron SteveB +Iron Stomp +Iron Stronq +Iron Stw +Iron Sullo +Iron Susu +Iron Svampe +Iron Swaffle +Iron Swag +Iron Takumi +Iron Tango +Iron Tanzim +Iron Tbar +Iron Terboo +Iron Teun +Iron Thi3ves +Iron Tiffers +Iron Tigr +Iron Tigran Iron Tinman +Iron TomT0m +Iron Tonkin Iron Tonski +Iron Tonymaa +Iron Torfi +Iron Toxxic +Iron Trashua +Iron Trixx +Iron Tudder Iron Tutnin +Iron Tweeker +Iron Tyrans +Iron Tyrant +Iron UIM Iron Uakti +Iron Unam +Iron Varuna +Iron Vatti +Iron Venema Iron Vexzed +Iron VickD Iron W1nk +Iron Weather +Iron Wespula +Iron Wii +Iron WindJr +Iron Woofy +Iron Wu5 +Iron XIIIV +Iron Xellana +Iron Xire +Iron YEE +Iron Yangus +Iron Z4ppie +Iron Zephyx +Iron Zeraph +Iron Zerg Iron Zilong +Iron Zinoto +Iron Zoha +Iron Zol +Iron Zrysen Iron Zul Mox +Iron anrowi +Iron d0gz +Iron da ddy +Iron dols +Iron dry +Iron feens +Iron flynn +Iron instead +Iron kitcat Iron l2asta +Iron ona Mac +Iron pappal +Iron werty +Iron whiff +Iron wiper +Iron x wolf Iron xyN +Iron-Ximena Iron10203 Iron2Pickaxe +Iron619 IronAgentP IronAjandre1 +IronAkleiss IronAlblad IronAlufolie IronAngst IronAnnaMay IronApples +IronAsbe +IronAssHole IronAussieM8 +IronAxe +IronBadAtPvm IronBankFull IronBeanz +IronBeerad IronBiGuyBtw +IronBistchul IronBobbyjoe +IronBoink IronBosse IronBound IronBrad +IronBrand21 IronBrick +IronBruBrah IronBuriedNU IronBurnsRed +IronC1aws +IronCagedRat +IronCemile +IronChillii IronChirpOTK +IronChompers IronChrisKin IronClarkey IronCoX IronComedy IronCooter IronCozza +IronCraiger +IronDab 710 IronDakotas +IronDandaman IronDavid IronDeadBoy +IronDefiant +IronDeredas +IronDethaele +IronDodo420 IronDog777 IronDong +IronDravekD +IronDringus +IronDtrex +IronDubble IronDubzy IronDudde +IronDunceCap +IronDurrrrr IronEagles IronEkakerta +IronEso +IronEtch IronFefe +IronFigment IronForFunnn IronForged99 +IronForyn +IronFrenzey IronFrogMan IronFungi IronGalihand +IronGatr44 +IronGear CEO IronGez IronGodsword +IronGoliathX IronGoneNorm IronGow +IronGuh IronHammy +IronHasta IronHomer IronIDKFA +IronInsano +IronIogibear IronIsNoJoke IronJValor IronJakeT +IronJames IronJamesG IronJarod +IronJeffZ +IronJenga +IronJezz IronJoint +IronJosehiha +IronJosephxo IronJuji IronKaboom IronKaiser IronKapp +IronKayodee +IronKest +IronKingdoms IronKukk IronKyloman +IronLadBtw IronLadel IronLaffin IronLankzey +IronLazure IronLegend +IronLegocy IronLepaus IronLifting +IronLionMAIN IronLucas IronLuckPlz IronMadePure +IronMagick +IronMainOx IronMan IronManBow IronManJar @@ -10718,17 +22534,26 @@ IronManSux IronMango IronMarius IronMarty +IronMason +IronMathwin IronMatty +IronMayne IronMetknot +IronMikado IronMinion +IronMitchKun +IronMollusk IronMountain IronMugz +IronMushy IronNWord IronNachos +IronNantt1 IronNateDogg IronNazguls IronNexuss IronNeya +IronNibletz IronNihilm IronNinja13 IronNord @@ -10737,11 +22562,19 @@ IronOdum IronOldBoy IronOnPatch IronOrca +IronOreAgate IronOsiriss IronOxideMan +IronOxidizer +IronPangtar +IronParis +IronPeteMan IronPills IronPrice +IronPrickles +IronProf IronPumitaz +IronQben IronQli IronQueen IronRait @@ -10754,83 +22587,168 @@ IronSantaMan IronSchmieds IronShadow5 IronSikruz +IronSkaro IronSly IronSmurfff +IronSparrow IronSpiderZ +IronSpitfire IronSplats IronSpoNeD IronSpoony IronSpray IronSquidly +IronSteve-O +IronStronky +IronSurf +IronSylar +IronSystolic +IronTarnum +IronTasseron IronTau +IronTedBundi +IronTeste +IronTestedRB +IronTimTim IronToff +IronTulio +IronTyson IronUnkilled IronVegtable +IronVib3s IronVikinng IronVikzo +IronVinda +IronViolette IronW3LEGEND IronWafflee IronWallnut +IronWantsRNG IronWayneker +IronWidovv IronWush +IronYoerik +IronYugo IronZettrox IronZong +Iron_ProDabs Iron_Slippy Iron_Sukulo +Ironarcanic +Ironatica +Ironator Ironblood7 Ironboba9993 +Ironbobsaget Ironborn +Ironborn PVM Ironbutcher +Ironclad Sac Ironed +IronedSwift +IronendJr Ironeyy Ironflox Ironfur +Irongrazvis +Irongusty +Ironiam Vir Ironic +Ironic Force +Ironic Kill +Ironic Name IronicBenson IronicSwag93 +IronicViking Ironicer +Ironiical +Ironija +Ironik 20 IroningB0red IroningSucks +IronishSana Ironkaka Ironmacman +Ironmale Ironman +Ironman Daff +Ironman Jase Ironman Jowe +Ironman Wit Ironman idk IronmanFookz +IronmanHutch Ironmanerno Ironmeme +Ironmeme Cx +Ironmeme Lol +Ironmies Ironmimmi +Ironmorriz23 Ironn +Ironn Yoshii +Ironnesses Ironpasta1 +Ironqa Ironred33 +Ironriwer +Irons suck +Ironshore Ironsoul2 Ironstone +Irontownhero +Ironwind747 Ironwinter Ironwork4eva IronxElite Irony +Irony Nib Ironylion +Ironyx +Ironzi Ironzilla +Irrelevant Irrumated +Iru +Irulon +Irxn Irxnized +Is E_wen +Is Idle +Is Slikker +Is a bich +Is this you IsAlexOk +IsButters +IsChazCool +IsDomIsGood +IsYaBoyDoive Isaac +Isaki Magari Isbjorn Isganytojas +Ishigo1992 +IshmaelDonny +Ishrandom Ishzn +IsidoroM Iskemia Iskolar Iskon Iskra Islaal +Islami Islefalls Isleview +IsmackfaceI Ismelitooo Ismo54 IsoG +IsoKitty IsoP Isoga Isoleucine +Isootoonik Isoptox Isosami IsraVM @@ -10838,51 +22756,141 @@ Israels IssaEly IssaStiffler Issard +Ist M Istanbulite +Istasher Istaxit +Isvig +It Slaps +It is Rex +ItBeJacK ItBurns ItIsWedsDude +ItPol ItWasntMe007 Itaky Ital Italian +Italian roxx ItalianAJ +Itaochi +Itchweedy +Itcoto +Iteyx Ithero Ithlinee +Itik +Itinkinou +Its Absol +Its Almog +Its Arj +Its B0B +Its Brother +Its Canadian +Its Conrad +Its Da J00z +Its Dally +Its Dyl +Its Hash +Its Jon btw +Its Jst Skin +Its Kaylee +Its Libo Its Me Dave +Its Me Q +Its Me Suns +Its Misha +Its Nipo +Its Peter +Its Rigged +Its Sebas +Its Sense +Its Seth +Its Smooth +Its Teddy +Its Wolfie +Its Yama +Its Zammy +Its a Shame +Its claw +Its me Josh +Its me eden +Its rob +ItsAboutTime +ItsAirdog ItsAllOgreM8 ItsAnobrain +ItsBarcus ItsBigNasty ItsBlitzyy +ItsBrtnyBch +ItsBru ItsClout ItsDende +ItsDynasty ItsFinn ItsFlooo +ItsHoggle ItsHosef +ItsInThePast +ItsKenshi +ItsLitttFam +ItsMarc ItsMeBlaze ItsMeDog ItsMev +ItsMrSir2u ItsNiels ItsOleGreg ItsOwenM8 +ItsRainor +ItsSirTech +ItsSkells +ItsSyn1k +ItsTaylen ItsTerpsWrld +ItsUnruly +ItsWeinstein ItsWicked Its_A45c Its_Fonte +ItsaTommyGun +Itse rauta +Itsemurha Itsmeborrid +ItsoktoGIM Itsssjustice +Itsy Bitsy Itupakointi +Itx +ItxJustin +Itz Haydn1 +Itz Hickton +Itz Kodiak +Itz Nate +Itz Scrubz +ItzAtlass ItzElise +ItzJroc +ItzKat ItzLogikal ItzMrNick +Itzla Itzvenom +Itzzz B +Iuc Iuckk Iulz +Iussy Iustrous Iuxio IvIegaDan +IvIr Wize Ivain Ivaink +Ivalice XIV +Ivan Knjklj +IvanEOD IvanMullet Ivanckonia Ivanka @@ -10890,167 +22898,448 @@ Ivansaccount IvelJet Iveljetorox Ivoos +Ivory Tower Ivryk +Iwanbro IxHunt3rxI +IxVenom Ixala IxonnoxI +Ixyc +Iz0p +Izaa +Izac +Izanani Izbec +Izhmash +Izi Claps Izone_Kev Izuria Izvorul Izzy +IzzyBigBoy Izzyboy300 +J 0 S H Y +J 0 S H l +J 4 6 +J 4 K E +J 6 +J A B S +J A I D A N +J A M A L +J A N K I +J A U N U S +J A W D +J Breezi +J C Trousers +J D +J D Ballew +J Dahmer +J E E V E 5 +J E T +J F +J Fred Run +J I M M +J I MM Y +J Kole +J Kribzz +J L A +J MF B +J Neat +J O E G +J O L T I K +J O T A +J O3Y +J R O C +J R V +J Rat Cow J S 2 M +J S Z M +J Saints +J Suave +J U S T A S +J U Z +J a c k a l +J a c k son +J a c k y +J a mie +J a n i +J ackal +J al +J ameper +J anii +J appie +J asnah +J ason +J asyra +J ayson +J bellfortt +J bird shady +J e sse +J eff +J effy +J ensen +J esse +J essica +J imme +J imy +J imz +J ipr +J lMM Y +J o b e +J o e +J o ey +J o f +J o s h y +J oep +J ohannes +J ointz +J oke R +J or G +J oshh +J oshhh +J osie +J u wu +J ung +J unko +J utch +J uun +J uwu +J x B +J yy +J-2tha-R-O-C +J-Man 93 +J-Turn +J-Yeezy J001_jf1 +J0DYY +J0E +J0E REAL +J0EEY +J0NE J0UF +J0YBOY LUFFY J0YRYDE J0dz J0hnnyQ J0ng J0ngeBV J0ppy +J0rdanDv J0rdo117 J0se +J0se Dtown +J0se Dtuwnn J0seph +J0shh +J1MB0H +J1NBE J1ub J2TR J2caine45 J2ck +J328 +J3Ciron +J3JU J3S5E J3rkkuHD J41AL J45e0wns J4QS +J4S J4gga J4my +J95 +JAABASNABBA JACK3DJOHNNY +JACKYOUNG93 +JAG0705 +JAI MA KALI +JAMA Open +JAUH0JENGI +JAVON 9-INCH JAYJ51 +JAYR0CKBABY JAlDYN +JB 2K +JB87 JBGard JBrody JBuck057 JBux +JC-1989 +JCS07 JCole JCon JCutler +JD MD +JD4 JDBass JDEP JDFlaSh JDJM JDez JDlion +JDredd66 +JDuck +JE B +JEEZE JENNY +JENNY DEATH +JERN SVIN TO JETSNBEERS +JF Kennedy +JFB +JFCK +JFK gone AFK JFKautopilot +JFL JField +JFrog JFryGuy +JGIRONTG JGIV JGlen97 JH3305 +JH91xx +JHINitalia +JIGGYWIGGY JIIVEE94 +JJ-M JJ23 +JJAAKKO +JJBW +JJPowers JJROCKETS JJRicky +JJS BBQ +JJSM_Dulker JJchris66 +JJoe +JJumalwelho +JK Rowling JKMi JKTimbo +JKUR ON 40S +JKeM JKingJ777 JKrollin JKuyaa +JLG JLowe +JLsone +JMI NG +JMTaipei JMack JMiddd +JOBBO JOCKO00O0O0O +JOE EROTIC +JOKER 709 +JON3ZYY94 +JORlCK +JORlS +JP Kotsnek +JP17 +JPB at Large +JPI +JPS Daytona JPant1ess JPantless +JPatness JPence JPountney +JPudz JR05E +JR0D +JRBeast +JRC Crypto +JROGU JRxs +JS Xpress +JSL34 JSLatvala +JSPACER +JS_Terminal JSco +JShep23 +JSilvester08 +JSlice94 JSplash JStepho +JT5 +JTAG +JTCS +JTI ain JTIAIC JTM33 JTscape JUGIB +JUIBE +JUICEWRLD9 9 +JULlAN JUST +JUST A HlPPO +JUST Nikk JUSTICE JWRLD +JXJ +JZB JZX1O0 +Ja k ey +Ja m e s +Ja mes A +Ja se +Ja sn JaBouris JaDig JaStraater +Ja_Fireking +Jaaaakkoooo Jaac JaackGP +Jaager Jack +Jaagmu +Jaahee Jaakako Jaalva Jaamm Jaant0 +Jaapievecht +Jaay G JaayC JabHook +JabbaU JabbasHut +Jabbaw0cky +Jaber +Jabesol +Jabiborinoni +JablezGIM +Jablooze +Jabo b Jabopanda Jabroni Jabroniii +Jabrs +Jabuz +Jac o b Jacckk +Jace C +Jacerhapsody Jacey Jacinto Jack +Jack Dyer +Jack Htoo +Jack Hughes +Jack Mac +Jack Maz +Jack Par-O Jack Reipan Jack1 +Jack12Broke +JackBadasson +JackBass4 +JackBlade +JackDanielsH +JackHammr +JackKnight +JackKvorkian JackMayte JackOscar JackRod +Jack_Herer9 Jacka JackandCoke Jackass +Jackass Sean +Jackazzz1 +Jackblaze +Jackbtw Jacke +Jacked Jackie +Jackingten Jackisbanana JacknHookers +Jacko Bei JackoPogU Jackoo Jackos Jackoz Jacks +Jacks0n Jackson +Jackspyrow Jackyboiii Jacob +JacobGhosty +JacobIsTaken +JacobT789 +Jacobb Jacobieus Jacobro Jacobs +Jacobwins1 Jacupy Jacy59 +Jad slides JadMaister Jada Jadakiss Jadavius Jaddok +Jaddy Chill Jade +JadedRapture Jadeine +JadenM Jads +JadsVag Jaecob JaegerBoom +Jaeson Jafacakeman Jafanto +JafarTooHigh +Jaffa Jake Jaffar +Jaffywaffy +Jafsx Jagdpanther JagerBombski +JagerIron Jagermeister Jagged +Jaggen +Jaghatai K +Jagoodiii +Jah toch JahIthBer Jahaerhys +Jahaok +Jaharred Jahbby JahgexPlzzz +Jahmay Jahmerica +Jahngles +JaiSiaRamJai Jaiden184 Jailbreak455 +Jaime 427 Jaimeebear +Jak Maximus JakMetalHead Jaka On Can Jake +Jake C +Jake D Snake +Jake Muzzin +Jake OC +Jake Scapes +Jake Solo JakeState68 JakeSteFarm Jakedajackal @@ -11062,64 +23351,127 @@ JakesSolo Jakesonville JaketheSnake Jakey +Jakey C Jakeyosaurus +Jakeyz +Jaknop Jakrem Jakrojan +Jakuli226 +Jal-Nib-Jmal JalNib Jalduii +Jaleesa +Jales +Jalim Rabei Jalite Jaljif Jalla Jalo Jaloenow +Jalordom Jalou +Jalzas +Jam Flex +JamBandDad +Jamaikietis JamalBobaman +Jamango +Jamania64 +Jamar +Jamaul Jr Jambaica Jamblaster +Jambreak +Jamburano +Jamchu Jameo James +James 6000x +James Comey +James Kakadu +James Kelp +James Poncho +James PvE +James Rolfe +James c +James osrs +James1087 James2709 James9 +James945848 +James9520 JamesBondss JamesCUFC JamesDaWizar JamesHaliday JamesJ2019 JamesLango +JamesMaynard +JamesNI JamesPratt94 JamesShotGG +James_22 Jamesay Jamesbombom Jamesrb1 Jamess Jamesy JamiBear +Jamiboy Jamie +Jamie Tacos +Jamie W +JamieJams JamieOliver JamieXV +Jamilan +Jaminnnn Jamixa +Jamjac Jamlicking Jammie +Jammmy +Jammoose +Jammy Dogger +Jammy Scone Jamn Jamosbondos Jamppa Jampy +Jampyre Jamuli +Jamurazi +Jamwa Jamyroo531 Jamzylockz Jamzz +Jan +Jan Griffith +Jan en Karel +Jan100000 JanHenkD JanVanLeiden +JanXtian +Jananas3 Janar +Jancentp Jandaer +Jandhob Jandie5 Jane Janer Janes +Janes Bond +Jango Style2 +Jango fet310 +Jangsta23 Janice Janikowski +Janissary90 Jank Jankk +Jankko Janksky JannaMain73 Jannanas @@ -11130,81 +23482,171 @@ Jansky Jansz101 Jante Jantex +JantheBear Jaooa Japanese +Japhur Japiohh +Japles1 Japoolie Jappieee +Jappieness Jappo +Jaqen +Jaqquu +Jar Of Pets +Jar Uh Dirt +Jar of Pukes +Jar of Swamp +Jar ofc um Jar3y JarOfCookies +JarOfH0les JarOfSmORC Jarab +Jare d JareZki Jared1234 Jareds134 JarlCantBank JarlEarlKing +JarlVaarg Jarnemelk +Jarnie Dimon +JarnoMirma Jarnte Jarra +Jarre Jarredn +Jarreelll Jarskie Jarsse Jaru Jarvis Jarx +Jas Melody Jasberg +Jaseel Jasey +Jasey Rae +Jashin Jasinador +Jaskanpaska Jason +Jason In Max +Jason73 JasonJ +JasonLRG +JasonRises +JasonSkillz +JasonT20015 +JasonVorheez +Jasonn +Jasonnn Jasperw90 Jaspi +Jasthew +JasuzChrist Jasyre +Jauneri Jaunius +Jav Jave +Javert +Javier Pwns +Javve Javvymuis +Jaw +Jawaloso +Jawgasm +Jawmac +Jawniz Jawntista +Jawor Jaws +Jaws 2 75 +Jawshy +Jax Evasion +Jax Teller +JaximusIV +Jaxy22 +Jay Ayy Why +Jay Baby +Jay Exotic +Jay Hughes +Jay M +Jay RZ +Jay Show +Jay Sparks69 +Jay United +Jay b1zzle +Jay kell Jay024 +Jay357 JayDaddy313 JayEngineer JayFlow +JayJee +JayKay Okay JayLunar JayManzarek JayMaster +JayMe x JayMuthaFknC JayPortal JayRu7 JayTheBurnt +JayTheRunner +Jaybiz Jayden Jayders +Jaydey Jaydia +Jaydo x +Jaydonn Jaydos +Jayeden Jayhawk225 +Jayhawkers Jayi JayjayNeo01 Jayksie Jayman +Jaymeh Lee +Jaymo123 Jaymyson +JaysATank +Jayshuunn +Jayspoks +Jayster Jaytea Jayteaf Jaytee1262 Jaywub Jayy +Jayy Slays +Jayy x Jayzar +Jaz Coleman Jazo Jazpurs Jazz JazzFlap JazzMaster09 +Jazzeh Jazzjr89 +Jazzly Jazzy JazzyFlips +Jb Godranger +Jba123 +Jbevzzz Jblieves88 +Jbob72 +Jbt Jbvff Jc_TheCops +Jcb4646 Jcdelavici Jchn Jcmalone23 @@ -11213,223 +23655,454 @@ Jcourt1 Jdaws Jdidy Jdoggy2018 +Jdp3 +Je nny +Je pppe +JeBoyHiddema +JeIlo +JeIly Man JeJoat +JeK0 Jean +Jean belette +Jean man Jeangaboudle +Jeankeh Jeari +Jeavoni +Jebaited825 Jebiii Jebrim Jebroid Jebroni +Jecht Shot Jecob +JedMosley Jedah JedaiKnight Jedi +Jedi Drue Jeebiez Jeebz +Jeefro +Jeek Jeemis Jeep Jeesuhs Jeet Jeeve Jeez +Jeez Christ +Jef fy JefFamous Jeff +Jeff Aero +Jeff C +Jeff pls go +Jeff says +Jeff the Cat +JeffEHPstein +JeffG JeffMusk JeffWiki Jeffermus +Jeffica Jeffie381 Jeffmyster5 Jeffre +Jeffrey Z JeffreyFNV +Jeffreypoe Jeffro Jeffy +Jeffy xD JeffyTheDahm +Jeffzuaos +JefriEpstin Jefry +Jeft Jefy Jeggesen +Jeguelson Jehdin +Jehobo Jehtty +Jeikeorb Jeimi JeisBtw Jejex +Jekd Jekquesting +Jelbishi Jelbringer +Jelen +Jelenner Jelior Jella Jelle +Jelle Bouma Jellie40 Jellisme_99 +Jellitots +Jellough Jelluh Jelly +Jelly Bears +Jelly Booty +Jelly Dub +Jelly Sucks JellyBeanRs JellyGenix +Jellydonuts Jellyfishes +Jelziin +Jelzini +Jemades Jemelope +Jemile +Jen na Jen0va JenaTulwarts Jenessa +JenfoxxSub +Jenicek Jeniuss +Jenkiins Jenla +Jenna Taylia +JennaHeather Jennacydal JenniTolls +Jennie Jennn Jenny JennyElina +Jensen641 JensenRS Jenskee +Jenten +Jeometri Jepp89 +Jeppe Jeppeonkurre Jeppie +Jepppe Jepppu Jepuzeka Jepz +JerBearGrizz +JerBearOmfg Jerak Jerayou +Jerbelle +Jerbil Jerec Jereh24 Jeremiahlewi Jeremy +Jeremy 2007 +Jeremy Ray +Jergs Jeri +Jerm +Jermothy +Jerms Pro +Jern Jan Jern Tarzan Jerne Jernladden +Jernlund Jernpung +Jero +Jeroen 31 Jerom21 +Jerrbear69 Jerre26 Jerrin +Jerrwie Jerry +Jerry Alan +Jerry Maine JerryChinger JerryMina +Jerryy Jers +Jers Knot Jersh +Jershawerr Jersion +Jertz Jeru +Jerunox Jerusalenm Jerziii Jerzy +Jes2x +Jeseeka +Jesh g +Jesh-mar JeshusChrist +Jesiah Jesk +Jesliq +Jesper Bratt +Jesper135 +Jesperyo Jess Jesse +Jesse Top Jesseq Jessica Jessie +Jessie Jones Jesspresso Jessy +JessyTrip +JessycaOSRS +Jester Head +Jesterbubba Jesus +Jesus I love +Jesus MPhys +Jesus Saved JesusComing JesusRedeems +Jet Jaguar8D Jet3010 JetFlame Jeththe +Jetlag Jetma Jetridder11 +Jetski Jetsmoke +Jettiesimp Jettrider +Jeugd Jev21 Jevel +JewFroBro Jewbaaca Jewbakah Jewbang +JewishRob Jewy +Jexlad Jexlo Jeyos +Jeyzuz Jezeus Jezkoz Jezuux JezzaripHD Jezzie +Jezzx +Jf-n03 Jfleeze +Jfrith +Jfuzzy Jglove +Jgus88 Jgy1889 +Jhackal +Jhb Jhebs Jhny +Jhonlovin +Jhoocy +Jhoocy Moot JhormanCovid JhosS +Jhow +Ji bs +Ji m m +Jia Lissa Jian +Jiara Jibajaba Jibberflexed +Jibbllyy +JibinSui +Jibneh JibrilMaster +Jiffee +Jig Bugs +Jiga Chad +Jiggalagg JiggerJoe +Jiggity Jigglyboy Jigglywoo +Jiggypufff +Jignite +Jigoogly +Jih +Jihe +Jihne +JiiU +Jiiko +Jiim Lahey +Jiiraiiya JikdarShark Jikkurr Jiklim +Jill Hyde +Jille Man +Jim +Jim Sauce +Jim Shorts Jim0k +Jim2k1 Jim38iron JimAdler JimTheGing Jimb0bMaster +JimballSlice +Jimbeamtndew Jimbi Jimbly +Jimbo Sween JimboJamzAF JimboQuan +JimboSlice21 Jimbob3005 Jimbogun Jimboi +Jimbub JimcakeKong +Jimdawgy Jimi Jiminuatron +Jiminy49 +Jimm JimmahDean Jimmerik Jimmi +Jimmi Lipper +Jimmmm Jimmy +Jimmy Jumper +Jimmy Plays +Jimmy Scimmy +Jimmy Wooo +Jimmy1408 +Jimmy3rdBase +JimmyBullard JimmyBuns +JimmyCarter +JimmyGrimble +JimmyStubble JimmyValmer7 +Jimmybe +Jimmygk +Jimmylovecox +Jimmynyge Jimmyo Jimosine Jimpertje Jimpiix +Jims Goon +Jims Ironing Jimsterjim +Jimver Jin Oh +Jin R n G JinDoritos +JinJ0 nJuice +Jinb +JingleBiloba +Jingy Jinimy +Jinjaman Jinkers Jinko +Jinkys +JinnyChatBot +Jinpa +Jinsinny Jinte +Jinxed Jake +Jinzo Doku +Jinzo Jinzo JioGetsMoney Jion +Jions Alt +Jipser +Jipske Jiqonix +Jiqura +Jiraiya +Jiraiyeah +Jirakh Jiren Jirka Jirou Kyouka +Jisska Jitaxia Jithra +JiveChompy Jixoxx Jixr +Jixx0x +Jiyool +Jizt +Jj Dynomite +Jj Jerk2 Jjar127 Jjjjjj210 Jjozzie +Jk Esq Jkdogg Jkhby +Jkl Kalja Jkolrur +Jkrexx Jku45 +Jl-SOO +JlGA JlLLY JlMMB0 JlZZB0SS Jlarge Jlova Jmagelssen +Jman021 Jmar Jmaster402 Jmbryn Jmca +Jmca Savage Jmen Jmies Jmjake Jmonelite Jmudford44 +Jmz Jnas +JnrSeneca +Jnsthenx +Jnup Jnwp +Jo Pain +Jo de Coeckh +Jo h 2 +Jo iron +Jo rd an +Jo shua +Jo we Jo0l +Jo3 H Jo3p Jo5p +Jo6p Jo711 +JoE ShMoee JoEiSaFiShEr +JoJi JoJo +JoJo Skriver JoJo79 +JoJoJoUrBoat JoKerRtheGOD JoMoe JoRouss @@ -11437,84 +24110,168 @@ JoWie JoXuh Joacco Joao +Joao EV +Joao Paulo Jobac Jobann Jobard93 Jobber +Jobbes Jobbits +JobbySkelper +Jobbydrinker Jobbyqq Jobless Jockward Jocqui Jodenzaad +JodioJoestar Jodon +Joe 7 +Joe Kronic +Joe Lol +Joe Mangina +Joe Mclaren +Joe Muggs +Joe Potato +Joe Schwa +Joe TheBeard Joe56543 Joe56780 +JoeBidens Bf +JoeEldenring JoeFoe JoeGas +JoeInglesGod +JoeJunk +JoeMothers +JoeStudd JoeTheGod JoeWoody +Joebie Joebobinator Joecuster +Joedaschmoe +Joee P +Joeholding +Joekle Joel +Joel 9444 +Joel In Pain +Joel Lobster JoelBtw +Joelemz Joell +Joenage +Joenly Joered40 +Joeri Joes Joeshmo Joestar Joey +Joey Diaz +Joey Mobile +Joey RS +Joey069 JoeyDabs +JoeyGatto +JoeyP +JoeySlays +Joey_1993 +Joeyboy +JoeyboySucks +Joeychh Joeydarebel Joeyer5 Joeyfernando Joeyjoe600 +Jofa Joferr Joffa +JoffreyLupul Joggaplanten Johan +Johan Cruyff Johanbtw +JohannesN JohannesRRL Joherk John +John Bilos +John C +John Cena +John China +John D4rk +John Ironman +John Mayer +John McCain +John Mugwump John Muir +John Paul +John Penn +John Sal JohnChaptr15 JohnDaBoss JohnGilespie JohnNoyes +JohnRambo43 JohnWLennon JohnWick39 +John_Wick007 +Johnathan Johnbovi JohndemenBTW Johnletics +Johnmcdodger Johnny +Johnny Chinn +Johnny Dinh +Johnny Lyu JohnnyBeanz JohnnyChimpo JohnnyHart +JohnnyLoco JohnnyMellow +JohnnyQuest JohnnySXC Johnnypants +Johnnys +Johno381982 JohnoMate Johns +Johns bad ma +Johnsons +Johnsters4 Johny +Johny Aus JohnyLocoo Johnyy Johuab Johzyn Joizz Jojis +Jojj Jojkan +Jojo Rabbit Jojora Jojy +Jok Jokar Joke +Joke xD +Jokelanopein Joker JokerzDice Jokinq +Jokkepappa JokuHeppu +JokuVaan Jokurul Jole JolliJolli +Jollyfarms +Jolonerz Jolteon Jolting Joltism @@ -11523,100 +24280,198 @@ Jombsy Jomer Jommann Jomoba +Jomppaa Jomppei +Jon +Jon Eusebius +Jon Meades +Jon Snow +Jon ny +Jon10 JonHDgamer +JonMarston JonOn JonVV +Jonadwyn +Jonahams808 Jonahcart Jonanism Jonas Jonas9115 +JonasLietuva +Jonas_Butte Jonasbroh +Jonassau +Jonatan Jo +JonathanRoid +Jonaz +Jonbaron Jonboy +Joncc Jondog JoneNikula +JoneZii +Jonesing Jonessy +JonesyBadger +Jonetsa Jonezey3 Jongo Joni +Jonibo +Jonie D JonisBaisus JonisSniegas Jonjo12knees Jonko +Jonko lover Jonn +Jonnay +Jonne r Jonnems Jonnisan Jonnisjoen +Jonno1789 Jonny +JonnyBiggles JonnyBoii +JonnyL2020 +JonnyLots +JonnyM Jonnybak Jonnybowler8 Jonnysniper Jonsamma +Jonsed +Jontzzz Jonxsy Jony Jonzza +Joo Potato +JooOvenPizza Jooe +JoojoThePk8r JoonSoo Joonas Joonayoyo Jooni Joontah +Jooogijuuu +Jooohnny +Joop Mgoon Joopaul Joose3 +Jooshica +Joosst +Joostvd17 +Jooxan Jophatmama Joptvajiumat +Jor dinh +Jor dy +JorVa Osj Jord +Jord 0_o +Jord Croft +Jord idk +Jord xD +Jord y +Jord z JordHarris +Jord_96 +Jordan 1 +Jordan 7218 +Jordan KY Jordan030592 +Jordan07 Jordan1063 Jordaneee +Jordanite +Jordans Jordany5b Jordavitch Jorden JordenFCB +Jordinee Jordnn +Jordo Am JordyM JordyMcSheep +Jordynn +Jordysteen Jordz_King +Jore Jorenator +Jorgeme +Jorgos +Joricki +JorkinMyWorm Jorkkelis Jorlund +Jornaleiro JoroEdde Jorsne Jorster Jorttis Jorzki +Jos Bezos +Jos de Mutn Josavi JoseLargeD +JosefStaIin +Joseluis1 +Joselyn Leo Josen Joseph +Joseph Sam2 Josh +Josh 2277 +Josh Jones Josh Tsutola +Josh x +Josh013 Josh2Godly JoshD +JoshDagainz JoshGymnast JoshMarksman +JoshYewAhh +Joshalog +Joshan Joshery Joshh +Joshhhh Joshii Joshimitzu Joshism +Joshjoshin Joshmaster31 +Joshpaul Joshua Joshuaaaah JoshyCii +Josiah Higha Josiahs Josoust +Joss +Josse +Jostavo +Josti O_o Jostle Josue Josukes Josylvio +Jota_420 Jote +Jothha Jotjemenotje +Jotq +Jottini Jotunheimar +Jotwe Joueur4376 Joulaha +Jovial Jovilius JovyGaming Jowah @@ -11624,43 +24479,86 @@ Jowalll JowatBTW Jowel Jowitt +JoyOfSatan Joycey1997 +Joynzz +Joystick +Joyun II Joz6 +Jozi Blue Jozu +Jpan1 +Jpellican +Jpq +Jqwss Jr Metro +Jr Muffin +Jr Stigel Jr2k13 Jrabbat Jrby +Jrdn Jreppks +Jrey Jroanirr Jrodjok Jrogo +JsJays JsN4 Jsag +Jsi7111 Jsnn Jsony +Jspot +Jsq Jstnn +Jstrife9 +Jthrust Jtownironman +Ju mala +JuGgLe BuG +JuJuzzz +Juan Dangle +JuanTalon +Juandissimo +Juaneria +Juanreliable Jubaah +Jubarte JubbaTheHut Jubilations Jubita Jublian +Jubster +Jubtus Jubu +Jubz Judah +Judas wept Judddy Jude +Judge +JudgeDredd +Judgewalker Judie Judies Judlmin JudoChop +Juduel +Judus Judybats +Juedons Juezz90 Juffo +Jug001 +JugMilk +Juga JR Jugalator +Juganog Jugernought Juggalo09R Juggerthot +Jugimaster JuhQ Juheniqus Juhhu @@ -11672,22 +24570,52 @@ Juhve94 JuiBoi Juib Juice +Juice W RL D +Juice WRLD +JuiceRock JuiceUSA +Juiceb0x44 +Juiceful +Juicewayne +Juicieee +Juicing Juicy +Juicy BIG +Juicy Jorma +Juicy Lil D +Juicy Pair +Juicy XPs +JuicyBigNut +JuicyJesusFE +JuicyJocelyn +JuicyJones +JuicyLap JuicyTear +JuicyTigr +JuicyWetNut +Juicy_Newfie JuiicyAF Juikki Juint +Juipp_Iron Juisy +Juk e +JukeLess Jukebot Juked +JukeduM Juko Juktes +Jul es Jules +Julesbak Julia JuliaFlorida JulieFromHR +Julieekins Julien +Julio Cesar +JuliusHotdog Julley Julma JulmaJoe @@ -11695,42 +24623,93 @@ Julmarsson Julpa Jumal Jumanji +Jumbalia420 Jumbo +Jumbo Joe +Jumpcut JumpingBean +Juncker June +June bug Junexo JungJulius Jungkook Jungle +Jungle Georg +Jungle Jepa +Jungle Jim +Jungle Wreck Jungle175 JungleKitty +JunglinTunes +Juniortank25 Junk168 Junmai +Junsimu Juntti463 +Juo Juoda +Juoppis JuostenKustu Jup1Ko1r4 +Juppi Juqqernaut Juranja Jurassix +Jurble Juri +Juri M +Jurikka +Jurisdiction Jurky Jurmalainen +Jurrie Jurtaani JuryRigging +JusAmain101 JusCauz +JusDoMe +JusPourVous Jusfat +JussJoshinYa Jussi Just Just Alright +Just Alt F4 +Just BeingMe +Just Bones +Just Bryce +Just Champ3 +Just Creepin +Just Dank +Just De-Iron +Just Jacob +Just Keith +Just Kurt +Just Logan +Just Malone +Just Milton +Just Mitch +Just Neptune +Just Reacher +Just Treason +Just a Dilf +Just a main +Just aFacade +Just for Fun Just1n Sane Just3lis444 JustAAPotato +JustADowny +JustAFroggy JustBob JustChillins JustDevon +JustDrew JustFollow JustGoInDry +JustIan +JustJS JustJake JustKidStuff JustLucky @@ -11739,14 +24718,25 @@ JustPeachy JustPertti JustRenne JustRoW +JustSarge JustSinful +JustTaxLand +JustTheFool JustTheTip +JustToast +JustTrynaMax +JustVibeWMe +JustVinny +Justa Wake JustaStar Justass +Justice v1 JusticeSven +Justicemoose Justiciaro Justified Justin +Justin NT JustinBieber JustinFromVA Justinkai @@ -11755,66 +24745,178 @@ Justins007 Justlfied Justnexuss Justo80 +Justorr +Justryin +Justyfi +Justyn Jusus Jutendouji +Jutku +Jutkunmetku +Jutter +Jutti +Jutty Ruckus JuuNinJi Juugmasta Juul +Juulxodia +Juun Solo Juuna +Juunaz2chips Juustis +Juuzo Juvi Juvian +Juzzy +Jvzy Jwad +Jwcsg +Jwd Jwett +Jwu JxJxV JxcelD +Jxck Jxcx Jxdidiah Jxdooo +Jxhnn Jxke +Jxmeson Jxnas Jxni +Jxstxn +Jxxst JxyStorm +Jy Jybais Jyeronese Jygers +Jyhlln Jyhy +Jype Jyppedi1992 Jysk Jyura +Jyy Jyypy JyzzBrah +Jz +Jzbeta +K 1 L L A +K 1 P +K AMI +K B D autism +K E K E B +K E L A +K E T CHUM +K E V 0_O +K Hades +K K Boef +K NW +K O L L I +K O M P I S +K OOOOOOOOOO +K P N +K P Sweet +K Smalls +K W +K W A L A +K Y Y +K a l e w i +K a z a +K aarel +K ayyla +K eller +K elvin +K en +K etaminer +K ev +K iNG IRON +K idd +K ilo +K im +K l 9 +K l N G S +K nauss +K nut +K o j o +K o o s a +K ohen +K orruptt +K ree +K rs +K t r n e +K una +K ushi +K uz +K voth e +K y u m a +K yogre K-Diddy K-epan +K-market +K0 +K0 4 K00LBR33ZE +K0F K0L0S K0NING +K0USE K0bus +K0edinator +K0ju Balius K0lbi K0ndemned K0rky +K0rnuit K0tleciokas K0veras K0zor +K19 +K1ERZ K1LLY K1ddl3 K1ller5169 K1ng +K1ngo +K1ngshredder +K1r1t0123 +K2 Jung +K32 +K38 K3KSE +K3U S2 K3azkoks K3fka +K3kt K3mi K3nn3d K44K K4IF20 K8iee +K99 2 +K9Father K9lenny +K9rk +KA97 KABBABEAST KAKAGUATE0 KAKERANDELIN KAL-JML KAMlX +KAT alyzt +KB1988 +KBARAKAT +KBX0 +KC Chiefz +KC M0 +KC8 +KCRB +KChiefn KDOG1419 +KDOT_OSRS +KDark22 KDrizzy KEEPOUNDING KEEPTHATSHIT @@ -11822,73 +24924,164 @@ KEEZY10 KEIF KEKDUBYA KEKLERO +KENTUCKYBLUE +KEllis KFBal +KFC CAMDEN +KFC EMPL0YEE +KFC OG KFCatz +KFChompy +KGBK KGCine +KGP KGee +KGoldy KHADER KIIIIIIIIIIH +KILLER COMBO KILO KIMB0 KING +KING GAYWAD +KING VILO KING332 +KINGNERON +KINGstayMAX KINGxGIZZARD KINNTO KIPPER +KIVI 420 +KIWI Stu +KIitor IS +KJ 2 +KJ6 KJLS +KKayK +KKela KKona +KKona USA KKrazyAl +KKronas KKurtiz KL93 +KLB +KLS +KM 30 +KMW +KN0G KNIKERSNIFFA +KO CANE +KOK enjoyer KOKAOKAA +KONMAI +KOS OVO KOlRA KQSS +KR Zangetsu +KR0VALI +KRN +KRON1C 420 +KRYMK +KRYPTONlAN +KRlT KRonHusker KS04rs1 +KSP +KSaucee +KT Ferrie +KT Tape KU51 +KUAY KUAYx KURD1STAN +KVM8 KVRPT +KXNG Razing +KXNGVEGETA K_sak +Ka0s50 +Ka92 +KaChiuSa KaIOKENRAGE KaMi KaMi_RnG KaTFeniX Kaan +Kaan Frog +Kaarel55555 +Kaaris95 +Kaarv +Kaasbaap +Kaasboom Kaasmantel +Kaaswater +Kaaz Channel +Kabatlor +Kachhow Kacinova Kacy Kacyy +KadHead Kadabara Kadan Kadarlu +Kadett Opel Kadeyr1 Kadocc +KaeBtw Kaelin Kaempen Kaepls +Kaer +Kaesar KafHaYaAinSa +Kafaei Kaffah +Kafficko Kage Boshi +Kahdoom +Kahis +Kahlx +Kahto Kahuna +Kahur111 Kahvikuppi +Kahvimaa +Kahvo Kahwoozy +Kai ri +KaiHos19 KaiXin Kaibaman +Kaif Kaihn +Kaiirl +Kaila Kails Kaimanll3kav Kaimorten Kainahuulio Kaine +Kaioken x420 KaiokenRyan Kaion Kaiser +KaiserBruno KaiserRS Kaister +Kaitaia KaitoSun Kaiven +Kaizen Reign +Kaizen Soji +KaizennxX +Kaizloh +Kaizooka +Kajbanan +Kajgils Far +Kakariiiiick +Kakarrot Kake Kaker Kakerandeli @@ -11897,84 +25090,144 @@ Kakoji Kakor Kaksitoista Kaku +Kaku bakudan Kakua +Kakuri +Kal9000 Kalakoi +Kalas Kalash556 Kalashnikova Kalavothe +Kalb Kale +Kale Henk +Kale vader +KaleKikker92 +KaleRS +Kaley Cuoco KaleyFan Kalezki Kalfite +Kali Maaaa +Kali Roses Kaliaa Kalico Cat Kalideos Kalihi Kalikow +Kalima Kalis Kaliumoxide Kallateral Kalle +Kalle Anka Kalleee Kalmaars Kaloex +KalopsiaSix +Kaloua Kaltsu Kalu1337 Kalub +Kalvaaja Kalveo Kalystas +Kam +Kam Lok Lam Kamal63 +Kamchi Kamelinpiaru Kamelovsky +Kamelze Kamen +Kami_no_majo Kamiel +Kamii Dad +Kamijou Kamikazi Kamino +Kamino Kage +Kammorie Kamp Kampest Kamphuijs Kanagawa Kanao Kanapius +Kanapizza +Kanbu Kandarin Kandeeh +Kandid GG Kandonesys +KandrakarsNX Kane +Kane 7687 +Kaneelkameel KanekiKun Kaneohe Kangru Kangst3r Kani +Kaninka Kanned KannonBalker Kannski +Kansas +Kansas Bill +Kansas State Kanseim Kansi +Kansloos Kant +Kantinejuf Kantoo Kantrimees Kanttis +Kanuk Kanye +Kanyeetzy +Kanzeigan Kaolo +Kaolo Spoon +KaozerMauser +Kapaa Kapakala KapeVing Kaphu +Kapiling +Kapitalisti Kapitein Kapiten Kapkeik +Kapkkha Kaporkchop +Kapot +Kapot Slecht +Kappa 73 +Kappa monkaS +Kappa xD +KappaZilla +Kappala +Kappalism Kapsaluun +Kapsones +KaptainPurp Kaptainkilla Kaptains KapteinK Kapten +Kaptn skev KarQ Karaboudjan Karaf Karagoz Karamjob Karans +KarateKon KarazySS4 +Karc KareemPie1 Kareir Karekano @@ -11982,73 +25235,118 @@ Karen KarenMaskin Karethaeis Kargas +Karhu48 +Karhuboiii Karhulohi81 Kari +KariJuice Karies +KariimsPik KarilSummer Karils Karim4lol Karkanii Karkotaseet +Karkov Karl +Karl The SUV +Karl1s KarlS282 Karlee +Karliah Karlit Karlology +KarlosBro Karlteine Karma KarmaRush Karmaa Karmadyl +Karmafox Karmah Karmas +KarnMother1 Karnivore Karnyx Karolik +Karolus6 +KarpyCarpe +Karrak7 KarsanHAM +Karsk kopp Karskeinmies Karso +Karsta Anne Kartelrand +Kartoesh Kartoma Karukas0 +Karumba Karumi Karuu +KarvaBanaani Karvajarru KarvaneMees Karvatatti +Karwos +Kasane +Kasdeya x Kasei +Kaseii 1 Kasejas +Kasen Kaser Kashak +Kasihan Gua +Kasoku Tesla Kasp +Kasperjus Kasprzak1 Kaspuhh Kasraa +Kass Glimmer Kassipotku Kassu +Kassu C Kasuel Kasugano Kasvikko Kat1nr +KatBeatitude Katagon Kataphatic Kate +Kate Bush KateTiffany Katenkos Katetuotto Katfishh Katiensam +Katikene Katikene0 +Katinas +Katiopeia +Katlink5 +KatnissGray +Katraenia +Katrielle Katski Katsuo666 +Katt 90 KattNiP Kattarui +Kattnakken5 Kattnis +Katze btw Katzes +Kauhee Hiki Kaukeneris +Kauldron Kaunas +Kaunas_Rulz Kaunopihlaja Kautschuk +Kavz KawaBonga Kawactus Kawaii @@ -12057,35 +25355,61 @@ Kawaiisaki Kawakuboku Kawkaw Kawked +Kawtik +Kay D KayJ KayWhyPee +Kayazy Kayback +Kaybizzle Kayden +Kayden Boy Kayen Kayla +Kayla Swift +Kaylidascope Kaylizz Kaylon Kayluh +Kayns Main KayranFootly +Kayso Kaytok +KayyPlz Kayzielol +Kayzo Kazatan1245 Kazave Kazaz +Kazcade95 Kazching Kaze8HerOut +Kazify +Kazuma Moon +Kazunni Kazuuhiro +Kc Collector Kcnny Kdash +Kdaw Kdens +Kdragons +Ke bajo +Ke era +Ke nt KeB4NG KeRah Keabler +KeanuCat Keasbey Kebab +Kebab Ari +Kebab Store KebabGuy93 KebabWrap +Kebabov iron Kebbabhallal +Kebbby Kebbe Kebintotero Kebs @@ -12094,35 +25418,62 @@ Kedakaki Kedirav Keebler Keef Supreme +Keef_Man Keegi100 +Keekar Keekers +Keel Keela Keelay Keenar +Keep Le Fe +KeepCup +KeepDistance KeepItWicKeD Keeper1OS KeepitStoney Keepo KeepoKeisari +KeepoNoIron Keepomaster Kees KeesCanadees +Keeslp09 +Keetgracht +Keevon Man Keff +Kegaz Kegern +Kegs +Kegse +Keh O Brien Kehaline1 +Kehno Keiala Keidy9 Keifay Keikoh Keinas18 Keiran +Keiran Perch +Keironn Keisari Keisarinna +Keister Egg Keit KeithMcChief Keithii +Keittokuppi Keizer +Kekaha +KekeLovesMe +Kekkonen +Kekkonen69 +Keksiviikko +Kel Darsam Kela +Kela maksaa +Kela-Olli Kelagang Kelb Kelbikers @@ -12133,11 +25484,14 @@ Kelgone Kelh Kellarivelho Keller +Kells o_o Kelly Kellycollin2 Kelohonka Keloo +Kelps Kelso523 +Kelso561 Keltik KeltonThaGod Keltuzazz @@ -12146,106 +25500,182 @@ Kelvin2GWW Kelvino Kelwus3 KempBush +KempDaShrimp +Kempisch Kempset +Kempy x +Ken +Ken Bolka +Ken Griffey +Ken Kill U +Ken RS +Ken Ten +Ken141 KenBone KenKaniffCT +KenPerm +Kendal x Kendal68 Kendlang1 +Kendrick3691 Kenery Kenesu +Kenfernal Kenleyy Kenleyy_TM Kenmaster Kenn +Kenn y +Kenna Kenndu Kennedy Kenny +Kenny Deez Kenny121 Kenny6397 +KennyKSD +KennyS KennyWhopper Kennyollie Kennypower5 Kennyyyyyyy Kenozh Kenpo +Kensan-7 +Kenshiro Kent KentWasTaken Kentacus +Kentarokins +Kentish Hops Kentucky Kenwood +KenyanChild Kenyann +Kenze +Keonii +Keox +Kep Kephan Keplunk Keppi +Keppi Einari Kepra Kepuck +KerZam Keraliix +Keratin Kerbeth KerfDaddy Keri +Keric Kerk Kerkerino Kermajorma Kermit +KermitTheRob Kernalpotato +Kernautist Keromasev +Kerrr Kertar Kerzara +Kes Sittus +Kesc Kesekui Kesohs +Ket +Ket Baggie +Ket Schep +Ket Uh Mean KetaKnallt +KetaYours Ketabar Ketanator Ketch Ketho Ketnet Ketonall +Ketsah Kettaz Kettu Kettyman Ketwards Keudel Keunic +Kev The Tonk +Kevain69 Kevali Kevb0t Kevbro9 Kevichan Kevin +Kevin Booker +Kevin Framed +Kevin Nguyen +Kevin5-0 +KevinDurant +KevinLawrune +KevinOSeven KevinTes +KevinTheGOAT +Kevinspostal Kevinw20 KevlarX KevolutionX +Kevsin2 Kevsin3 +Kevslays +Kevstrong Kevthebos Kevva +Kevve +Kevzy Kewl Kewlaidman Kewnsauce +Kewwl Kexkaka +Key Concept KeyNoir Keyashes Keydiss +Keyfob Keylock +Keylord Keyney Keyoh Keyori +Keyose Keywork Keyzz Kez0 Keza132 Kezmaya +Keztra +KezzerQ Kezzerina +Kgb Spy +Kgsnipe +Kha Zx Khaant Khabib Khader +Khajgold Khal +Khal Bow Khaled Khaleeeesi Khaleesi1113 +Khalesar Khalisee Khamoshi +Khan of Iron +KhanhDung +Khans Wrath Khanzed Khao +Kharium94 Kharjo Kharn Kharos @@ -12254,10 +25684,15 @@ KhatrickZain Khayri Demon Khazad Khazad Dum +Khazanedar Khazu +Khealim Khest +Khint +Khirean Khiru Khleb +KhooNBust Khoosh Khoppa Khornebull @@ -12265,16 +25700,31 @@ Khovansky Khride Khrollo Khryptik +Khrysus Khufu Khune +Khunt Flapz +Ki Adi Fundi +Ki Arts +Ki Stu GSK +KiD BeeR KiIIa4realz KiLn +KiSMET6 +Kia Jon Kiarama Kibb +Kibeleza Kibisai +Kibito Kai +Kibra Kick KickarseCale +Kickback Dw +Kickbums Kicker +Kickrolls +Kid Inferno Kid K O N G KidClutch17 KidLeaderKTY @@ -12282,237 +25732,482 @@ KidSativa Kidalien Kidchilly100 Kidd +Kidlizard KidneyBean Kids +Kids Bong Kidtrilogy Kidur +Kie Kiedis +Kieftron +Kiek um goan +Kieken +Kiekkoilija +Kiera Kiero Kierzo +Kiewit +Kifooo Kifsa KiiDSKOLiO +Kiinja +Kiinnostaa Kiirii +Kiisu Kiivi Kiiwityty Kiizoru Kijucatm +Kikikissa Kikiniki2 +Kikkel Kikkertje Kikyo101 +Kil U +KilIerDaddy Kilamanjaro +Kilbot0 Kiliann Kill +Kill Crazy10 +Kill Credit +Kill the GE KillConfirmd KillSwitch26 Killa +Killa Cam +Killa Kamali Killa213 KillaSilence Killab0rtion Killabrew Killadelphia +Killadeuce Killamemsta Killaowns Killatonicus Killed +Killed Elmo Killed145 +Killekalekoe Killemall Killer +Killer Jr +Killer Kurce Killer7502 +Killer81093 +Killer9823 KillerFlix KillerSlee +KillerVibes +Killerank202 Killercaster Killerdog HC Killerdustyn Killerkotti Killermatt +Killeroh Killers455 Killertribes Killgor138 Killi +KillianRS Killing KillinxLivin Killjoy4fun +Killjoyer2 Killmankind +Killme5551 +Killmelol Killnloot2 +Killoah +Killsw1tched Killswitch83 Killua +KilluaSam Killzone +Kilminater +Kilo Meter +Kilodoreo +Kilograms KiluaZoldyk +Kim +Kim Chungha +Kim Trails +KimKallstrom Kima +Kima Ronso Kimano Kimbr0 Kimbu +Kimex +Kimi Hendrix Kimoja +Kimosabii KimtonLe Kina Kinaesthetic +Kinboat Kind Kinda +Kinda Blue +Kinda Sinful KindaDerpy Kindaddy Kindom Kindoou Kindozsodln +Kineettinen +Kinetic King +King 0f Gout +King Ankle +King Aussie +King AvonIV +King Blob +King Boned +King Bud +King Bugs +King Chev +King Dong +King Dongo +King Edolus +King Elf +King Elon +King Emurxer +King Flipsta +King Frodon +King Green 2 +King H0d0r +King Ian +King Junkie +King Kabanos +King Kenneth +King Lexius +King Mariin +King Milky +King Nino 1 +King No Fear +King Noc +King Paf +King Rabbit +King Rayman +King Redeem +King Rico +King Runs +King Ruonis +King Seb +King Seneca +King Size V +King Slime +King Snowman +King Spudz +King Swine +King Tsar +King Westor +King XRPL +King Zaros +King davey +King of Imps +King of PvM +King0fHelll King0fSkane King1631 KingAcorn85 +KingAwowogay KingBOO KingBailey KingBoo +KingBuliwyf +KingClark KingCudii +KingCurtis11 +KingDarkVII KingDweebus KingEider KingFlugal KingGoblin09 KingGrekus KingGunshow +KingHawk O_o +KingInYellow KingJacula KingJelore +KingJoey +KingKahuna KingKlick373 +KingKlye KingKongKoen +KingKong_Qc KingLeonidas KingMikeyD KingMongo KingMonk KingMook +KingNate1 KingNathanx KingOfIron +KingOfQuests KingParcival +KingPatVII KingPlAnchor +KingPractice KingRobStark KingRustyVII KingSandCrab +KingScribles KingSizeD1CK KingSoge KingSpirel +KingStyle KingTheGreat +KingTsjubbie +KingValencio KingYoshi KingZac KingZerka +King_Kerran Kingaroo +Kingben +Kingbuddy40 +Kingdom Rush +KingdomBlade Kingeri Kingg +Kinggg +Kingism Kingjake18 Kingmonkey23 Kingofhearts Kingpfx2 Kingpin +Kingpin Xp Kingpk3r +Kingrobster7 +Kingruler456 +Kings67 KingsBluue +KingsGrace Kingspadex42 Kingsta +Kingsthor KingstoAces KingstonWall +Kingsty +Kingz Haki +KingzWorld Kinjello Kink +KinkerScaper +Kinkers Kinky +Kinky Kenny +Kinky Scr Kinkybuns Kinkytoast Kinkyy +Kinno +Kinobles Kinokird +KinokoZoku Kinp +Kinsaic Kintoki300 Kiopley2 Kiox +Kipee Anneli Kiplup Kipnuggets Kippenbro +Kipsel +Kiptandori Kiqz +Kiraci Kiraga Kiraly +Kirara +Kirbi Smart Kirbngo +Kirbsey +Kirby Kirby7670 +Kirchberg +Kire1n +Kiree +Kiriel Kirimah Kirito +Kirk4 +Kirk97 Kirkburton1 KirkyBeast +Kirne Kirrion Kirry Kirstin Kirthor +Kirumibe Kiruzonu +Kirx125 +Kirz Kisa +Kiseki Kisizel Kisle +Kiso Valley Kiss +Kiss Kiss +KissMeHomies +KissOfFire +KissOfFury +Kissahomie69 Kissanpentu Kissmyase111 Kist +Kit Fistoo +Kit10Kat Kita +Kitashan +Kite Solo Kiteman +Kitfox IV Kitsch KitsiKitty Kitsune +Kitsune Kami +Kitsune Ness Kitsunemimi Kitten +Kittenz Kitties +Kittle Bits +Kittoh Kitty +Kitty Perry +Kitty v2 KittyMeow83 +Kittymouse 1 +Kittys Newb Kiusatus Kivespulla +Kivick Kiwa Kiweh Kiwi +Kiwi Icons +Kiwi Stona +KiwiIskadda KiwiMacJ +KiwiSteve +KiwiTheBot Kiwiana Kiwidude Kiwiskurt Kiwwion +Kiyak Kiyoko +KizJ Kizminsky Kizone +Kizz mo Kizza +Kj Kai KjellEllen +Kjellberg94 +Kjomo Kjottfifaan Kkobe +Kkobugi +Kkokkom +Kkoppa Kl2AZY Kl3RAN +KlDDO KlLL +KlLL 4 BONES KlMI +KlMpossible KlND KlNG +KlNG MlKE KlNGY Klaar +Klaarkomer +Klaasie Klacky Klanezy Klanks +Klankss Klapzak +Klarence Klariany Klarna Klaskdeng +Klassic +Klassified Klatscher +KlausLittle +Klaussie Klavan +Klavelon KlazBooy Kleatus Klebold Kleened Kleiades +Kleine bolle Klementtii +KlemmDawg17 Klemonhaze Klengie +Kleo +Klepdiezle03 +Klepp Kleptic Kletz KlewKlew KliKlack +Kliewer Kliffa +Klik gebit +Klikke +Klingons Klippzz +Klo0 Kloefklapper Klogin Klonzyon +Kloofiool Kloorijoodik +Kloot-Zuk Kloover KloreCore +Klotho +KlovneKrabbe Klowdstr1fe +Klub Dubx Kluftritter +Klumpy Klumsie Klunchkey +KlungePlunge Klunkii +KlutzyKay +Kluuntje +Klvtz +Klyd Kmac KmartWorker +Kmd +Kmie +Kmk_Yawgmoth +Kmorken Kmtfwtm +KnD xP Knaap Knabbelbaars +Knabbernossi Knacker Knakenstein Knaksaucage @@ -12524,77 +26219,146 @@ Knaught Kndd Knead Kneeeled +Knewby8 KngBlkDrogon +Knickers +KnicksNati0n Knife +KnifeStory Kniffes Knight +Knight Crip +Knight Drake +Knight Jo +Knight Mors +Knight of 3 Knight50 +Knight522 Knight5247 +KnightKettle KnightMike KnightO +Knightcrawle Knightenator +Knightlock +Knightmare +KnightmareNL Knightrainy Knightromite Kniili Knikkerbal Knivezii +Knob End +Knobin Hood +Knod with Me +Knoo +Knospi KnowMadss KnowPurpose Knowing Knowledge Known +KnowsNoFear Knoxville333 Knuckle +Knuckles +Knulle Knummi Knurrbauch KnusCaboose Knusseprulle +Knut Knut +Knwn +Ko Addiction +Ko D Ur Dead +Ko0tje KoBe +KoH Zoro +KoPvM +Koa +Koak Koala +KoalaBear819 KoalaBwala +KoalaHeist +Koalarobe +Kob Bryant Kob3oshii +Kobakat +Kobayashi89 Kobe Kobenna +Kobi btw Kobold Kobushi Kochelas +KochiiBear Kocjancic +Kocyte Kodai Kodak Kodax Kodeth Kodfunk +Kodie Kodipar +Kodo +Kody IRL Kodywithac Koekebakker +Koekenpan Koekienator Koelkast1 +Koelkast3120 +Koelogg +Koemi +Koenfu +Koeppy +Koerdistan Koff +Kofferbak Koffie +Koffie Shop KoffieBoer Koffiekan Kofnx +Koga09 Kogarah +Koge +Kognito +Kogsin +Koh Me Lo Kohda +Kohrosian +Koii Diva Koka Koki KokiriSword +Kokki Kokkue Kokkugoburin Koko10tkd +Kokoftu Kokou Kolade123 Kolariah +Kolarino Kolbot +Kold Pizza69 Koldbetrayal +Koldz +Kolicee Kolmay Kolmiojuuri Kolodinsky +Kolomit Kolton Komai Komarov26 Komijn +KomisarzFlak Kommunist +Komog Kompact Kompania KomradeScape @@ -12602,61 +26366,125 @@ Komugi KonDaTV KonKai Kona +KonaTheCake KonaTheGent Konami +KonarMilkies Koncept Kong Kongen3609 +Kongherodes Kongklunk Kongmoakim +Kongon Musta Kongz +Konigs Tiger Konigsblau +Koningnoob +Konjakkia Konkelbearet Konkuuu Konnie +Konnor Ray +Konny KonosubaAqua Konroy11 +Konstiq +Kont Crumbs KonteNeuker Kontentti Kontoret +Kontrafakt +Konu Kony +Kony4ever +KonyRomo +Konya Slayer +Koo +Koob +Kooben Kooch50 +Koochie +Koogs +Kooiker +KookieDough +Kooky Kal +Kool Aid76 +Kool Hwhip Kool Iron KoolKriegs +Kooleey Koollpop +Kooloo Limpa +Koomar Koomorang +KoontzyJr +Kooopa Koopa +Koopa Kash Koopatrol Koopley +Koops +Koothi Jr Kop0nen +Kopet +Kopites +Kopn Alt +Kopoes +Koppa Olutta +Kopra_0nu +Kopzilla0 KoqPuuser Korado Korail +Koral64 Korangarr +Korasko +Korazi +Korbet KorbolJestem +Korby_K +Korea KoreaBread Korean +Korean Neet Korelivia Korihoko Korisas +Korkesh101 +Kormulus Korneel +Kornettos Korok Korpokkur Korravalvur Korsair +Korsan Kortti KosChiquiss +Kosaki +KosarevSpawn KosenRS Kosey +Kosha Engler +Kosken Kovin Kosmckid +Kosmo +Kosra Kosst Kossukissa Kostaja123 Kosteezer Kotaki95 +Kothfan2 Kothfan3 Kothu +Kotimaista Kotkatapult +Kotoe +Kotov +Kottis +Kotze Koucii Kouen Kouhais @@ -12665,111 +26493,227 @@ Koulupummi Kourend Kourk Kov0s +Kova Kova Kovacs +Kovacs Bela Kovaley +Kovalux KovidKai Kovit +Kow +Kowawa Koxu +Koy +Koy5 +Koyix +Kozileks +Kozmic +Kozojebec Kozyshack +Kpiy Kprs +KpyosX +Kq Kr0jm +Kr149 Kr1sten Krab +KrabbiePatty +KrackScape +Kradoth +Kraff +Kraft Slave Kraftra Krafty Kragjay +Kragwa +Krahhling KrakaJ Kraken +Kraken Beerz +KrakenSnacks +Krakenarse +Krakenmom +Krakkars Kralik Kralovna +Krammetje +Krana +Kranky Kraut Kranox +Kransie KrapNSchitz +Krapinschitz Krappa Kraq +Krasznahorka +Kratic Kratje Kratos Kratos-Arepa +Kraujukaz Kravcik +Krawall +Kray +Kraytoast +Krayz818 Kraz3d +Krazed Zen Krazezor Krazy +Krazy Kramer +KrazyKillah3 Kream +Kream PIE KreampieKing Kreams Kree +KreeM_Pies4U Krel +Kreme Krispy +Krepe +Kreshink +Kreupel hond +Kribo KriegFleisch Krigare Krigersej Krigsgaldr +Krikkos Krile Krillin +Krillin It Kriltar +KrimenReborn +Krios Kriptog +Kris 2 +Kris Kross +Kris Tis +Kris Toffer KrispCowMilk KrispyCow +KrispyJello +Krispz +Kriss42 +Krisse Krisstian +Krisstof Kristina Kristo1337 +Kristoh +Kristops KristySlays +Kristya Kriszx Krith +Kritters Krizalid Krizee Krllykins +Kro Kroeg +Kroekoek Kroer1 +Krogan +Krohmos +Kroketten +Krombop Mike Kromuh Kron0 KronScape +KroniKStyL Kronic KronicPlague +Kronic_gtt7 Kronjuvel Kronoryx +Kronstedt123 Krontio +Kropium Kross +Krownax +KrthisIrnhrt +Kruber KruimeIkoek Kruisboog Kruizar +Kruncha +Krusch Qc Krusk09 Krustorb KruttGutten Kruuxt +Krvavec +Krw Kryder Kryderman95 +Kryl +Kryllz Kryoge777 Krypsiss Kryptanite Kryptic +Krypto Pup Krypz Krystalbead Krystalized +Krystie Krytl0rd +Kryxon KryyptCeepR Kscott +Ksed Ksteg Ktran4 +Ku Kuaalo Kuanta +Kubanna Kubfu +Kubiak +Kuchera Kudos +Kudra Shade Kudsk Kudt +Kuemper Kufke +Kuger +Kugi Kuha +Kuhis Kuhki +Kuhnlicious Kuhvon +KuhwazyyPVM Kuhz +KuidaoreTaro Kuistikopone Kuittis +Kuji Otoya +Kukii +Kukko +Kuksukka Kulak Kuler Kuli +Kullilutku +Kullirausku +Kumara Fries +Kumduh Kumeku +Kumetis +KumikoOkada Kummars +Kummens Kumoga +Kumonyru +Kunakana Kunaphela +Kundalini888 Kung +Kung Aguero KungFuKennyy KungFuhr3r KungFury01 @@ -12778,18 +26722,25 @@ Kungler Kungzaki Kunie Kunkku289 +Kunkulu Kunsel +KunukGL Kuola +Kuollu Homer +Kupla +Kuppi KurSiens Kuran Kurasaki21 Kurask +Kuratus Kurayamii Kurdishtan Kurfue Kurgan Kurib0hh Kurim +Kurios Kurko Kuro Kuro6757 @@ -12798,141 +26749,368 @@ Kurohige-Jm Kuroma Kuroshin4 Kursdragon +Kurt Cocaine +Kurt1sdbr +KurtMaxed Kurtan +Kurtismo789 +Kurtiz +Kurtowogei Kuru Kurumin +Kurums Kurune Kurza Kurzgesagt Kurzi Kurzol +KusOnRNG +Kusai Kush +Kush 999 +Kush Grey +Kush Nerd KushWizdom +KushalaDaora Kushanada KushedKnight Kushies +Kushioned Kushkhalifa +Kushmooms Kushtyyy Kusiaks Kusimuna +Kusimuna JR Kusle KusmarPavola +Kuso Saiko +Kut Jack +KutInternet Kutasy +Kuthay +Kutirons +Kutkip +Kutori Kutunawa KuuDuu Kuudere +Kuulus +Kuuw Kuvakei Kuwolski +Kuxx +Kuykendoll +Kuzh +Kuzi +KuzzzTruckin +Kvacky Kvamsdal +Kvaoathe +Kvzy Kwadraat +Kwakske Kwambam Kwan +KwanDynasty3 +Kwani Kwarkark Kwastaken +Kweem Kwei +Kwep Kwieppie Kwintal Kwondeo +Kwong Kwongo KwuarmFarm KxKxW KxNoTTz KyIll +KyPolar +KyWi Kyaandere +Kyafa Kyberpaavi +Kye187 +Kyeb KyeeG +Kyelee Kyersago Kygehn Kygesm Kyhlen Kylanater Kyle +Kyle 8990 +Kyle Esq +Kyle G +Kyle Kmac +Kyle VA KyleFammm KyleGrounded +Kyledog829 Kyler +Kyler Moss +Kyles Cute +Kyles GIM +Kylesteven +Kylewwashere +Kylex Kylianvb +Kylliel Kyloman Kymeister +Kynodontas +KyotoSadness Kyouan Kyouko Kyouma +Kyouna Kyouran +Kyphrus +Kyps +Kyr0s +KyraXIV +Kyrakishuun Kyrdaar Kyrie Kyrist KyroThanatos +Kyroba Kyronius +Kys Ironmen Kyski +Kytkinpommi KyuubiKurama +Kywt Kyykin +Kzyl +L 0 S T Y +L 3 G 4 C Y +L 67 +L A R A M S +L Ben +L CBO +L D A +L E G E N D +L E G O +L E L O +L E P I +L F C +L FC +L Hus +L I N U X +L I V M +L OAFS +L S Q +L U I G +L U U K E +L X R D S +L achie +L aika +L aka +L ama +L ang +L apras +L atias +L aw Rune +L aze +L e an +L eafErikson +L egs +L ek +L emaster +L ev +L i m a a +L iamm +L iar +L ilo +L inox +L ions +L o w g +L ockdown +L ofty +L osi +L paradoxum +L u m i +L u mi +L u x +L ucas +L unatic +L00000 +L00M L00OL0OO00OL +L00T SNIPER +L00tacr1s L00termember L0G0 +L0GI +L0L L0L0LL0L00LL +L0RA L0RD L0RDGAINS +L0ST S0UL +L0Z L0kur L0rd +L0rd Arthur L0rdMullett L0st +L0vecraft L0ves2Splooj L109 L115Squirtle +L1L GN0ME +L1ghtweaver +L1l bits L1ncoln +L1ndman +L2 L2Crash L2D3 L2Loki L2love L2mmutaja +L2sit L2tankbandos L2tob +L337 GodHand +L3RKON +L3V3L L3apOfFa1th L3gg0 L3mur +L3prec0n L3sius L3tMeBreath +L4D2 Enjoyer L4rry L4st +L522 +L64 +L86A2 +L8CP +L8ers L8rT8r +LADFS LAMAFAO LAMARJACKS0N LASBE +LAUFY +LAZY88 +LB9 +LBS M +LBaccer +LC33 +LCpl +LE0 MESSI +LE54SON LEARNINGTHIS +LEGOSl LEN0 LENZ +LET ME COOK +LEWlS +LF Content +LFG 69 LFGrills +LG 8anter LGBTQLMNOP +LIL BLESSED +LIL PRINCE +LILKHALAMARI +LIMEWORLD LIQUICITY LITEIT LITHITS +LJ 8 +LJAP +LJPalmer LKIT +LL Trigger +LLAMA_ears93 +LLIRSHSLSIEi +LLY Duramax LLeffe +LLich LLol +LMAO LOL KEK +LMAOImDying +LMFAO IRL LMFAOIRL LMNOP3 +LMParrot +LN41 +LOADED_AR +LOGAN109 +LOGlC LOH3 +LOL BMW +LOL MAD LOOP1456 LOOSECOOCHlE LOSTinGAMES +LOUlSlANA +LOWIQXD LP Smokie +LRG LRGD LRichyy +LS1 +LS3D +LSD-25 LSDDS LSDe +LSDeezNutttz LSDonny +LSDreamy LSxD LTUD3stroy3r +LTapperz +LUBRICAT0R +LUKA DONClC +LUR3ME +LURED BY ROT +LUTlN +LVN iZN0 +LWE +LXFY +LYAM femboy +La Benezra +La Bollita +La Brebis +La Croix +La Fiesta031 +La Frontera +La Moo +La Porta +La Sopa +La7itude LaCroisssant LaFroob LaLegende LaMachCaron +LaMarii LaPichula +LaRS 07 LaYdlN Laaban +Laadvermogen Laady +Laagvliet +Laaki +Labas Utak LabbatBlue LabbattSplat Labbis +Labor +Labstev +Labundo +LacDeaths +LacedKoolAid Lacedonian Laceinyoface Lachosocko @@ -12940,82 +27118,160 @@ LachysMain LacinorI Lack Lacking +Lacking IQ Lacoline Lacraio Lacrymosa Lactosite +LacusClyne +Lad J Ladbrook Laderstall Ladme +Ladrian Lads Lady +Lady Calais1 +Lady Chompa +Lady Devil +Lady Porky +Lady Storms +Lady2022 +LadyBuck +LadyGagaTbh LadyJan +LadyJan BTW LadyJessicaL LadyMidn1ght LadyOfCyprus LadyOfMagick LadyPidge +LadySaurus94 LadyStarlite LadyStorm83 +LadyVamperic Ladydoll Laedzter Laeretes +Laethia2 +Laetor +Laf +Laffam Laffy +Laffy Taffy +Lafondaa Laftonn +Lag out LagArtist +LageLanden Lagerhaus +Lageri Lagg Laggbeer +Laggro +Laggy Brain +Laggy Clicks Laglet +Lago +Lagodzacy +Lagomancer Lagoon Lagrange Lagscape +Lagu +Lagwagon +Lah Di Dah Laharl +Lahn +Laid +Laid bare +Laika Wolf LaiskaJake Laiskiainen4 Laitoin Laitti +Laitue +Lajer +Lajfi Lake +Lake Show +Lake Valor LakeMonYew Laker Lakeshire +Lakka +Lakonic Laksa Laku +Lakuni +Lakupiippu Lala +Lalochazia Lalundi +Lam lul +Lam_12 Lama Lamanent Lamb1237 Lambretta62 Lambs +Lambzilla Lame +LamePuttE10 Lamented +Lamfear +Lamh Lamie Lamiia Lammen Lammy +Lamns Lamp +Lamp Burner +Lamp master Lampepit Lampyy +Lampzki +Lamshnarf +LanJiaoDuaKi Lana +LanaLuna +LanceDaPantz +Lanco Lancy Land Lander Landeskoging Landlord Landofzoa +Landucey19 Landyll Laneeta +Lanell Latta +Lanesstee +Lang Chang +LangeNieWies +Langeboy Langecries Langner Langosman Langscape Langtu102 +Langutan +Langzzy +Lanier +LankFrampard LankaKekw Lanky +Lanky Josh +Lankyfeed Lann +Lanny Pan +Lanoue +Lanpzki Lanrico LantadymeRey +Lantern Snow Lanx Lanzlol Laogai @@ -13025,69 +27281,142 @@ Lap0tai Lape Laphloredos Lapsa +Lapsapp Lapsivesi Lapua +Lapuz +Lapy +LaquishaBaby +Lar0i +Laraelias Larcadum Larcenex +Lard Jaysus Lare Lare240 +Larecia Larecio +Larenz Tate Large +Large Chompy +Large D +Large Npc +Large Tasty +Large Toads LargeExpLamp +LargeGrandma +LargeZock +Largelad2 +Largeprune Larie Larissa +LarksTongues Larkypoo Larotux +Larrey Larry +Larry 0G +LarryB LarryIsHere Larrys +Larrys btw Larryz +Lars Ohly +Larser +Larsinosrs Larsjns Larso +Lartsi +Laruka +LasBoi Lascooby +LaserLad LaserSchlong +Laserati +Lashi Lasjstts Laska +Laska Siara +Laski +Laskii +Lasne LasoliLeiffi +LassT Last +Last Bison +Last King +Last N1te +Last Stages +Last Texan LastAttempt +LastBoss LastHours +LastMark LastRecalI LastTexan +LastWookiee Lastinis +Lastkaiii Latanoprost Late +LateKnight LateOwl Lateksiuljas +Latelaturi Latero123 Latias Latins Latissimus Latitude Latompachy +Laturaivo +Lau396 LauQT +Laudine +Laufeyson965 +LaughTale XD Laughed Laukage Laukie +Laukki1 Laundry +Laundry Room LaupieLaupie Laur +Laur ex core Laura +Laura J +Laura uwu +Laurat J Lauren +Laurentina Lautanen Lauw LauweEgberts +Lav x Lava +Lava Buster +Lava rune +LavaFountain LavaKitty Lavadude42 Lavafrost +Lavagirl420 Lavak +Lavak Bob +Lavanis Lavasat LavendrGoons +Lavigne +Lavina26 +Lavios Laviuthen Lavvv LawOfBirds +Lawesy Lawfox +Lawhrer +LawlAsYouDie Lawlie Lawliet Lawlimon @@ -13096,96 +27425,221 @@ Lawn LawnChair48 Lawncat Lawnmower73 +Lawre nce +Lawsilk Lawson +Lawyer JD +LawyerGirl24 +Lax_315 Laxar Laxx +Lay +LayDead Layden +Laydwnandr0t Laydyn Layes +Layes X +Layezy Laylaa +Lays Layzi +Laz GIM +Laz Lo Mein Laz2510 +Laza Ferro Lazer +LazerVizion LazerWork +Lazgo Laziest Lazikiel LazloDaLlama Lazy +Lazy Fare +Lazy Jazz +Lazy Lump +Lazy Matt +Lazy Mauler +Lazy Moo +Lazy Shell +Lazy Soul +Lazy Stona +Lazy XP LazyB0y +LazyBoneZone LazyDevon LazyFarmer LazyOwner +LazyThom LazyTurtleRS +Lazydrink Lazyie_KiD +Lazysmokes LazyyySloth Lblacc Lbuzz +Lck Lda237 Lder +Lds +Le Bibu +Le Biff +Le Big Sad +Le Catptain +Le Doda +Le Foole +Le Gio +Le H0NK +Le Jeffeh +Le Peanut +Le Petit BH +Le Pogo +Le Reject +Le Stu +Le Sus +Le Tom +Le von +Le4f +LeAUD +LeB0ng James LeBlownGames +LeBoobie LeBrianJames +LeDamos LeDerpski +LeIronJims LeKinguin LeLuuk +LeMontBlanc LeMoonMan LeMoopey +LeNnEeX +LePeach +LeVarrock Lead Leader9922 Leaderless Leadfeathers Leadley +Leads Dead Leaf +Leaf 7 +Leaf Parker +Leaf998 LeafStoneDab +Leafffy +Leafpool Leafyshade +Leaga +League Brain +Leak +LeakedDMs Leaks Lean +LeanFavaBean Leanlce44 LearnCatMeow +Learningosrs Learti Lease0fLife +Leather Top LeatherJan +LeatherRat Leatherr +Leaux Key Leavepigrun +Leb Crotch +Leb Man +Lebanesee +Lebennn +Lebor +Lebowski2033 Lebrons +Lebusoft Lebzima LeccaJr Leckie12 +Leclerc 2024 Lectaminol +Lectosh +Lectrix +Led2000 +LedXia Ledgendairy Ledning +Ledu +Lee +Lee Ratt +LeeFelix LeeGavGav LeeHen +LeeV_V Leecherboy Leechy2399 +LeedsUnited Leegotalo +Leeleebug210 +Leemer +Leemi +Leemo Leer0y Leerjet Leeroyy +Leers OSRS +Leeshore LeesusChrist Leet +Leet Blues +Leet Bug Leetroopa Leeuwarden Leevar Leezy Lefonzeee +Left gf 4 Xp +Left inPeace +LeftFistt +LeftHerFor07 +LeftHerForRs +LeftTwix Leftist +Leftwich22 Lefty +Lefty-x +LeftyWarrior Leftytexan +Leg Day 99 Legacy-Blade LegacyOfErik +LegalCounsel Legalism Legally +Legally Dumb +LegallyAfk +Lege Legend +Legend 0007 LegendHarold +LegendKingz LegendOfShao LegendSuz LegendaryDTM LegendaryFoe +LegendaryLVP +LegendaryMe LegendaryRS LegendarySte +LegendaryZ0 +Legenddiary Legenddreams Legendelek +Legendish +Legends Epic +Legendsam7 Legened248 +Legggggooooo +Leggo Leggy Leggz Legikt @@ -13194,17 +27648,29 @@ Legion2410 Legionals Legislatore Legit +Legit Ape +LegitGarbage LegitSamuel +Legitidrown +Lego Brick Lego Fisher +Lego Mania X LegoMyBob +Legolas +Legolas I Legolic Legoliker999 Legosas11 +Legosaur +Leguminati +Leha +Lehawek Lehmo Lehnert Lehoir Leid Leider +Leidmavo Leif LeifBestLord Leiff @@ -13212,16 +27678,27 @@ Leigh Leilin Leimstiift Leion +Leist1nas +Leistus Leito +Leitwolf +Leivermorale Leivonnaiset Leiza +Lejj BTW Lejoon +Leka xo +Lekira97 +LekkerMoeder Lekkeri +Lekray Leky LelSickMeme Lelalt Lelbow Leldorin +Leldra +Lell Lelott LelouchV Lelysia @@ -13229,113 +27706,230 @@ Lemako Lemillion141 Lemiy Lemke +Lemmelleni Lemmy LemmyThePerv Lemon Lemon5000123 +Lemon9000000 +LemonTart +Lemonade max +Lemonguin Lemonmooffin +Lemonowo Lemonrider +Lemony Earl Lemonz +Lemosgomes Lemphys Lempsu Lemurcow Lemuria88 Lemursnore +Lendtable +Lenegis +Lenel Devel Lengzz +Leni Epiza Lenience3 +Lenify Lenlami +Lenn375 Lennie Lenning +Lenny Euler LennyPro +Lenovel Lenreys +Lenses Lentiano1 Lentil LenvisCZE +Lenya +Leo Gagner +Leo Messi +Leo The Fish +Leo12_1993 Leo2 Leo96 Leodero Leoma Leonatwo +Leonbozz LeonidasIV +LeonidasThor LeonidasXCIV Leonl Leontje Leorio Lepek38 Lepkilla +Lepra Leprechau Leprincias Lerch +Lernaean +LeroyDanknz Leroyvdk +Lerzbot LesGetIt +Lesbean Lesen Leshrac +LesleySnipez Less LessIsMore Lessar +Lesser Gods +Lessking +Lestamsakul Lestersaurus +Lesva +Let Us Dream LetBrettBang +LetJimCook Lethal +Lethal Blade +LethalClick LethalMortal Lethally +Letharil +Lethwei +Letits now LetrahL Lets +Lets Blaze +Lets Boss +Lets Glide +Lets Plop OK +Lets Ride +Lets See +Lets Toke +Lets Ziggy +Lets pep LetsGetSmity +LetsGetem LetsRouQ +LetsgoDaddy Letsjjj +Letspoint Lettersloth LettuceLegs +LettuceLime +LettuceNut Lettuce_8 Lettulainen +Leturpentinr Letz +Leukaremmi +LeukemiaLord +Levante Frog Level +Level Denel +Levelations +Leveldegree Levi +Levi Squad +Levi lmao Leviate +Levibeelevi +Leviud +Levven +Levyiah +Lew Sanus +Lew sid +Lewd Queen LewdTouchMe Lewdberrypie Lewey Lewf Lewfu +Lewhh Lewir Lewis LewisMcLaren +Lewis_y Lewism22 Lewison +Lewiss Lewithetui +Lewu +Lewwiis LexaSteel +Lexatrax Lexay Lexdegekte Lexer +Lexington73 Lexkai Lexxi Lexy +Leyon +Leyr +Leysim Leyton Leyzen +Lezio7 Lezley +Lfts +Lhk +Li am +LiI Tii Lii +LiT Clutch +LiT on Dabs LiTxEXODlAx +Liam McPoyl +Liam1994 Liam6780 +LiamLate +LiamLollypop +Liamas Liberate Liberaxa Liberty +Liberty Cap Libolik +Libracorn Librain +LichKiing +LichOneeChan +Licharus Licht +LichterLo +Lichtert +Licitness Lick +Lick My Wyrm +Lick Time LickAhrim +LickMcFicks Lickeris Lickilicky Lickumss +LidawgMcChad +Lidless +Lidlman +Lie Lie4it +Liebzer +Lies of S +Liet Katsu Lietuviskass Lietuvisz4 +Lieve +Lieven Lif3 Lifal Life +Life Road +LifeInsAgent LifeIsBaked LifeIsRockie Lifeform Lifehunt +Lifeisnow13 Lifeliners +Lifelulz Lifes Lifes12Rules Lifesteal @@ -13343,247 +27937,528 @@ Lifestyle Lifewaste Lifezajoke Lifticus +Lifting Fe +Liggen Lighning Light +Light Ace +Light Shell +Light age II +Light1324 LightAura LightB0ne LightRigger +Lightarrg255 Lightaxo Lighten LightenUp Lighter Lighterfalz +Lightfister Lightg0d Lighthead45 +Lightlord440 +Lightning MF +Lightningess Lightqt Lightstoria +Lightww +Ligma Balzaq +LigmaMufin +Ligt Lihatanko Lihtnenolife Lihtsurelik Lihu +Liiga-Ari +LiikeAGlove +Liite Liitokissa +Lijk +Lik me knie LikMijnRaid +Lika C Likalatopus Likark Like +Like Saint LikeABrother LikeAGloveee LikeCinnamon LikeToetally Liketocombat +Liko Likrot +Lil Callisto +Lil Dickyy +Lil Donut +Lil Dor +Lil Doss +Lil Eso +Lil Fika +Lil Garbo +Lil Hogg +Lil Knight +Lil Koda +Lil Lilyxo +Lil Lotus +Lil MCM4A1 +Lil Moe +Lil NllLO22 +Lil Rat Mann +Lil Roli +Lil Shay +Lil Stoma +Lil Tragic +Lil Weeezy +Lil Woowoo +Lil Yeeter +Lil Yungen +Lil Zeusy +Lil Ziik +Lil Zimp +Lil parasite LilBirb +LilBlueMew LilBobbi +LilFaulk LilGBigThing +LilGay btw LilHorny LilMissSlays +LilNubbins +LilPeepBoi +LilSeany LilSquidgy +LilSteamBoat LilStonks +LilSuzieVert LilUziBlyat LilWitness +LilYungChitn Lila +Lilac Devil Lilangrydude Lilbobsters Lilchris Lildirt +Lildischarge Liles Lilhotshotv2 Lilinss Lilith +Lillia n Lillie Lillyz +Lilmoose99 Lilnenedemon Lilqtforeva +Lilsauce72 +Lilsutts Lily +Lily Mayy Lilyalatea +LilyaxD +Lilypily0 +Lilysaurs +Limb +Lime Light +Lime Season Limed +Limeguy21 +Limewire +Limit Form +Limited Room +Limiwinkz +Limiy Limmert +Limmy Limo Limp +Limp Lime +Limpsioo Limpwurt +Limyt Linalool +LincolnButt +Lind say Linda +Linda Lou Lindegaard +Lindewyn Lindsey Linearr LinedFury +Linelis99 Linen +Linen Cry Linerz +Linewa Liney Lingwood Linh +Linhson Link +Link Adam +Link Click +Link Noises +Link Tribute LinkKing +LinkTheIron LinkedList Linkedln +Linkin Boy33 +Linkinaz Linkmoon Linkseratten +Linkv21 Linnara Linsey Linsunt +Lintell-lad5 LinusEkedahl +Linxis Lion +Lion Gin Lion-021 Lion0fZion Lionbatdog Lioncheart +Lionclaw Lionelliee +Lionheart +Lionheart Xl Lionhrt +Lionyxia +Lioui +LipShits +Lipalow Lipperr +Lippy Jimmy +Liqu +Liquar Liquicity46 Liquid +Liquid Apple +Liquid CSGO +Liquid GZA +Liquid Mire +Liquid Nexus +Liquid Reign Liquid8 LiquidRmt LiquidTheory LiquidTrails Liquidat0r +Liquidated +LiquidatorRS +Liquidwood0 Liquified Liquir LiquorGrain +Liqwid +Liridon Lirrix Lishenna Lisica +Lisle Liso +Lissino Lissoms Listeffect Listen2me ListenMorty +Listerine_TC Listics +Listifyy Lisuna +Lit Dawg +Lit Guy +Litas +LiteForge +Litefooted Litelii +Litem Liten +LiteralCow +Literic +Lites Evil +Litey Liteyr +Lithassa Lithuano Littens +LittieTover LittjeterRS Little +Little Allie +Little Flirt +Little Jaxon +Little Knob +Little Sloth Little Snor +Little Ticks +Little Twig +Little meow LittleGhosty +LittleIron +LittleNudger +LittleTrev8 +LittleWillis +LittleZorro Littlebluefe Littleblueju Littlechirru Littlefarms1 +Littlefire01 Littleguyz +Littletickle +Litto +LittyNoCap Litvintroll +Lium Liuo Liutkemenas +Livahpewl +Livand Live +Live Jaked +Live Moose Live2Win Live4thefigh LiveItRight LiveLoveAsap LiveYourLife +Livedasniper Livepan +Liverpewle +Livey +LividPharm Living +Living Life +Living Lust +Livingenemy +Liviu230 Livvii LivyLo Liwyn +Liyum +Lizard Siege +Lizily Lizinginis +Ljb Ljudmila Ljuu Lkarch +Lkky +Lkn Lkoi +Lkue LlHAPIIRAKKA +LlLY ALLEN LlLYUFFIE88 +LlMEWlRE +LlNT LlON +LlSA ANN FAN LlTEWORK LlTT +Ll_lK3 Lla234 Llama +Llama BTW +Llama Pharm LlamaDawg LlamaMcfenis +Lleucu Ann Llirik Lloydyy +Lluuk1 +Llychlynwr Lmao Lmaz +Lmfao Lmlxlk +Lmmortai 07 Lmsjfjksdnhb +Lmurs Lncln +Lng +Lo Kii +Lo My God +Lo cation Lo07er Lo0pyy LoGiiKBah LoPintos LoadMySkeng +Loaded Loadizzle Loadstar LoafOCelery +Loathed Kmd +Loathing +Lobb Dad Lobby +Lobby Two +Lobo Blanco +Lobo Sonora LoboStark1 +Lobot96 +Lobotomizer +Lobstars +Lobster12 Lobstero Local +Local FEmale +Local Idiot LocalHero7 LocalJoint +Locale Lochi LockDownLife +Lockdown v2 Locko Lockpicks Lockski +Locksnap Lockssley +LocoDoritos LocoHamsterz Locococonut +Locothegenie +Locrian +Locton +Loda Lodarion Loders Lodestar Lodestones +Lodgehunter +Lodiedoo +Loebas Loeffen +Loez +LofaBred Lofi Log62 LogHog8 +LogHunterKaz Logan Logan2x Loganator34 +LogansPrayer Logavano LogiTekton +Logia LogicTerror +Logical God Login +Logitech +Logitekton Logless Logos13 LogsKnog Lohan Lohkey Lohruken +Lohwi Loikoi +Loikoi Lee LoisGriffin1 +LokieDokie +Lokolow Lokos +Lokrand Lokur +Lol Ur Dead Lol466 Loladactylll +Loldg Lolek +Loli Cox +Loli Neko +Loli Pantsuu Loli-Remain LoliChan LoliElie LoliStrangla Lolicons +Loligagging Lolipoparty Lolitsleeroy +Lolmanever Lolmc Lolopipop Lolwierdo Lolwuts +LomLy Lombachs Lombardi Lomborghini +Lome +Lomobuu Lompardo Lon3y Lonan +Lonan Arikos Lone +Lone Gym Rat +Lone Lee Axi +Lone Pop +Lone Requiem +Lone runkero LoneCorp LoneMage LoneStarWit Loneful +Lonehenge922 Lonely +Lonely Eevee +Lonely Neo +Lonely Norms +Lonely Ride +Lonely Salty +Lonely Slave +Lonely Table +Lonely Tugs +LonelyLight LonelyOnT0p LonelyRS +LonelyTugs Lonelyy +LonerSushi +LonesomeSoep +Lonewolf117 Long +Long 4skin +Long Demon +Long Ol Dong +LongBoneee +LongRodGod +LongSnapper +Longbow +Longest Head +Longie Longjohnz +Longmeatlog +Longnech +Longo Doggo +Longstaf +Longsword950 +Longview Longwave +LongySlongy Lonjick Lonksu Lonz +Loo p LoofiePoofie +Loofoo +Loogs Loogy Look +Look out bro Looked Lookin Lookingman @@ -13591,62 +28466,166 @@ Lookn4Puzzy Lookout Lookup Loon +Loon Master +Loonetick jr +Looney278 +Loongstickyy Loonlette Loony +Loony RS +LoonyLunar Loonykilla +Loonyluke5 +Looolo LoopGoon +LoopSwoop Loophole336 LoopieFish Loopy586 Loose +LooseAnos LooseLesley +LoosebuLdge +Loosh +Loot Chemist +Loot Party Lootcifer Looted Lootedyou2 +Lootorz Lootrich Lootsharing +Lootsi +Lootski +Loox +Lopmkinjubhy +Loppy Killer +Loquwsea321 Lord +Lord Amonite +Lord Bees Lord Buud +Lord Bv +Lord Caldlow +Lord Conte +Lord Cryer +Lord Cypher +Lord Devil +Lord Doofy +Lord Elfen3 +Lord Extropy +Lord Grefven +Lord Grim +Lord Grofyth +Lord Guam +Lord Hents +Lord Jebbe +Lord Joona2 +Lord Jostyh +Lord Keithus +Lord Kratos +Lord Loss +Lord M0RG0TH +Lord Mantra +Lord Masonic Lord Mjosh +Lord Of Cows +Lord Pengu1n +Lord Pillow +Lord Richie +Lord Runes +Lord Shayne Lord Tarkus +Lord Valzin Lord Vioarr +Lord Vishnu +Lord Xeth +Lord Yaksha +Lord Zq +Lord iFlex +Lord kuro +Lord polak LordArtonius LordAusticus +LordBaphomet LordCoffee2k +LordCorreia +LordDanko +LordDaxel +LordEpic69 +LordFarquxd LordFoxy LordGuthanPK +LordJRazE +LordJuba LordLambo91 LordMullett LordNorden LordOfHarems LordOfOtakus LordPh1L +LordPolgoth LordQuinker +LordScoobert LordShrubber +LordSloppy LordThyas +LordUSA LordYawgmoth LordZahard Lordalbert0 +Lorddraconal Lorde Lordjoeman +Lordkaes Lordstails Lordtwinky +Lordy Flame +Lordy608 Lordza Loredon +Lorehold Loreland Lorencia Lorenz +Lorenzoh sr +Lorenzokazoo +Lorithean Lorkaa Lorki +LorqueldIM +Lorre46 +Lorry +LortJob Lorttomies99 +Lorvikatari Lorwic LosAngeles Lose Losel +LoserKid Loshambo +LosinAllHope Losing +Losing Fat +LosingXP +Loss1525 Lost +Lost Angel +Lost Baby +Lost Baggage +Lost Logan +Lost My Baby +Lost My Sock +Lost Oliver +Lost On You +Lost Roomba +Lost Snail Lost Tadpole +Lost Weed +LostBank +LostBankKey +LostChad LostCloss LostDude28 LostHalls @@ -13655,258 +28634,608 @@ LostMoon LostOcean LostOnThePCT LostSauce +LostStatus LostVorki +LostandFound +Lostdog03 LostlSoul +Lostpetrock Lostplzhelp Lostrelic93 +Losty99 +Lot of Beans Lothariou +Lotilyx Lotion +Lots of Eggs +Lotsa Regret +LotsaOSRS LottaPotAgo Lotto Lotu15 +Lotus bless LotusKid +Lotwik +Lou Le Dur +Lou Red Wood +Lou Sputho1e +Lou Surr +LouBug LouSass +Louezzi Loug Lougle LouiVui +Louie Bags LouieMurphy Louiec3 +Louiseyy Lounckie +Loupak +Lousy Drunk +LouvicDank +Louzy +Lov ed Love +Love Caley +Love M Poker +Love Roman +Love Taylor +Love Yourz +Love me +LoveMyAnzaa LoveThat +Loveable Lovelili Lovely +Lovely Clawz +Lovely Cola Lovepoot +Loverboy Loveskillin Lovexdragon Lovey +Loveyan823 +Low Alched +Low Chief +LowBaller LowKeyPickle +LowLifes +LowPines LowPower +LowRunEnergy +Lowanse Lowballz Lowercase Lowery +Lowfield19 LowkeyBallin Lowlander +Lowlife121 Lowlux Lowner +Lowrey73 +Lowstar1 Lowtempterps Lowy +LoxoJ +Loxtos +Loyal2pvm Loyalcaptain +Loyd Nichols Loze +Lpfan Lrauq +Lrian +Lric Eotter Lron Lronic +Ls4 LsummerC +Lsv +Lt Burgers +Lt Golpar II +Lt Mantas +Lt Purekarys +Lt-Iron-Lt6 LtCastiel LtJan +LtRemigijus Lt_Torch +Ltfreggin +Ltk Iron Ltman42 Ltmf +Ltufighterlt +Lu Diabla +Lu be +Lu0nto +LuauKing +Lub Lub3edUp +Lubbz Lubed LubinLen +Lubos +Lubosek Luc4rio LucSynthesis Luca +Lucaemar +LucarioLVL X Lucas +Lucas Solo +Lucas1192 Lucasmelo11 Luccaa Lucho Luchtloper +Luciah Lucid +Lucid Cynic +Lucid Dream +Lucid Josh +Lucid Meme +Lucid Truths +LucidFeels +LucidPie Lucidcr Lucidfever LucidityX Lucie SkyDia +Lucifenrir +Lucifer v2 Lucifer06 +Lucifer4Real Lucifer_link Luciifer Lucil +Lucina +Lucipur Lucivert Luck +Luck Lost +Luck Voltla LuckRunsAlt +Luckario Luckd0ut Luckeh Lucker LuckieStein Luckless Luckpvm +Luckscaper Lucky +Lucky Arian +Lucky B Boss +Lucky Bambam +Lucky Baws +Lucky Cash +Lucky Chance +Lucky Clover +Lucky Clown +Lucky Duck +Lucky Greg +Lucky Kev +Lucky Link +Lucky Lombax Lucky Lukey +Lucky Mofo +Lucky Rabbit +Lucky Sofa +Lucky Yoru +Lucky skrue +Lucky when LuckyAce LuckyDog +LuckyDog x LuckyEmerald LuckyKroketa LuckyLackey LuckyMatch +LuckyNrSeven +LuckyVic Luckybamboo1 Luckynuts Luckyxx +LucoUK Lucyfer22 +Luczzs +Lud a +Luda-Dan Luderan Ludo +Ludo Sand3rs Ludomo +Lueis +Lueke LuffyAce LuffyDMonkey Lufidius Lufue +Lug RS +Lugal Ki En +LugiaWaifu Luglys Lugs Luider Luigihbk Luis +Luis Anico +Luis Antonio +Luis Dk LuisFarm +Luis_Vzla Luka LukaBrasi Lukas +LukasFlux Lukaz Luke +Luke S Luke3 LukeOS Lukeee LukefonFabre +Lukeh Lukeicth +Lukesfish LuketheDM Lukey294 +LukeyUK Lukezz LukiOne Lukiekuipie +Lukio W Lukiss Lukoo911 Lukse Lukytisz +Lulani 13 +Lulla Lullie +LuluTheCat +Luluca Lulucifer +Lumber Yak LumberStevo +Lumbidge Lumbo Lumby +LumbyCalled LumbyCastle +Lumbys Waits Lumiaris Lumifrost Lumimies +Luminatti +Lumm1475 Lummer Lumo +LumpiaFan69 +Lumpqua +LumpsMcgooey Luna +Luna Koneko +Luna Lucero +Luna Mexi Lunacy +Lunaec +Lunamarie Lunar +Lunar Haze +Lunar Lotus +Lunar Tear +Lunar Tones +Lunar Ursa LunarDemon99 LunarEquinox LunarSC2 +LunarTheCat LunarTigerr +Lunarann +Lunarcrow614 Lunardini Lunarism +Lunarmoat638 Lunate Lunati Lunaticmo1 Lunatric +Lunaverse Lunch +LunchboxLLC Lunchtime007 Lund LundXCV Lundh Luneasa LuneyTunez +LungTied +Luni +Luniaxis Lunie +Lunier Lunis Lunizzzz +Lunytic +Luonteri Luonto Lupah Luper +Lupi +Lupo +Lupuloid Lure2G +Lurjus LurkinTurd +Luru +Lush Vibes Lusitropy Luskidoo +Lussen1 +Lustwaffle Lusu LusyTheGoat Lutha Luthien +Luthien T2 Luthors Luthstorm Lutinrouge Luucy Luud Luukie +Luuloterve Luuseri +Luuuseri Luuuuna Luuwana +Luv2spuj +Luver Focker Luvholic Luviii +Luwucy +Lux Please Lux211 Lux7thSaga Luxatio +Luxeo +Luxilie Luxire +Luxoul +Luxumine Luxury +Luya Luzu +Lv ranger +Lv 5 Psyduck +Lv Camo Lv100 +Lv100 Raichu +Lv5 Treecko LvI3stak3 +Lv_1_Mew +Lvcretivs +Lvl 1 +Lvl 3 Nub20 +Lvl 99 Goon Lvl100cheese Lvl30Ditto Lvl99 Lvl99Docking LvlUpUrself +Lvls Up Lvqquvs +Lwy Lx1I +Lxbe +Lxfleur +Lxnes +Lycix +Lycoe Lycstoned Lyct LydeZGrod +Lyderis Lydia +Lydia Kenney Lydmix +Lyfe Tyme Lyin +Lyin Eyes +Lyke625 +Lyks +Lymaks +Lymez +LynX ZrLeX Lyna Lynamet +Lynching Leo Lyngo +Lyni Lynics Lynx +Lynx Jitan +Lynx Tiger +Lynx Titan LynxTitan Lynxes +Lynxy LyraLyraLyra +Lyrad002 Lyrich Lyricidal +Lyrilusc Lyron Lysandra +Lysdexic4986 Lyse +Lysmann +Lyyli PL +Lyzu Lzin +Lzs +M A G 3 +M 1 KE +M 3 3 P +M 4 T R I X +M 8 A 1 +M A D O X +M A D Storm +M A T E J +M A X O U T +M A X PAYNE +M B Z +M E H I S +M E L D O +M E T R O +M E X I +M I K B A R M I T H O X +M I U M I Z Z O U +M INDGAME +M Itty +M K J +M O B I L 3 +M O C 0 +M O C H +M O ID O K +M R NaCl +M Super Buu +M T C +M U D K l P +M X G P +M a V 3 Rick +M a d Shrumn +M a k a i +M age +M aikel +M ak0 +M ammoth +M ana +M ankDemes +M anu +M aples +M arcel +M arquim +M artin +M ask +M athis +M axou +M azzz +M c +M elz +M enzy +M ga +M gh +M i t c h y +M i y a +M ikasa +M ike +M imik +M int +M itchel +M ittens +M l Q Q +M merz poL9 +M o C e +M o r t e n +M oist +M onkfish +M orytania +M the Maxed +M ugger +M ulas +M urphy 2 +M usashi +M-theory +M00CHIE +M00SE M00SE +M01 M0IST +M0IST B0X M0LE M0NALISA +M0NEYM1TCH3 +M0NST3R1PP3R M0ON M0ONCAKES +M0R TEN M0RG0TH M0RGE +M0SS3Y +M0ST D0PE +M0hannad M0ist +M0ist Midget +M0ney Swag +M0nk3y DLufy +M0nke y +M0nkey15 M0nsterTuk +M0obs +M0resheth M0rningstr +M0use M11CK +M11ka M1911A1 +M1GU M1NG3_GOO +M1TTLE +M1ch M1gos M1ke50 +M1ksu +M1nd Master +M1ntberry C +M1ss Di0r +M1stak3nly +M26 +M3 L M30W M30z +M3GAD0UCH3 M3L10DA5 M3MEL0RXD +M3MoRY M3X1CO M3XICO +M3ch4nics M3ll3 M3lllll M3lviin +M3ntal M3rking +M3ry +M3t4l1n1S M40A M4D399 +M4g3 +M4g4z M4gnus666 M4k3d0 M4tte M4xt0R +M5 +M52 M523 +M6 Mill +M6N +M7J +M7TT M855A1 M8NoFreebies M8TT +MA N E MAAAAK +MAAGES MACKOGNEUR MADLADz MADRNGJACK +MAESTR0FRESH MAFKINCHAMP MAGA MAGA1292011 @@ -13919,140 +29248,275 @@ MANT4S MARCOPOLO MARLB0RO MARMlTAO +MAS0N MASKED +MASSGAINER32 +MASTER DREAM +MASTER J0KER MATUTVlTTUUN MAURERA79 MAUROPICOTTO MAX3D MAXABILITY +MAXED PINOY +MAXXEDDADDY +MAX_HER0X +MAXlME MAlNEVENT MAlNTENANCE MC Cheep +MC catheter MCBURNOUT MCHammered MCKenny91 +MCSIZZLE +MCmattt +MD7 +MD96 +MDE Presents MDPS +MDaher +MDucks +MEAT G0D +MEAT HEAD36 MEGATRON MEK4KK4KK4KK MELAN00MA MELT METM0NKEY +METR0lD +METS0 +MEV4NS MF DC +MF MORTEN +MF SofaKing +MF TEE +MFE +MFKU +MFLI MFSTEVE +MG5 MGFS +MGKRevs +MGM WAY +MGreengrass +MH Therapist +MHA C +MHBC +MHS MIDAs MILFmauler MILSHAKE MINH +MISOGl MISSYGAMlNG MIST MITCHY MIX0R MJ23 +MJKaboose2 +MJT MK-HARDSTYLE +MK6R MKAM +MLB +MLTEEZY +MLaidman +MMMahogany +MMORPGenius MMTera MMitchh +MMtheMachine +MOABDADDY MOMHUNTER666 MON5TER +MOODSWlNGS MOOSEM3AT MOTM MP17 +MP9 +MPB +MR JOHN WlCK +MR LETHAL2u +MR REET +MR TMR MR UDZ +MR556A1 MRButter +MRC Tugboat MRGAMEZ MRHD +MRLARGE +MRM MRPlZZAPRIZE MRWHlTE MR_MAITO +MSPaint64 +MSceneFF MSpacePotato MStarkz MTBMB +MTPETE +MTV CRlBS +MTXP MUISSS911 MULHERndaEXP MUMM0 MURMELl MUSK MUSK0KA +MUSUMUMM MUZAMMIL +MV Flow +MV Welshy MVIII +MVKirby +MVP HERO +MVP Mahomes MVRDA MVTIASD MWwarzone +MY L U N A +MYRSKYVIITTA MYTHICROYXLE M_Sariol +Ma Band +Ma Dood +Ma Titi +Ma t t h e w +Ma5onx Ma7e +MaCoMb MaHeelsHurt MaJeShTic MaQtPie MaX662 +MaXam00se +MaXeDSani +MaXx0wnage Maacc +Maake88 Maanman Maasegyr +Maax2 +Maaxiking Maazako +Mabbbs Mabbs Mabel MableLake Maboe +Mac Diver +Mac Donald +Mac Garfield +MacDuff MacFredrik +MacFredrique MacMillers MacMoblins +MacRae MacSandwich MacTheRipper Maca +MacaFazoL Macabre +Macaron +Macaroni 73 Macaronni5 MaccaM +Maccaroni Macduffe +Mace Windu Macedo +Mach Sigma +MachV2 Machado Machfredy +Machine Girl +MachoTimo Machtig +Machtige Machtigeman Machtigemeid Machto Mack +Mack x +MackD +Mackadactyl Mackadee Mackdizzle99 Mackeo +Mackerel Sky +Mackerels MackhNL +MacksEntropy MacksIsland +Maclairin123 Maclas Macoinho Macre demia MacreedyLove Macromage1 +Mad Dog Eris +Mad Flavour +Mad Krampus +Mad Max 20 +Mad Papper +Mad Suss +Mad Vlad +Mad Watson +Mad X +Mad arrows12 +Mad as heck MadBoy20 +MadBruh +MadCanadian +MadChemist8 MadD0g11 +MadD0gL4d69 MadDogged +MadJeffs MadKingMikey MadMaxMan +MadPubes MadSnowman23 MadaRook Madaddam Madam305 +Madarah +Madd177 MaddAntelope +MaddMann5 Maddape Madddiieee Madden +Maddie Baddy Maddogjr Maddux Maddy +Made of Sand Made4Slaying MadeInAfrica +MadeYouClick MadeinTYO Madeiraa +Madeleine Madeweine Madhu Madlaina Madmaxie +Madness Max +Madodee Madona +Madra Madrock01 MadsG Madsenn Madsermad +Madshatter71 Madskillz756 Madslasher30 +Madslax2 Madsosaur Madula Madvantage @@ -14062,48 +29526,83 @@ Madysen Madza Madzilla Maeda +Maegera Maela MaerlinTaz Maertynas Maester +Maester Trea Maestro +Maestro Heil +Maestro1 +Mafia-BP MafiaMan MafiosiDad Mafooma Mag1c Mag1cJohnny Mag1c_W33d +Maga Kahn Magalator +Magani C Magawie Magc Mage +Mage Eh +Mage Kume +Mage N Skil Mage7master7 MageFish MageHax +MagePriece +Mager Magerold Magers Mages350 +Magestus +Magethirst Magezi +Magggorical +Magginator Maggot MagiTurtle Magic +Magic Bonus +Magic Clicks +Magic Mackee +Magic Moose +Magic Rino +Magic Tree +Magic Wand +Magic fTail MagicAppel +MagicMemorys MagicPanda91 MagicSchoBus MagicSilver Magic_Elmo1 Magical MagicalRuby +Magican Magicarp Magicbox Magicdefence Magicken +Magicx +Magija +Magik Magik773 Magikilo Magiok +Magisteerial +Maglet Magma +Magnaboy +Magnati Magnautism Magnesium +Magnesium J +MagnesiumIV Magneticism Magnetite Magni99 @@ -14111,99 +29610,217 @@ Magnifice Magnilo Magnis Magnu +Magnus Gram Magnusungam +Mago +Magsd1 Maguneru +Maguro Magus +MagusChum Magyk +Magzem +Mah Jae +Mah Jong MahSeed Mahalusa Mahanimal +Mahe Mahkelroy +Mahler +Mahmoud Mahogany +Mahomes +Mahonoken Mahtitykki Mahzius +Mahzka +Mai kel +Maiba +Maide +Maidenheir +Maignansdead +Maikel J Maikhol +MailTime Mailor Main +Main 3s +Main Cactus +Main Ginger +Main J0urney +Main Mynt +Main Natey +Main Pando +Main Path +Main Product +Main Twiddle +Main Wario MainCharlie +MainCoach +MainCringe +MainDong MainForever +MainHamendex MainHolm +MainJ +MainMan_Mads MainNudley MainPurp MainSt MainStand +MainStreamx MainWabbit Maind Maindeer +MaineDeno +Mainline Mainly +Mainly Pure +Mainly Ricky Mains +Mainslet +Mainuru Mainz +Mainz Gainz3 Mair +Mair017 Maisa +Maisa Torppa Maisteri +MaitoRimpula +Maix +Maizon Maizpilao01 +MajMischief MajQ Majakanvahti Majcew +Majcew 2 Majehjk +Majer4 +Majin Buuwu +Majinbooo Majinjon +Majki Majokko Majooty Major +Major Damien +Major Gains +MajorEar MajorMammoth +MajorObesity MajorOwnz MajorSnizz +Majora MajoraMasked +Majoras WRLD Majzako7 +Maka +Maka2201 +Makagago Makaule Makaveli +Makaveli I +Makaveli II +Makaveli l Make +Make A Pile Make Carrion MakeItStack +MakeMeKing MakeNotes Makeboy +Makeeesful Maken +Maken Gainz MakenMakkara +Makenna +Makhachev +Maki Makilake +MakinExcuses Makiverem Makk Makke Makkeii +Makker Benja +Makker Trane Makkiavelli +Mako Nox +Makoea +Maks Cape +Maksimit +Maksoinvelat Maksuamet +Maksui Maksukka +Makzd MalaLechita +Malachai Maladec Maladiec +Malagah Malbec Malboulgea +Malcz Malding Male Maleurous Malfoy +Malhavic +Mali RS MalibuMan96 Malibuux +Malignant00 Malik Malinerix +Malitiae +Malkav Mall +Mall Gang +MallbuRo Malleus Malli +Mallieero69 Malloc +Mallomar Malmi Malmot +Malomalo +Maloo Malqy Malse Malt +Maltapkiller Malteadita +Malter +Malterz Malucoftw +Malurian283 Malvian +Malvidus Malvoliuus Malware Mamachii +Mamas Meat Mamba +Mambaaaaaa MambasWRLD +Mambo No5 +Mamboita Mammad +Mamoul Mamupatja +Man Asian +Man Bag +Man Killa77 +Man Kip +Man Raccoon +Man Throater +Man gos +Man-Yak ManBearPiggy ManGoBzzzt ManOfGold @@ -14213,20 +29830,30 @@ ManWT ManWoox Manaburna Manafont +Manakiel ManakuraJP Manantti123 Manantti321 Manardog Manawatu +Manby +Mancandy +Manchest Mancino Mancunion92 Mand1ng0 Mandelbrot +ManderSala +MandoCheese Mandor1 Mandred Mandulorian Mane Manegaming +Maneirinho +Manelzera +Maneter56 +Manevolent Manfa MangJoe MangeMeister @@ -14234,95 +29861,179 @@ Mangle Mangled Manglican Mango +Mango Monkey +Mango Rat +Mango Season Mango10 Mango1997 +Mangobnana +Mangostangos Manhoos Manhunt +ManiacBison +ManicNode ManicRS Maniek Manila Maniwani +Manju Manke Mankitten +Manlanter Manletti Manly Mannekeuh +Manni Penny Mannie +Mannix270 Mannjpip +Mannnekala Mannowrath +Manny21 +Manokin Manor +Manorvic MansaMusa Manser2300 Mansup5 +Manswarm Manswers Manta +Manta Wray2 +Mantarayo MantasA +Mantaslocoo Mantelio8 +ManteliseXe +Manthe MantorokDIA +Mantoshka Manttt Manuelh3 Manumatti +Manx_Scaper Many +Many Walrus Manyi Manyvids ManzGotViewz Manzo +Maose +Maozn +Mapanza1 Maple +Maple Jay +MaplePoutine +MapleRoyals +MapledOaf Maplejuana Mapletech Mapne Mappl1n +Mappzz Mar0e Mar782 Mara +Maracruz Maradonaa Marassa Marathonius Maraud Marblez Marc +Marc NL +Marc Paul +Marc Spac MarcVinicius Marchmello Marchuk +Marcia Ress Marcikarp +Marcilicious +Marcius 7 +Marco225 +Marco7k Marcoli64 +Marcoo Marcooow +Marcos Vibe +Marcski +Marcussius Mardiie Marducas +Mardy O G Mardzz Mare +Marenki +Maret Marg3 Margana Marganer +Margiella Margins +Margodx +Margon +Margonite Xu +Marguana Marheim Mari Marianas +Mariano 1 +Mariasonic +Marib +Marijuano +Marikadere Marines Marinez +Mario Bros +Mario Goatse +MarioKartDD MarioTennis Marioh MarioisKewl +Marioman Marioneta MariosPeach +Maritime +Maritozzo +Marjapuuro +Marjatta +Mark Pledger +Mark Swenson +Mark-777 +Mark-Zuk +Mark0vDeath Mark12387 Mark1ta +Mark5Barki MarkBuns +MarkJongejan Markald87 Marke Markerr +MarkhamON Markie994 Markipedia +Markku Marklyft +MarkoOSRS Markop100 Markovia64 +Marks Main +Marks Phone +Marks lron Markus +Markus Stier +Marky428 Marlbrozo Marleth Marleyy Marlin1993 +Marlon0817 MarlonisGod +Marlooo Marlopped +Marluxia Pwn Marmita99 Marmp Marms @@ -14332,40 +30043,66 @@ Marni Maro202 MaroO Maroj +Marokkaan Marokkaantje +Maroko111 Maroon5 Marpollo MarquiseDmnd MarrCuzz +Marrcy +Marreldil +Marrer Married Marrio +Marrius Marro75 Marsal Marsalkka +Marsel Marsg Marsgl +Marsh Marrow Marsha Marshal Marshall1 +Marshmont Marshyy MarskiLark Marsmash +Marsovec +Marstead +Marswatt Mart0103 Marten +Marten x MarthProMonk Martial +MartianLynch +Martiba +Martiiian +Martijn Martika Martin +Martin 2007 MartinGameTV +Martindeq Martinjsh Martinkyle20 Martins Martinside +Martip MartnShkreli +Marty +Marty150 MartyG MartynMage +Martyr Main +Maruna Marv Marvelli +Marvick +Marvins Dad Marwan Marwin MarxRoux @@ -14373,16 +30110,22 @@ MarxTheMyth Mary j4n3 Maryj Maryland +Marylandd Marzcorw +Marzeo Marzhy +Masaca Masacre599 +Masade Masago Masakado Masandalf Maschok Masconomet +MaseLitt Maserati Mash +MashedOP Mashimarq Mashiwo Masin @@ -14390,31 +30133,57 @@ Masiron Maskedpump Maskin Masochisttwo +Mason OSRS MasonJarr Masonatorr Masonitte +Masons Dong +Masoo MasquedMan +Masryy +Massa +Massacre Fc +MassageMan97 Massif +Massimo130 Massinissa7 Massive +Massive Noob +MassiveJonas Masss Massterduel +Mast3rOogway MastaHeff Master +Master Bakes +Master Bogs +Master Byro +Master Garni +Master Jos +Master M V +Master Riven +Master Skizz +Master Vates MasterB1994 +MasterBarter MasterBlazee MasterDragxn MasterGrogu MasterKiefff +MasterMake +MasterMasa2 MasterNeigh MasterOzzy +MasterPanda MasterPookie MasterRooshi +MasterSlayrr MasterThresh MasterX Masteragota Masterbihno Mastercat +Masterdemon4 Masterflick Masteri MasteriMori @@ -14422,43 +30191,79 @@ Masterkindem Masterrgod Mastervile Mastirida +Masturbeerke +Masurda Masuro Masylvain +Mat 1 +Mat Jacko +Mat Share +Mat thew11 +Matchbox 20 Mate Matematikk +Materium Mateusz1210 Math +Math Is Fun +Math ematic MathVibes Mathcore +Mathematics Matheor Mathers Mathers1996 Mathew Mathias +Mathias Nemo Mathisse Mathmic Matholemeu +Mathrotus Matieus Matix +Matkijanarhi Matmo +Mato Seihei MatoPotato Matoaca Matrak +Matreex Matrix Matrixpachi Matruusi2 Mats +Matsu Matsuri Matsyir Matt +Matt 162 +Matt Bman +Matt CFK +Matt Cat +Matt GIM +Matt K +Matt Lad +Matt Mo +Matt Slay +Matt Smash +Matt V +Matt Wy +Matt1494 MattLeedz MattMattBro +MattNDew MattRanger MattRoux MattSeal7 +MattStyle +MattWallet Mattaclysmic +Mattakuda Mattam66 +Mattaroo Mattec +Matteos1 MatterOfTime Matternot Mattex @@ -14468,29 +30273,52 @@ MatthewDK MatthewRS Matthewwww Matthidan +Matthis +Mattias Mattice +Mattie osrs Mattie43 +Mattj Mattniss Matto +Mattor Mattorel Mattrate Matts +Matts RNG Mattsbro Matttbob Matttt +Mattty +Mattus50 Mattx1 Matty +Matty A Matty Gibbs +Matty Ic3 +Matty467 +MattyChasee +MattyLight91 +MattyNW +Mattych Mattyflight Mattys +Mattyz6 +Matu btw Matuba Mature Matwo +Matygoyo Matz +Maub1 +Maucca Mauddibb Maui MauiBeach +MauiMallard Mauidude +Maukas +Maukka MaukuMaija Maul 0n Top Mauler4500 @@ -14500,80 +30328,234 @@ Mauna Mauno Mauricio555 Maury +Mauvayy +Mauvier +Mavdagin Mavel Maven Maver Maveric Maverrick +MavicAir +Maviie Mawch Maween +Mawie +Mawlocke +Mawn +Mawsen +Max 4 Mikey +Max Ape +Max Attacken +Max BD +Max Blacks +Max Botter +Max Cape +Max Deiron +Max Eff +Max Entropy +Max Gadget +Max Gav +Max Here4Pet +Max House +Max Karma +Max Klett +Max Methi +Max NoLyfer +Max O7 +Max Obi +Max Pkr +Max Puffs +Max Re3oo +Max Relax +Max Scape +Max Snek +Max Temper +Max The Hero +Max Tilting +Max Tonks +Max Vandal +Max Weezy +Max Wotif +Max XP +Max Zack +Max Zeus +Max btw +Max cape 420 +Max hits +Max killz6 +Max plus +Max uwu +MaxAchoo +MaxAnarchy MaxBTW +MaxBadAss +MaxBear MaxHare MaxHaus +MaxHomieJose MaxIgnorance +MaxIrl +MaxKhalifa +MaxMainJake +MaxMuffin MaxNeander MaxSAVAGERY MaxTix MaxTurbo +Maxchar +Maxd Maxe2968 Maxeado Maxed +Maxed Beans +Maxed Code +Maxed Egirl +Maxed Emm +Maxed Eric +Maxed Groot +Maxed Hippo +Maxed II3en +Maxed Jeff +Maxed Kiraly +Maxed Loser +Maxed M0bile +Maxed MVP +Maxed Masak +Maxed Newfie +Maxed POH +Maxed Phells +Maxed Rat +Maxed Rob +Maxed Scaper +Maxed Spyike +Maxed Total +Maxed Twice +Maxed Velho +Maxed Whip +Maxed Zeb +Maxed Zuk +Maxed osrs +Maxed when +MaxedActuary +MaxedBruh +MaxedBurnout +MaxedButPoor +MaxedFrog MaxedIn2074 +MaxedInDa6ix MaxedMain MaxedMax751 MaxedMike +MaxedMobile +MaxedNbored MaxedPleb +MaxedRepel MaxedYP Maxedlegacy +Maxedtoasty +Maxedwell Maxell Maxerder Maxiboy4a9 +Maxidy Maxime5100 Maximonster Maximum +Maximum Tier Maximumist +MaximusAM +Maxinator87 +Maxing zzz +MaxingMyMain +MaxingSucked +Maxingthis Maxiorek1200 +Maxisbaws +Maxisen +Maxitaxi777 Maxiu Maxjuhhhh19 +Maxkenzi Maxmemix Maxnominus Maxo +MaxoBlasto +Maxoou +Maxpappy Maxpro Maxst Maxstalker67 +Maxstatz +MaxtanosXD Maxwall Maxx +Maxx y +Maxxed Alt +Maxxed Dusty +Maxxed Trash MaxxedNoob +Maxzet MayanFuror +Mayate +Maybe CIA +Maybe Idiot +Maybe Jeff +Maybe Shady MaybeMason MaybeMitch Maybemnam Maybez +Maybizzle Maybon Maydole +Mayerdynn +Mayhaps Mayhem +Mayhem Maker MayhemMakers Mayl Maylive +Mayo +MayoForFunds Mayonaz Mayor +Mayor Jiwana Maytona +Mayuri +Mayushii Mayvex Mayweather Maz0n1k Maza Mazala +Mazande Mazariner +Mazauu +Mazda168 +Mazdan +Mazdaspeeed3 +Mazdraith +Maze +Maze of Iron +Mazel613 Mazersyy Mazhar Mazlol +Mazmarazor Mazoni Mazpls Mazta Mazuma +Mazz y Mazzacre +Mbox Mbyoo +Mc Dragans +Mc Florry +Mc Red McAlakazam +McAsssBlast McBurn McCheddabomb McChimkenGOD @@ -14581,204 +30563,402 @@ McChubbin McCllin McConaughey McCreJ +McCringle McCune McDanky42O McDizzle15 McDongles +McDoodle_21 McDouble McFantasy McFizzleDady +McFly93 +McFozzar McGooser +McGorm McGregor60gs McGruff +McHammock McIllu +McIronLeech +McKennon +McKerma +McLOVlN +McLaeNz McLeaNz +McLemore +McLovin707 +McLowry McMeekin McMillan McNeal +McNoob10 McNoodle +McNorm McNugget McP0P0 McQuaker McQueensy McRibs +McRibs Back +McRip +McSchloogan +McShrubbery McSomf +McSplitter McStarley McSuperNoob McTaskupillu McThomzie +McVittties +McWeaksauce +Mcards Mcbelsito Mcberra Mcdally Mcgingerpony +Mcgregorini Mckelvie Mckinconn Mclaren88 +Mcneill Mcpatrice12 Mcpielover Mcturdson Mcwaffle1 +Mcy +Mderg +Mdub Suhh Mdzvwz +Me Acoustic +Me Ca +Me Dead +Me No Brains +Me Woody +Me and Jr +Me lvin +Me phisto Me3lem +MeBigPoppa +MeBlast MeMillionthD MeThudZ +MeTwo MeadowFall Meadows MeagerSkills +Meals Mean +Mean Crusher +Mean Street Meano +MeanrangerFE Meap Mearm +Mears Measles Meat +Meat Rat +Meat Shank +Meats Meatspot MeatyLoaf +Meauner +Mebo Mech +MechantBozzo +Mechaodin Mechelen +Mechvengance +Mecidon Mecone Mectofion +Meddler +Meddlr +Medi Mobile +Medi i +Medi ocre +Medical Herb Medicate Medicides Medicinal +Medicore +Medievh MediocreMatt MediocreRye MediocreTime Medispensary Meditations +Medium Cloo +Medler Medoletics Medon Medorable Medusa228 MedwayDragon MeechIsCute +Meechy Dark Meeeseek Meelays +MeenBeans +MeepBeepMeep +Meeran Meerca +Meerkz Mees126 MeesKees MeetMyMeat +Meetti Meew +Meewerp +Mefaustofele +Mefco Meftah Mefy +Meg 3 Mega +Mega Butt +Mega Farce +Mega Kyle +Mega Mort +Mega konn MegaAmpharos +MegaDoris MegaDrive MegaMustarn MegaNaziHatr Megabyte6 Megadwarf47 Megalo +Megalodont +Megamanyo Megamind +Megamind Jr Megans Megaronii +Megastoffe +Megatortle Megatrax +Megis Megnificent +Megpan Megumin Megustio +Meh Noob +Mehrunes Mehts Mehuelin +Mehuo MeideC94_BB +Meido Mein lron +Meinkul +Meiousei +Meister Sho +Meiyo +Mek +Mekaanik Meklo Meksa +Mel Meow +Mel Yakutia +Mel btw +Melanderr Melanoma +Melayna +Melc0n Melchuzz Melcoor Meldianx +Meldynoir Melee Melee_Range +Meleny Meleven Melhoop +Meliodas420 +Meliodin Melisma Melk +Melkin Melktietje +Melkzuurtje Mellakka Melleruds Melling +Mello Gello +Mello Yello +Mellodynamic +Mellow Jingy +Mellow Tones Mellow6 MellowSoul MellowTokes Melly +Melly YNW Melo +Melodicolt Meloenschijf +Melongrab +Melotoninn Melpan +MeltedCash Meltman Meltok +Meltryllis +Melvin dew +MelvinTheOK MemberBerry Memberlist Meme +Meme Loord +MemeDoge111 +MemeVendor +Memedalorian +Memem3 Memeologist Memeshake +Memmor Memory MemoryCard MemoryWorm +Memphis lol +MemryLoss Mena Menap Menaza Mend +Mendicant Mendieton +Mendokusai +Mendota Meneer +Meneer Nijn +Menesus Menetoeihin +Menial Luck +Mennie Menrey +Mensphysique +Mental Coach Mental4Metal MentalAbacus Mentally Moo Mentalmissy +Mentoes +Menyu Menza +Menzola +Meoooow +Meow Ghost +Meow Im Hawk +Meow Softly MeowKiki Meowed +Meowgi +Meowler Meowmagic +Meowrijuana +Meowskeys Meowtheduck +Meowzer7 +Mepiff +Mepn Mepthadr0ne Mer Train +Merami fan Meramon +Meraxus +Merbz +Merc-Raa +Merc_Lobo +Mercedes F1 +Mercenary V Merces +Merch MerchantUrch +MerciTwingo Merciulago +Mercphobia Mercules +Mercury Owl Mercury15 Mercy +Mercy Osrs +MercyfulFate Mercys +Merderr +Mergician Meric Meridians +Meris Merisalu Merisorax Merkdalat +Merke +Merked Ko Merksick Merlin +Merlin Otter MerlinMonroe MerlinPT Merloc21 Merlucius +Merlvin Merricat Merry MerryTuesday +Mersunperse Merten Mertguy2p0 Mertiin +Mertjaars Mervyn Meryam Meryath Meryl +MesH3aL Mesa +Meshi510 +Meshkot +Mesin +Mesmeriize +Meso cyclone Meson +Messaa Messersmitti +Messi Messias +Messorium Mestari +Mestaristick +Mester B MesterAbekat MestreGlados +Met Promise MetDoobie Meta +Meta Mammoth +Meta l +MetaCTF +MetabolicPro Metadragon Metafisico3 Metagel Metal +Metal Booty +Metal Little +Metal Shad0w MetalGear +MetalMaiden3 +Metalest Metaling +Metallic Neb Metallifog MetallikDeth Metallproz Metalproz +Metamorphic Metamorphs Metamushroom Metaphwoar @@ -14787,45 +30967,89 @@ Metaxia Metbol Meteora Meteoriitti +Meth Teeth +Methal Methodical +Methusal Methylanara Metix Metoprolol Metropolia +Metsavaras Metselaar Metsiq Metters +Metukka +Metzifer Metzz +Meulendijks4 +Meune Meuosh Meur +Meuyou +Mevvz MewIlicious +MewPulse Mewby +Mewli Mewllicious Mewrad Mewtwo MewtwoKing Mewww +Mex Arkantos +Mex_N_Flex Mexicanchild MexiePie +Mexlet123 +Mextex +Meykaa Meymer Mez-qt Mezane Mezecs +Mezenburn Mezeroth Meziriti +Mezmereye +Mezomel Mezy Mezzalarry Mezzito +Mf Fe +Mfey +Mfin Taylor +MfknNewports +MgZz +Mglegolas +Mgn +Mhh +Mi Hoy Minoy +Mi1os +Mi3pelst3in +MiAdidas MiIIenia MiIIers MiQuu MiTDro +Mia Kalphite +Miacon +Miami 305 +Miami Cane +Miami Rebels Miasm +Miawnation +Mibs +Micaso +Miccolo3 MiceMan +Micecream Micella Mich +Mich Bz Mich49 Michael +Michael4655 Michael67676 MichaelB MichaelScarn @@ -14838,103 +31062,203 @@ Michanderma Michano1992 Michaud Michel647 +Michelau +Michelle jnr Michiel +Michiel_96 +Michinaki +Michism Michlenn +Michxa +Michy Man +MickZagger Mickael +MickeyJoe Mickeyr4nge Mickul Mickyy Micosa Micro +Micro Cosmic +Micro Nerd +MicroButt420 MicroShrooms Microdot +Microgolf +Microman0000 Micromelo1 +Microo +Micropyle +Microsoft22 +Mictie +Micxyz +Mid Fade +Mid Pack +MidValley +Midareru Midas224 Midday +Middelkerke Midget +Midget Bones +Midget Rave MidniteGreen Midori +Midori Enju Midside +Midway16 +Midweeks +Midwife Dan Miega +Miegalius Miekka Mier Mierdapier Mies +Mies Lapsi +Mig Wizard Migalos Miggles Mighty +Mighty King +Mighty MP1 +Mighty Milk +Mighty Oak +MightyDolt MightyNemo MightyOrange MightyPieBoy MightySlappe +Mightyfrosty Migjiris Migou +Migrainelife Migraines +MiguelDyson Miguu Migy Migzee +Mihu o_o +Miiammi +Miig +Miiiild +Miika +MiikeyG Miillls Miiori MiissMandii Miitt +Mij +Mijae +Mijniebelle Mijuzo +Mik +MikTheKing +Mika Kuwait +Mika x +MikaDMM Mikael Mikasana MikazuAugus Mike +Mike DIY +Mike Davis +Mike Hochuli +Mike Hunt +Mike Mike +Mike Unit +Mike Xp +Mike-NL Mike1 Mike745638 MikeChang +MikeConleyJR MikeDangr MikeDitka +MikeHasMoobs MikeM MikeMontana MikePunts +Mike_Oxmaul Mikebaker417 Mikeey Mikeje13 MikelCz Mikemyers31 Mikerockshhh +Mikeroscape Mikeroscope +Mikes1995 Mikethafarm Mikey +Mikey B +Mikey Bai +Mikey Hey +Mikey Milk +Mikey x Mikey1plate Mikey47745 +MikeyBigDick MikeyPat Mikeygirl94 Mikeyscape MikeyyG +Mikezalwinnu Mikezilla Mikezzup +Miki4 Mikilly Mikki Mikkon +Mikouich Mikrobangine MiksuBTW Miksuuu MikuNakanoxx +Miky852 Mikzel +Mil Z +Mil ky Mila +Milagre Milaz MildRussia +Mile Stoner +Mileage MilesQPR +Miley Cyrush +Milfcocktail Milfguardian Milhous Mili +Milico +Milieu +Milieudienst +Milimoowolf +Miljoona Milk +Milk Energy +Milk Expert Milk Sausage +Milk my milk MilkDaughter MilkMan227C MilkOhh MilkToast +Milked Cox Milkki Milkless +Milkmate +Milkn Tiddys Milkopia +Milkraze Milky MilkyGalaxy +Mill Reef Mill385 +Millbrook92 Milleks +Miller 40 MillerLatte Millerlite40 Millerlite95 @@ -14943,37 +31267,82 @@ MilliMillzy Millie Millionsppl Millkk +Milllf MillsMCR Millsbay +MillzyRS +Milo +Milo Iced +Milo Monster Milol Milpe Milsurp Miltank Milton Mim3r +Mimahh +Mime Tan +Mimi20 MimiKe +Mimic +Mimori +Min Botter +Min XD Minalinsky Minar +Minazuki Mincene Mind +Mind C Crew +Mind F-ed +Mind of me +MindBender84 MindYoStep Minde Minde0777 Mindf4ck7 +Mindforce MindfulGnome +Mindgrnd Mindhead Mindlet +Mindreaver +Minds Hunter Mindys +Mine is frai +Miner00 +MinerMvp Mineraal +Minerii Minerock +Minerock Man Minesweeperx +Mineta +Miney Minfri +Minga Minga Mingdee +MingeKing Mingerd +MinhteaFresh Mini +Mini Aurelia +Mini Biceps +Mini Chocobo +Mini Elba +Mini Hazy +Mini Jaack +Mini K +Mini M +Mini Misty +Mini Van +Mini Xander +Mini o MiniBuilt +MiniBundy04 MiniCoat MiniDrew +MiniDuckling MiniNinjo MiniSoMini MiniStew @@ -14981,96 +31350,201 @@ MiniToast Minibini Minidefiant Miniglass +Miniguez Minijazz Minimaps +Minimizing Minimum Mining +Mining Runes +Minioz1 MinipeTh Minirio Miniscus +Minish Gal +Minisnacks +Miniuzy +Minix Mink +MinkMiller Minkyeung +Minnesoooota Minnick Minnie Minski Minslee +Minstrel RS Mint +Mint1s MintEastwood +MintSoldier MintWestwood +Mintaras Minton Mintvolcano +Minty Elder +Minty Rogue Minty28 MintyBeaver MintyBreath +MintyFreshFe +Minty_Duos +Mintyyyy +Minu Kamp MinusMinitia Minyons Minzy130 +Mio Magic Mioceen Mipu Miqdad Miqote Miquuw Miracle +Miracle Grip +Miracle Sun MiracleToy Miraculous MiramiS +Miran a +Mirana Mirari +Mire Mirek +Mirepoix +Miriage Mirin_Gloots Mirith +Mirize Mirk Mirkat +Mirkoi +Mirkys +MiroSemberac +Miroh Miroki Mirthless Miruki +Mirxcle MisClickPro +MisLilToe +Misaka 10032 Misakipillow Misano +Misantrofia +Miscellaneum +Mischief Miscrint +Miscy Misdeal +MisfitGrinds Mish +Mishie x +Mishigamaa Mishkkal Misimo Miskil +Misks MiskySam Mislabeled +Miso Horney Miss +Miss Amanda +Miss Amped +Miss Beth +Miss Clover +Miss Inform +Miss Judging +Miss Kipatzu +Miss Kitty +Miss Nothing +Miss Piggy +Miss Tinaa +MissArcane MissMockingJ MissMystique MissRosie +MissTowlie +MissTwistedx +Missandrist MissclickGG +Missel0 Missen MissinTicks Missing MissingRNG +Mission +Missu +Missus Missvmk Missy +Missy Cee Mista Mista 1337 +Mista Wright +MistaPhelps +MistaWubz +Mistagainz Mister +Mister Chill +Mister Ex +Mister Gone +Mister Jar +Mister KJ +Mister Texas +Mister Tiger +Mister Xanny +MisterBass MisterBensy +MisterBidoof MisterCobb +MisterGurn MisterJager +MisterPatego +MisterStats MisterTrump +MisterWhale +MisteruDongu Misterwieb1 Mistheos Misticwok Mistify +Mistik Soda +Mistio +Misto-Flies +Mistr Morale +MistrGiggity Mistral +Mistre Apple +Mistres Pres Misty Misty12 Misty7632 Misuryu Mitabi Mitch +Mitch Gate +Mitch Izle +Mitch Peters +Mitch W MitchHardo MitchRap Mitchggee +Mitchman987 +Mitchs 2nd +Mitchys Iron Mith +Mith Man214 +Mith Scimmy +MitherHobo Mithoon Mithrality +Mithralking8 +Mithrane +Mithter T Mito Mitologas +Mitqh Mittag MittensRS Mittum @@ -15079,18 +31553,26 @@ Mituse Mitver Mitzchy Miuted +Miuw Mivo50 +Miwi +MixBro MixKit MixTapeFiya +Mixes Mixieg Mixtape +Mixun Mixxush +Miyags Miyui Mizis Mizo Mizro Mizty +Mizuno Akane Mizusawa +Mizz Horror MizzFrizz Mizzzura Mjay @@ -15101,57 +31583,130 @@ Mk60 MkLeo Mkiller120 MknlC +MlCOOL +MlDGARD MlKE +MlKEL MlLFnCOOKIE +MlLLlSECOND MlLO999 +MlXOR MlZORE Mlao +Mlddel1 +Mlopes Mmgmike24 Mmichel +Mmkbro +Mmm Food +Mmm Mushroom +Mmm Xp +MmmBathSalts +MmmButter +Mmmmm Bacon Mmorpg +Mmu Mmurder825 +Mmv Mnemic +Mnir MnkyDLufy +Mnmnnmnm +Mnts +Mo Fo Sho +Mo Hit +Mo slays +MoFo Boss MoIon Iabe +MoMoYaP1972 MoPhobia +Moathog63 MoaxD +Mob Partyhat +Mob Punch +Mob Up +MobILe_Own3R +Moba All Day MobbDeep +MobbyWang Moberger Mobiel Mobile +Mobile Data +Mobile Maxin +Mobile Rng +Mobile Skiff +MobilePlayin +MobileSkeng +MobileXPOnly +Mobilekraan Mobilist Mobiusyellow Mobo Mobs MobyWoby Mobz +Moca Pet Moccamuna +Moccies Moccona Mochasins +Mochi Donuts +Mochyy +Moe Amien +Moe Is Back +Moe You Up +MoeCipher +MoeTx Moeberg MoedZhafa +Moedameyer +Moefugganutz Moepog Moer Moes +Moestieee +MoetIkLopen Mofaer45 MoffelRS Mofleminator +Mofo Lt +Mofo Shadow +MofoMo92 Mofoe +Mog Time xD Mogcow Moggok Mogtime +Moh Mohahm Mohamedss Mohanad +Mohannah +Moheed +Mohh Mohib +Mohlo Mohnday +Mohphy +Moiqol Moist +Moist Dreamz +Moist Holy +Moist Maori +Moist3allsac +MoistButh0le MoistMaker MoistPainal +MoistWreck +Moistium Mojimot0 Mojito +Mojo Fiki Mojojojo171 Moju +Moju GG +Mojurico Mojzis Mok101 Moka @@ -15160,78 +31715,149 @@ Mokkasiini Mokke-senpai Mokkis Moksa +Moksu Moku Mokum +Mokum Syl +Mola +Molag x Bal +Molars +Moldy Nuts MoldyChips Mole +Mole Slides Mole1 +MoleMan-II Molemountain +Moles Rule Molinas +Molkkipo Molle Mollem +Mollium MolllyPop +Molloyo Mollt MollyLlama Molokotorby +Molotoffer +Moltea Molten +Molten B +Moltenclawz +Moltres X +Mom Hunter +Mom Madness MomICantPaus Moments MomentsApart MommaCuz Mommy +MommyMish +Mommys Hand +Momoh flip Moms +MomsLasagna Moms_Spagett +Mon key +Mon3yy Mona +Monarchis +Monarchy Monday +Monde MonehBabeh Money +Money BTW +Money Bagz +Money Grabs +Money Mann10 +Money tyven MoneyMalone Moneyman +Moneyman 199 Mongke +Mongrel Hick +Mongstaman Moniez +Monika Liu Moniosaaja +Monitor Monk +Monk Fists MonkFishking Monka +Monka Blunts +MonkaFront +MonkaGrizz +MonkaSFish MonkaZter Monkadile Monke Monkees80 Monkey +Monkey D C +Monkey News +Monkey Pox +MonkeyDGazoo MonkeyHeadAF +Monkeyannie +Monkeyboy991 +Monkeydodo45 +Monkeyhug +Monkeys Ass +Monkeysick Monkie +Monkphish Monksmen Monkster MonnerMads +Monnivalas Monnual Monny Mono +Mono Y Mono Monopoly +Monopoly Mvp +Monotone 07 MonrealkaAfk +Monroejo Monsemand92 Monsilly Monsta Monstaarr +Monster Hog +Monster Tits Monster333 +MonsterKush +MonsterTrain Monsterofpk Monstrius Monstrocity Monsutaa Montagne +Montak +Montanaro Monterey +MonthOldMilk Monty +Monty Jr Monumental Monuu Monxanvrooo +MooGoo Mooch Moocow910 Moodeer +Moodoo Mooffa MoofinnMan +Moogly Moohcake Mooi Mooie Mooing +Mooing Cow Moojin Mookinater Mooksy @@ -15239,58 +31865,98 @@ Moon Moon688 Moon7I8 MoonCard +MoonDunes +MoonStaIlion +MoonWolf MoonYagami Mooncrafting Mooner Moongazing Moonish +Moonish Ale Moonjock Moonlightcb +MoonlitNika +Moonly Moonman392 +Moonr0cks +Moonshadow18 +MooonCakeee +Mooony Moopy +Mooqie Moordaap3 +MooreNoyce Moorleey Moortgat Moose +Moose Land +Moose713 +MooseEatBear Moosecup +Moosefangs Mooseheads Mooserini +Moosewizza +Mooseyt Moosy Moot Moowi Mooyee +Mopdussy Mopeds Mopedstian Moppeharry +Moppy +Moq puW +Mor Ul Neck Moral Morales Morally Moray Morbid +MorbinThyme Morch MordLacaroni +Mordacai Mordenumero2 +Morder-Biest MordinSolus Mordy More +More AFK +More Chillli +More Coala +MoreThermite +Moredots92 Morelos +Morfeusz +Morgan Donor +Morgan69 MorganAtkins MorganaTits Morghuan +Morghulish Morgo +Morgoth Man Morgue Morguhh12 +Mori Memento Morijo Moriquendi Moritz Morkius Morkret +Morkula37 +Morland Morlun Morlyx MormonFTP21 Morniingstar Moronavirus Morot +Morph1ng +Morpheus Aus Morphing Morphogene Morpice @@ -15300,71 +31966,311 @@ MorrisTheCat Morsey Morski Mort +Mort Eater +Mort Rigo Mortal +Mortal Coil +Mortalize MortarMan MortemPlaga +Mortituros +Mortle2 +MortonRamsey +Mortonnizer +MortresxD s1 +MorttMoryt Morue +Morvas Morytania +Mosade Mosaq Mosbol Moscosung Moseley Mosess +MoshPitJosh2 Mosio Mosiph Moska +Mosmo +MossadKiller Mosse +Mossels +Mossgiant +Mossy +Mossy Taint Most MostDreaded +Mostly Godly Mostoes +Mosychuk Moteha +MothQueen +Moth_29 +Mothafuqueur +MotherFigure Mothless Motikeli +Motion1010 +MotivaSean Motivated Motmans +Motochorro +Motor +Motse Mottis Mottly Mottookaa Motx Motyas Motzimoo +Moubu Mougi +Moukie Moul Mouldied +Mouldy Lamp +Mouley Mount Mountain +Mountan Drew Mourn Mourners +Mournhold Mous +Mousecream Mousekeys Mouselifter Mouseman298 Mousenn Mousie23 Mouteo +Mouthful Pip Move +Move Over +Move Ziggy Move2791 +Movember +Moviestar Moving Moving20 +Mow on mate Mowgli13 Mowk Mowt +Mox +Moxy Senpai +Moyaha5 Moyd +Moyis Moysey MozDef Mptrj +Mq4 +Mr 0wnage +Mr 223 +Mr 7o7o +Mr 9ine +Mr A +Mr Airborne +Mr Alba +Mr Alex +Mr Ashh +Mr Ayye +Mr Bagel +Mr Baggy +Mr Barakas +Mr Bazzi +Mr Beefman +Mr Beekman +Mr Believe +Mr Biceps +Mr Bijpakken +Mr Bond +Mr Boogley +Mr Brunoo +Mr Burton x +Mr C0KE +Mr CHM +Mr Chonky +Mr Chopz +Mr Comrade +Mr Costy +Mr Count Up +Mr Cryin +Mr Dan Solo +Mr Darzelis +Mr Deivis LT +Mr DirtyDan +Mr Dong322 +Mr Dreamcast +Mr Dry +Mr Dudeicus +Mr Dumbiller +Mr Durian +Mr Dylesxic +Mr Easyscape +Mr Egret +Mr Elmar +Mr Elysian +Mr EsKei +Mr Fathead +Mr Father +Mr Foe +Mr Freezie3 +Mr Fridge +Mr Funguses +Mr Geese +Mr General +Mr Gentil +Mr Ghillie +Mr Ghizar +Mr GnW +Mr GodLuck +Mr Hanki6 +Mr Hassan Fe +Mr Hollowx +Mr Hrodbert +Mr Hulbert +Mr Infinity +Mr Iron Bar +Mr Iron Gray +Mr Iron Owl +Mr Iron Poo +Mr IronAss +Mr Jacko +Mr Jam10 +Mr Jam1O +Mr Jasperr +Mr Johbi +Mr Jonh L +Mr Jordy +Mr Joshing +Mr Jot +Mr Kaasmof +Mr Kato +Mr Keyss +Mr KingCobra +Mr Kirbo +Mr Kiwi Bird +Mr Kman +Mr Kodai +Mr Kokkok +Mr Krabbsss +Mr L0af +Mr Laser +Mr Laz +Mr Leeee +Mr Looch +Mr Loser +Mr Lysol +Mr Mackie +Mr Manual +Mr Martel +Mr McCune +Mr Mentos +Mr Mikers +Mr Missiles +Mr Miyagy +Mr Moncler +Mr Mordacai +Mr MrX +Mr Nagoh +Mr Nasty Kev +Mr Nate +Mr Nebs Main +Mr Neckless +Mr No Rift +Mr No Sleep +Mr NoMates +Mr Nonya +Mr Ohio +Mr Oizo +Mr OldSchool +Mr Osc ar +Mr Ozzy +Mr Palm +Mr Pandaz +Mr Pastorius +Mr Peggers +Mr Phelipe +Mr Pita +Mr Platypus +Mr Pondman +Mr Poup +Mr Pretzels +Mr Questerr +Mr RaWaR +Mr Rebirth +Mr Rich T +Mr Robert +Mr Romm +Mr Ryker B +Mr S N S +Mr SZX +Mr Sasquatch +Mr Shaman +Mr Siege +Mr Sighco +Mr Slabs +Mr Slaw20 +Mr Spark +Mr Splitter +Mr Squicky +Mr Stan Man +Mr Syvan +Mr T C +Mr Takoya +Mr Tbh +Mr Teej +Mr Thornbery +Mr Troll +Mr Trump +Mr Untainted +Mr Vaidys +Mr Vanhanen +Mr Veegs +Mr Wieler +Mr Wild x +Mr Woods +Mr World 420 +Mr Woz +Mr Wumbo +Mr Xray +Mr XuRu +Mr Yozef +Mr Yum +Mr Yzi +Mr Zevosh +Mr Zio +Mr ensayne Mr noli Fe +Mr rubler +Mr spikey +Mr stony +Mr2ndPlace +Mr44 MrAlcotester MrAmazing MrAmbaal MrAngelxz2 +MrAngerfist MrAnkan MrAwesomeNL +MrB1u3 +MrBananaJoe +MrBeadyEyes +MrBeastly MrBeerNTits +MrBic MrBigLoads MrBigPecs +MrBigWheels +MrBingly MrBlueScreen MrBooBlocca +MrBorreby +MrBouncyButt MrBraa MrBry MrBryne @@ -15377,36 +32283,62 @@ MrCivilian MrCoolSkills MrCoook MrCoopahh +MrCowey MrDTails MrDairyHeir +MrDaive MrDarkBlue +MrDarkSide76 MrDarling MrDashwood +MrDeadInside MrDevice MrDevour +MrDoake MrDoofes +MrDraconite +MrDumbaldore MrEasty +MrEdgaras +MrEmerica +MrEnd +MrEwan MrExterm +MrFANG MrFancyboots MrFarrell MrFlizz MrFluffy97 +MrFreakyFrog MrFrizzesh MrFro +MrFrogRS +MrFuack MrGaalis MrGainz MrGanksta +MrGatorGnash +MrGeeeeeee MrGibbletts MrGixxer MrGodBrand +MrGoodbye +MrGraveLord MrGrind +MrGurbz MrGurn +MrGustuv +MrHatN Clogs +MrHeftyBag MrHobokian MrHummus +MrHypeK0 MrIWontMax MrIronChef MrJTheIron MrJad +MrJargon +MrJermu MrJoestar MrJohnnyx MrJonMan @@ -15414,17 +32346,32 @@ MrKadir007 MrKerrie MrKevinR MrKoalas +MrKyle +MrLaffy +MrMag MrManny +MrMatchett MrMaxLvl99 +MrMayhem +MrMeeseek420 +MrMeeseexs MrMell0w MrMime +MrMonkeyJock +MrMonopoly MrMonsterrr MrMooCows MrMose911 MrNL +MrNaf1 MrNatee +MrNeverWrong +MrNice98 +MrNieksas MrNikeKush MrNoBank +MrNoFgiven +MrNoFilter MrNoLove MrNook MrNorwayOS @@ -15433,75 +32380,169 @@ MrOceanic MrOctapus MrOngh MrOriginal25 +MrPK74 +MrPaip +MrPapaJoe MrPezcu MrPige0nz +MrPiggles +MrPlasters MrPoopiehead MrPork +MrPotionz MrPowers MrPutterLite MrQuaker MrQuick1 MrRaidUrGirl +MrRakoon +MrRangingElf +MrRawDog +MrRiceBucket MrRidder MrRight4U MrRobinHood6 MrRoboto MrRocco +MrRodgersHD +MrRojo31 +MrRumarak +MrRuneDrag +MrRyo +MrSanta94 MrScape2Much MrSensimilla +MrShneeeebly +MrShoarma MrSimQn MrSirOne +MrSkydive MrSlongerton MrSmiiLey +MrSmikkel MrSocorelis MrSolo MrSteelYoRNG MrStickyBudz +MrSucellus +MrSuibhne +MrSupo MrSusans +MrSwift00 +MrTanglez MrTibberfitz +MrTickMaster MrToad +MrTobyG +MrTooSerious +MrTough rs +MrTrashclikz +MrTryHard69 +MrTurds MrVillanelle MrVitable +MrWallnutt +MrWeageZ0311 +MrWest +MrWhetFartz +MrWobble420 MrWorldwidee MrYachie +MrZandra +Mr_B1ff +Mr_Hairless Mr_Kadir007 +Mr_Kapoo Mr_Manchello +Mr_Ostroum Mr_Schmeckle +Mr_Scoff Mrawrs +Mrbadwrench +Mrbaird +Mrbd Mrburnsss +Mrcoconut22 Mrdeemoney +Mrevil1 +Mrfuntime517 Mrfuzzy Mrglex +Mrhood +Mrjellyfish Mrjli Mrloonetick Mrm0rm0r +Mrmagoo0 Mrmanguy Mrn1ceguy89 +Mrpokoz +Mrpoopyface1 +Mrs Budie +Mrs Pure Cut +MrsFabler MrsJonesUFT +MrsMushrooms MrsPurpTurt +MrsSocorelis Mrslipkn0t Mrwaffleses +Ms Goblin +Ms K +Ms Lizziard +Ms Trap +Ms Zik +MsAnderzen +MsArachnea +MsBoothanqq MsFreakyFrog +MsGroves MsSev +MsethOWarq p +Mssp +Mstr Postman +Mt Eben +Mt Olympus +Mthrfckrmike Mthw +Mtn Dew Dew +MtnDewford MtnGoat +Mtp69 +Mtra +Mtv Cribs 07 Muad_Dib +Muahahaha45 Muahhahaa Muahuahuahua +Muas MuasBtw +Muay Mat Muaythai +Muc Gib +MuchBetta MuchGpWaste Muchly MuchosAssias +Muchy Muckball +Muco +Mud Cricket +Mud Temple Mud9 MudCatBigDog +Mudda Focha +Muddas Main +Muddy patch Mudguard +Mudkins Mudkip +Mudmur Mueezy MuerteBella Mues Mufasa +Mufasa IXI MufffinKing Mufficer MuffinActual @@ -15513,7 +32554,9 @@ Mugarooni Mugen Mugge Muggerman3 +Muggled Muggles +Muggtjuven Mugrub Mugy Mugzy @@ -15521,116 +32564,232 @@ Muhaji Muhamed Muhfn Muhkea +Mui Gokuu Muilpeer Muki Mukkaram7861 +Mukkey Muktheduck +MulasRevenge +Mulberry Mtn Muleleaveox +Mulky Mulle-mek +Mullet Mafia +Mullishus Mullvaden Mully MullyMammoth Multi +Multiloquent MultipleRats Multiplying Multitalent Multiway +Multizeta Multrix Mulugga +Mum3nRider +MumBanker Mumble Mumbles +MumblinSloth +Mummzy Mumriken Mums Mumspaghetti Munansurvain +Munchise +Munder Mundt Mungles Mungoe Mungu +Munia15 Munich Munire Munk Munkea +Munkefar Munkinut +Munky Munk MunkyBizness MunkyKnuckle Munn +Munnchicles Munqqi4862 +Munqqii +Munshae Muparadzi +Muphat Muppz +Murasa Murbez +Murda Turk Murder +Murdered +Murderings Murdimius Murdock Murdur +Murgambit +Muri Murilokooo +Murimekko +MurkWahlberg +MurlokMurlok Murogrim MurphRS Murphys +Murphys Lag +Murpman Murray +Murray 1991 Murre Murrloc Murrrum +Murt15 +Murtti +Murzum +MuscleNation Musftw Mushmom Mushroom +Mushroom Mtn Mushrooms1 +Mushtime MushuPorkPls +Musi c Music Musica MusicalMurdR MusicalStory +MusiclRevivl +Musicpunk Muskets +Muskintine Muskogee +Musky Whale +Muslimi Musschoot Mussy Must +Must Slay MustBeGanja +MustKillBill Musta Mustachio +Mustamae +MustangYak1 +Mustangs Mustard +MustardXD17 Mustasurma Mustekala MusterShelby +Musto Task +Musty Ropes +Musty Sailor MustyToofer Musuwu +Mut3dHasselt MutaNep Mutants Mutapets Mutated +Mute Pker Muteself Mutilated Mutsurini +Mutt a dile Muttadale Muttadiddle +Muttadude +Muttallica +Muugz +Muumipappa +Muunchyy +Muurxi +Muushuu Muutz +Muws +Muy Booty Muzbo Muzi +Muziek Smurf Muzket Muzz Muzzle +Mv P +MvP ov RnG +Mvk +Mvko +Mvp Scrub Mvvs +Mw2 +MxMay +MxSloot +Mxb36 Mxffin +Mxr Mxteorites +Mxtta +My Damie +My Dark Hell +My Dark Soul +My Death Bed +My Demons +My Dont We +My Dumb Iron +My Faith +My Fantasy +My GF isMILF My Gentiles +My God +My Iron Man +My Ironmain +My Ki +My Kill +My Kingz +My Limp GIMP +My Main Game +My Maine +My Mate Elm +My Name Vyga +My Pet Mocha +My Rng Ass +My Sana +My Secrets +My Ticks Now +My Ult +My pp stink MyAssLucky MyCatKillsMe +MyCtyNeedsMe +MyDuck MyElyNow MyFitPal +MyHandyMan MyIiu MyLastGo MyLoveFaith +MyMercy MyNAm3BubBA MyNameIs MyNameIsCole MyNameIsKent +MyNameIsTina MyNameIsTrev MyOSAccount +MyPetRicky MyProject MyRifleMyGun +MyRight Nut MySeed +MySkillsKill MyStinkHurts MyTakao +MyWanderlust MyWyvernAlt Myams Myat @@ -15639,88 +32798,240 @@ Mycreation1 Mydlong Mydragon8u2 Myers +Mygary Mygraine +Mykeh Mylar Myles +Myles_008 Mylifeisless +MylilBrony +Mylle Mylo +Mylo 7 Mylos Mylosas Myndemo Myoboku +Myosarcoma +Myoshi Myotoxin Myriadic +Myrm Myron MyronAynes Myrupz Mystearica Myster +Mystery Btw +Mystery X MysteryGifts MystiCool Mystic +Mystic Mak +Mystic Pizza +Mystic Voke MysticChompy MysticDrift MysticErebos MysticRiver MysticStrega +Mysticpuffer Mysticrobe +Mystics Main Mysticvaa Mystiic Myth +MythCerix MythCraft Mythic +Mythic Hiero Mythicat +Mythrin III Myto +Myuf Myuk Mywa +Myz Myzi +Mzaa +Mziy +Mzr +Mztar +N 10 +N 55 +N A l N E R +N E L L +N E N E +N Gallagher +N I GH T96 +N I N O X +N I S H +N ICK Y +N O B S Y +N U B U +N W M +N a t t z +N ang +N anko +N arb +N dejay p +N e a k i le +N e i l +N e0 +N iall +N ibbler +N ightgleam +N l C K O +N l E V E +N o m a d +N occo +N oggin +N u d +N uggies +N-J-R N0 H34RT +N0 Invictus +N0 pid +N00biistyl +N00bslayer27 N04me N0B4Marri4g3 +N0Ballz N0ID +N0LlMlT N0OneCares +N0T HC N0XPWaste N0blelegend N0llie +N0obalhao N0rse_k1ng +N0sie +N0t Maxed +N0t special N0tliketh1s N0ul +N1 CK +N17 Jake +N1C3 N1NJASK1LLZZ +N1ckai +N1cwho +N1nja N1njaCat +N3XX +N3p N3se +N3x +N4L +N4ruto +N4styNat3 +N7N +N8vy +N9NE420 +N9T7 +N9U +NABEEL RJ +NAGAKAMAL +NANORA NARPslayer +NASDBOY +NASSERN NASTY NAVl +NB8K 2K13 +NBA +NBA NewMain +NBA Youngb0i +NC90 +NCG +NCG 2 +NCPS +NEBR0SKY NECR0DANCER NEEDAMETHYST NEENEE NEET +NEET forever +NELSONORS +NENOS NEONiC NEWBEYE NEWFISHMATE +NEX IS WET +NEXFARMER NEYOVIC NFed +NHL Blues NHrn +NIIXIIK +NINJABLEV1NS +NINJATMNT NITR00 +NJC20 NJWW +NKJ +NL Chronos +NL Gilbert NLDeluXe NLFreakky +NMM Jaoel NMOS_giant NMZLetics +NO 1s SAFE +NO CHlLL +NO FEEL +NO MlDS +NO N R G +NO PlD NOAH +NOMETHESPORT NOOOOOOOOOO NORTHSEA NOSTALGlA +NOTTDY +NOTaPancake +NOmki +NPC Healer +NPC Icedrake +NPYJ +NRA NSFL +NSHN +NST Praise +NT FiSHvv +NT Y NTBeerBandit +NTMR RADIO NUEL NULLI +NULLlN KUPPl NUTSHOT +NVQ NWHL +NWI 219 RAT +NY +NY C +NY G +NY x LEGiiT +NZ Dave +NZL +N_MB +N_other_day +Na Mn Or I NaCl +NaCl Sprite +NaCl is salt +NaQ Naaath +Naafiri +Naais Naakkeri94 Naakt +Naaon +Naautilus +NabD Rank 5 Nabroleon Nabshadow Nabss @@ -15728,191 +33039,375 @@ Nabua NabyLad Nach NachB +Nachitto Nacho +Nacho Alt Nacho2030 +NachoByte NachoDragon +NachoPapi +Nacht Nachtkastje NachuiTuda +Nadaa +Nadeo +Nader +Nadsage Nadund +Naethera Naftininkas Naga +Naga Morich Nagisa +Nags NagyonJo Nagyung +Nah0824 +Nahida +Nahiida Nahkamestari Nahuel +Nahyol +Nahyun NaiiSha Nailbox Nailed Hard +Naimsen +Nainenoon +Nairda Niam +Nairda Nori Nairino +Naisu Najda +Nakal +Nakama NakedTwister +NakednFeared Nakke176 +Nakkee +Nakkesleijer Nakkimauno +Naksitr6ll +Naku Nakuz Nakuz0r +Nalle Puh FT +Nallieheai Nalsara +Nam +Namcel Name +NameECJ NameRjected NameWasBannd +Named NamedGenius Namelessdude Namelus NamesAreMeh NamesHisoka +Namine Silke +Namir Nammer Namuko Nana +Nana Steel Nanado Nanciscor Nancyy Nander +Nandos +Nandos Man +NangMan Nangdalorian +Nanimavi +Naniva Nannertee Nanno +Nannu Baba +NannyCam69 +Nanoh +Nanoman360 Nanonymouse +Nanook IV +Nanoray +Nanotech Nans NansBeaver +NansCumTowel NantaoL +Nanyts Naoe +Naoe Y +Naoe Yamato NaofumiLTU Naoka Naow +Nap Tackle +Napaaaaaaaaa Napalm Napanderii Napettaja +Naphtic NapkinFork +Napnapnap8 NapoleanBra +Napoleon +Nappy Head Napsutaja Naqu +Nara ven Naradas +Narbose Narby +Narcan Saves +NardDog +Nareg +Narel1a +Narendradath Narg +Nargarothian Narked Narkon NarnodesNono Narrative Narry Nartens +Narth Calior Narthalion Naru808 Naruto Narval Narvalow Narwallus +Narwhal1731 Narx4 +Nas is like Nasa Nasaman +Nascar Naseeph +Nashh Nashluffy +Nashonic Nashy +Naskarsdad +Nasko 07 Nasper Nasrullah Nasseee Nassim Nast +Nastacov Nastie +Nastik Nasty +Nasty107red7 NastyAss +NastyCasual NastyDonger +NastyGarbage +NastyGeUsers NastyV6 Nasuuu +Nat Scheetje Natalie +Natby Nate +Nate Dogg g +Nate Joggers NateDoge +NateStayLove +Natebroh +Natedog 1104 Naternater +NatesOSRSAcc +Nath Meister +Nath btw NathJeaL Nathan +Nathan A +Nathan Lyon +Nathan Novak +NathanTurn +Nathandrthal Nathanyuelle +Nathn Nationwide Native +Nato is Jake +Nato888 +Natoh +Natoroid Natrox999 Nats Natsu +NattNub Natte +Natte Dop +Natte Dream +Nattetid +Nattkungen +Nattmara Nattraps Natu +NaturalBeaut Naturally +Naturally Me Nature +Nature B0Y NatureNymph +Naturealis Naturejacks Nauce +Naudra +Naught Good Naughtpure +NaughtyPanda +Naukahtaa +Naukkiss Nautis +Nauuyn Navas +Navbrah NaviRio Naviaux +Navidson1 +Navn Navraj Navtik Navy NavyDD214RET +NavyLight +NavyPunk +Navyseals199 +Nawberry +NawtyMonkey Nawus +Nax Naxacid Naxiius Naxito Naxos Naxtzi +Nay RS +Nay0101 Nayna Nayrus +Nayrus Love +NazTehRpR Nazgul Nazty Nbba Nbcw +Nbsniper69 Ncpure Ncrr +Ne x is Ne3po Ne86 +Neak +Nealonl4 Nealos Neals +Nealvy Neanderthal Nearly Nearomancer Neatkii Neav Neb0lith +Nebb Nebbo +NebbyGo2Bag +Nebezaidu Nebijok Nebrosky +Neccto Nechromancer Nechryass Nechs NechtKnecht +NeckBat Neckbeardis Necksnipped NecroEric +NecrodedGSF Necrokingns +Necromatus +Necromooser +Necronomist Necrophorum Necrosauruss Ned1991 +Neddam NederweertV2 Neduks Nedus +Nedved Nedward +Neeby Need +Need 2m +Need A Pint +Need Femboy +Need GP Now +Need Protein +Need Rogaine +Need Spoon NeedInternet +NeedMoore +NeedPurp4Bed Needs +Needs Coffee NeedsADad Needtomboy +Neeech Neefey +Neeisnet +Neekkoco NeekoNeekoNl Neekomata Neekss +Neelen +Neems +NeetScape +Neetman +Nefarious fe NeferSeti +Nefereous Neffiz Neg14 +NegAttiveI Negan Neganeogami Neghed +Nego Baby +Negrong Nehxy Neil +Neilthedead +Neintein +Neitiznut +Neitiznut Jr +Nej tak Nejvalt Nejvash +Nekalbink +Nekkopanda +Neko Inori NekoSayNyaa +Nekomata Nia Nekomimi +Nekromant Cz +Nekrukeisari +Nelaimingas Neleron Nelhium Nelkirr +Nelli Matula Nelliell +Nellysan Nelox Nels0N Nelsi @@ -15926,154 +33421,295 @@ Nemaksciai Nemba Nemerrr Nemesis +Nemesis2020 Nemf +Nemijin +Nemipro Nemisys +Nemj Nemmo Nemo +Nemo_o Nemonia +Nemoven Nenah Nendou +Neng_Thao02 Nengah +NeniHead Nenio Nentsukka +Neo +Neojoramony Neon +Neon Christ +Neon Juan +Neon Slinky +Neon Volts +Neon xxv NeonChilli +NeonGenxsis NeonM1d +NeonO7 NeonPaladin NeonRhythm11 Neonik Neonke Neopia +Neoplasia Nephedes Nephyrion Neppe +Neptune169 +Ner +Nera Obuoliu NerbBlaster Nerbles +Nerbs Nerd +Nerd Body +Nerd Owns +Nerd Stone Nerda +Nerdbob Nerdcore +Nerdier +Nerdpuff +Nerf Bowfa +Nerf Coffee +Nerf Th1s NerfMePlz NerfTG Nergegante Nergg +Nerio x +Nerithma +Nermzz7 Neroxcen +Nerraw +Nershi Nerve +Nerve Cell Nesk +Nesquiik KlD Nesretep Ness Nestedloops +Nesties Nestl3 +Nestorianos Nesuvarzytas NetTrap +Netamu Netanelba +Neteroem +Netgear Nethada +NetherToxin Netho Netjes Netmn15 Neto +Nettipoliisi NetturDab Neturtingas Neural +Neuro Lepsis +Neuro-Tomb +Neurologist Neurophys Neurovelho +Neut +Neutral Game +Neutralised Neutrally Neutronas +Nevar89 +Nevena Never +Never Die113 +Never Spoon +Never die +Never2Clever NeverAssume +NeverBudge NeverEnding NeverFreeze NeverGetRift NeverGunaMax +NeverImprove NeverLateWiz +NeverMoves +NeverMyFault NeverQuest NeverRespawn +NeverSailing NeverSober NeverTrading Neverender27 +Nevermaker Neverover Nevi Nevik94 Nevinnn +Nevit +New Monkey +New Tooka +New journey +NewAgsWhoDis NewBoy4321 NewDad NewFavorite +NewHobby +NewMobWhoDis NewPlayerx13 +NewToBossing NewWrldHodor +NewZealands Newb +Newb Ranger Newbillnye +Newby NewcastleFC NewfieTinMan Newfiebanger Newguy2003 Newleesh +Newlut NewrockkuOS Newst0nekid +Newt Jacuzzi Newtman907 +Newton 1 +Neww Zealand +Nex Afg +Nex FFA King Nex Is My X +Nex Minion +Nex Myself +Nex leecher Nex1s Nex2169 +Nexarion Nexcellent Nexeiy +Nexfinity Nexgen Nexi Nexil Next +Next Day NextPet +Nexus Sire Nexx +Neya Neyburr Neye +Neyes Neyugn NezFix Nezqi Neztea +Nezuk o NghtPnda Ngis +Ngyessiree +Nh3r NhaleXhale Nhan +Nhest Cutter Nhimzo Nhouse20 Nhyrenn +Ni +Ni ke Niahl +Niall x Niawag +NibblyTitter +Nibbyus Nibexx NiblaNoss +Nic +Nic Machine +Nic Nasty +Nic Nerc +Nic co NicMachine Nicannand +Nicao Beck Nicashi Nicathan Nice +Nice Adept +Nice Caulk +Nice Iron +Nice Nice +Nice One +Nice Peaks +Nice Pets +Nice try guy +NiceGuy Edy NiceGuyEddyy NiceMarkMark +NicePotatoes +Nicely High +Nichohls NicholasRage +Nicholaswei Nichy NiciValbuena Nick +Nick Grows +Nick Ints +Nick Lopes +Nick Sellers +Nick X10 Nick0hwolf +Nick1234101 +NickChubb24 +NickReady +NickScape NickSpoof NickTSMITW NickVo NickaClassic Nickernack55 Nicki +Nicki Menage NickisBackyo Nickk Nickmitz23 +NickmyKnack Nicknao +Nickolai Lol Nickool4 Nickos +Nickoss +Nickoudbier1 Nickroo1234 Nicks +Nicks OSRS NicksNipples NickyPunky Nickynice23 Nickyy +Nickyy G Nico +Nico BE +Nico Le Chef +NicoFreakOh NicoIas NicolaSturge Nicolai +Nicolaj Nicolas +Nicoo +Nicophilia +Nicsyth NidaNewName Nidalee +Nidgen Nidh Nidhogs Nidk @@ -16081,26 +33717,45 @@ Nidmann Nidoking110 Nidraugas NieI +Nief Niefable Nielaahh Niels1608 +Nielscorn +Nielsen9312 +Nielssjeee Niely +Nieman NietzscheJR Nieve +Nieve Hamtaa +Nieve Rivers +Nieve Task NieveIrwin NieveIsBad NieveIsThic NieveMeAlone +NieveTitties Nieves +Nieves Bean +Nieves Boob +Nieves Butt +NievesDead NievesThighs Nifelhiem +NifkeCove +Nifty Gifty Nigel Nighlist Night +Night Elf45 +Night Shade NightNurse +Night_Furor Nightbass Nighter Nightmare +Nightmare CX Nightmare028 NightmareSHW Nightolas @@ -16108,174 +33763,354 @@ Nights9 Nightsharp Nighttman NightxxShade +Nightz Watch Nihilismi NihilistScum +Nihplod Niihilism +Niiick +Nijah123 +Nijn +Nik-fil-A +Nikab Nikco Nike NikeOnMyFeet Nikez Nikit Nikkerpoo +NikkiJaxx NikkiQT +NikkisDD +Nikkisaur Nikko Niknea +Niko Solo NikolasOG Nikoo Nikronic +Nikskill Nikumei +Nikun +Nikz Niley +Nilitsu Nilpferd +Nils +Nilwh NilzZ +NimaNaruto Nimajineb +Nimble Snail Nimbubu Nimex Nimismies2 Nimlasher234 Nimloth_666 +Nimma Crew Nimmongel Nimrod Nina +Nina Dobrev +Ninanunana +Nine E L NineTs +Ninef +Ninesax +Ninetails Ninetales4 Ninevolz2 Ninfia Ningaa +Nini N Kiki Ninja +Ninja Bamsen Ninja Cow Jr +Ninja Rasta +Ninja1978 Ninja66 +NinjaNoodles NinjaPunch72 NinjaSpeed NinjaTurt Ninjaloff NinjasDark3 +Ninjee xo +NinjerNick Ninjunzo Ninjymate Ninook Ninorc Nintendogz +Nipaahv Nipes Nipitipi Nipp +Nipp-on +NippleSundae Nippppppppee Nipps89 NipseyHpvm Niseraru +Nishax Niskha Nismo +Nismo Ranqe +Nisramont Nissan NisseIadden +Nissmo Nistipata78 Nitefall3n +Nitepearlz +Nitetrip +Nith Nithe Niton +Nitro Crate +Nitro Dax +Nitro Rng +Nitro306 NitroCrate Nitroalkenes +Nitrognhorse Nitros +NitrosOxide +Nitrous Bro Nitsch +Nitsuj42 Nitt +Nittany +NitwitSchool +Niukkis Niva Nivex Nivory Nixby Nixi_NL +Nixiro +NixonsJowls +Nixvet +Nixxay Iron Nixy NixzTez Nizqa Nj0y Nkpi NlCK +NlCKT +NlLREM NlMBY NlSO NllCK NllL022 NlppleX +Nmf Euphoric Nmofm +Nnavs Nnoitra +No 1 There +No 99s btw +No Arcane No +No Autoclick +No Basic +No Death Tax +No DexL +No Diff +No Emphasis +No Fear X +No Fkeys +No Honor Yah +No Joggers +No Kwuarms +No Legs Lazy +No Logic +No Lucko +No Means Ya +No Meds +No More Bugs +No Much Bulk +No Normies +No Old +No Pets +No Rationale +No Rugrats +No Saint +No Senpai +No Sleepin +No Souldier +No Splits +No Submit +No Tax Due +No Tbow Andy +No To War +No Xp Waste +No Zily Luck +No ah +No toleranss +No ur roll No0dlehead No53 +No7 NoAntiVenom NoBankIRL +NoBanksNoThx NoBans NoBp +NoCreamy NoDayMercy NoDealEU NoDropForMe NoDurex4you +NoEdge707 NoFame +NoFang Wizza +NoFapp +NoFlipZone NoGoodSkills NoHelp +NoHesitation +NoInferno +NoIronManBTW +NoLife4Him2 NoLifeCue +NoLifeLevi NoLifeMain NoLifeMcgee +NoLifeScape NoLifeSincRS +NoLiferSoul +NoLimit NoLootBag NoLootNoWoot NoLove +NoLuck Mario +NoLuckBearku +NoMarinero +NoMercyUIM NoMo NoMore NoMoreTrades NoNumbers4me +NoOlmletSig +NoPainNoMain NoPolnT +NoPrims2k_kc NoPurpleAku NoRNGsmalle +NoScyOrStaf NoShit +NoSprayNoLay NoStore NoStrategy NoStress2Day +NoTangleroot NoTank4U +NoTexture +NoTradeBrad NoTradeForMe +NoVa ITI +NoVow +NoWaR +NoXpWast3 NoXpWasted NoZulrahIM +No_Dumping +No_Remedii Noah +Noah Dinnae +Noah G +Noah Luvs U +NoahB NoahGriffith +Noahfireball Noahs +Noam Chompy Noamonts Nobber Nobbys Nobelanerr +NobilityTorn Noble +Noble Gage +Noble Oats +Noble Sticre NobleCorey NobleVespo NoblesForRec +Noblood +Nobody Good Nobodyz Nobrain +Nobu +Nobudy Nobularlol Nocando Nocarred Nochill NocnyKoszmar Noco +Noctivagous Nocturnal +Nocturnal RS Nocturnal024 +NocwocDaLord +Nodens +Nodonge5 Nodorcro NoeLT Noeka Noem +NoemMeJePapi Noexi +Nofolkn whey +Nogal +Nogi Nogoodnms NoiScape Noig +NoirSandy +Noiserv Noisy +Noisy Luigi +Noita-akka +Noith NoizRock Noizex +Nokil Nokiny NolanAlpha +Nolch +Nole Jr +Noley Nolifepvm Nolifer +Nolifer 28 Nolly +NoloadRIP Nolungs Nolzeyy Nolzzz +Nom C +Nom Ruby NomCensurer +NomNomSoulz +NomadForseti +NomadRek NomadsLife Nommmy +Nomoenoobs Nomonater Nomskichooo +Nomtiq Non5ens31 +Noname Nate Nonc None +None Feared +NoneOther Nonke +Nonni Nonplayer Nonreal Nonsense @@ -16283,29 +34118,50 @@ Nonstop Nonvalid Nonwestlee96 Nonwitzki +Noo Promises Noob +Noob Man +Noob N Smoke +NoobHey NoobPkedMe NoobWrecker Noobacleese Noobalhao +Noobe15 Noobest Noobicidal +Noobike00525 +Noobirini NoobishAct2 Noobs +Noobsscape +Noobtyle Noobtype +Nooby Noodle +Noodle Dude +Noodle Me Noodle Soups Noodles +Noodlike Noodz Nooki Noolbort Noon Noons +Noonshake Noopi757 Noorbee +Noorden +Noosii +Nooster Noot NopePope +Noportals NorCalGreens +Nora Flora +Norbet +Norbsi Norchis Nordas Nordfeir @@ -16316,270 +34172,660 @@ Nordstrand Norfok Norge Norhig +Nori Nitsuj Norilsk Norimasa NormHadAFarm +Normabel NormanBates +Normans Normie +Normie Main +Normie helm +NormieDalzii +NormieFYI Normies +Normy Narked +Norn Norqe +NorrSken +Norska +Nortenosk North +North No 2 +North Rmmbr +North1ane Northerly Northfork Northic +Northnewfy +Northrod +Northtoeast +Northxy +Norti Norton Norton_Gman +Nortoon +Nortron +Noscythe bad Nose +Nose Beerz +Nose Puncher NoseBeersies Nosetkl Nosevesey NosferatuBoi Nosni +Nosnorb Nosorow Nost4lgia95 +NostalgiaNp +Nostalgic +Nostalgic00 Nostalgimon Nostos +Nostrily +Not 0A +Not A Lover +Not A Robot +Not Bad +Not Brizz +Not Carlyle +Not Cynical +Not Dzop +Not Eternal +Not Flicking +Not Frac +Not Gaming +Not Ice +Not Internal +Not Ketho +Not Level +Not Lime +Not Lorppe +Not Med +Not Mesijus +Not Mikers +Not Muppet +Not Muppz +Not Panik +Not Perry +Not Rudy +Not Ryu +Not Salinger +Not Schuproo +Not Sharp +Not Soft Yet +Not Sotetseg +Not Tanzu +Not Tarczon +Not Tarny +Not Twistie +Not Vaxx +Not Yakosu +Not Your Nan +Not ZB +Not Zema +Not a Fan +Not a UIM +Not all +Not an elf +Not enough +Not even Dan +Not main btw +Not rn Babe +Not trivial +Not wow man +Not-afraidem NotAFKenough +NotAJ +NotALurer532 NotASkrub NotAStreamer NotAlright NotBiabanana +NotBot893294 +NotEp3 +NotEvenRare +NotGilthresa +NotHCJames NotKD +NotKailey +NotKnowMadss NotLefty +NotMatched +NotMaxinSoon NotMinty +NotN07NeMo NotNecro NotPker +NotPlateWolf NotReady NotShaneska +NotSoCasual NotSoHCZoro NotThaFather +NotUIM NotUrAvgTim NotUrBisnis NotValidName NotYerGuyPal NotaCola +Notanedgepkr Notanoob12 +Notfatprism Nothard +Nothin Lasts Nothing +Nothing Sold +Nothing2die4 NothingMate +NothinqThere Notjoeybloom Notlayla Notmyrsname Notori Notorious +Notorious N1 +NotoriousALB Nottarg +Nottingham +Notutoring +Notwind Notz +NouGIM Nour +Nouvelle Vag +Nov Nova +Nova Lova +Nova NK Nova Rez +Nova X1 NovaDragonX +NovaNovNov NovaNova_v1 +NovaaDay +Novacult Novahearts Novahria Novalisky +Novamus Novaqueen +Novaspell +Novelled Novem +November Air Novesey NovicePlayer +Novist Novizy +Now Boarding Nowa +Nowa Lyfe Nowlan2 Nowoxifer Nowt Nowuh +Nox Irea Nox1de +Noxai +Noxeek Noxelder Noxia Noxied +Noxif +Noxifer Nino +Noxifer Noob Noxifers Noxiti Noxitrall +Noy rs +Noys NozFox +Nozel +Nozzie +Nozzles +Npc Contact Nppb +Npzu +Nrsa Nrse +NsG Flames Nshit +Nsns khalifa Nsoak +Nsor Nssc +Nstfu Nt0y NtQuiteRamb0 Ntsf +Nu ff +Nub Cakes +Nub Cape +Nub Die Plx +Nub II Elite +Nub at PvM Nubby +Nubby Nubnub NubbyOtter +Nubciclez Nube Nube101 Nubje +Nubslie +Nubthhh Nubutraum +Nuc1ear Nuck +Nuck Formies Nuckie34 +NuckinPhutty +NuckkinFuts +NuclearDron3 NuclearShift +Nucleons Nucleotide Nucu +Nue +Nuffedyr +Nuffers Nuffyz Nuffz +Nuge Hews Nugget +Nugget Farm +Nugget8453 +Nuggo Nugs Nuhapiippu Nuigaia Nuirokay +Nuk3rO +Nuke Nuke Rofl NukeDuke98 Nukkumassa +Nukooa +Nukster +Null God +Null Quiet +Null Scaper NullObject +NullTalisman +Nulliko Nullish Nullly Nuloh +Nulsen +Nuluki +Num1 StepSis NumTaker Numazu +Numb God +Numb Too +NumbButWhole +NumbaWanGIM Number +Number Forty +Number is 47 Number1 +Number1 Bass Numbermatch Numel Numenor21 Numerater0 +Numerikaali Numidium +Numinious Numismatist Numpad +NunWithAGun Nuna Nunac Nunc Nuno NunsRtight Nunsbox +Nuoli Z +Nuori Karhu Nuparu22 Nupayne Nupiso Nuqubba +Nuqx NuranJeezes +Nurco Nurdal NurgleBurgle +Nurrminatorr Nurse +Nurse Turbo Nurselie +Nursering Nusky Nussi Nust +Nut Nut +Nut on Nieve NutStains +NutZak +Nutgood +Nutipaa +Nutjes Nutrino +Nutta Nutu Nutviper +Nutwic Nuubby1 Nuubi85 Nuuh Nuuhi Nuux Nuwanda +NvMe NvMy +NvMy Madness NvUs +Nvh Nvidas +Nvious7 +Nvr Ironman Nvsh Nwkz Nxcv1 +Ny e +NyThinIsFine Nyaa Nyalesh Nyam2 Nyannoid +NyarkThunaki +Nybocs +Nydarb +Nydde Nydloh Nyesqui +Nyholmeen +Nyk X1 Nykat Nyler +Nymlonn Nymph Nyppe96 Nyquistt +Nyranger99 Nyraxion Nyrkki +Nystuen +Nythan Nythos +Nythsama Nytrate +Nytrogen Nyula +Nyx Avatar Nyx296 +Nyx_TrueShot NyxxiePixxie +Nz Str +Nz Boy 96 +Nz Str Nzfisher +NzhiaT Nzpkers +Nzz +O C M W +O E M +O G Herbs +O G RasTa +O K E Y +O L D Z E +O Lewis +O N W +O O 9 +O O O O O O +O Rodrigo +O TUGA LINDO +O X Y G 3 N +O ctopus +O dhran +O hio O l m e w +O m a r +O o g a +O s s e +O stake +O-Scape +O-Six Newb +O-SobaMask O0BY +O5O5 +O64 +O7 offroad +O93 OBD2 +OBGYN doc +OBKush69 +OBLV Empson +OBLV KKRKAT +OBLVPIPEBOMB +OButterstick OCBP OConnell ODarnUFail +OEM +OFFS NFS +OFS I +OG AlanEspo +OG BlowsCOX +OG Boney +OG Ezena +OG Germ +OG Greenery OG H4zed +OG Horizon +OG IronAaron +OG K1ng +OG Kassu +OG Kenobi +OG Mr Crispy +OG PvMer +OG SUOMINEN +OG Sage +OG Soul +OG StrongBoy +OG Totti +OG not OJ +OG-Bebardo +OG2 OGBloodFam +OGJezus +OGKingRavenZ OGLman416 OGRockstar OGTeacher OGThicPickle OGWhitePanda OG_Westside +OH NO PLZ +OHC +OHP +OH_mes OHv3rdose +OINK OINK XD +OId Nite OJ AVENGER OK Thug +OLD MAKAVELI OLUTMIES +OLY Krzanich +OLlVER +OMEGAPEG +OMG U DID IT +OMG a GIM +OMGstepbwana OMNIV0RE +OMX OMYLANTAAAA ONCES ONDERBOKSEM +ONE EYE Q ONEBIGJOKE ONEM0REBEER +OON HUMALASA OOOGAABUNGA +OP Yoyoei +OPEN NA N00R OPIronman OPName +OPP2 +OREG0N ORLZU ORlCHALCOS +OS Anthony +OS BAKA +OS Badger +OS Flexin +OS Gdtn +OS Glory +OS Hadley +OS Iron Life +OS Jordan +OS Lou +OS Maroko111 +OS Masochist +OS Nat +OS Reecy +OS Versace +OS Weesh +OS dah +OSBudknight OSBug OSCannabis OSCastleWars OSColbyStock OSGingerkiin +OSGraham OSGrindscape +OSHH OSRASS -09Reaper -09Rhys -09Rvape +OSRS +OSRS Brody +OSRS Cyze +OSRS DANI +OSRS Denmark +OSRS Dresse +OSRS Hostage +OSRS Jim +OSRS Joshua +OSRS Keegs +OSRS Moey +OSRS Rapaaja +OSRS STIMPY +OSRS TOBI +OSRS Tired +OSRS Zezima +OSReaper OSSD OSSoulwars +OS_AndyB +OS_Jazzy OSzerox +OTF SPRINKLE +OUTSlDERS +OWE N OZANN +O_o +OachKatzal +Oak Cliff +Oak Tree Ted +Oaonui +OasisRS Oathbringer Oathh +Oatmels +Oatre OavTXXrC2Bc +Ob1tuary ObZenpai Obama Obama2014 Obanana Obby +Obby Apples Obby Cam +Obby Cape +Obby z +Obby200 Obedar +Obelisks +Oberschicht Obese +Obese Scrub +ObeseMaurice ObeyBdubb +ObeydaWalrus +Obfuscatiion +ObiHiGround +Obib Object62 +Objection +Objektas Objektijuht Oblitergator +Obliticks +Oblivion Oblodzisz +Obloid Oblv +Oblv Jass +Oblv mobi +Obnoxious Oboi +Obos +ObscureFruit ObscureHeart ObscureLogic Obscuremelon Obsel Obsidian Obsidian222 +Obtain Shade Obvious Obviouseboyz OcaJomba Ocachobee Ocara +Ocara Debrew +Ocarious 2 Occ0x Occcy +OccrumsRazor Occulent92 Occupation Occupational Ocean +Ocean Fog OceanMachine Oceanside Oceanux +Oced Ocellari +Ocelvon +Ocg +Och Ochen Ochit Ochrie +Ockult +Oclusvision Ocra Ocst +Octagawn +Octane07 Octane09 Octanes OctarineJake @@ -16587,155 +34833,375 @@ Octave Octavia Octet October +October 12th +Octoberain Octopossy Octoppus +Ocuflox +Ocular Rift +Oculist +Ocyd +Odang +Odawg121 +Odd Focus OddPhallus +OddSaus OddSnipa +Oddjob46 +Oddloop Oddni Odeee +OdenKozuki OdensWinterG Odeon Oder Odezt Odin +Odin Slayin +Odissious +Odiums Champ Odlaw Odonodb +Odophru +Odors Odssy Odsza +Odyssey Fe Odyssey310 Odysseyyy Oenhausen Oenonymaus +Oepeloetje +Oepsie +OetOet Oetje +Of The Rune +OfThePirates +Off Pace +Off Rate +Off a Bar OffMyChest +OffPeesh Offer Offering Offers +OfficerTFist Offka +Offlime +Offret Offset OffsetPaul +Og Voldemort OgAcco OgChrisCudi OgDankRiddim OgHood +OgSephiroth Ogazumu Ogg25 +Ogisek +Ogkek +Oglong King Ogmj Ogopogo5000 Ogre Oguussie +Oh Bee One +Oh Billy +Oh Canadian +Oh Daddy Yes +Oh Edgeville +Oh Gzuss +Oh Hi Marc +Oh Im Ben +Oh Its Toast +Oh No Oreo +Oh So Alone +Oh Tylor +Oh Vacancy +Oh sh Oh4Sure OhDannyBoii OhEmGe OhHaiMark OhHeyGrant +OhIScott +OhItsMatt OhJezuz +OhLookABot OhManOhJeez OhMyJosh OhMyShoulder OhNo OhNoMyHymen OhTurtle +Ohbi +Oheck Im Fe OhhCaleb +OhhMikey OhhTiS Ohio +OhioTexas +Ohlander +Ohm Calvin +Ohm Free Ohpiate Ohseeya Ohtoodles +Oi +Oi Matey +Oi You +Oie Oifey +Oij Oikeisto Oikis +OilHawk +Oiler1k Oilertay +Oilux OilyMeat Oinky Oishii +OisnesA Oispa +Oispa Kaljaa OjibweJohn +Ojigkwanong +Ojman +Ok Bud +Ok G +Ok Jose +Ok a y +Ok-sail zip +OkYh +Okadolf +Okageo Okaiko Okay +Okay Mommy +Okay Papi +OkayCCal OkayTwilly +Okeechobee Okeydoke Okino +Okke +Oklahoma Rat +Oksa Okupant +OkzobRS +Ol Mucker +Ol Salty Dog +Ola Nordmann Olajuwon +Olav Jr Olav2002 Olaveraaa +Olaviitsio Olbyy +Old 9m9a9 +Old Abe 101 +Old Anao +Old Beer +Old Bill +Old Birdy +Old Boy +Old Champ +Old Dabs +Old Delf +Old Dry KBAC +Old El Paso +Old G ordao +Old Greggg +Old Jedi +Old Lancer +Old Man Bob +Old Man Cris +Old Man Rabb +Old Maxie +Old Mog +Old Monkey3 +Old NlCO +Old Nutbag +Old Olorin +Old Pixels +Old Player22 +Old Rangoon +Old Sbi +Old Schooled +Old Toaster +Old Yammu +Old man Kent OldBae OldBlueish +OldBruce +OldDirtyMan +OldFishLife +OldGamma OldHitta +OldHulver +OldQwaltz +OldRzzlTrog +OldSkewlRS OldSkoolKush OldSteinlein +Oldbaldman5 Oldburnzy Olde +Olde Nite Oldemark +Oldinsh Oldizjr Oldman +OldmanP +Oldmanhandz Oldschool Oldschools OldskuleRS +Oldsusik +Oldtimate +Oldwhiteman +Ole +Ole IrIsh +Ole Martin OleFrumpacus +OleMusky +OleStinkBait +OleTejas Oleg Olei Olemand8 Olen +Olen Iron +Olenpromees Olertomb Oles OlesDpaul Olette Olfre31 +Oli +Oli xo +OliTheFarmer Oliivi +Oliivi Rouva Olimpus Olios Olivas Olive +Olive You +Oliveinho +Oliver GIM +Oliver The G Olivia Olkikalsar +Ollie 01 +Ollivanders Ollonjonny Olly +Olm Alone +Olm Likes me +Olm Nut +OlmMyGod +Olmar Olmek +Olmie Wolmie Olminous Olmlet Olmlit Olms +Olms Daddy OlmsNutz Olmsbish +Olmspetwhale +Olobor Olrox +Olspa Rahkaa +Oltsu Olvi +Olympic 33 Olysian Olze +Oma r +Omaci Omar +Omar Kai +Omar Khaldun +Omar Uchiha Omarinski +Omatunto Omaxiu +Ome Sjors Omega OmegaBeast OmegaRs3LuL +Omegathletic +Omegatron +Omen Forrest +Omena3 +Omerta Omfg +Omhellz yea +OminousMan Ommetje Ommy Omne Omni +OmniRick Omnicide97 +Omnient Omnivaw +Omolon +Ompikuusi +Omroep Max +On Blocks +On Rate +On Steven Dr +On Tv +On a Bar +On the alt +OnAMeme OnDeaTHrow +OnPoint OnRedPanda OnTheEdge +On_log_n +Onana Onbekend Onbekende Once +Oncey Onchune Oncle +Oncle Jazz +Onderwerp +One Arm Chin +One Eyed Owl +One Lazy Cat +One Leaf +One Off +One Piece X +One Record +One United +One Wolf +One au One life lad OneBadNWord +OneBoxBox +OneCallBTW OneClickMan +OneDayIs2Day OneFalseStep OneFive +OneForAll8th OneFunkies OneInchDong OneInchPeen OneJohn +OneLeg1 OneLifeGiven OneLostMain +OneManBand OneManBucket OneManNoob OneManTeam @@ -16748,201 +35214,383 @@ OneSillyBoi OneSnappyBoi OneTera OneTickAway +OneTickBrick +OneTickyBoi +OneWhoKnox +OneWish Onebuc +Onedeadthing Onehidefrog +Onelaughbob Onesecafk +Onesome Onetick Onexia +Onfight +Ong Gia Onidhre Onika OnionGoggles Onism +Onitir +Onkdah +Onke +Onkel Dunkel Onkelleif +Onkologen +Online Coach Only +Only 1 Iron +Only Bussy +Only Crazy +Only Fight8 +Only Scotch +Only Victims +OnlyBans OnlyBrand +OnlyChompies OnlyCraig +OnlyDanss +OnlyDrags OnlyFFAns OnlyFans OnlyFansss +OnlyFlies +OnlyGain +OnlyJars +OnlyJoel +OnlyJordann OnlyKingKoob +OnlyKisses +OnlyKnuup OnlyPepsiMax OnlyRussell +OnlySolos OnlyTheCold +OnlyWithTime Onlyfans Onlyfarm OnlyyMike +Onn Onno Onoezeleer Onoin Onox OnthatGrindd +Onu-sama +Onubis +Onus Onuzq +Onxaba Onya Onyksi Onyx +Onyx Farming +Onyxe Onze +Oo Step Back Oodenssiii Ooelluoo +Oof Dankus Ooft Oofus +Ooga Booga C +Ooh Skill Em Oohf Oohfunkyme +Ooiboi OokOokStrats Ookfried Ooktism +Oomp Oompy +Oooh Baby +OopsiePoopsy +Oopsralla Ooskabible +Oouu Weee Ooyf Ooze Oozymooz +Op Closed +Op Wout +OpGetGood +Opa Bravo +Opa Snipes OpaJei +Opacki +Opal Mafia +Opalfruen Opatsuno +Opel Vivaro Open +Open GL +Open osrs +OpenAI GPT4 OpenTheTill +Openpuppygo Opera Operacional Operetta Opex Opgezwolle Ophelia +Ophelia Prim Opiez +Opihr Opinionated +Opitz OpiumWard +Opkikkeren Oponn OppaSky +Oppai Heart +Oppai OwO Oppanox Oppheng +Opposite OppositePoop Oprego +Opreus +Ops +Opt Optic +OpticAGS +Optical itch Opticplex Opticplex09 +OptimistLuke Optimus +Optimus Pork +Optiver Opts +Optus Optyfen Optyfengauww +Opus Deo +Opus149 Opus22 +Opyomi +Oqagin-san Oqlak OracleOf +OracleStud Oraclez Orange1213 Orangeboy333 +Oranje Panda Oranqe +OrbWeaver Orca Orcanater +Orch Orchazm Orchid Orchidzx OreSpasm Oreano +Oreeezy +Oregon Weed Oreinstein +Oreo Empire +Oreo UwU +Oreo Wafer Oreos OreosInMilk Oretizm Oreton Orezzer Organic +Organic Hemp OrganicOnion Organics Orgasmique +Orgo +Ori lion Oriaks Orienterare +Origens +Origin Puff +Original BA +Original Blu OriginalGoat Originaljim Originexo Orilion +Oringl Wafle +OriobanaOuhi +Oriole Orioles OrionDragon OrionRenegd Orjan +Ork Merchant Ormi +OrngFish OrngeManBad Orodyn +Oronius +Orphaaan +Orphaan Orphan +Orphan Annie +Orphan Soul +Orphan Twin +Orphan8r +Orpheus +Orpoerpo Orrey +Ors striker Orthago Orthiss OrthodoxJoo OrthopodMD +Orvud +Oryx Aksis +OryxZebu +Os Him +Os Hitman +Os Rolex Os SMDJ OsBlackBolt OsBrody OsBudknight +OsDave +OsIronCrash +OsRs Vibes Osav +Osbenji +Oscar Jacob Oscarnight90 Oscietra Osdorp +Osetek Osgu Osgub Oshawnasi +Osidric Osimhen Osiris Osjuicey Oskar +Oskar XVII Oskari Oskinathor Osmo +Osrs Bane +Osrs Dimitri +Osrs Goku +Osrs Mistren +Osrs Woolley +Osrs2007 +OsrsBawlz +OsrsDavid +OsrsVibes +OsrsWiki +Osrshabibi Osscar69 Osseb Ossi666 OstBakarn Ostengar +Ostentatio OsteoSoon +Osteonics Ostidecriss4 +Ostmumten Ostracized Ostrich Osumi +Osvo1d Oswaldorun +Oswin Oswald +Osxu47 +Osykoo Osyrs +Otago Nz +Otakar Otaku +Otan Olutta +OtarsBeast +Othantos Othelllo OtherJackets +OtherWize Otho +Otomycosis Ottarl Ottchy Otter OtterMG OtterMan Otto +OttokarIII +Ottoman1907 Ottwanbre Ottzor Otyugh +OuchIFarted1 Oucho +Oud Nieuws Oudenophobic Ought OuiDaddy Ouija +Oukashi Oulusta +Oumaji Oumarou Oumu Ounaaja Ouncie +Our ClapTrap +Our Kid +OurLordAllah OurPage +OurTbow +Out Of Herbs +Out of Town OutOfBreath OutOnBail OutR Outbid +Outblasted Outblasting Outbox +Outbreak46 +OuterBodyXP +OuterShock +Outgrinded Outie OutlawBTW OutofQontrol +Outofcontrol Outohere Outperform +Outplayy Outra +Outs Outsider058 OutsidrR OuttaThePan Ouze Ouzi +OvO Ovaryacted Ovaryacting Ovela +Oven Dish +OvenGold +Ovenschotel Over +OverRoid +Overburner67 Overcame Overclockers +Overdose1911 Overhand OverkillWill +Overklokkd Overlegen Overload Overload_inc @@ -16953,155 +35601,334 @@ OverpowTV Overprepared Overrated Oversight +Overspan +Overspec +OvertPyro Overtaxed Overthinker Overtus Ovid +Ovl +OvrLrd Zaros +Owed OwenFever +Owip +Owl Full +Owl MD OwlMyLove +Owlea +Owlpeth +Owlson +Owly +Own Risk +Own Skillz70 Own617 Ownage Ownd Owned OwnedByMK +Owner Previn +Owning Owning4babes +Ownsz Ownt Ownyouall6 +Owusu Owwi +Ox Prez +Ox0 Ko0ol O0 Ox310 +OxStache Oxct +Oxcy +Oxeon OxideIon +Oxidising +Oxidizing Oxiduck Oximas Oxit Oxium +Oxjenks +Oxoi +OxyCottN OxyDream Oxycotton Oxyoxyoxyoxy Oyrn Oyster +Oyster37 Oyugock Oyvin Ozaiii +Ozak Ozcred Ozeana +Ozenc Oziarch +Ozin OzirisRS Ozium Ozmone +Oznerbon +OzokuLight Ozuhan OzyMex +OzzMate +Ozzb0 Ozzerro Ozzie Ozziemate Ozzio Ozzy +Ozzy Hobbit +Ozzykye Ozzys Ozzzyy +P 0 W N E D +P 0 E F +P 0 R G I E +P A M K A +P A P Z +P A R N Y +P DIRTY SODA +P EDRAO +P Eng +P I T +P K +P O L I S H +P O T A T O +P O V I +P Purts +P R O X +P R S +P V M Lew +P W A Y +P Y R O +P a n x o +P eachy +P erfect Ten +P h Z o r o +P i +P ix +P ixe l +P o z e +P oH +P ockets +P r o g r am +P udding +P ure +P uss Wizard +P00ksalukes P0CKETPUSSI +P0ES P0PE +P0RKB0I +P0ST HUMAN +P0TIONS +P0TTER P0TUS P0ddy +P0nti4c P1CC0L0 P1CK3R1NG P1Hat +P1SSBANDIT +P1ZZA EATER +P1ZZY +P1ckleR11ck +P1ssDr1nk3r +P1stols +P1tts P1zzaTheHutt +P1zzaa P2Chill P2D2 +P2P Race Car +P2w Scape +P3C P3RKELE P3RLICH P3RMMUT3D +P3T3 IS G0D P3TAR P3aceful +P3loce P3rcy +P46 +P4CK P4DLA P4RKER04 P4cH P4sk P5ythix +P6D +P7DRO +P9 +PA N I C +PACK WATCH +PACKETLOSS +PACTV PALAK1KO +PALL3N +PALSAM PALSANMAKI +PANTY DROPER PAPA +PASTORIKONE PATPATMAX PATRlOOT PAUL +PAULWALKER +PB Barbie +PB Dauntless +PB J +PBT Yumi PBTM +PBnJ +PC PrincipIe +PC0TE PCCZ +PCDO +PCE PDGA PDizzleworth +PE strategy +PE3ST +PEAMINlSTER PEARSON92 +PEC Zwolle PEEKYNUMBER1 +PEEinAcup PERMA +PERSE PET3R +PET3R PAN PETEAIR +PF Killaz +PFOF PFalciparum +PG MAIN +PG-13 +PGA_Pope PGMid PGmeupe93 PH1SH3DB4N +PHGomes PHLP +PHURY PIAN0 PICC +PIITAA PING +PINKYxBRAIN PITFIGHT PJ Salt +PJRovers +PK NOOB BR 2 +PK Shark PKTHEOTHER PKing PL4T1N4 +PLANET SHlT +PLAYER827324 +PLAZID PLSDONTPLANK POGPOGPOG POGvM POMP3Y +PONYRIDER73 POTTER +POTUS DJT +PPale PPorappippam +PQMF PQWNEM12XXSS PRADA PRAISE +PRAISE FOOT +PRAISEHELIX PRETTY +PREW0RKOUT PRIM0B0LAN +PRIZ0NM1KE +PRODaintOFFu +PRlNCEE +PSMark +PSO Pokie PSRB PSTjager +PSY OPERATOR PSacz PTKNT PTMN PUBG PULL +PULL A GLOCK PUNANYCEZZ PUNANYMASSIF +PUPPY SIT +PURP xx +PURPLE MAN X PUSHUPS PV60 +PVM PLUS PVP +PVM Zem +PVM lifee PVMGrim PVMramranch PVPJACKUS PWNJoLee PWNl +P_rple +PaIe Nimbus PaIm PaJau PaPaSaucee +PaPePiPoPuPy +PaR0 PaShango PaTeraNauu +Paaaants Paaatriick +Paarse Kat Paarty +Paatse PabIoEscobar Pablana +Pablo Q +PacDan PacYakJack Paca Pace +PaceYourself Pacellino Paciar +Pacifico0ler +Pacifist Pack +Pack Yarack PackAnother1 +PackYakistan Packapunchd Packers +Packers Ammy +Packfan35 PackingCope Packmanjr PacksOfBagel +Paco 41 Pactii +Pad of Note +Paddy Simcox +Pademelon Padge Padraig Padrew Paduann Paehkisfe Paem +Pafffy Paffio Pagan PaganFox @@ -17110,60 +35937,115 @@ Page Pagman73 Pahapukki Pahizz +Pahys +Paid 4 Paike Pain deliver +PainInTheAx +PainRain Painb0ws Painovoima PaintBoxx Painta Painted +Painz Wrath +PairODocks Paixo +Paiyrd Paizuri Pajaaay +Pajama0sam Pajaripipari Pajita Pakaika Pakanajumala +PakasteOlut Pakford Pakmaniac +Pakr04 Palabutoh Palad1um +Paladijn Mb Palapak +Palataan Palatro Pale +Pale Marble +Pale Player PaleCocoon +Paledeano +Paleek +Paleek sucks Palenaatio +PaleoRanger PalestineHC +PaliRebel Paliperidone +Paljad Pallas +Pallas Cat +Palle Panik +PalliJarn Pallihintti Palllister +Pallof Palmboom +Palmboom95 Palt +Palworld CEO Paly PamPams Pamdora +Pamela Isley Pampas +Pamz +Pan Biceps +Pan Jawel +Pana Sicknez Panacea +Panal +Panarin +PancakeOSRS +Pancaker Pancakey Pancukeshi Panda Panda Godz +Panda Pride +Panda Rua +Panda Seany PandaBier PandaBrommer +PandaKIKI PandaMan PandaMcfanda PandaPowa +Pandasadge Pandaux Pandaz Pandeh +Pandemicbowl +Panderal Pandiman +Pandini_O +PandorazBox +Panelka Panera +Panesii +Panette Panferno7 Pangolins +Pangs Panic +Panic King +Panic Papi +Panic Switch +PanicThenRun +Panicupdate +Panini Panixate Pankekee +Pankki on Panky Pannu Pano @@ -17171,32 +36053,82 @@ Panoople Panorramix Panqueques Pans_Gaming +Pansendoras PansophicaI Pantaloon +Pantelic Pantera1230 PanteraWalk Panthalassa +Panther_Cap +Panties D0WN Pantix +PantlessDuck +PantsShitter PanzerMarkV +PaoZi41 Paoul +Pap Smoke Papa +Papa Beat Me +Papa Beer +Papa Chibs +Papa Falco +Papa Glock +Papa Hitman +Papa John +Papa Kev +Papa Peck +Papa Penguin +Papa Raniel +Papa Rook3 +Papa Scape +Papa Shaydee +Papa Sheek +Papa Snacks +Papa YY +Papa Zuk +Papa jinx +Papa tim PapaCart +PapaFozzy PapaJohn PapaJoyce13 +PapaMoxie +PapaParsley +PapaSarducci Papabeer +Papagecko Papaija Papalotee Papapa Papaya +Papaya Pete +PapayaGod PapayaPetee Papegaaitje Paper +Paperbag Papi +Papi Baz +Papi Chop +Papi Feb +Papi Fish +Papi Gains +Papi Okapi +Papi Sam +Papi Sean +Papi Six +PapiTron Papicito +Papicodone +Papido PapieLexus PapierHier +Papierschere Papii Paplip +Papo Sauce Pappa Mauly PappaPizza Pappanopolis @@ -17204,65 +36136,120 @@ PapperNapper PaprikaBoi PapuIsThatU Papukaia +Papyea Fruit +Papz Mage Paqan +ParKy_ParK +Parabhjeet Parabolic Paracleis +Paradoos Paradox +Paradox 2277 +ParadoxAU +ParadoxCrux Paradoxal Paralimpian Parameters Paramost +Parandrus Paranosys +ParaplegicIM Parappa ParasiteHunt Parasitoid ParasoxX Parazino Parc +Parc Ferme +ParchedToast Parciparla Parent +Parfour Parhaat Pari +Pariera +PariguayoGL Parikh +Parikkala Parish +Parjesh Park +Parkchae Parkehh +Parker177 Parker1770 ParkerSquats Parkerrr Parkuh Parky Parkz +Parley0 ParmsG8 +Paroni007 +Paront +Parotis Parox3tine Parrot +Parrot Champ +Parrot Le Fe +Parrot Poop +Parsecs Partey Parth4903 Parthnix +Particulae Partly +Partonax Parttime Party +Party Fraud +Party Pete +PartyRock +Partyhaty Partynexdor ParuParu Parvovirus +Parx98 +Parxe kurita +Parzival32 Pasadinas PashkaPushka Pashmina Pasianssi Pasiiba90 Pasikaustes +Pasito PasitoBandit Pasje +PaskaJatka69 PaskaneHomo +Paskie Paskis Pasmo +Pasrules Pasta +Pasta Lance +Pasta Mancer +PastaShel Pastabrain +Pastafreezer Pastagonia +Pasticcione Pastor Pastry Pasu +Pasza1357 +Pat NoScythe +Pat The Nerd +Pat the Cat +Pat twumasi +Pat_y +Pata2006 +Patalopodus +PatchRS Patchley +Patdog666 Pate Patella PatentedName @@ -17271,6 +36258,7 @@ PathTracker Pathogen Pathways Patio +Patious Patmantheman PatoVelaz9 Patojo @@ -17279,19 +36267,34 @@ Patrexion Patrician Patrick PatrickClick +PatrickLaine Patrickp35 +Patrik Laine +Patriot88 +Patriotscape Patriotsp +Patriottj +Patrolliin Patronique Patrouski +Patryk 1 Pats Patsey PattMyGun Patta1 +Patte Pattos +Patty C +Pattycakes19 Pattyrick8 +Patyfatycake Pau1ekas Pauk Paul +Paul 0 +Paul Beer +Paul Ski +Paul0 H Paul007 Paul90333 PaulD @@ -17299,13 +36302,23 @@ PaulOH10 PaulTheRabbi Paulchritude Pauleee +Paulest Paul PauletteRose Paulius +Paulk23 Paulrat +Paulrat 3 +PaulyNFS +Paum No Xeem +PauperPlayer Paupy +PauseGuy Pauwels +Pauwels v2 +Pavies Pavio Pavla +Pavlacci Pavlvs Pawggrs Pawgz @@ -17313,46 +36326,80 @@ Pawko Pawlmeister Pawlu Pawlzer +Pawn 69 +Pawn M8 +Pawrie Pawwsy +Pax Mundus Paxin Paxy +Pay2ClickLol PayMyDeedFWD Payne_Trayne Paznos6 Pazuta Pazzo-Repack Pb Fe +Pb and Jd Pbby Pbkillua +PbyOne PdaddyReborn +PdfPKerNoGF +PdiddyReborn +Pe en +Pe t er +Pe1ipp3r Peabody PeacandLove Peace PeaceNLove31 +PeaceOfMind Peacebuild +Peaceer Peacefrog +Peach s PeachKing +Peaches5000 Peachh Peachhh +Peachy Tank +PeachyBrute PeachyDean +Peaco Peacoat Peak +Peakado Peake +Peake7 Peakleaf Peaky +PeanutBTW Pear +PearOfApples +PearlNechlis +PearlWeed +Pearlito Pearshaped9 Peasant +Peasyy PeatMoss Peatie Peba +Pebbex Pebblez PebisGuan Pebl +Pebu Pebz PecanBread11 +Pecker Punch +PeckerFish +Peckerflexer Pecki Peco +Pecs +Pectorialis Pecuni75 Peddler Pederbuus @@ -17360,30 +36407,57 @@ Pedestrian01 Pedro Pedro5901 Pedtato +Pee Jay Salt +Pee Kay Err +Pee Mud +Pee On Irons PeeJayPlayz PeePee Peeanut +Peeble +Peeeeeekaaaa +Peeke Peel +PeelMyCarrot PeeledBanana Peely Peen PeenLover61 +Peep Taimla Peepo +Peepo Parker +PeepoPants +PeepoRiot420 +Peeps Cx +Peepzilla PeerTheSeer Peernicus Peeshuuu +Peest PeetrusEst +Peevish Peevy Peezerthecat +Peg Boots +Peg My Arse +Pegasian Pegasians +Pegasus51C Pegi18 +Peha Pehkis Pehmolelu +Pei898 +Peighniss Peiin Pein Peipeilaile +Peipi Pekaia +Pekays Pekdon +Pekka Elo +Pekka-Eric Pekka557 Pekkaa Pekkaad @@ -17393,105 +36467,211 @@ Peksaad Pelaa Pelaaja1 Pelco +Pele Marreta +Peli Moapa Peligroso Pelikaen Pelippper +Pelirroja Pelmee Pelosi +PeltiPirkka Peltolammi +Pemiel +Pen Sir Pena +Penally GIM +Penance Boom Pencelis PencilVesta Pendax PendulumC Penetrader +Peng Lightey PengMaster +Penge1616 Penguin +PenguinBelly Penguino Pengwim +PeniAnus +Peniel Peninsula Pennstate Pennsylvania PennyPinchnJ Pennybag +Pennycord Pennywise Pennywise070 Pentagrammi +Pentektonyx Penthera +Penthrox Penthus Pentu +Penward Peopledud Peoples111 +Peor +Pep Kroket Pepar +Pepar Kakan Pepclub101 Pepco PepeGUH PepeHang PepeLaughing PepegaScape +Pepeh Pepemiguel +Pepo Is Me Peppey +Peppi PeppiEnSossi +Peppusieni +Peppy Pete Pepsi +Pepsi Maxed +Pepsi Maxia +Pepsi575 +PepsiMaxL1me PepsiTwist Pepsies Pepsipowars Pept +PeptoGlizmol Pepuszka126 Pequenaud Pequette +Per Ke Le +Perc 3m +Perceive +Perceptivity +Perch Curry Perchlorate +Percy Grail +Percy Nash +Percy Turner +Perduh +Peremees Perfect +Perfect Dark +PerfectCurse +PerfectJerni PerfectProof +Perfectaaa +Performax Perfringens Pergert +PerhapsGuy Peridots Perikles460 Peril Perkinatored Perky +PerkyPorker Perlen +Perm II Mu8e +Permabulk Permded +Permedd +Permer Permuh Pernix +Pero Scoped +Perox Perp67 Perpl +Perplox Perrault +Perrif +Perry Hubes +Perry T Persephatta Perseveranca Persian +Persilja +Perspective +Pert +Perterritus +Perth +Pervy x Sage Pesiz Pesoprkl Pessimism Pest +PestGodd Pestilance7 +Pestilent Bt Pestle +Pet Alpaca +Pet Awowogei +Pet Cape +Pet Crusader +Pet Eric +Pet Hunta +Pet KBD +Pet King +Pet Luck +Pet Meowtain +Pet Smuggler +Pet The Cats +Pet Ty +PetBoost PetCorp +PetHuntBrad PetHuntGrind PetHunta PetHunting PetPlease +PetSpooner PetUrSausage +Petal Petalite +Petar228 Petarss +Petched +Petchy inis +Petcord PeteRePete +Pete_901 Petega +Petelgeuse1 +Peteniice Peter +Peter DIY +Peter Dupas +Peter Kent +Peter97 +PeterLimbeek +PeterPlanker PeterRS PeterTheSalt +Peterh Peterm +Peterszoees +Petey pleb Petit PetitKeBeQ Petite +Petite Poire Petless +Petooted Petpet +Petrenkovitz +Petricsh Petriq PetroX77 Petrosian +Petting GIM PettyName Petuhh Petz +Peuramaa PewTheMeow +Peweherman +Pewming Pex3 PexBoi Pexci @@ -17499,56 +36679,97 @@ Pexezz Pexterity Pextill PeytyPoo +Pez xX +Pez za Pezy Pfiny Pfister Pfle Ph0g +PhD Fred +PhD Funk +PhD in TCG PhaMaTics +Phader Phaero Phai Phainesthai +Phalamon Phallic PhallusBigus Phamia Phamit Phant +PhantomDred PhantomFiend +PhantomLogic PhantomVS +Phantombear7 Phanton PhappleSauce +Pharaoh Chad +Pharaoh Ion Pharmafia +Pharqen Phase +Phasedd Phasing Phasmatus Phasmatys Phat +Phat Dik +Phat Pat PhatAsher +PhatFrat Phathead Phatman348 +Phats PhatsoCallum Phatt Phatty Phattyftboy Phaxe +Phd n Arteez Pheasant +Pheasant Egg +Phels PhenXX +Pheniex2 Phenomenal +Phensa +Pheonixess +Pher Phernix Phetty Pheus Phex Phi184 +Phibes Phil +Phil Coulson +Phil Mcrakin Philip +Philip424 Philipp Philkwl +Phille +Philli Blunt +Phillies +Philliez Phillip +Phillip R Phillipp Philly +Philly Sucks +PhillyFiller +Philoi Philosophia +Philou +PhilsBent +PhilsUnlucky Philth Philtration +Phin_C Phinxu Phinz Phipple @@ -17556,90 +36777,181 @@ Phishn Phizo Phlaja Phlegmy +Phlemming Phloppster +Phlopsy +Pho Z Phobias PhobosDeimos Phoibos Phonatik Phone +Phone Number +Phone Scaper PhoneHC Phonebook Phoneman +Phools Phoque +Phor Skin Phosani Phossa +Phostus Photographs Photon Photonic +Photos +Phragasm Phrak +Phrexyian +Phrez PhriarPhace +Phrizo +Phrocks Phsteven +Phug +Phugma Phuke Phuket Phwan +Phxrm Phylum +Phyrlo Phyro Phyronex Physics +Physics Jedi +Phytz Phyzxs +Pianio +Piano Hands +Pianoanddrum +Piantissimo Piasa +Pic of Feet +Picante Piccoloownsu Piccy +Pichi Slayer Pickl3Lover +Pickle Josh +Pickle Vick PickleKez +PickleSaucy +PickledWater Pickles4Me +Pickles69314 Picklzz PicnicBomber +PicoDico Picolo PictureID +Picuu +Picuwu +Pidbull +Pidbull 1 +Pidgeot18 Pidgey +Pidgy Pidiot +Pidrux +Pie +Pie Dish +PieGPT Piecekeeper2 +Pieces Pieck +Pieerio Piemaksa Piemans +PiemieJurrie Pieper +Piepieweenie +Pier Just +Pierced Wolf +Pierenbadje Pierniczek00 +Piestove Piet +Piet 21 PietKrediet Pieter +Pieter Zwart +PieterPep Pieterjan Pietermans Pietn4 +Pietrangelo +Pietrnogiets Piety Piety Prease Pieza +Pieza de oro Piffen Pigeon +Pigeon Soup +Pigeonmight +Piggi Smalls Pigi373 Piglette +Pigman461 +Pigmeu +Pigpikerush Pigpuffer Pigs Pigwardfrog Pihl +Piip MEEGA +Piipar Piirivalvur +PikaChar222 +PikaChin +Pikachewed Pikachos Pikachu +Pikachu87 +Pikachurine Pikachuz Pikkets +Pikkett +Pikku +PikkuSheikki Pikkupbrix Pile +Pile O Poo +Pile o Dirt Pilgrimage Pili +Pilli +Pilliad Pillifnutten PillowBoy PillowCowCow +Pillukarva23 Pilmir Pilodro PilsToTheMax Pilsners +Pilum +Pim Pam +Pimay +Pimfortune Pimp +Pimp Ralpert +PimpWeazel +PimpWhistle Pimpert +Pimpin Thots Pindaro Ping PingFlopped Pingu Pink +Pink Avocado +Pink Bar +Pink Cow34 +Pink Iron +Pink Lube PinkDrinkSip PinkShopRag PinkVoidZ @@ -17650,99 +36962,205 @@ Pinkywinky9b Pinkyx0xo PinndOutGoon PinnkBunny +Pinnn Pinpi Pinquana Pinsamt +PintOfMilf Pintel +Pinterests +Pinu +Piolin +Pioniers Piovendo +Piparikissa PipeQlo +Pipluppp +Pipo-badeend Pippinmary PippleNinchy Pipposan Pippuri +Pir Alain +Pirat Pirate +Pirate Kanye +Pirate Patch Piratey Pirelly Piri +Pirig0 Pirihuora666 Piripaque +Piripippeli +Pirjo +Pirjo69 +Pirk the pk +Pirkka Olut +PirkkaJokeri +Pirkkamies +Piroskinha PirpleSlirpy Pirres +Pirulitim Pisatronas Pisau +Piscarallius Piscis Piscolaz Pistacchio Pistoliftero +Pit Stain +PitGamin1311 +Pita Diver +Pitbulterje Pitby Pitchfork Pithikos +Piticarus Pitiless Pitinator Pitt +Pittaaaaa PitterPattr +Pittsburqh +Pitudin +Pivach +Pix Pixelle +PixarMarsTTV Pixel +PixelMuffin +Pixie Henge Pixieragnar Pixkekoa +Pizdec Pizza +Pizza Addict +Pizza Box +Pizza Flip +Pizza Mang +Pizza Patron PizzaDaHutt +Pizzaman5510 +PizzasIron +Pizzler +Pj The Pj +Pjotr +Pjt77 +Pjumi +Pk Bot 37 +Pk M4ster007 +Pk Wit P Hat +Pk3iru_Osrs Pk3r Range 7 Pk996 PkGoW PkMeIfUgey PkTheKid +Pker nr 1337 +PkerTrysHard PkforFun Pking +Pkingranged9 PkmnTrnrRED +Pkmort Pksorwd +Pkyr Pl0xed Pl3b PlELS +PlFFMAN PlGGY +PlHLAJA +PlMP PlMPPl +PlMl PlPPY +PlRATE KING PlSS +Plaagen Plaasda Plaat Placedoemax +Plaeggs +Plaid10 PlaidMarquis Plain +PlainIronMan +PlainNoGood Plaininsane +Plamt Plan +Plan B-gs Plane4611 Planet001 +PlanetPaul Plank +Plank Egger +Plank Sodplo Plank2G PlankForBank +PlankTan Planken bos +Planker +Planking Brb +Plankrunner8 PlanktSoms +Plant Herbs +Plantdaddyy Plap +Plasma Taco Plasmore +Plastikos +Plastiscines +Plat Kont PlatGX +PlatScrub Platinum2008 PlatinumHerb +PlatinumSage PlatinumSeif Platnuim Platnum112 Platpus3000 +Platyroo Play +Play Lazy +PlaySoloBro PlayToAFK Playbunny +PlayedBe4EOC Player +Player 0ne +Player 4519 +Player Gap +Player Name Player235711 Player35254 +PlayerIsBusy +PlayerSlayaa +Playpro5 +Plays high +Playtest Playthious Pleasantries +Please pot Pleasurehole +Pleb Nick +Plebbocs Plebiside +Pleblio Plebnex +Plebs Plebz Plece Plegh Plexasaurus Plexx Plez +Pliemp Pliep +Pliep Ploep Plips Plisski Plixious @@ -17750,210 +37168,412 @@ Plocc Plogbilen Plogdog Plomono +Ploop Plootbot +Plop deez Plopi +Plopr +Plopzorg Plorky Plorr Plostic Plot786 +PlovasYonder +Plovilas Plow Plowthrough PlsAName Plsmrlizard Pluc321 +Pluckky +Plug In Baby Plugatron Plugged Pluggen Pluhdl +Plukketje PlumTickler +PlumbThatA +PlumpGiraffe Plumper +Plundered Plunkton Plunukki +Plupian Plus +Plus Vite PlusZack +Plush Mole Pluto Plutonium +Plutonium 94 +Plym Plz LD Nat5 +PlzGiveBonds PlzRnGesus +PlzSpoonFeed +Pm Me Imps +Pm me to spy +Pm4 eloboost PmFun Pmy39 Pnak PneFc Pnia +PnutButtrJly PoachedLion +PoakedSussy +PoaneFoane PoarIIneemn +Pocholo Pocket +Pocket Cards PocketBandit PocketSock +Pockipickle Pocy2 +Pod x +Podvodnik +Poeiermolke Poem Poenes +Poephoofd Poepi12 +Poepjong +Poepsiee +Poerto Poet1321 Pofka +Pog Dog +Pog isma +Pog-kek-Lul PogChampagne PogMeister PogTato +PogTato Iron Pogba +PogoPochette Pogsled +Pogue4Lyfe Poikanen Point +Point Guards PointTax +Pointstormy +Poison Ives +Poison x Ivy +PoisonCobra PoisonX7 Poisonblack Poisonous Pokaroo2 +Poke Champ3 +Pokeaotics +PokedexNo258 Pokefreud11 Pokemaster Pokemon +Pokemon Ruby +PokemonScape +Pokemonguy16 +PokerPro +Pokerboy19 Pokergod720 +Pokifeet +PokimanesBF +Pokoloko +Pol o Polaar PolakYT +Polar PolarTrip Polarin Polarisi888 Polarus Polarward PolderLatina +Pole Met +PoleVault Polio23 Polish +Polish Eagle Polite +Politoed Polixo Polkapolkka Polkastarter Poll +Pollar i Pollekeuh +PollenJ0ck Polllie +Polllo420 Pollnivneach +Pollo Dry PolloGrande Pollofrit0 Pollum Pollywog PoloG +Poloskyy Polter +Polyamorous +Polycoffin +Polyesterday +Polygonise +Polyhedra Polynomail Polyphobia +Polywoggle +Pomi Xd Pommy Pompeyo Pomposity Ponas +Pond Scum +Pondres +Ponkito +Ponobi Pont Ponti +Pontifex Pony PonyOwner938 +PonyPorker +Ponzy FTW +Poo Socks PooAtPVM +Poobanans +Pooby Bag +Poodle Poofpooh +Poogen +Poogster Pooh +Pooh Breezy +Pooh Say Poohead92 Poohsea Poojabber364 Pook +PookiDaPooki +PookieB3ar +PookieBearOG +Pool Noodlez +Pool Toucher PoolboyFiji Poon +Poon Lips +PoonHandleMe +PoonTangPie PoonTappa Poonanjo Pooned Poonjuu +Pooohie +Poop +Poop Shidder +PoopyRico Poor +Poor Advice +Poor Clicker +Poor Content +Poor Nooby +Poor You Poortom24 Pooscaper PootLoops Pootzie +Pootzie_xox +Pop In Smoke +Pop N +Pop-up ad +PopTheIron Popcornachi Pope +Pope VI +PopeChamp +PopeDenis Popeet +Popepu +PopeyezGain Popfumes Popkorni +Poplo Popovich +Popoyoi Poppa +Poppa Bev PoppaChoonie Poppajohns +Poppin Awf +Poppscotti Poppy Poppymatt Pops24 +Popstar2 Popular PopyHarlow +PorWrx +Pordiosero +Pore +Pork Jerry +PorkCH0PZ Porkkanakana +Porksters +Porrie Porsche +PorscheDaddy +Port Khazard +PortUnionMan Porta Pro +Portal of I +PorterRobnsn Portrays Portsari Portuguese Portvakt +Porunga +Porygon-Z PoseidonDrip Posemann Posh PoshPenguin Poshker +Poshki +Poshkii +Posino Positivar Positive +Positivehp Possessed +PossiblyAFK PossumPally Possumi Post +Post Malone +Post Max +Post-Absurd PostCodee Postie PostmanPatt PostureCheck Posty2k +Pot Gatherer +Pot Of Greed +Pot Often +Pot Sharer +Pot cms +Pot v X PotScape +PotScape CC +Potapto Potato +Potato D Ben +PotatoButGay PotatoLlorch PotatoRangr Potatoqueenn +Potecito jr +PotentJay Potezny +Potge Pothaaai PotionFayD Potland +Potland o_0 PotoSekwati Potoo PotsAlots Potsi +Pottawatomie +Potter Paypa Potth Pottieface +Pottsi +PottuTatti +Potzy Poucher +Pouches +Poucie +Pounake +PoundSandNub PourMeBleach +Poussemoila Poutz Poverty +Povvo Powacat Power +Power Aid 4 +Power H +Power Outage +Power Surge PowerJoe +Powerade Powerdude153 Powerful +PowerfulBram Powerfull Powerlines Poweron6 +Poww LUIS +Powwil +Powxrs Poxuistas Poyig Pozar +Ppl +Pposkyor +Pql +Pr0 Ph3t +Pr0pvm Pr0way Pr1MaL +Pr3par3 2die Pr3ttyW0man9 +Pr3y Pra1seTheSun Practical +Prada Praecellemus Praedy +Praenthos Praes Praestantia Praetor +Prage699 Prahlad +Prairiez PraiseCorn Praisemyrng +Pramikon Prankzy Prari Praskle +Pratt Mario Praxahr Praxys Pray +Pray Salah +Pray for me +Pray t o God Pray4TheWin +Pray4me PrayForDeath +PrayForYou +PrayToAres PrayarN +Prayer Pro Prayerr +Prayis +Praynr PreMDPepper PreNew PreRuined Precise Precixion +Precondition Predaxx Predicition Preedy @@ -17961,12 +37581,21 @@ Pregnant PregnantSock Prelash Prelet +Prelex +Prellifunky +Prem PrematureNut Premie +PremierZarry +Premium Dude Premz Prep Prepared2 Preposo0 +Prepotting +PresPoon +Presearing +Preserve PresidentMo Presley93 Presley93BTW @@ -17977,42 +37606,90 @@ Prestor Pretendy Preternal Preto +Preto Ownsss +Pretten Pretty +Pretty God +Pretty moist +PrettyGood +Prettyokqt Pretved Pretz Prevengeance +Previn Prey Prey4Pocius +Preying +Priapismi +Pricy Priestopher +Prif Daddy +Prifddinas +Priide +Priimitive Prilliam Prim Prima Primacy Primal +Primal gain Prime PrimeSeries +PrimeToaster Primed +Primetime190 +Primetime829 +Primevil PrimexReborn +Primid Primiitive +Primilivum Primitive +PrimoVG Primordial Primster Prince +Prince Drt +Prince Ferro +Prince Mate +Prince Tuten +PrinceCrypto +PrinceJozef PrinceZuko Pringle287 Pringles Prins +Prins Pi1s Prinses PrintedCash +Prion Priority +Priorrr +Priselac Prisimenu Prismalitic Prison +Prison Joe +Prison M1ke +Prison Soap Pritje PrityBoiSwag PrivateSmorc +Prix Prixma Prncss +Prncss II +PrntScr +Pro Flicks +Pro Per UIM +Pro SKiiLLz +Pro Slacka +Pro Slacker +Pro Tanto +Pro Tato +Pro Zoe +Pro twin33 +Pro-tag ProForm ProFortnite ProLegend @@ -18020,18 +37697,30 @@ ProSKatona ProSups ProTweakius Proba +Probability Problemski +ProbzDrunk Procedures Proclivitas3 +Procoptodon Proctiv Procts ProddyP Prodigy 99 +Prodigy Matt ProdigyTape ProdigyThief +Prodigy_77 Prodoughtype +Product99 Prof +Prof Bruce +Prof Cheese +Prof Ribeiro +Prof Shroom ProfGanon +Prof_Plums +Prof_Snack ProfanityBox Professa ProfessorBot @@ -18039,50 +37728,85 @@ Proficent ProfoundPnda Prog Project +Project Deku +Project Paat +Project Sept ProjectGamma +ProjektUri Proklus Prokopios +Prolapsi +Prolly High +Proly high +Promacta Promepheus +Promise +Promtel Prone Prongs97 +Pronx Proots +Prop Gun Propagate Propas Proper +Proper bald ProperTroll Prophecy +ProphetSnayk Prophylax +Proponi Propulsions Prosit +Prospicktor Pross +Proster Protagg +Protanopia +Protassium +Protease ProtectdLeft Protectlilb9 Protectorate Protege Proteiini +Protixen Protlong Protocell +Proton Drum +Protopteryx PrototypeGOD Protox +Prouser +Provei ProvidenceL +Providential Proviro +Provoke Rage Provokeskill Provoxo +Prowz ProxDemSox Proxeum Proximitus +Proxymine3 +Prozac Manic +Prozac Peter PrrMeowPrr Prro Prrr +Prrr Perry Prryvdoof Pruillip Prune +Prune Slayer PruneHub PrusaSlicer Prutturp Pryn Pryxl +Pryypsas +Pryza91 PsYcHo130 Psalm Psalms116 @@ -18092,219 +37816,435 @@ Pseudo Pseudomonas Pseudonimas PsiTempest +Psichedeliks Psiki Psiklone Psmaster +Psoup Psuedoniem +Psy0tic Psy13m PsyDellic PsyWaps Psybae Psyc Psyc Paladin +Psyc0-I Psyched PsychicL10n PsychicType Psychinis Psycho +Psycho Fungi Psycho11111 PsychoPatty Psychodeathk Psychohexane +Psychokilla PsychoxX Psyco Psycub420 +Psydvekoosi +Psygo PsyhexGOD +Psykoz Psylocin +Psyminds +Psynar +Psyqualogy Psysyk +Psytoro +Psytrnce +Psyyke +Pt 79 Ptalm +PteShank Ptfo +Ptol +Ptopaz Ptyh Pu-94 Pubeless PubicThumb +Pubicon +Public NJP Publics Publuske PuchaLibre Puck Pudding +Pudding Man +Puddingdude +Puddingtons Pudge +Pudge Pudge +PudgeJackie Pudgeegee Pudnik Pudota +Pudota basso +Puerto Varas +Puff Daddy Puffed Pufffml Puffy +Puffy xx +Pufr +Pug +Pug Boots +Pug Champ +PugToots Pugasaur Puggin Puggzy Puglet Pugna Pugsyy +Pugwest Pugy +Puhd Puhd1staja Puhuri +Pui Puin PuinguimGOD Pukeking +Pukeko Puli1111 Pulkjes Pull +PullOutKing PullOutRange Pulli Pulma1 PulpFlctlon Pulza +PumGiver Pumba +Pumba Bot Pumba89 PumpUpTheJam Pumper +Pumper Joe +Pumpflexin Pumpkin PumpkinLatte Pumpui Pumuckl +Punani MD Puncakes Punch +Punch Main +Punched Pundareen Punde Pune +Pune rouch Punegonde +Pung +PunishinBird PunjabSamosa +Punk-06 PunkPang +Punkt Punt +Puntenjagert +Puntje ket +Puny Duck +Pup +Pup in a Cup +Puppadile Puppana Pupper1 +PuppySeal +Puppysaysftw Pur3 +Pur3 Bow99 Pur3death2 PurMain Purate Purcey Purcs Pure +Pure Evil99 +Pure Great1 +Pure Hunk +Pure Paprika +PureBread +PureFilth PureIronEyyy PureOGIronMn +PureOwner +PureWax Pureanger728 +Purebas alt Purebasalt Purebish +Purell Pureloot21 Purely Purelybo0ty6 Purenerd +Purepappa Purepappa66 Purescarybuu +Purey Arcane +PurgatoryBro +Purgy +Purifyin +Purjj Purka11 Purkkatukka +Purkkaukko Purkkius Purko Purnex +Purolator Pl Purp +Purp Nasty +Purp Nips +Purp Please +Purp or Bust +Purp where Purp1eWizard PurpLightPlz +Purpl3 Lean Purple +Purple Flap +Purple Meeep Purple-Broly PurpleDabz +PurpleGoat PurpleHippoo +PurpleMurple PurpleNinjja PurplePlez PurpleTeemo PurpleThorax +PurpleVex PurpleWhaki Purpledile +Purplefox Purplekills Purplemudkip +Purpleowns Purppurainen +Purpsauce PurrPig +Purse seine PursuantACE Pursuedd Purtherapist Pusat +PuscSquirt Puse PuseSlayer Pushgold +Pushovermage +Pushy Bubes Puss +Pustut +Put Me 4th PutMeInCo4ch +Putaria +Puthiegh Puthy +Putkipommi Putrescent Putse Putyte1 PutziVikat Puud Puujalka10 +Puukko Pekka PuuluuP +Puumba Puuteri Puylayer +Puzzle Drops +PuzzleTheCat +Pv Was Here +PvEmerald +PvM Andrew +PvM Beireh +PvM Bhunt3r +PvM Blake PvM Boganeer +PvM Cas +PvM ColdBeer +PvM Crooky +PvM David +PvM Eestlane +PvM Gemeos +PvM Grindz +PvM Hawk +PvM Hero92 +PvM Items +PvM Jack +PvM MeK +PvM Miku H +PvM Muzzy +PvM Nex +PvM Nug +PvM Oliver +PvM Packers +PvM Panda +PvM Rev +PvM Rin +PvM Spectral +PvM Tom +PvM Wiz +PvM guy +PvM zoom +PvMAlone PvMCerlow PvMKoala PvMNightmare PvMNotorious PvMProfessor PvM_Nugg +PvMalexDR PvManchester PvMs +PvP +PvP Master PvPRoTiGy PvPete +PvPs Pvkk +Pvm 4 Pets +Pvm Aero +Pvm Demigod +Pvm Garley Pvm M0lz2 +Pvm MageBoss +Pvm Runeboyz +Pvm Tigers +PvmBrad PvmQ PvmRNGesus Pvmpets Pvoet PvtStevens Pvul +Pw4 PwDhorizons Pwca Pwdz Pweet Pwew +PwincessK +Pwn Dat Noob PwnTommy Pwnage Pwnbaa Pwnd +Pwnna +Pwnnzz +Pwno +PwnrPizzaman Pwnslayibex Pwpw +Pwxn Px_7UX8Cy8 +Pxcs +Pxie Pxtrick +Pxxch +Pxzls +Pyet Pyfa Pygmysteaks +Pyia +Pyjamas99 Pyki Pyloric Pylseboden +Pyocola +Pyra +Pyrate Aeon +Pyre Lord3 +Pyre Mouse Pyretic Pyrl Pyro238 +PyroEagle13 +PyroF3AR Pyro_OO1 Pyrocore Pyrotemplar PyrrosDimas Pyssu Pyst +Python +Pythonx135 Pyux Pyykki Pyyli Pyzdalupi +PzK +Pzju PzzaPredator +Q 1 +Q K M Nophis +Q Proserpina +Q S L +Q cs +Q l ii Ma X +Q of Spade +Q uincy +Q-Dance Jr +Q-Qs Q1NH4N Q33N +Q53 +Q60 +Q7L +Q8 I +Q8vv QAWSED +QGM QIKNQFRDNUQF +QKen +QPR QPness +QQ3 +QQQ Gang QQQQQQRRRRRR QQuaLLeH +QRcoding +QT Bri +QT0 QTRDS +QTY1 QTZedd QTom +QU0TE ME +QU42TION QWIWRDIGIDFG Qadir Qaem +Qaidos Qajvha +Qapz Qash +Qc power1 Qc-Elfmage Qeassaris +QeeQ Qego +Qeko +Qeln Qemi +Qeni +Qenobii +Qi Kua Mai +Qi Ling QiangxD +Qilat +Qiok QlKNQFRDNUQF +Qlhp xD Qlioux Qmain QnBumbleBee @@ -18312,38 +38252,79 @@ Qnon Qoil Qoki Qombat +Qosmio +QpSc +Qpaki +QpzQ +Qragon +Qrax +Qridan Qrischin +Qrollop Qrwewa Qryptic Qtey +Qtreb +Qu3stm4st3r Quacamole +Quack Attk +Quack Boom QuackForMe +Quacka +Quacker +QuadJake +Quadder 4444 +Quadralobsta Quadrifoglio Quadrioo2 Quaerd +QuaffPotion Quaffle9 Quahzai +Quakbou +Quakenet +Quakoss Qualitative Qualities +Quality Kek +Quality Name QualitySleep Quang Quantities +Quantization Quantuh Quantum QuantumTurtl Quarentitty +Quarks +QuarterToBen +Quarterback Quarters +Quartia Quav Quawka +Quazi +Qucks Qucu QueBolaAsere +QueefInMyEar QueefMaestro +QueefWiggum QueefedOnYou Queeffing Queen +Queen De +Queen Juggle +Queen Keegs +Queen of CoX +Queen0fScots +QueenBadJuju QueenJada QueenOfCorn +Queenmystque +Queenn Elsa Queer +Queer Peter Queerzzly Queijo9 Quelana @@ -18351,131 +38332,329 @@ Quenched Quero Quessswho Quest +Quest Dodger +Quest Giver +Quest Lckd +Quest Noobi +Quest Person +Quest Plug Questikels +QuestsAreBad Quety +Queue Pea +Queueie Quew Quey Quezzimoto +Quf +Qui GonJinn Quica +Quick200 QuickClicks +QuickELMN8 +QuickNDeadly QuickShot +QuickShot x Quicken +Quickman1000 +Quickshot343 Quiddich Quiet +Quiet Place +Quiet Please +Quiet Toot QuietStorm44 +Quiettravlr Quikstache00 +Quilicura Quillninety3 Quimbo +Quinker Quinncidence Quinnyb0y Quintendo Quirkless24 +QuirkyBeaver QuirkyPurple +Quit 4 iron QuitForIron QuitForRS3 Quite +Quite Shy Quitegoddish Quitoris +Quitting Alc Quiz +Quizav Quizzy Dee +QukinoTe Quonloo Quorhum +Qureshi Quria Qurix Quti Qutie +Quukske Quvma Quweix Quyron +Quz Quzzini Qverkuz +Qwagmire Qwaltz +Qwd Qwezz +Qwibz Qwinny Qwinoa Qwozii Qxevym1 +Qzbxd +Qzst +R 0 E +R 0 w d y +R A L L S +R A V I +R A Z T A +R E +R H Y T H M +R I J K A R +R I K +R I P B R O +R L +R N G Pwnz5 +R N Geo +R O B B IE +R O E L +R O O N +R O X +R P N +R S J +R S Life +R S M +R U A Terd +R U MAD BRU +R Y A N +R Y D +R You Jelly +R a uL +R a z u z +R ack +R amon +R eece +R em +R hin o +R i a d +R ift +R l C H +R mr +R o r o no a +R ob +R obert +R oger +R osss +R uiN +R ya n +R yun +R-66Y R00M +R00P R00SE +R00T R0AD R0B0C0P R0BBO +R0FL AT L1FE R0GAN R0KKIN R0LX R0MICH +R0NES R0NNIEB0Y R0OZ R0R0R +R0Y R0YAL R0ad R0bain R0bbby +R0binho R0n13L +R0seofthorns R0yksopp +R14H R1CO +R1chs R1ghtupth3r3 +R1ku R1ng +R2TB +R32 N Z R33c0NN +R34 R3Dtjee R3d3mtion +R3dlegend R3vq +R4 n g 3 r z R4LLY +R4g4n4 R4ndomUs3r +R4ndyM0n +R4ndy_lahey R4ng3 +R4ng3 Nubie +R4ng3r 00 +R4nge +R4nge 2 Lpk +R4re Pepe +R4v2n +R6boi R8nny +RA NG ED RAB9 RACELIS24 +RAD x GLiDeR +RADATOUILLE RADlATAstory +RAFA LOKO +RAG YOUR A55 RAKETTAA +RAPPUHN +RATMON3Y RAUTA ARTO +RAlN +RAlSED RBNY RBeardWBrow +RC Blows RC350 +RC93 RCFreak +RCW +RCY +RD Alten RDHG RDJ94 +RDT King +RDW_Dark +RE2PECT RE4LG4LIFE +REALLY RITCH REBOOTING REC0N +RECEPTED +RED DEM0N +REDBUHLL REDD0GJR REDLlNE +REDRACECAR99 REEEject +REEEsuns REINCARN4TE REKNAW REKTmlg99op REMAlN +REMIIIX +REN0 REZlN +RElNHARDT +RF Bushee +RF Genesis +RFDesigner +RGB Titties RGGregar RGet RHCP-Solo RHK Ezze +RI Sleepy +RIP Base +RIP Meme Man +RIP Mitch +RIP Pork Pie +RIP life +RIPAUGUST +RIPAccount94 RIPT4H +RIPgirthgirl +RIllipieru RIpuim +RJCI +RJE RJL129 RKBM2 +RKN RKOd +RKOd My Mum +RKTA +RKZY +RL Adam +RL Wolf RLBurnside RMAC-97 +RMF Dead +RMG solo +RMax +RNG Alche +RNG GOD +RNG Kevin +RNG MAD +RNG Msg Me +RNG Replace +RNG Tomas +RNG dez nuts +RNG9518 RNGBandit RNGVRNG +RNGiesi +RNGrim +RNGsetMeFree +RNJohn +RNJoseph ROADTO100B +ROBAlN ROBBOsickdog ROCCAT ROCKYY +RODRlCK ROF0LFOR ROFOLFOR ROHTEENMUTSI ROKO +ROLL TlDE ROLLinPEACE +ROMBU +RONNIE COLE +RONZE ROR0 ROSTA +ROT LOST 6K ROVANIEMl +RR Fourshore +RR55 +RRAMPIDD RREKKLES +RRX YY RRX RRayhan +RRevelation RRickkert RRobert +RRocket02 +RS Bread +RS Cinema +RS Jesus +RS Myrthe +RS Nico +RS SLAG +RS UFC NRL +RS ape +RS2 Legend +RS3 Refugee RS3izBetter +RS6 Quattro +RSB Fox RSBadLifeBad RSDonny RSGO @@ -18483,231 +38662,462 @@ RSLtSgtZen RSMAXD RSPSisBETTER RSPup +RSQT +RS_Tytin RSfork +RSmake RStrength99 +RSuomivihu +RTGoldleader +RTK86 RTLadNumber4 +RU B Y +RU Petrified +RUBBERI +RUUNIKUNN +RU_Engineer RUlS +RWB Codeine RWSDOUG +RX S +RX7 FC3S +RXen0 +RY N0 +RY P RYAN RYN16 RZAlex +R_D TRAJANO R_andy13 +Ra +Ra fi Ra1d RaMR0D +RaRaJuana +RaSToG RaaWa Raaagnaar Raab Raaban +Raahe Raahh Raakanipsu +Raamsdonkje Raanduin Raassig +RabarberBarb +Rabbagust Rabbie +Rabbit MD RabbitFlats +Rabbitdude RabbitsDong +RabidDolphin Rabidherring Rabiosa +Rabrt +Rabs xo RacccAttack Raccoon +Raccoon King Raccooncow +Raccoonus Race Rachael +Rachey B +Racholini +Rachscape +Racteal +Rad Renegade RadFeenix Radacia +Radaenne Radar Radeo Radia7ion Radiance +Radiant Lux +RadiantSkye Radiator Radini Radio +Radio DJ Dan +Radio Love Radiohead66 Radiologics +Radish55 Radja333 RadoslavvBG Radrieldor Radsurlak Radsy Radtastic +Radusa-Two Raeghal +Raelir Raen +Raf0da Rafaael +Raffealy +Rafff +Raffineret +Rafi Tafy +Rafiniya +Rafn Rafnar0G Raft15 +Rafy +Rag +Rag Demon +Rag Doll +Rag Gdz +Rag List RagToRiches Ragani Rage +Rage Fuel +Rage233 RageCold +RageCombo RagedIronMan Ragefire1 +Ragegold1163 +Ragez Fury Ragga +Ragga Muffyn Raggedy Jeeb Raggers +Raggnag +Raggy Reapzz +RaginBatts RagingK9Fury RagingReddit RagingTroll +Ragingg Ragnar +RagnarOsborn Ragnariukas Ragnarok96 Ragnell56 Ragni RagsIIRichez RagucciRafa +Ragurain +Ragy rag Rahamasin Rahamies +Raharu RahbHurt Rahdyxc +Rahedo +Rahimo +Rahis Rahjer +Rahy King +Rai Rai_3 +Raibu +Raichue Raicun Raid +Raid Lewis +Raid My Body Raideris Raiderrediar +RaidriarTGK Raien Raihan +Raihou Raii +Raiicrow Raikesy Railee Raimar1 Raimiss Rain +Rain Days +Rain Please +Rain ee +Rainbird +Rainbow RainbowBeast Raine Rainer Rainforest Raining Rainingbroz +Rainingmeltz Rainman446 +Rainnos Rainold +Rainshower Raintown17 Raisedbycows Raiskausrapu Raisson +Raisson X Raistlin RaistlinFasa +Raisya Raivan90 Raizerr Rajat76 Raje +Rajgo +Rajs +Rajvir Rakaah RakadB3 +Rakblood +Rake +Rake ur weed Rakettikala RakiRaki +Rakiata +RakingPurple +Rakkir Rakoins +Rakru1 Rakuko Rakun Rakan Rakupenda +Ralalallei +Ralgurr Ralle1208 Rallis RalorLeetor +Ralos Rise Ralp +RalphLauren Ralphed Ralphie3 +Ralphy Jnr Raltsz RalvekZul RamYe +Ramae +Ramallah Ramathorn +Rambling Rambo +Rambo Prods +Rambo The 3 +Rambo The 4 +RamboBrad RamboDaddy +RamboKiddo +RamboOmega +RamboSambo +Rame +Rame s Ramen +Rameses B Rami +Rami Tsunami Ramleh Rammdude +Rammmsteinn RamonZera Ramonix Rampa +RampageOnly Rampauttaja +Rampenisse Ramrod Ramsas +Ramsay RamsayGrejoy +Ramxious Ramzis Ramztad +Ran-D RanShakHazar +Rana pipiens Ranamen7 Ranarr +Ranarr Bowl +Ranarr Czar RanarrScape RanarrSmoke +Ranarrjuice Ranarrseole Randalf Randalfen Randalicious +RandallOG +Randdall Randeaux Randecker Randinator42 +RandlesAlt +Random Day +Random763 +RandomGuy771 RandomLemon1 Randomguy38 Randomized +Randomkul Randomojojo +Randsoms +Randy Rawdog Randy Rolex RandyBanger Randyke +Randys +Ranestumies Rang +Rang3d Xp +RangPang Rangahhh +Rangatan44 Range +Range 4 mage +Range AR +Range Kiing2 Range4Free RangeGawd RangePs Rangeddddddd Rangedly Ranger +Ranger Fain +Ranger Hood +Ranger Uhle Ranger82592 RangerBoots +RangerFain Rangerofgold +Rangerrr Rangish +RangoII RanjaForLife Rank +Rank 1 +Rank 1 Gamer +Rank 1 Korea +Rank 1 Norge +Rank 1337 Rank1Espada Rank1NA +Rank350 Rank62 RankOneTurk +Ranox Ranty +Ranul Oracle +Ranusian +Ranzo +Raoul +Rap50Cents Rapasuu +Rapid RapidCycling +RapidPancake +Rapidd Rapiers Rapolas Rapolu +Rapt0ram +Rapterzz99 Rapthor Raptor +Raptor Jesuz RaptorLuck Raptorcaw +Raptorjk +Raptorman Raptorsin6ix Raptz Rapu +Raquelo Rare +Rare Arugula +Rare Chance +Rare Doggo +Rare Hat +Rare Panda +Rare Pets +Rare Vibes +Rareinpepe Raresocks Rarity +Rarity Belle +Rarma Rasa Rascus +Raseni +Rasenkage Rashford +Raskasta Raspatil Rasput1n2k13 RaspyLemon +Rassaasolo +Rasta Panda Rastaclat +Rastafarlion Rastamain Rastaman +Rat Digward +Rat Flicker +Rat Has Dice +Rat Hunter23 +Rat L Traps +Rat Lettuce +Rat Sensei +RatKing365 +RatWithAGat +Rata +Ratchet48 Ratcliffe +Ratdrin +Ratenda RathianSP Rathmai +Ratikka07 +Ratlifenerd Ratlordmax Ratm +Ratpno Ratrace12345 Ratrero +Ratsy J RattleSsnake +Rattled Rattlerskill Rattlesnake +Rauhanrekka Rauokse Raur +Raurshank +Rauski696 +Rauss Rauta +Rauta Juoppo +Rauta Sorsa +Rauta Tuomo +Rauta Urho +RautaGoblin +RautaLeksa +RautaPertti +RautaPizza +Rauta_Kessu Rautafantic Rautakuma Rautis +Rauzee Ravac +Ravangerous Ravanth +Rave Pants +RaveBoy21k Ravedeath Ravemaste476 Raven +Raven Frost Ravency Ravensword +Raventodt Ravers Ravesyy Raving @@ -18717,119 +39127,267 @@ Ravlar Ravs RawPhazon RawRyan +RawToast_13 +Rawbean +Rawduggie Rawest +Rawke Rawr RawrItsKirby Rawrr Rawsome Rawssss Rawst +Rawwr Austin Raxen Light +Raxorn +Ray Btw +Ray Kurzweil +Ray Rwars +Ray Sensei +Ray Squared +RayCon +RayMarshall Rayfort +RayiiRios Rayjin_Storm +Raykin Rayleighs Rayleight RaymanXo +Raymo Raymone Rayne +Rayne Drop +Raynel Raynelie Rayner91 +Rayo McFly RayofLight2 +Raypac +Rayrunner +Rays Max +Rayye +Raz Wolf +Razaia +Raze +RazenOh Razer +Razer Elite Razeren +Razguld Razie +Razih RazleBadazle Razor +Razor Beast Razor654991 Razorblat Razoriginal +Razorshark +Razrback Razz1eDazz1e +Razzle Razzzoor +Rb Salvation Rb2790 +Rc +Rc Dark Rang +Rc_Power +Rcardio Rcer +Rckr Rddrz +Rds 44 +Rds Nerd +Rdushi ReDrOc +ReFueld RePeTiiTioN +ReRemakeAndy +Re_Rauzo ReachAround Reachinator +Reactate +Reaction Tec Reactionary +Reactionss +Reactor15 +Read Art +Ready 123 Ready4pwnage ReadyToReap Reah Reaks Real +Real Cute +Real Earth +Real Friends +Real Jesiah +Real Kev +Real Me +Real Reasonn +Real Skyline +Real Umbreon +Real Unlucky +RealAndBased +RealBoobs +RealBruceU +RealDrSeuss RealJonner RealKing11 +RealKingBee +RealLifeBot +RealLyre RealMemeVPJr +RealPeelBoat RealRicFlair RealScythe RealShyGuy +RealSpeed RealStoney Realcorkpig Realdent Realflamesss Realismi Realissm +Realist Knut +Realistg +Reality RealitysGod Really +Really Spicy +ReallyToxic Realpk2004 +Realtank +Realtor R1 Reap Reapa Reaper Reaper0321 ReaperLucid Reaperkiller +Reaping Inc +Rearrange +Reaver Tru5t +Reavus +Rebaulten Rebel +Rebel Serj +Rebelatto9 Rebelgian Rebelican +Rebellgutt Rebelqt +Rebir7h Rebirth +RebirthFlame +Rebis +ReboKhaihang +Rebooting Reborn +RebornMuscle +RebornNips +Reborninc +Rebounder234 Rebuild Rebuild4free RebuildLewi RebuildMitch Rebuildep Rebuilders +Recable RecentKill +Recess Recharge +Recited +Recker1o1 Reckless +Reckless Ell +Reckless Fyb +Reckless Lad Reclaimar Reclaiming +Recline +Reclined +Recnamalad +Reco Fam +Recolddone Reconjack Recore Recover +RectalRoni RectalRumble Rectophobia Recuder +RecyclePls Recyr +Red Asia +Red Cavalier +Red Courier +Red Duke +Red Hick +Red Hippo +Red Hood +Red Hot Go +Red Kool-Aid +Red Nine +Red Osrs +Red Pepper65 +Red Rays +Red Rockin +Red Simon +Red Staal +Red Style +Red The Pom +Red Wings +Red XIII FF7 +Red hat +Red partyhat Red3 +Red6 RedCavalier RedDwarf RedGraceful +RedMare +RedRambler RedRatedGame +RedRathalos RedRegent RedRusker RedTwisted RedWarlock31 +RedWol +Red__Mage9 Redael +Redarekun +Redbackpker Redben45 +RedberryLean Redbull +Redbull Od Reddo Reddragon884 Redflame +Redgit Redikarp +Redisleft Redius RedkaWodbull Redland +Redlonghorn RedneckYoshi +Rednecks Rednex +Redninja02 Rednipplee Redo +Redo Undo Redouan94 Redrevolve11 Reds Redshapes +Redshok Redshoker Redsk1ns Redskin @@ -18838,12 +39396,16 @@ Reduced Reductive Redus Redux +Redux Lux Redz Redza14 +Ree Mastered Reeb Reececup Reecesoa +Reecie ReedBadass +Reee Reeebow ReeeeZ Reeeps @@ -18854,7 +39416,10 @@ ReefyReefer Reel_Arphios Reels Reemov3d +Reer Reeyu +Reeze Tech +Reflection44 Refleksfrode Refleksi Refluxerino @@ -18863,168 +39428,346 @@ Reformer Refried Regal Regan +Regelios Regement +Regent Pve +ReggieWeggie +Reggin Cx Regie +Regiis +Regio Regular +Regular Iron RegularJerry RegularPlank +RegulusAurum +Rehbein RehlapzZ Rehoboam +Rehst +Rehzzy Rei-chan Reibnitz Reichart Reidar +Reids +Reidtheweed Reign ReignInBlood ReignOfThor Reigns +ReikenGIMP Reimie0x Reimo ReimuHakurei +ReinLassen ReinMoose Reince +Reinkmyster Reipas Reisnom Reisy +Reita Reizzaz +Reject q p +Rejected Alt +Rejected Son +Rejected btw Rejects +Rejet-01 Rejey8 Rejuvenating +RekaScape +Rekans Rekenz Rekkaboi Rekkerta Rekkt +Reklats +Rekna +Rekt +RektHavok Rekunleashed +Relaaax +Relapse Relapses Relative Relatived +Relax to Alt +Relent1ess +Relevancys Relianah +Reliefed ReligionBad Reliinqish Relik Relinquishh +Relit RelivinYouth Relizent +Relizent2 +Relizents Rellnquish Relloktion +Relmu Reload ReloadFaster Reloox Relrekis +Reluctants +Rem Dogg +Rembs Remcodingho Remedial Remiejj-BV +Remiel Remillia +Reminded Reminiscon Remitin Remix +Remixed +Remmos +Remmyy +Remn +Remo Remorsed Remspoor +Remtar74 +Remvrkable +Remylz +Remytouille +Ren to RenLady +RenZelus Ren_Otori Renacan +Renall Renarin Renas Renato Renauddd +ReneeIsMaxed Renegor Renesau Renewed +Rengar +Rengeki Renger +RengokuRS +RengokuSmile +Renixion Rennuts Rennz Renovato +Rensafari Rent +Rentb0y +Renzo Bamf +Repathor Repeats +Rephia Replayroyke +Replenished Replikantti Replo +Reply To Me +Report Biden Reported +Reporting U Reppin519 RepresentRS +Reprexain Reps4Jesuz Reptile +Reptile God Reptiliano Reptillian +Republic55 +RepublicofRS Reputism Reqtile Requaahv Requilog Requin RequireBone +Reredrum +Rero +Resallude ResenBallZ +Resentfully Reseriant Reserv3D +Reshop Resident +ResignWonki Resilient +Resistzz Resizable +Resk +Resoluutio Respaze +Respeat +RespecTheMax +Respect Me +RespectMyDab Respek Respire +Responders +Respy +Respyy ResrvdFinest +Ressara RestIess RestInPce +RestTooLaid Restinp3ace +Restless99 RestoredIron Restrial +Restrio +Resultss Resultzs Resupplying Retendous Rethie +Retired Dead +RetiredMaboe +RetiredStic +Retr0virus11 Retro +Retro PvM +RetroDeviant +RetroRaz +Retrohiili +Retrovision Rettent +RetuKettu Retupelle ReturnToOne Reub +Reubon +Reuborn +Reudo +Rev Hayter +RevDragon RevZamorak Revamp +Revamp cF Revanche +Revellius Revelwood +Revenant MKX +Revenant Rag RevenantBoy RevengerII Revent ReveredB3ard Reverenciar Reverenz +ReverieDC +ReversEffort +Reversedd Revi21 +Revilia Revofev Revoke Revolted Revolving Revs +RevsRebuild Revus +Revvanth Revx Revzvy +Revzy +Rewbs +Rewinds Iron Rewnlite Rewns +Rewo +Rewriting +RexCox RexM +RexMouser Rexicur +Rexkat Rexun +RexxNotReal +Rexxasaurus +Rexxer69 +Reyes407 Reyko ReynoldsWrap +Reystov +Reyswaldo +Rez X +Rezi +Rezident Rezka +Rezki +Reznx +Rezy +Rezz Main Rezz79 +Rezzn +Rfsu +Rgimenez007 +Rgk +Rgnfrg Rhaegalion Rhaegar Rhaegard +Rhaegor +Rhagaea +Rhastae +Rhcprule6 +Rheal Rheece Rhette +Rhezmyn +Rhianne +Rhien +Rhiley +Rhino +Rhino Art +Rhino Prime RhinoNuts +Rhit Rhiyia +Rhk +Rho Ur Boat Rhoads +Rhocky Rhodesia Rhogue Rhondda +Rhorrn +RhuBarb3 +Rhubarbino Rhuina +Rhyio Rhyl +Rhyme Rhyming +Rhyninn Rhynoob +Rhys Irl +Rhys25 RhysieBoy Rhysos Rhythm +Rhythm Games Rhythms +Rhyw Peth +Ri v en +RiJect RiKoCheP +Riaever +Riamo +Rias Ribb +Ribeye1611 Ribinha Ribitz Riboku @@ -19038,30 +39781,63 @@ Riccario Rice RiceAndTacos RiceWrangler +Ricefishie +Ricepicker89 Rich +Rich 1 Hita +Rich Noob +Rich Snoe +Rich13 RichAround RichGuy X RichHarambe9 +RichMansDrip +Richaard Richard +Richard Baer +Richard Moss +Richard12391 Richards2705 +Richart 123 Richguy3213 Richie Richterr +Richxrd Richy +Richy Rich Rick +Rick scape0 RickAndVorki +RickDeckard RickEscobar RickRab +Rick_NLD +Rickard +Rickeh 1 +RicketyReck Rickhs +Rickky Bobbi +Ricklet +Ricklewinks +Rickness666 +RicknessAlt Rickolas +Rickos Ricksking +Rickstaverse Ricky +Ricky Bob E RickydeRoach +Rickyj13 +Rickyrevine +RicoMangos Ricoo Ricotayqueso Riddim RidePine +Ridemyhomies Rider +Rider Galara RiderOfRohan Rider_99 RidgeRacerR4 @@ -19070,166 +39846,309 @@ Ridley Ridley1990 Ridored Rie1yReid +Riektanminol Ries2 +RiesBatsbak +Rif +Rif T +Rifampicin Rifen +Rift Archer RiftOversoul +Rig Pig Riga +Rigelius +Riggas Rigged1 Riggerlyworm +Righ Right RightKnight5 Rightthisway +Rigid Tony Riglr +Rigondead Rigondeaux Rigourlas Rigr Rigtop25 Rihmakallo5 +Rihsky Riickkert Riiggs Riii +Riiico Suave +RiimuRatigan Riippptttt Riisi Riitasointu Rijst +Rik M +RikCastle RikSavage +Rikador Rikalero +Rikeez +Rikka-kun +Rikka-san +Rikky Flames +Riksaah btw +Rikten X +Riku887 RikuLehDeku RikuRNG RikvWesting Rilah1212 +Riled Up RilesWright +Riles_Nation +Riley Mort +Riley1098 RileyReidx RileyVoelkel +Riltor Storm Rimbton Rimorix +Rimppa97 +Rimuru BTW +Rinby Rindos Ringboi5 +Rinhx +Rini Keeper +Rinky +Rinoa Rinrus Rinsumageast +Rio-Dono Riolu +Riona Riot +Riot Breaker +Riot Starter Riotdeath +Riotz v4 +Riou Riouha +Rip David +Rip25kChins RipBigDoinks +RipFeKeenan RipLo RipMagikarp RipRoidie RipSnorter46 +RipTheBilly Ripd RipePineappl Ripper Ripperhino RippleBear +Rippn Ripsin Ripstart Ripulieinari Ripulirulla Riri488 RiseOfTerror +Risencambrie +Rishhh Rishloo +Rising serum RisingHonour Risingodslay Risk Risker RiskinPixelz +Riskule +Risky Pete +Risky Ryan +Riskz92 Rislearn Rismani +Risotto Risperdal +Risque Moose Risqy +Ritchi026 Ritle Ritopikachu Ritrole1 +Ritthback Rittz Riubuli RivalIron +RivalTitan Rivalguy25 Rive RivenGasm +River Kelda +River OSRS RiverOfIron RiverOut +RiverToSea +Riveriea RiverzRaging Rivy Riwer Rixhy +Riyazzz RiyuK RizeNSkrine +Rizi Bizi +Rizla King Rizos +RizzardWizrd Rizzhole +Rizzzoo Rizzzyisback Rj8532 Rjtyyi +Rkaksi RlCE +RlCEHEAD RlFK RlGBY +RlP Harambe RlPMYMANX +Rlly G Rmus1 +Rmve RnBieber +RnGator +RnGisFocked +Rndl +Rng Depleted +Rng Maestro +Rng Rami Rng4Display RngIshit Rngclown Rnjeesus +Rnkji Ro The Boat +Ro s Ro2no RoBoRhino +RoBoat RoNNalDis RoSki +RoT Nick +RoT is GAY Roach Road +Road Run +Road sign 73 +Road to kek RoadBoat RoadSpartan RoadToALLPet Roadbiker15 +RoadetoMax +Roadways Roadz Roam +Roaming Sky Roanen97 Roastedsteak +Rob Aero +Rob Hot Dog +Rob Iron +Rob Pooner +Rob Zombie +Rob2G +Rob94401 RobDirkson RobThePole +RobWilliams RobWorker Robb +Robb uwu Robbbana +Robbbbb Robbiboi +Robbie EN +RobbieJ547 Robbin +RobbinDaHood Robbo Robby +RobbyMatrix +RobbyRott3n Robeartoe Robejose Roben +Roben Jr Robert +Robert Pires +Robert Stein +Robert T +Robert1f Roberto700 +Robidoux Robigo Robin +Robin Van H +Robin564 Robinhoodnow Robjee +Robjon7 +Roblit Robln +Robln l-l00d +Robodinho Robodyx Robok0p +Robokiller86 Robolisten Robologist Robot Robpar6 RobsonV +Robtoe123 Robust Robyn550 +Roc3 RocheLimit Rochette Rock +Rock Knight +Rock Nin +Rock Rage +Rock The Red +Rock Wizard3 +Rockabbages Rockadelic Rockafellah1 RocketRock88 Rockfists1 Rockford +RockfordT Rockin +Rockin Ness Rockland Rocklore +Rockmaker Rockrets +Rocktail +Rockwell Rocky +Rocky 1535 +RockyYourMom +Rockydig Rocokoko Roczor +Rod Goesinya +Rod Man +Rod Monan +Rod My O +Rod i +Rodefe Rodeva Rodimus55 +Rodje Rodney +Rodney Farva Rodnirt Rodnizzle Rodrick @@ -19238,77 +40157,133 @@ Roe2 RoeGut Roebie1 Roei +Roekoeloos Roel Roerbakei +Roexzy Roff +RoffXD +Roffie Roffy Rofie RoflOs +Roflaye Roflmao +Roflmaomg Roflologist Roga Rogerthatxd +Rogier504 Rogiertjuuuh RogretheOger Rogue Rogue nr2 +RogueSoul03 Roguestoney +Roguor +Rohafin Rohekonn +Rohman Rohmu +Rohr Roice Roid Roidsy +Roihuvuori +Roithamer +Roixie +Rojas +Rokec993 Rokki Rokushoo Rolann +Rolav +Rold Rolde Roldeh Roldie Rolex +RollOurOwnJs Rolla +RollandSmash +Rolleston Rollin +Rollin Clean Rolling +Rolling Kush Rolling2Hard Rollingo +Rolls Rollsie Rolluik Rolly83 +Rolpharoni Rolson +Rom3 Roma Romad +Romare Romarigo +Romboy619 Rome +Romein Romen +Romen Ranges +Romeo for u +Romeos GIM RomeowL Romic Romka Romo RompLord Rompmaninov +Romway +Ron Swansong RonBurgundy +RonCumtastic Ronald +RonaldReagan Ronbiara Ronch Ronco6 +RondVierkant Rondey +RoneRackal Ronescape +Ronggo Rongor Rongrongie +Roni Hagert RoniKysh +Ronn Coleman Ronnicle Ronnie +Ronnie Fe +Ronnie Ponny Ronnie518 RonnieKray +Ronniebob Ronny Ronokroo +Ronpoo Ronti Rony Ronz Roof +Rooftop Laps +RooieRotzak +Roolon +Roomhoorntje Roommaster1 +Roon2h +RooneyDUNX +Roonscape Roosa Roosevelt19 +Roosevelt69 Roosevelt79 +Roost Coffee +Root Exoi Rootman34 Roover Rooxy @@ -19316,77 +40291,146 @@ Roozom Rope RopeDart88 Ropemaker +Ropew +RoqeSVK +RorannoaZ +Rorek RorekMalc Rorr Rorvis +Rory Gall +Rory Stone RoryBurnout +RoryJb Rorys +Rosalktha +Rosco Jenkn Rose +Rose Fritz +Rose of May +Rose tiara RoseGold_A +RoseOnCasket +Roseanne +Rosemarrie Rosemary +Roses R Red +RosetaStoned Roshidragon +Roshyz RosieBtw +Rosieflower +Rosin0nly RosinHead710 RosinScape +Roskamaster Ross +RossOSRS Rossaboy Rossed Rossssi Rossys +Rosters +RostiKz Rosv0 Rot3x +RotJofaJofa Rotagilla +Rotated Rotation +Rotelli Rothen RothesLatrin +Rothgor RotiPrata Rotiforms Rotom Rotta Rottapoju Rotten +Rotten Vale +Rotten Wang +RottenEyeMoe +RottenGurl Rottencotten +Rottenzotten +Rotterdam +Rotzschrank Rouge RougeDev RougeManiac RougeThief02 Rough +Rough Games RoughINSERT Rounded +Rounded Xp Rounder +Rounding Rousey Rousseau88 Route Rova Rove +Rovicus +Rovka +Row Sripple +Rowdy Tobias +Rowe5000 Roweeee Rowg +Rowinovich92 Rowls Rowoep RoxanX Roxasroxs564 Roxer +Roxie Valor +Roxxe Roy385 RoyLay RoyZay Roya2Face Royal +Royal 69 +Royal Chief +Royal Death +Royal Rain +Royal Violet +Royal Wax RoyalD3sire RoyalMess RoyalPurps RoyalRolf +RoyalScumbag RoyalShaco +Royal_Shock +Royal_inc Royan +Royboy16 Roye +Royo Royollie Roysrols Roytang5 RoyyyAfca +Rozensho +RozirPanda Rozkol Rozlucka Roztomily Rozzaa RricketyREKT +Rro +Rs Bartender +Rs But Afk +Rs D +Rs God +Rs Jamal +Rs Kajcsa +Rs Olm +Rs Pikachu +Rs Wiki RsBryce RsGoku RsOverGirls @@ -19396,59 +40440,116 @@ Rs_ReeCe Rsanaded Rsebb Rset +Rsh Katje Rsh10 +Rsky B +Rsn Kmt +Rsn_Adam Rsn_Dillon Rtlo18918B +Rtslash +Rty +Ru Ru +Ru2uo +Ru6 RuMoRRs +RuRu-nyan RuRu09 +Ruan Rubber +Rubber Alt +Rubber Apple +Rubber Luffy RubberApple Rubbery +Rubbery Ra Rubee RubenD +Rubenich RubiconGuy +RubikMouse +Rubinx81 Rubish Rubrek RubsSandwich Rubss Ruby +Ruby Opal +Ruby Runes Ruby5001 RubyBlue RubyBoltSpec RubyEvelyn RubyLvledUp +Rubystealer +Ruck +Rucked Faw Ruckus Ructions Rudadoodadoo Rude +RudeBoiElj Rudekid1035 Rudetopia +Rudi Andre +Rudii Rudin +Rudin Alt Rudolph Rudy RudyGiuliani Rueben +Ruel Rufie +Rufles Rufu +Rufus Shaw +Rufus xD +Rug Klachten Ruge Rugg0064 +Rugpijn Rugrat Ruhl +Ruhpetitive Ruhtrik +Rui HIMimura +Rui Sand RuiMariz Ruimte RuimteMan Ruinationnn +Ruined +Ruined Kap +Ruint +Ruizoeki Ruk1a +Rukavi Ruker +Rukias Feet Rukusama +Ruky +Rule Elite +Rule34 Grogu Rulesy Rulesyy Rullmane +Rum Lad +RumJeDobry +Rumble519 RumbleYT +Rump +Rump Kicker +Rumpetask +Rumpkondens Rumpleminze Rumpstag +Run Alt +Run Down Mid +Run n Alch Run1t3 +Run1t3 Cape +Run2paradise RunCMP RunFreek RunNhideZom @@ -19459,104 +40560,199 @@ Runcatsmith Rundamental Rundi Rune +Rune Scimmy6 +Rune Strg +Rune Toi +Rune Trophy +Rune Tunes +Rune Woolf +Rune l Scape +Rune lennyFe +Rune mendigo +Rune-Taneli RuneAlt1 RuneArguero RuneBri RuneFiend RuneKing2h +RuneLight RunePuuro +RuneShape +RuneWeems Runebie93 Runeblaizer +Runebryter Runechi Runecraft +Runecrafts +Runed Ltd Runeiro Runely Runeman3134 +RunenSohn Runepalmu Runeranta1 RunesCrafted RunescepxD RunescepxDD +Runeshaft16 +Runesniffer2 +Runesr4nerds Runester9999 Runeukko431 +Runeveeti +Runeventure Runey +Runey Toons Rungne14 RunicXanadu Runita Runite +Runite Ricky RuniteNerves +RunkoPalkki RunnerTwoD2 RunninBlood1 Runnn +Runny Rectum Runnynumber2 +Runolf Runs +Runs Laps Runtellthat4 +Runtu Reiska +Rup +RupOSRS +Rupea Ruperd +Rupert Whale Rupiss +Ruptured +Rupus +RureScape Ruri +Rus s Rush618 +RushAl0t Rushana Rushflame555 +Rushing RushnSuch +Rushoo Rusinahousu +Rusinapulla Ruskajan +Ruskeareika +Russano RussianLives +Russian_Fire Russkill1000 Rust +Rust Oxide RustLasagna +Rustaaaagh Rusted +Rusted Ape Rustee +Rusticis Rusticus Rustig Rustigggggg Rustix Rustu Rusty +Rusty Spoons Rusty99 RustyFoot +RustyPlastic RustySimon RustyStipps RustyTuna +Rustydog125 Rustynice Rustyw Rusy002 Rut469 +Rutgar +Ruthacus Ruthers +Rutje Ruto +Rutten +Ruttuperse Rutu +Ruudcz +Ruuddie +Ruup Ruut Ruutana +Ruwe Dollo Ruzafa Ruzy +Rvght Rvilleboy +Rvndy +RvneHQ RvrseGiraffe +Rvsta +Rvvvy Rwkw +Rwolfe +Rx bones Rx-ll +RxHarmacist +Rx_Sparky +Rxscro +Rxzm Ry Jones 21 +Ry T +Ry n +Ry-07 RyGuyyy1 +RyKush RyQzz +RyRgrMcFrly +Ryaite Ryalae Ryan +Ryan 178 Ryan8529 RyanApollo +RyanC203 +RyanFar25 RyanKristoph +RyanNagato +RyanPick RyanTW Ryanandneal Ryaneal RyangalO +Ryanmattin0 Ryann737 Ryanpage RyansRebuild Ryanwouldsay +Ryawh08 +Rybizzle Ryca +Rycerz Arek +Ryd Ryder Ryders +Ryderwear +Rydwarf RyeAnn RyeEx +RyePie +RyeYun +Ryeron +Rygelon Rygo111 Ryhan +Ryi Ryjgar Ryking +RylanIsLive Ryleegh Ryley Rylios @@ -19568,76 +40764,216 @@ Ryokojin Ryolm Ryoma Ryougi +Ryougi Shiki +Ryouma Ryoush +Ryronman +Rystaloid +RystyRane Rytacus +Rytis +Ryu Deadass +Ryu L Ryugo Ryuk Ryukiral +Ryuko +Ryul Silvyr Ryvik Ryxidius +Ryyd Ryzelf Ryzema Ryzho Ryzloh Ryzou Rzasa +Rzy4k +S 0 R A +S A S H O +S A VV A G E +S C N +S C U Z Z +S E T C H +S G M B +S I N +S J H +S M A S H ER +S M I L E S +S O Z O +S P E E E D +S Q5 +S Rye +S Spirit +S T ACK E D +S T E F AN +S T E N +S T I L L +S T R 4 ever +S T R Pwnz5 +S U F F +S V N E +S W A N N +S a z z y +S acrificed +S aevar +S aii +S ammmy +S co t t +S cruffy +S e 7 e n s +S e a f o +S e e j +S ebbe +S emi +S enu +S err +S h a n e oo +S hadw +S hayzz +S hea +S hep +S imply +S k u ll y +S kari +S l M B A +S l3 +S nipz +S ofa +S offa +S ofus +S orrow +S oulz +S outh +S p00ky +S secy dy +S t e v e n +S t i +S t i g +S t i x +S tas +S u b j e ct +S uffa +S unz +S wan S-525 +S-Market ll S00000O0000k +S0FTPUPPY S0L0 +S0L0 SL0AN S0MALISLAYER S0O0O0O0O00S +S0S JERRY +S0UVLAKI +S0Z4 S0ar +S0ciety +S0dra +S0gg3 S0ldern S0lomon44 S0lty +S0nical S0phie S0tg +S0thra +S11om +S1MPSLAYER S1N6 +S1NA +S1ORM S1SU +S1ayzAllDay S1n0fWrath S1ngul4rity +S287 S2u2 +S3 Matt S3ID S3W3RSLVT +S3n4_Hc +S3oulful S4M0HT +S4M20N +S4v4s S5peedy S60RDesign +S7arburst +SA alter ego SAAB_Toxic SAAIXX SAGLAM SAINt +SAINt Steff SALM00O00N SALM00OOON SAMCRO SAMMMMMY +SANDFROG53 +SANFEWLUVR78 SANO +SANRIPOSYCHO SANTERCOMTER +SANTO5 +SANYANOSTRAH SAPl SARAH SARPBC +SATAN GOD +SATORIIN SAUCEnJUICE SAVVON SAWPREME SAlNT SAlNTS +SAlYAJlN +SB DRIFT +SB Luke +SB Mac +SB Mully SB2K SBSBSBSBSBSB +SC0V SCARED SCHOOTSFIRED SCOTUS John +SCR0TUM +SCRAMMMBLED SCRIMS +SCRT SAUCE SCYukino +SC_Josh +SClarky +SDC Thursday +SDV SDVX SDemotivated +SE Alaska +SE N SE SECDEF SELF +SEMI CHUB ON +SEMl DEAD SEPTlC +SESACO SETU +SEXY GRETA +SFTYALWYSOFF +SG +SGNG SGTA +SGTX SH333S +SH5 SHAD0WBANN +SHAGED +SHANK SAMA SHELDOR +SHERMnNATOR +SHElD-HEDA SHIA +SHIA_WIRE SHIDDED0N SHIIIELDS SHINY @@ -19645,76 +40981,143 @@ SHINZ0N SHOTGUNSUGE SHRIIlIlIIMP SHlESTY +SHlFTER SHlNl SHlVER SIBS SIDB +SIGMA MALE69 SIGNZ SIKKaudio SILENT SIMAA +SIR KIRSI +SIR_WHAT_NOW SISDIS +SIayer +SIoopie +SJ7 +SK octorock SKBToast +SKBerk +SKD +SKDMRX +SKDee +SKEEZUZ SKKi63 +SKlDMARK SKlNNY +SKlTZO SL0AN SL1T +SL3DNECK SL3VEN +SL90 +SLAAYEEER SLAP +SLAP A SL0TH SLAY SLAYER +SLBM +SLE Casper2 SLEEEZY +SLNT SLOpro +SLV +SM Esura +SM Tech N9ne SM0KE SMANisonfire +SMEGMAEATER +SMGO SMHeMbRBoB +SMKoolin SMOrc +SMOrc Weeds SMOrc3000 +SMRT KO SN0W SN0WBUSH SN3AKBO SNAX SNAlIlIlIlIL +SNUIVEEEEEH +SOCIAL SCARE +SOLO D0L0 +SOLlD SOMALISTEVE9 +SOUL MASTER +SP1NK +SPAICC SPANKWlRE SPLEEBTEZ SPLODGE +SPOODERMENNN SPOOKAH SPlCER +SPlCY RAMEN +SPlDER-MAN +SQLite SQSHD SR0o0RM SR20 +SRH IM +SRSwiper +SS Iron +SSF Fe BTW +SSJ2 GhostMO SSS-Latch SSamm SSco0by1 SSironMage +SSlavic +SSnakeling ST0n3 ST3AKS STAIGZOR STALlN STAMPEE +STARSZSZ +STAUNCHEST STEEZ +STEEZ UwU STERGS +STEVEYZERMAN STEVIO STElNSGATE +STElNSGATE 0 STIFT +STM32 F103 +STOERE OMA +STPN +STR m8 +STRZI STRlCKEN STRlDERMAN STSTSTSTSTST +STUB0RN STYG STZY STlFFMElSTER +STlNGRAY SUBAROO +SUJET0 +SUP3RCLAUDIA SUPA SUPERSET q SUPERTAIKURI SUPREME SURPRlSED +SV_Exon +SW3GG3RZ SWAGL0RD +SWE4TY +SWlPE LEFT SYMBI0TE SYMPATHY SYRpaperclip SZP-Qlimax +S_nacky SaHiB SaItbender SaSoeur @@ -19723,61 +41126,144 @@ SaZz Saagarius SaakeliMies Saambellah +Saaq Saarinen Saassz +Sabba Sabbatix +Sabbelaar +Sabeket Sabel26 Sabelt +Sabeltann26 Saber +Saber Alter +Saber Lilli +Saber Six Saberloco +Saberos +Sabich Sabina +Sabitoo Sabler Sabo +Sabor Fresa +Sabotaging +Sabre Doge Sabretooth Sabu549 +Sabuwu +Sac Sacck +Sacha Boeyy Sachets +Sack 2 Dirty Sack_Lunch Sackofeyes +Sacolyn +Sacre Sacred +Sacred Eli +SacredArtist +Sacrificed +Sad Boi J +Sad Fat Ugly +Sad Juggler +Sad Leaf +Sad43 SadClownP +SadJB +SadPanda94 SadWhiteFalc SadamBHangin Sadass Sadcatxd +Sadge Izzy +Sadge2407 +SadgeCry +Sadgetseg Sadiq Sadism +Sadistic Sadivy Sadness +Sadting Sadystic Sadz +Sadz Rax +Sae Bae +Saena +Saenmin Saeph Saerdna97 Saerom +Saetama Saetre +Safavid +Safcon +Safe N Sound SafeFromWork +SafeUp YOLO Safecamp +Safelyfast +Saff +Saffat Saffi Safiir +Saftsack8 +Saga +Sagar Sage +Sage Lao Tzu +Sage Lily +Sage North Sage Vegas SageO6 Sageinventor Sageme Sager +Sager Main +Sagev Saggy +Saggy Weenis +Sagittarios Sags +Saguaro Joe +Sah D +Sahin +Sahonym Sahra +Sai +Sai Chosis +Sai Saici Sai1420 +SaiPhai Said Saifphire +Saigyouji Saiin Saika Saikoh +Saikoo +Saikou Saikshin +Sail Door +Sailing Prod Sain Sainola Saint +Saint 7 +Saint Acura +Saint Andrew +Saint Bjorn +Saint Fabioo +Saint Fellow +Saint Hatred +Saint Hyde +Saint JJJ +Saint Silver +Saint Tmi +Saint Zero SaintBurgs SaintDienda SaintDovey @@ -19786,253 +41272,466 @@ SaintMilk SaintPablo68 SaintShiba Sainted +Saintri Saitam Saitama Saivaa Saiyajin Saiyan +SaiyanSkinn Saiyanns Saiyans Sajjad +Sajzi Sakhmett +Saki Nikaido +Sakix Sakk +Sakke Sakkyun +Sakobi SakooR Saksa +Sakuragi +Sal Magluta SalBobo Salacity Salad +Salad Car +Salad Kareem Saladhead25 Saladsoup SalamPacanam Salamalion98 +Salbo Salcal Salda Saledor Saleen Salford +SaliBronze +Salih +Salii +Saliis Main Salista Sallad +Sallariina +Salllamander Salma Salmelainen Salmo Salmon +Salmon Ikura SalmonCookr7 +SalmonsRule Salmos33 Salms Salomon Salomon666 +Salone Salorassi Salsa SalsaMurango Salt +Salt Mines SaltInTheCut SaltPJ SaltasVolfas Salte 55 SaltedCorona +Salten Saltia Saltigast Salty +Salty 4 Life +Salty Cris +Salty First +Salty Fooker +Salty Gimp +Salty Guava +Salty Slimy +SaltyNakul SaltyPears SaltyPilgrim SaltyQuackr +SaltyShrimp +Saltydivo Saltzpyre +Saluskenas +Salvats +Sam Biddle +Sam F +Sam II +Sam Knight +Sam S +Sam Tha Man +Sam Zi +Sam btw +Sam1 Sam5453 SamFC +SamGaspacho SamManSnow SamV007 SamW +SamWisely +Sama ntha Samadier Samadieron Samaji Samalorian Samanthix +Samantics Samay Samb0Slice +Sambal Sauce Samblader +Sambwo +Same Deal +Same Kid +Same Thanks +SameerM0 +Sameling Samfew Samfundet Samgonz3 SammGemm Sammi80 +SammieRose Sammito2 Sammjoey +Sammstera Sammuel +SammyGer Samo Samoski +Samphet +Sample size SampleText Samprini Samps0n Sampson Samqqa Samri +Sams Main +Samsara Kama Samsoniene Samsora +Samster8812 +Samu Saukko Samubidladin +Samuel Hall +SamuelBrian +SamuelP223 +Samuell C Samurai Samusar7481 Samwse Samxy Samzh +Samzju Samzor +Samzy +San Quentin San0 +San200 SanHolo SanPaid SanSheng SanTie +Sana Sanada +Sanark +Sanbir Sancta Sancte Sand +Sand Raider +Sand Shrew +Sandal Slap Sandbar +Sanddemon527 Sandelzi +Sanderr +SandhillFrog Sandhog +Sandhur Sandiey Sandman1 Sandmannen9 Sandogan Sandokan +Sandoval Btw Sands +Sandviper40 SandwichBag Sandwichfish Sandy +Sandy Claps +Sandy Land +Sandy OS +SandyLand +SandyMonkey SandyWexler +Sane Panda +SanePizza +Sanellyyy +Sanfew Serun Sangers +Sangi Sangotha Sangtuary +Sanguinala +SanguineHawk Sanguinezti +SanguisDurus +Sanitary Poo SanityCntrl Sanjachi +Sanjai13 Sanjin +Sanka +Sanka93 SanodersNL Sanoske13 +Sanphew +Sans Soleil Sanshou Sansos Santa +Santa 07 +Santa Chielt +SantaChristo +Santapwner Santasos Santeri Santeri333 Santhacine Sanzoku +Sanzoku HC +Sanzu_o SaoriHayami Saoro Saosiiin +SaphiTheBear Saphie +Saphirakush Saphiro Sapin +SapphicDiana +SapphicEmmmy +SapphicJade Sapphirebear +Sapphirelove SappieWappie +Sappihron Sappphire +Sappx +Saqi Saqib +Sar a Sara +SaraNotDomin Saradominas Saradontmin +Saragomin Sarah +Sarah Hagan SarahWalker +Sarahsaurus Saraie Saran +Saran07 +Sarangae Saraph +Saras BBD Saratomi Saraziel Sarbles +Sarcasmder Sarctopus +Sarcy Csp Sardaukar Sardlz +Sarecren Sarfar Sarge +Sargon III Sariel Sarkie Sarmagedonas +Sarms Knight +Sarodin Sarrafo +Sarrdukar +Sarri Sarriesque Sarro Sarrz +SarthaMewart Sarthe +Sarthinox Sarthorm Sarumite Saryit +Sarytis Sascha +Saserdoti Sash Sashadow Saskel SaskiaNaka Sasser Sasslax +Sasssie Sassy +Sassy Rookie Satada88 Satamari Satan +Satan Claus SatanSquared +SatanarchyXX SatanicGrill +Sataniciocus +Satans Ktten Satchmoi Satchyl Satisfyed +SativaDreams Satizfy +Satoo +Saturdayxz SaturnHippo Saty +SauLau +Sauce McBoss +Saucestad +SauceyFox +SauceyHorsey Saucy Saug +Saugy Saul +Saul Bloom +Saul Pvm SaulMozarela Sault Sauna +SaunaPaska Saund Saunders SaundersRNG0 Saundo Saunty92 Saurogar +Sauroiv SauronsTower Saus +SausageRyder +Savabeel Savage +Savage City +Savage Gamer +Savage Kush Savage Mind +Savage Pimpn +Savage Titan +Savage izJkD +SavageBigL SavageSource Savaged Savakuda Savalar Saved +Saved Me Savg Savilahti99 Savior +Saviour +Savj Savjry Savoureux Savpryan Savu +Savu Nahka Savustettu +Savvo +Savvune +Savvvy Savvy Savy +Savy Neals +Saw Em Off +Saw Siege Sawanne Sawaz1 +Sawbonez Sawdey +Sawrik Sawzall12 +Sax +Saxerpillar +Saxlord2500 Saxon +Saxon_knight +Say Allo +Say Freeze +Say Geronimo +Say Go Put +Say Gz Rn +Say aligh SayMyName +SayNo2Ebola Saya Sayadzin Sayersi +Saylorbtw Saymouseart Sayonara +Sayooo +Saypa Sayrim Sayrr +SaysNiceAlot +Sazch +Sazere Sazme Sazzarull +Sboy_90 Sbraga +Sc ruffy +Sc0nesy Sc0ttishDave Sc1ttl3 ScOx +ScOx ChrisM +Scabrick Scaf Scaggy +Scaha ScalerSlut +Scales +Scampr ScandalousHC ScannerBen Scape +Scape Jam +Scape Snake +Scape Steve +ScapeChad ScapesGhxst Scapeskater +Scapin Scaping Scar +Scar TNL +Scar of Time +Scarb OS Scare077 Scared +Scared Jr +Scared Zebra ScaredOfAll Scarfade Scargut @@ -20041,63 +41740,99 @@ Scarlett ScarredScape Scarsx Scarves +Scary Face +Scary Me +Scary Parrot +ScaryBrandon ScaryShadows ScaryTerry ScaryVacoom Scat Scat19 +Scatheful +Scatter +Scea +Scemblix SceneGfX Sceneryy Sceptile Scew Schaapers +Schams Scharnhorst +Scheemz +Schepo Scheubz Schienenwulf SchijnWerper +Schildkroote Schildpad +Schileru Schiller1994 Schimy Schiphol +Schismo +Schizphrenia +Schlaide Schlak182 Schlanged +SchlipityDop +Schlitz Schlock Schlowmo Schlynn Schmaltz +Schmelting Schmidtty641 Schmokeyboii Schmoovin +Schmuck Schmuel Schmuk +Schneemann90 +Schnei Yo Schneidster7 Schneyy Schnitzel Schnitzl +Schnocks +Schnoob Schnops Schnozz Scholi Schoolies Schools +SchoonCombat Schooner Schoonzoon Schoopity Schorr Schout Schovaval +Schretty +Schricky +Schrodingus Schuabinator Schuck Schuyler SchwanzLord +Schweiaos Schweigende +Schwiemann Schwingy +Schwog Schwoopity +SciCloan +Sciamachy Sciario Scids Scils Scimi +Scimista Scimmy2face +Scion 1T ScizR +Scizorp Sclass707 Scodran Scoffs @@ -20105,79 +41840,160 @@ Scofiz ScoobaQ ScoobiedO0 Scooby +Scooby T1 +Scooby Yew +ScoobySnack +ScoobySnacks +ScoochPoooch Scoopie Scoops12 +Scooter999 +Scootr +Scoots Scoped Scorchbeast Score +Scoreox +Scoreyy +Scori +Scorial Scorialator Scorlibran Scorpiex ScorpioBoy +Scorpslay +Scorqion Scortyx +Scot ty ScotianHerbz Scotse Scott +Scott 0888 +Scott Men ScottJ +ScottKZ Scotteh Scottie ScottieP ScottsTotlol +Scottsdale +Scottvc Scotty ScottyPippen +ScottyThrall Scottyx Scourging +Scouse Lyfe +ScouseBandit +ScouseThanos Scout +Scout Dorvis +Scout Troopr +Scout a raid +Scouting UIM ScoutnStake Scowder +Scp-2786 Scr0t Scradmaster Scraex +Scrafo Scrambledleg Scrap Scrappy666 +ScratchGolfr Scratchzilla +ScreamIM ScreamSavor Screeb Screen Screws +Screws Floor ScrewstonTex +Scrig +Scringo Scrkidzl12 Scrotboy +Scrotetseg Scrruff Scrub Scruba10 +Scrubgod Scruffy +Scrums Scryzen SctyDsntKnow +Scuba Steev7 ScubaSnacks +ScubaSteveOG +ScubaStweeb Scuff Scuffed +Scuffed Jesu +Scuffed Ming +Scuffed Sire ScuffedGoose +Scuggie ScumBagAlex Scumbag +Scumbag Joe Scummy ScummyMonkey +Scumpi Scurlll +Scuro Scurrilous Scuto ScuttleAway +Scuzz OS Scvthe Scwubs Scybin +Scyld Scythe ScytheOrNeck +ScytheShreds +Scytheer Scytheplease Scythes Sdelta230 +Sdsdsd789 Sduckk +Se Ki +Se rena +Se7en Rye SeBambi +SeDzz SeLeV +Sea 7urtle +Sea Anemone +Sea Buoy +Sea Hawkins +Sea Lobster +Sea Monk +Sea More +Sea Saint +Sea Slugs +Sea Turtle +Sea Turtles +SeaManTaster +SeaShanty +SeaShellKing Seagal +Seagaru +Seagate Seal Sealab +Sealegs Btw +Seamlessly +Sean E +Sean Is Sean278 +SeanIsHiding +SeanRich1 +Sean_C Seande4 +Seangie 91 Seanii Seantjl Seany4444 @@ -20185,22 +42001,54 @@ Seanye Seanz Seapy Searched +SearingPain +Searob Seas +SeasTheDay +SeatGeek +Seated Seater +Seater HC Seath1552 +Seatoad Seaweed +Seazeee +Seb Fe +Seb Lt +Seb Sob +SebBTW +Seba Iron +Sebis Sebla +Seboeber +Seboobs +Sebukai +Sebzy +SecBongToke Secho +SecondMars SecondStage +Secondary +Secondes Secret +SecretlyACat Secretly_Bad +Sectokin +Sector Six Seculish Secwai Sedap Sedentary +Sedlom +Sedrf23 +See Too Far +See u em +SeeMeGoing SeeYou Seed +Seed Of Dee Seed386 +Seeeek Seeejay Seek SeekerofIron @@ -20208,222 +42056,425 @@ Seekerz Seeking Seeliss Seems +Seems Legit SeemsFair Seen +Seen A Babe Seenoevil +Seery Seerz Seffer Kover +Sefirah +Sefka Sil +Segelpaatti +Segma Sego +Segs69 SeibaRaion +Seifeltz +Seifer666666 Seig91 Seighilde +Seigneur5000 +Sein Blut Seiskalehti Seiverna +Seiya216 +Seize Sejaeek +Seje Sejj SejjNesta +Sek Loso +Sekaiyatra +Sekava Seko SektorQ Selaphiell +Selastiri +Selbyen Seldrium Selectic +Selena Gomes +Selesi Self +Self Attempt SelfStanding Selfie +Selina Kyle Selj SellMaxMain +Sellerz Selling ROTS +Sello_Hunter Sellu Selmer Selostaja +Selyk +Semantic +Sembient +Seme Semestro Semeul Semi +Semi-Stable SemiReliable Semih Seminary Seministi Seminoles +Semirare Semlan +Semours Semper SemperOP +Semperlex Senarii Senbonzakra Sencillo +Send Backup SendItKing +Sengster Senhor +Senhor Prego Senia +Seniab88 Senior +Senior Wells +SeniorCui SeniorLep +Senis6 Senjor Senkaiye Senkuu +Sennno +Senny Bridge +Seno 45 Senor +Senor Grumpy +Senor Nub +Senor Patata SenorMarcSux Senpai +Senpai Avv SenpaiDecer SenpaiiGod +Senpenbanka Senpukyaku +Sens Army Sens249 +Sensas Senses +Senses Fail Senses12 +Sensie SensualSnail Sentinel Sentristi Sentrosi Senturia Senzel +Seoyeonnie +Separi Sephiore +Sepi testaa Sepper Seppiee +Seps +September 26 Septic Septimus +Sepv +Sequenced Sequin Sequinex Sequissimo SequissuAnau +Ser Bone +Ser Boris +Ser Kelvin +Ser Paddles +Ser Percy +Ser dracarys Seracid +Seraff Fe Seraph +Seraphine BF +Serblicious SerenShadows +SereneMarine Serenity +Serenity Now +Sereri +Serfival SergeiSativa +Sergonomicus Serial +Serialist Serine +Serioga One SerionKiller SeriousLip Seriousruss +Serkr Serkus +Serls +Sernara Sero87 +Seroplex Serori SerotoninBTW +Serpent Sin +Serpico Serpula +Serpy +Serros SertCocuk Server Servia Service +Service Jobs +Service user Sese Sese778 Sesil +Sessanna +Set h SetItToWumbo Seta Seth +Seth1711 Seth6191 +SethH350 Sethalas Sethliu Sethmare +Setko Seto292 Settled +Setzal Seut Sevadeg Sevalt Seved223 +Sevenation Seventh +Seventh Son Seveoon Seveoonn +Severith Sevie Sevvy Sewdri +Sex Beast +Sex Gods +Sex Sea +Sex Vibe +SexEZ SexFerguson +Sexi Rat +Sexperience +Sexsi Sext0n Sextor +Sextuple +Sexy Carl +Sexy Cholo +Sexy Style +Sexy Will +SexyFatGirls +SexyLefty +SexyPeaches +SexyShooterr +Sexyteurs +SeymourAses Seyriu +Seytan Sfa05 Sfannie +Sfd Sfen +Sfouf +Sg Mini +Sgn +Sgs flames +Sgt +Sgt J0hns0n +Sgt Jacques +Sgt Moogoo +Sgt Ru642 +Sgt Salliss +Sgt SaltySac +Sgt-Sub0_99s SgtBuurman +SgtSausy +SgtSlaughtur +Sgtbea93 Sgtfatboy Sgtsoldier Sh00t3r2O2O +Sh0rt Lived Sh0rty Sh0w Sh3pathome Sh4d0w009 Sh4dowfox Sh4rice +Sha r k Shaan ShabooBopWoW Shabot +Shaboun Shabuski Shaco +Shaco Bot ShaddyB +Shade T ShadeSlay +ShadedWizard Shadedmedic Shadeknightt +Shadesypoo Shado +Shado Man69 +Shadonnon Shadow Shadow Flare +Shadow Left +Shadow Specs ShadowBobado ShadowBoy001 +ShadowFink +ShadowGodEU +ShadowJosh ShadowMakerz +ShadowPrism9 ShadowSinz ShadowSniper +Shadow_006R Shadowclysm Shadowcra Shadowlimes +Shadowmax666 +Shadows Fall Shadowscreen +Shadowsun +Shadowwheel Shadsnp2017 +Shadum ShadwRoca Shady +Shady 716 +Shady Glade +Shady Shin +Shady417 Shady78 ShadyMilkMan +ShadyMinkins +Shadys Bro +Shadysider ShaftyKrafty Shafu Shagnarok +Shags Bussy Shagzy +Shahe D +Shahub ShakaLakaInU Shake +Shakeem +Shakeman ShakenSoda Shakkas Shakuuuur +Shalamaar Shalendra Shallistera +Shalomga +Shalysa +Sham on Osrs +ShamSavior Shaman ShamanJon ShamanLizard Shamanletics Shambler96 Shame +Shamiina Shampoe +Shampoo Shamsal +Shamu +Shandaie +Shandooobie Shane +Shane F3 ShaneSJ Shanee-oo +Shanem24co Shaneobrahhh Shaner Shanfew Shania +Shania Twain +Shanizmo Shanked +Shanked it Shanks +Shanks Haki Shanksen +Shantaaa +Shantar +Shanteven Shantideva +Shapalo Shapaz Shape Shaper Shapii Shaqs +Shaqster1k +Shaquayquay Shard +Shard 2 +Share E R +Share Sucks Shareit Sharing Sharingan +Sharingan x SharishaXd Shark SharkTheBait +SharkiFan Sharkie200 +Sharkie92 Sharktail Sharkyyyyy Sharmac Sharmz +Sharn Sharoxi Sharp Sharpeyy Sharpgut +Sharpi e +Sharpie Sharpman767 SharpyClaw Shart +ShartEnjoyer +Sharven +Shasan501 +Shat 2 Hard Shatllef Shatter +Shatter Day Shaun +Shaun R Shaun135961 +Shauna Vayne +Shaunhunni Shaunni1 Shaunrnm3 Shaunyx @@ -20434,234 +42485,460 @@ ShawnBW ShawnBay ShawnXL Shawni +Shawnoh +Shawrysx +Shay Kirbuti Shaya +Shaya Rene +Shayd Shaykolade Shaymuz ShaynRS Shayne +Shayne 1 +Shayne Topp +ShayneW +Shaziyen +Shaznon Shazu00 Shazzys +Shckizm +She Moist +She Slackin +She is lvl18 +Sheashanera Shebbi +Sheep SheepKhalifa SheepTrainer +Sheepi Sheeppo +Sheer freeze +SheeshSkicka +Sheeshlor Sheetm3tal Sheetz +Sheev +Sheff-Rah +ShegoSimp Sheguey +Sheikk Sheilaaaa2 Sheivattu +Shelbgasm Shelbi +Shelburz Sheldore Sheldozer Sheli Shelios +Shelkun ShellBeRight ShellLeeBee Shelldunk Shen +Shen Btw +Shen-pai Shenfu Shenkie Shep +ShepShep95 +Shepher +Shepherdess Sheq +Sherbert96 +Sherkey Sherlock +Sherlock 07 +Sherlockness +Shernz Sherry +Shertie +Shes Nasty +Shes Royal +Shesterkin Shev64 +Shevzzz +Shewolf Nova Shezwick +Shh +Shh Im Maxed ShiaLaBuff Shiba +Shiba Miyuki +Shibidy Shiddy +Shieldy +ShiftWorker +Shifteroo +Shifterr Shiftless Shifty Shiftynifty Shiftyy Shifu +Shigure +Shihyi Shiifty Shiiftyy Shiit +Shiit head Shikhar Shilo +Shilo Void Shilomies +Shilvah +Shimmeister Shimosh +Shin Nohara ShinMalphur Shinchi +Shine tales Shing +Shinichiro Shinigami +Shinigami 07 +Shininess ShiningStar0 +Shining_Bind ShinnGee Shino +Shinobi Shinook +Shinox Shintaz Shintygod Shiny +Shiny Dragon +Shiny Glue +Shiny Mew +Shiny Olm ShinyMimikyu ShinyNoctowl ShinyShyvana Shionono +Shioshii +Ship I +Ship s +ShipCaptCrew +Shipoden +Shipppo +Shippy +Shir +Shir0u Emiya +Shirakami Shiramasen Shiranaii +Shire Fox +Shiredragon Shiri +Shirlan Shirts +Shishkemax Shiskey +Shiv HD +Shivals Shivered +Shiveron Shivers888 Shivoc +Shiyah +Shizlo +Shizzus +Shmazza +Shmeckington +Shmecko +Shmelf +Shmerlin +Shmew +Shmexy +Shmidtie +Shmo Shmoah7 +Shneeb Shnoobed +ShoToy +Shoarma Shoarmadyl Shocka +Shocked Shockedme +ShockerMe +Shockzeyy Shogaol +Shompy Shongrislomg Shonion +ShooK em Shoog +Shook Shoopdiesel ShootYaSkool Shootem40 +Shooty Tater Shop +Shopferix Shora57 Shore +Shorelyy +Shorez Short +Short Mike +Short Tale ShortHoppe +Shortec Shortguy Shortstop819 +Shortz360 Shot Shot1200 Shotixx Shotoer +Shotzz +Shoulderr Shouto Show +ShowFeet +ShowUrPits Showby14 Shower +Shower Maxed Shown Showtek Shozen Shpee Shpikey +Shpongle +Shrabbaa Shran Shredded +ShreddedMD Shregz Shreikkiller Shrek +Shrek 6 +Shrek BTW +Shrek2 ShrekLovr +ShrekazoidPR Shreksalot Shrik Shrikems +Shrimp 72 ShrimpForx ShrimpStick Shrimpa +Shrimping Shrimpster Shrimpy5 Shroden +ShrogTheBarb Shromik Shromu +Shroom God +ShroomLord17 Shroomjak Shrtct +Shrumes Shtankybruce +ShuZ Shuaston Shuckle Shuddie Shuffleee Shuiin Shultzy +Shun Conery +Shundo Mew +Shungite Bar Shuno +Shunolog Shunpown Shunrai Shunseh Shupa +Shure Shurty Shut +Shut Up Megg +Shut Up Mmeg Shutnik +Shv Shvne Shwift +Shwiftzy741 +Shwoompy Shwoop +Shxtn +Shy Kirino +Shy Mew +ShyElfTrap ShySphincter +Shyhorsegirl +Shyphii +Shypsena +Si Ya +Si d +Si mp SiFu +SiIver Fang SiRzZ Zeref +Sia +Siallus +Siamees SiameseCat Siamogale +Siane +Sianna Siber Sibling +Sic Mundus SicSolaFide +Sicariiarum Sicily Sick +Sick Degen +Sick Ego +Sick Giraffe +Sick Irony +Sick Main +Sick Mark +Sick Pet +Sick Skill +SickFam +SickGainsBru SickNasty SickPete +SickPump Sicka Sicker Sickhuman +Sickhunt +Sickle76 Sicknez Sicko +Sicko Stronk +Sickvibe +Sicxness +Sid and Geno Sidabras +Side B +SideChump +SideMeatt +Sidecutters Sides Sidewinder15 Sidge SidijuS +Sidin1 +Sidka60 Sido Sidocahn +Sidro +Sie SiebScho +Siegmeyer14 +Sielunveli +Siemke SiennaEhtycs +Sierra Echo Sierzant Sierzy Siesta Sifu +SifuPlankton +Sig Man +SigSauer Sigarda206 +SigeFrid +Sigefride Siggen Siggie Sigh Sighlent SightUn +SightUn Seen Sighted SightlessDog Sightlines Sigiis Sigin +Sigma Blood +Sigma Oasis +Sigma Seven +Sigmarz Sigmazeous Sigmet Sign Signd0g +Signoth +Sigrudson Sihana +Sihl SihuiRS Sihva +Sihva Dark Siida +Siiiiiiix Siimon3 Siimse Sijmen Sika Sika-Mika Sikauss +Sikerrim Sikko +Sikko NL Sikkosaurus Sikn Sikruz Siks Sikskilla Silanz +Silasco Silavex Silcaria Silcooper Sild SilenceSC2 +SilencedAF Silent +Silent Sky Silent too SilentEnmity SilentFabric SilentFaux +SilentQ Silentzsword Silicon Z +Silinsh Silixi Silky +Silky Beauts Silly +Silly Asian +Silly Kev +Silly Rum +Silly Virgin +Silly Zilly SillyPsybin Sillyibexe Silmarilli +Siloxane SilvaAlzir42 Silvaa Silvado +Silvanla Silver +Silver Cave +Silver King +Silver Lugia SilverForM SilverLining +SilverPuni SilverRizlas SilverTeej +Silverado Silverang Silverbluds Silvercrux @@ -20671,89 +42948,244 @@ Silverimo Silverxlion Silviii Silvoan +SilvrLining Sim Aero Simeeon Simel Simetra +Similuck +Simmcheck Simmi091 +Simmie Simmololol +Simmondz +Simmumah +Simno Karys +Simoggy +Simon Bruh Simon Jnrr SimonCookie +SimonCowell +Simone Weil +Simp Andy +Simp4Jessy +Simp4Science Simp4Tanz +Simpa +Simpinz Simple +Simple Helm +SimpleSlays +Simplekinz Simplest +Simplex +SimplexDabs Simplicitey Simplicityx Simply +Simply Quiet +Simply Sam +SimplyBadass SimplyBetter SimplyFresh SimplyLlama +SimplyNikki SimplyPro +SimplyRiolu SimplySendIt +Simply_JS +SimppCatcher +Simpson +Simse SimulatedYou Simutrans +Sin Cena +Sin Cere +Sin City +Sin Dragon Sin0fWrath SinCrux +Sinappihauki Sinasappel Sinatra Sinbad Sinbvd Since SinceOldDays +Sinclair Oil Sincoura Sindo +Sinefeld Sinerule Sinetine +Sinferna SinfulDesire +SinfulDingo Singerderek +Singulares +SingulariTx +Singularry +SinikOG Sinister +Sinister C6Z +Sinister Key SinisterLeft Sinisterz06 +Sinistr Dave Sinitank Sinixx Sink Sinka +SinkingUnder +SinnerUK Sinner_Blue Sins +Sins of Thor +Sint Truien Sinta +Sinterglass +SiontC100mph Siorri Sioux +Sip Cleeko +Sipa SippinBeers Sippy +Sippy 1 +Sipsta +Sipuliparoni +Sir Zhao +Sir 0wnalot +Sir Ali +Sir Ante +Sir Boberton +Sir Botje +Sir Brandito +Sir Burritoe +Sir Chancho +Sir Colossos +Sir Daven +Sir Deedge +Sir Delwin +Sir Doland +Sir Doobies +Sir Elusive Sir Epic 3rd +Sir Ferreus +Sir Fire Ki +Sir Frekkel +Sir Fudmire +Sir FurDude +Sir Galleth +Sir Godfree +Sir Graj +Sir Ial +Sir Idwaly +Sir Iron 4th +Sir Jamie N +Sir Jinhai +Sir Junn +Sir LafaLafa +Sir Lanofg +Sir Leadfoot +Sir Lidd +Sir Lobster +Sir Luke1627 +Sir Max Tb +Sir Mordacai +Sir Mr Bro I +Sir Nak Sir NichoIas +Sir Nuggers +Sir O Tonin +Sir One Shot +Sir Peg +Sir Poisonfa +Sir Pur Paul +Sir Queeffin +Sir Reflex +Sir Reos Lee +Sir Rhaenir +Sir Rolf exe +Sir Seifer +Sir Shrimps +Sir Slyck +Sir Son Goku +Sir Sparhawk +Sir Stidi +Sir Stitch +Sir Sucellus +Sir Sven +Sir Tanner +Sir Ton4 +Sir Towliee +Sir Truble +Sir Var4 +Sir Ving +Sir Vkk +Sir Whipit +Sir Wismar +Sir Wojtek +Sir Zakar +Sir friezer +Sir philippe +SirAirik SirAmikVarze SirAngo SirArcAngel +SirArimal +SirBabbers SirBerus +SirBjustice SirBoma +SirBoopin SirBroderick SirCapper +SirChknNuget SirCoolAsian +SirCumsAlil +SirDanSolo55 SirDarrBear SirDeimos +SirDouglass SirDougles +SirDoyvid +SirDude SirEfficient +SirEskimo +SirFroggits +SirGillespie SirGoki +SirGrimsby SirGrizzly SirHines SirHmm SirHollywood SirHumanBean +SirIronyMan SirKakoiml SirKefir SirKill SirKulls +SirLarry55 SirLoinalot0 SirLunarias SirNexALot +SirOcean +SirPegAsians +SirPhockQ SirPoo SirPwn SirRebs +SirRuck +SirSanders +SirSchmoopy SirShakes SirSilky SirSlayalot SirSpicius +SirSplinge +SirSunTitan SirSwaggzz SirTrea SirVenompool @@ -20763,49 +43195,92 @@ SirYiffer Sir_massmo Siraxis3310 Siraz +Sircamm +Sircarenot2 +Sirdedalot +Sire Edward SireSucks +SirenMan Sirhca Sirius Sirixen +Sirjoshihad2 +Sirkuspelle Sirlagger Sirlightboy Sirmordred44 Sirnuclear +Sirroyce +Sirsa +Sirsteve99 Sirstynkalot Sirswish4 Sirtippy23 +SisiJones Siste +Sit Quietly +Sit xD +Sita Koll +Sita Rama +Sitharii Sithlord Sitinduck Sitt +SiuMan Sivior000 +Six 3 Ohh +Six Dix +Six Kills +Six Seasons +Six Sen6e +SixFootAmigo SixOhFour Sixcess +Sixfoot0001 Sixint Sixpkabs Sixpounders +Sixten106 +Sixthflag Sixty +Sixty N9ne SixtyNine73 +Siynistr Sizashi SizeMan Sizer +Sizzle Cat +Sjaak +Sjak Sjakk +Sjakk Spill Sjappy Sjeb Sjele Sjenjatje Sjenkie +Sjokoladebit Sjoni SjonnieDon +Sjonsen Sjotha Sjulstad99 +Sk Gollum +Sk Scooter +Sk ky +Sk u lly Sk1llzy_RS Sk1ttlz Sk8rgrl474 SkHiCharisma +Skaaddoosh +Skaane Skachoo Skada +Skada N +Skade Skaduw99 +Skaf SkaffeAgent Skaicius Skaikru @@ -20813,69 +43288,124 @@ Skailas Skakkae Skal95 Skanderbeg +Skank Hunt32 +Skarboeblie +Skarking SkarmBliss +Skars Iron Skaryth Skate +Skate Squirt +Skatemyboard +Skater 1299 +SkaterJord SkaterSkillz Skatergod Skaterlegs Skaterune3 +Skaufel +Skcoot +Skedeby Skedsauce Skeeb Skeer +Skeesh +Skeeterstein SkeetlzPopz Skeff Skeggy Skelebral Skelethon +SkeletonSex Skelex Skelnik +Skelpy Skely527 +Skelytom +Skepp +SkeptasMum +Skeptical +SkepticalSol Skere +Skere Bert Skermy Skerp_Perp Skertt Sketch1e +Sketchiin +Sketti Water +Skewp Skezi +Ski Gim +Skiba +Skibidi Tob Skibidibills Skidoodlee SkiemLord Skifree +Skilane Skill +Skill Will +Skill issues +SkillFatigue SkilldSkitzo Skilldriver Skilled +Skilled Boof +Skilled Roy Skiller SkillerC4 +Skillerbabes +Skillersj +Skilless +Skillexi Skillhunter1 Skillian89 Skillius Skilll +Skilllzdan Skilln +Skillpace Skillpadden Skills +Skills Ahoy +Skills Boss +SkillsPets SkillzGainz SkillzLmao Skillza Skillzzz Skilor +Skimer SkinStitches SkinTone5 SkinnedYoshi Skinny +Skinny Mage Skinthix Skinyoualive +SkipTheTask Skipi Skipper996 +SkipperMcgoo SkipperPing Skippy +Skiptutorial +Skito +Skittlz Doc SkitzRs +Skitzad Skitzpatrick +Skizzles +Skj Skjaera Skjeberg Skjelve Skkarpz +Sknnywhteboy Sknor +Sko Birds +SkoalSnus Skodem Skoh Skol @@ -20883,6 +43413,7 @@ Skold Skolder Skolzera SkoobiDooby +Skooier Bibs SkoomaPls Skopix Skopusnik @@ -20892,22 +43423,37 @@ Skorne Skorned Skotten Skov +Skovduen Skovtt +Skox +Skral +Skramlan99 Skreecher Skreeeech +Skreeeech Z Skriptz Skritman Skrl SkroTam +Skroooodge Skrote +Skrrrtle Skrudzas Skruf SkrumpKing Skrychi Skryze +Skubert Skuddar +Skul SkulbIaka Skuldebrev +Skull 2148 +Skull Cove +Skull64 +SkullDemise +SkullFkdNex +SkullFricker SkullTrailYT SkullTricked Skulled @@ -20915,46 +43461,77 @@ Skullhead6 Skullking199 Skullpit Skulls +Skulls Jr +Skumbag Xell Skummi Skummy Skumpy Skundos Skunk +SkunkAsylum +Skunkerinho Skunkieblow +Skwat MD +Skwurtle +Sky BIues +Sky Em +Sky Mages +Sky Satan +Sky VR46 +Sky and Sea +Sky is mine SkyBouncer +SkyBroadband SkyFlyPie SkyKnight +SkyRizzy1 SkySailing SkySkyClover +Skyblownet +Skybussa Skyda SkyeKuro Skyenss +Skygirl Tami Skyleosaurus Skymonster77 Skymore Skynet1188 +Skyom +Skype Call Skyreacher Skyrider Skyrider50 Skyrion993 Skywalkingyo Sl yx +SlGURD SlGlL SlLPH +SlRCUMSlZE Slaats SlabOfCorona Slabs Slackington Slacky +Sladdbarn Slade9100 +Sladist +Slagter Slain +SlainThemis +Slaked Slakito Slakje Slakoth Slam +Slam Daddy Slambo +Slamdabooty +Slamdark Slamer1993 SlammDaddy +SlammalS Slamz Slance Slap @@ -20963,35 +43540,71 @@ SlapShot777 Slapen Slaphead28 Slapmeister +SlappChopped SlappaBogan +SlappeStront Slappenn +Slappers SlappinMango +SlappybagsFC Slaps +Slaps Nuts Slapzinger Slaqk +Slarkan Slash Slasher +Slasher z99 Slasher0708 Slaskepott +Slate Snake +Slats Slaughta +Slaughtered +SlaughtrMeat +Slav Kings Slava Slave Slavish Slavtonio Slay +Slay Addict +Slay More +Slay Or Bust +Slay Pets +Slay R +Slay Tim Slay0rDie Slay3rmate SlayAChicken SlayEeryDay SlayForFame SlayForJoy +SlayImpulses SlayIsMyfame SlaySir +SlayToSkill Slayaholic Slayborhood Slaycation +Slaydenoob Slaydo +Slayem Maul Slayer +Slayer 247 +Slayer 267O +Slayer Azye +Slayer Clues +Slayer Helmz +Slayer King +Slayer L0G +Slayer Mid +Slayer Rome +Slayer Shark +Slayer Task +Slayer XPs +Slayer bond +Slayer n gp Slayer314 Slayer360 Slayer7515 @@ -21000,6 +43613,8 @@ SlayerKingv2 SlayerRyan Slayerburger Slayerdog +Slayerg1g +SlayerisGame Slayerknox Slayerman Slayers @@ -21009,22 +43624,32 @@ Slayhades383 Slayic Slayin SlayinSkills +Slaying Poon SlayingBooty SlayingDave Slayingit14 Slayn +Slayology Slayosaur Slayr Slays SlaysIronman +Slayscaped +Slayter +Slaytr +Slayv Slaywolf Slayyer59 SlayzyDayz +Slck Mark SleanClate SleazySEAL +Sleazyscape Sledgendaddy Sleeep Sleep +Sleep Cycle +Sleep When Sleepercell Sleepgoood Sleepierz @@ -21032,181 +43657,376 @@ Sleepiness Sleeping SleepingCow Sleepinonice +SleeplessC47 +Sleepnaut Sleeps Sleepy +Sleepy Fr0st +Sleepy Hoop +Sleepy Mulli +Sleepy Tired +Sleepy sound SleepyPlantz SleepySus Sleepybear Sleepzy31 Sleet56 Sleete +Sleighbor Sleightyyy +Sleighyour Slemmy Slender SlepinHose +Slepinn Slepping123 +SleppySnek +Sletmar Kets +Slettenbak +Sleuth +Slev711 +Sleve +Slevenderrrr Slewwyy Sleya +Slibbon Slice +Slice Dice85 +Sliced1Bread +Slicer3 0 Sliceseau Slick +Slickslipply +Slicore +Slidecast Slides Slidpanther Slidz +SlieNinja +Slienced +Slievemore +SlihgtyWrong +Slikjee Slim +Slim Gandalf +Slim Paul +Slim Yogurt SlimJD +SlimeShat +Slimey Fish +Slimjim Slimy +Slimy Uim +Slingin Slinkeh Slinky +Slioter +Slipery Peet Slipory SlipperySnek Slise745 Slit Slizzle +Sll8 SlnOfWrath +Slo Slo w th +Sloanee SlobOnMy +Slojsarn Slokei Sloopie Sloothy Sloper +Sloppy Joey +Sloppy WAP +Sloppy Zoot +SloppyGnomes +Sloppyninj Slorkie +Sloshpack +Slotche +Sloth +Slothenly +Slothery Slothhs +Slothly Sloths Slothy SlottedPig +Slough Slow +Slow Down +Slow Spirits +Slow blow +SlowRoaster +Slowky +Slowly Dying Slowpoke SlowrolI +Slowxpgamer Sludgy1 +Slug Kiss +Slugggy +Slugjob Slugr +Slum Village SlumberGoose Slumptality Slurggi +SlurmsMcnzie +Slurmz +SlurpDaTerp Slurpez +SlurpinBrews Slus Slush Slushhy Slushii Slushiiii Slushyy +Slusserbust Sluthra Slutism +Slutzilla Sluwe +Sluwe Odin Slxpz +Sly CHRlS +Sly Guy Matt +Sly Shy Guy +Sly Spyro +SlyPancakes Slyfocs Slyjack Slymax Slypes Slythiren +Slyzuh Sm0keyMcpot +Sm0rc420 +Sm1thyy +Sm6 +Sm8 AJ +Smaarips Smac +Smack Snr +Smacka Fish +Smackatosh +Smacking 0s Smackintoshh Smaeow +Smai Smajli +Smal Iron Small +Small Chief +Small Idea +Small Iron +Small Paul Small3y SmallBlock SmallBrained +SmallPepino +Smallemans +Smalley +Sman Smang +Smart Dog +Smart Foam +Smart One Smartbabe SmarteeRS SmarterMop +Smartfon Smartiest Smartyross +Smash Vials Smashing Smaug SmaugSlayer +Sme Cape Smeared Smecher +Smeeezy Smeegoles +SmegGIM Gr8 Smeggyweggie SmegmaJesus SmegmaL0rd66 Smegmatism +Smegmboy123 +Smekkes Smeklius Smeli0das Smelix3 SmellMyClam +SmellingMill Smelly +Smelly Butt +Smelly Guy +Smelly Kip SmellyGymSox +Smellyjeans Smellypillow Smess +Smetvrees +SmexyBaker +Smezzie SmiTHSaNiTy Smiddels Smiddle Smidjorgen Smii Smil +Smil3r +Smile My Boy SmileBro Smiled +Smilee Smiles +Smiley AK SmileyCyrus +SmileymanTim Smilf +Smiliprophet Smilts +Smimi Smumu SmiqelAngelo +Smirk Smit +Smite Bait Smite4Ags SmiteForAgs +Smited Time +Smites Smith +Smith Inc +Smith a Cat Smith2109 SmithRebirth Smithin +SmithingWhip Smithinz Smithoxmagic Smithy +Smithy NZ Smittyk15 +Smo3 Smoak Smob +Smoel Dicht SmoggyB +Smokahontaz Smoke +Smoke A Bag +Smoke Brb +Smoke J Brb +Smoke Oil Smoke2Fly +SmokeChedFTM +SmokeCheds +SmokeM0B SmokeSocial +SmokeandCum +Smoked Bacon +Smoked Ed 92 Smokedale Smokee SmokeeLoki SmokerOfDank Smokes Smokey +Smokey Eyes Smokey201 +Smokeynutz Smokeynz +Smokiecat +Smokin Clans +Smokin Perks SmokinBlunts SmokinChills Smoking +Smoking Hash SmokingFlax SmoknDReefer Smol +Smol Big Toe +Smol Kupo +Smol Lyra +Smol Pikachu +SmolDeer Smoland +Smolrice Smooooove Smooth +Smooth Draft +SmoothRabbi +Smoothlu Smoothpossum +SmoqueWeed +Smork It +Smoss +Smot Poking Smpli Smuddy Smug +Smug Advice +Smug Grin +Smulknul Smurf +Smurfboard Smurfed Smurfingt0n +Smurfman254 Smurfukas +Smurghilda +Smurky Maart Smurphington +Smush +Smush Things +Smushma SmushyCows +Smuvies +Smuzzle +Smythy Sn0op Sn0rkath420 +Sn0wb4ll Sn1p3 SnYp Snabba +Snack Bar SnackJack +Snackspace Snacktime SnafuPC Snaggapus +Snaghyrnd +SnailNipples Snake +Snake Boss +Snake Jazz SnakeSpec Snakebite37 +Snakebites Snakedeath2 Snakedogbear Snakeling +SnakeskinRag +Snaks Snaky +Snaky Snake SnapMyCarot +Snapa7 +Snapboog1e +Snape Grass +Snapeeh +SnapperSnafu +SnappySplash SnarTheCook +Snarbo +Snarf Snarflaxus Snarfsnah Snarglefox @@ -21214,62 +44034,110 @@ Snarl Snarx SnaszAndrejj Snatchquatch +SnazzyLt Sneak Dissin SneakEnergy SneakKhajit +Sneakbo Sneakee Sneaky +Sneaky Chair +Sneaky IM +Sneaky Shark +Sneaky47 SneakyBaloup SneakyCake SneakyEmu +SneakyFrogg SneakyGnomne SneakyOD SneakyPete SneakyTay +SneakyZoot Sneakymag3 Sneakyvicn Sneakywaffle +Sneakz Snee Sneekerz Sneekey SneekyBeeky +Sneekydied +Sneeuwpoeper Snefnuk +Sneide O_o +Snek zy SnekInMyBewt Snekkerboden Snekling Snekty +Snekty Jr +Snelms Deep Snelmz Snibzy Snickers Snickersbite +Snicklesz Snide +Snide Senpai +Sniff K +Sniff this L +Sniffi +Sniffin 24-7 +Sniffin Bags SniffinBags +SniffingClue Snifflematt SniffmySmoje SniffyMonkey Snike4 Snikepaven Snipe +SnipeOne045 Sniper +Sniperfrank1 Sniping Snipor +SnkyGreninja +Snlly +Snny SnoWorm Snobby +Snobby Elite Snoep +Snollen Snoobsteri +Snoogens +Snookies Snoop +Snoop D9 +SnoopSiah +Snoopfyzle Snoopy Snoot Boops +SnoozeBolton SnoozingBear +Snoper +SnoreLunaLax Snorff1 Snorklarn Snorlax Snorlax9030 +Snorlaxz iLy Snorona Snot +Snotlapje Snow +Snow Day +Snow Poff +SnowBunnyhvn +SnowEmpress6 +SnowManSam +Snowangel18 Snowballerz +Snowbaru Snowclub +Snowdaze Snowearth Snowelle Snowfall @@ -21279,71 +44147,144 @@ Snowiest Snowii Snowmen Snowscythe +Snowsmith43 Snowvof +Snowy Winter Snowy2653 Snowyo26 +Snowys Main SnuSnuShi Snubby +SnugLikeABug Snuge +SnuggleSnail Snugglez +Snugins Snuift +Snuite SnusMumr1ken Snuskig +Snusti +Snuup Snuus Snuuska +Snype_U +So Far To Go +So Flattered +So Inagawa +So Iron BRUH +So Jamiezing +So OSRS +So Quiche +So Warm SoHighhhh +SoLoH DoLoH SoMoist SoSimPol SoSleepy SoStronk SoUhBtw SoVeryInvis +Soaa7 Soad +Soally Soap013 Soaphia +Soapybubble Sobe +Sobe VII Sober SoberFarmer +Sobibor +Sobotka Soburin +Sobzy Socca Soccerc12 Soccermom02 +Soccy SocialAnxty +Socialism SocialistGuy +Sociality +Sock Jesus +Sock Starch +Sock Stealer Socket +Sockks Socks Sockum Socrates Socrates001 +Socratestes Soctch +SodaGrab SodaLambz SodaPopPunk Sodaseg Sodasokwa +Sodd +Sodo Pop Sodobrasil1 +Soecara +SoegLeppel SofaKingHI6H +Sofakingdom Soffachka Sofia +Sofia Cooper +SofiaVergara Sofiero +Sofoni Soft +Soft Chicken Soft Dump +Soft Geeves +Soft Jesus +Soft to Hard +Softboil +Softcorejmac Softer Softest +SoftestFox +Softice SogSteelrock Sogeking Soggy +Soggy Napkin SogxNeutro Sohcahtoa090 Sohh Sohl Sohrac +Soi +Soi Cowboy Soija +Soisox +Soivio Sojuah +Sol Buendia +Sol Heredic +Sol Heretit +Sol Leo +Sol Rock +Sol o +SolTeevo +SolaDuaeManu +Solakoust Solan +Solar Stone +Solar754 +Solarized +Solarrr Solaryohm Solarys +SolasLexeth Sold +Sold GF 4Gp +Sold My Name SoldMyRNG +SoldMyWife SoldUrDad4GP Soldaat Soldaat824 @@ -21352,80 +44293,169 @@ Soldjer Soldtheman Sole226 Solek +Solely Soley Solibiobois Solid +Solid Snack Solid Stache SolidSnape +Solidtag Soliform +Solimanx Solitary +Solkrieg +Sollor +Solmlet Solo +Solo A7X +Solo Aero +Solo Arron +Solo Blitzy +Solo CM +Solo Chumb +Solo Dani +Solo Foxy +Solo Geeber +Solo Gira48 +Solo H3llz +Solo Hayden +Solo Herbo +Solo Idenn +Solo Jawn +Solo Jones +Solo Jord +Solo Kev +Solo Mace +Solo Mus +Solo Road +Solo Romte +Solo Russian +Solo Sieb Solo Sirva +Solo Trojan +Solo Umbreon +Solo Ward +Solo Wind Solo Xenopus +Solo Yoyo +Solo kris +Solo snapper Solo2424 +SoloBandit +SoloBongo SoloCanadian SoloDeicide +SoloDeth SoloDynamix SoloFireFist +SoloFlar SoloFletcher +SoloHotDog +SoloIron30 +SoloMerx SoloMishMosh SoloMission +SoloNotAlone SoloProject SoloRob SoloShow SoloSloop SoloSmackbar SoloSnhaas +SoloSupOnly SoloTibbs Soloable +Solobussy +Solocek +SolohellRat SoloingLife Soloman50 Solomasi +Solomon +Solomon Kane +Solomopp +Solos Turn Solowerkz Soloyo Solsa +Solucki +Solufana Solus +Solus RS Solvent +Solvent less +Soly Handal SomalianRats Somalor +Somavrana Some +Some Pets +Some Ranger +Some Use Some mad dog +Some1youno SomeDirtyOar SomeGoldDust SomeRSIdiot Somix +Somniac One Somnolent +Somoshoho Somppup +Son Goku MF +Son Huntr +Son LeGeND +Son o Rous +Son of Akkha +Son of Hail SonArtorias SonBuns SonIron +SonNamedBort SonOfDecay SonOfGandalf SonOfWright SonVsStepmom +Son_Jo Sonc +Sondey +Sondr Sondra Sonekta Sonett Song +Songs Of +Songsteel Sonhov +Sonia Strumm +Sonic 513 +Sonic Kp3 +SonicYouth Sonicgg1 Sonics +Sonics Alt +Sonidoo Sonjini SonneTeufel +Sonol Sonsofkyuss +Sonx SonyVegas17 Sonycboom SooCrispyy SooUnlucky +Sookadiik Soolo Soon SoonAHusband Soonifer +Soop Pock Soop3rpig SootSpritez Soothe SootyWhale32 +Soph Sophinx Sophlex Sophomaniac @@ -21436,18 +44466,31 @@ Sora Soraaxle Soradin Sorado6 +Sorarpegius Sorbits Sorbosander Sorcere10 Sorcerer5555 SorcererOdin +Sorcerio SorceryFish +Sordnatra +Sore Sorec +Sorenisbad Sorenn +Sorgrum +Sorinvv +Sormeton Sorose Sorry +Sorry Iron +Sorry1mLate +Sorryganster Sortable Sorted +SorthIon +Soruve SosBoerrr Sosig Soskiller6k @@ -21456,131 +44499,244 @@ Sosraid Soss Sossukorva Sossumafia +Sosuke Aizen +Sotamage +Sotetseg Sothe1 +SotonSteve +Sottetseg Soujy Soul +Soul Burger +Soul Scaper +Soul Sleep +Soul v9 SoulEater957 +SoulMadness +SoulOfMidir SoulSteala SoulUnleash +Soulbarrier Soulbearer +Soulcape +Souldia +Souled 0ut +Soulja Lance Soulkilled3 Soulkn +Soulles SoullessMask SoullessWood +Souloh0 Soulololol Soulplay Gee +Souls Mate +SoulsRampart +Soultiller Soulxicution +Sound Soul Soundbar +SoundsOfSoul Soup +SoupEyes +SoupOfTheDay +SoupTheDuck +Soupshi Sour Sourzilla SousChefLuna Sousse South +South Dakota +South Park55 SouthPillar SouthSide Southampton Southenders Southern +Southp0le Souvenierr Souvis +Souvis5 Soux +Sovacam Sovakat Soveth +Sovetskie +Sovi Soviet +Soviet Union Sovryn Sow Love Sowodasoap Sox207 +Soxinder +Soy Duro +Soy Sauce +SoyBhoy SoyJuanEuro SoySoySoySoy Soyez +Soylemagne Soylo +Soz Misclick Sozan Sp00n Sp1cy +Sp1cy Ramen +Sp1cyAvocado +Sp33Dkillz Sp33dy +Sp3c Deck Sp3nC SpARioN SpBongile +Spaar Lampje Spaca Space +Space Bussy +Space Lily +Space Marine +Space Orbs SpaceC +SpaceEnter +SpaceScape SpaceToker Spacealt +SpacedPinata +SpaceeCowboy +Spacejumper0 +Spacing Outt +Spade-e Spadey Spag +SpagBolCol SpagHacked Spaget1111 Spagett Spajina Spalling +Spam Lite SpangeBerb Spangebob Spangl +Spangledbun Spanish +Spanish Dave +SpankFox +Spankies Lit Spanky +Spann Sparacino916 Sparc +Sparc Mac SpardaWallet Spare Spark +SparkRS Sparkles Sparklesbean +Sparky3433 +SparkyD5 Sparkyo +Sparre +Sparta2 +Spartac_us +Spartacus855 SpartanDrgon SpartanSheep SpartanSlick +Sparte Spartin028 Sparty Spasian +Spat Fastic +SpatialMagic +Spattu +Spaustin80 +Spauz Spaynce Spctwm +SpdrBiteJosh Speak Speakerbocks +SpearRue Speat +Speats +Spebi +Spec A Ho +Spec Master +SpecAgent713 Special SpecialBus SpecialGuest SpecialPVM Speciiik +Speckles SpectrlHeist Specz Speechwriter +Speed Colas +Speed Wobble SpeedPony44 +SpeedSouls +Speedalot ak Speedball1 Speede +Speedlund +Speedrun Speedster +Speedy Cd +Speedy Click +Speedy Rulzz Speedy9921 SpeedySpider Speeldoos +Speen Big Speeze SpejsonNM Spek +SpekKas Spektur Spellgoth Spenc +SpenceSuh +Spencee Spenceey Spencer +Spencer O Spencerr Spencerz Spend +Spendlove Spengineer Spennel +Spennel V2 +Spenno 1 +Spensaurus Sper Spermlet Spero Speshal +Speshl +Spesho Spettkaka1 +Spewis Spewler +Spewlie Spezza Sphere Sphinx +Sphinx66 Sphooner Spice +SpicelessJoe SpiceyBoy39 Spici +Spici Boi Spicy +Spicy Noods +Spicy Rahman SpicyMemeGod SpicyMoney SpicyPepe @@ -21588,41 +44744,71 @@ SpicyVitus Spide Spider Spider1357 +SpiderPhenom Spiderhead +Spiderpig Spidersnot8 Spiderw5 +Spiderz 3 SpideyC +Spidoo +Spiering +Spierpijn +Spigniv +Spike05 +SpikesterHC +Spillage +Spillingx Spin911 Spineweilder +Spinkle +Spinnenking Spinnenweb Spinolyp Spirit +Spirit Box +Spirit Cube +Spirit Owner +Spirit Rok +Spirit Sin Spiritcraft Spisek +Spit man SpitfireSR +Spitiz Spizazzy SpizzyMarz Splarf Splarn +Splash LT Splash-8 Splashattak +Splashcarrot Splasher +Splashes Splashley Splashworlds Splasion Splatter300 Spleaner +Splessp +Splezaa SpliceTFY6H Splidge SpliffMaster +Spliffore +Spliiffy Splinta +Splinterhand Splitarellie Splitfaction Sploof +Spluge Splyce Spo0n Spocuch Spodey +Spoice Spoilted Spoka Sponch @@ -21631,17 +44817,36 @@ Sponkz Spoodersussi Spoog Spookachtig +Spookadoodle Spookfish +SpookiBoogi9 +Spooky Beech +Spooky Ramen SpookyCarl SpookyMain Spookyou Spookytoot Spoon +Spoon Full +SpoonFedFred +SpoonFedii +SpoonMePlz +SpoonTracker Spoonay +Spooncera Spoondow Spooned +Spooned Simp +SpoonedBunni +SpoonedNormi +SpoonedWhen +Spoonfed BTW Spoonihomo +Spoonku +Spoonletics Spoonmannn +Spoons +Spoood Spoookems SpoopyDooToo SpoopyNooper @@ -21649,191 +44854,365 @@ Spoorwijk Spooxky Spop Sporer +Sport Leader +Sport425 +Sports Bike +Sportsnut911 Spotifee +Spotmaster 5 SpottedBass Spottednoble SpotterN Spottie SpowSaus +Spr1ditis SprLemonHaze Spraahz Sprattet SprayM0re +SpraynMantis SpreadButter +Spreader Spria Sprice Springer Springii +Sprinty +Spritbilist +Spro max +Sprog Legend +Sprogdor Sprooti Spruit Spry Spubb Spud +Spudalumps Spudge Spudinator Spuge +Spuhcific +Spuhgetti +Spuitopbruid Spunknik SpunkyLlama Spurted +SputtyBTW +Spyder Sin +Spydig Spyike270 +SpykeZim Spykes Spyriano Spyro +Spyro 3 Spyrooooooo Spytech44 +Sq Head Sqe3zy Sqeat Sqiit +Sqix +Sqoub +Sqreech +Sqrwl +Squab Cat Squabs +Squad of one Squadleader +Squaire Squall Squall44444 Squallicious +SquanUK +Squanch Rat Squanchhy Square +Square One Squarebodies +Squat Slav SquatCobbler SquatJogsBro SquatNation Squatting Squaw Squeakes_x +Squeaky Cow +Squeedly +Squeeeegs SqueegeeLord Squeeze +SquidNumber0 SquidSquad +SquidTofu Squidby Squiddle +Squidgling Squidie +Squidler +Squigiglius Squigs +Squigtime +Squinch +Squinch Alt +Squinton Squintts SquintyNinja +Squire Yaper Squirlruler +Squirly69 +Squirrel +Squirrel Pop SquirrelTM Squirreled8 +Squirrely W +Squirt2God +SquirtsAlot SquirtyHersh SquirtyMcD +SquishySir Squonking Sqwalle Sqwarka SqwezMyLemon +Sr Dipper Sr8d +SrBambino SrMick SrMuerte Sraracha +Srelek +Sri Srogi Wujek +Srs SrslyTired SryAFK +Sshscaptain Sshuggi05 +Sssnakepit +St Chrewin +St Liam +St Newman +St eve +St itch +St un +St0kStaartj3 St0ney St0oge St4bU +St4rCh11d +St8hunter +StBasilthGr8 StDecker +Sta Mama StaIemate StaJeOvo Staasi Stab3r +Stabalot +Stabobis Stacii100 Stackdude +Stackey Stackieks Stackin Dosh +StagBeer Stagden Stagmuss StagnantAlt Stagnation Stago +Stahlinski +Stain Master Stak +Stake Me StakeOrQuit +Stakebril Stakens Staker +StakerOhWait Stalactites Stale +Stale Chips +Stale Dough +StaleBagle Stalefloe Staley +Stalifax Stalk +Staller Stalline Stalphie Stamin +Stamina Pot +Stamkoz Stammer +Stampie Stams Stamuhnuh Stan StancedMK Standard +Stanimal Stanimal244 +Stanjuuh Stank +Stank Rat StankBreath +Stankfist Stanley Stanleye Stanlux Staparik +Staparik Lt Stapper6 Star +Star Doctor +Star Say 962 +Star Scourge +Star Spoon +Star zy StarDreamSam StarFell StarGlimmy StarKist +StarSnitcher Starboyyyyy +Starbuckss Starcharts +Starchild Starcry Starfallx +Starfight789 +Starfish +Starjoker8 +Stark Btw +Starlette Starlighte +Starmaker Starmemoria Staropramen Starr Starrlightss Starryfina Stars +Stars Hockey +Starshark +Start Game +Started Late StarvedLlama +Starvi Starwulf +Starzhine Stash +Stashes Stasik25 +Stasik77 Static +Static Peak +Staticshook Statoke StatsisZero Stattelsson Statuhs +Status Bar Statutory Statykk +Statzy +Staubach +Staunch +Staunch_BJD +Stax I Stay +Stay Breezy +Stay Frosty +Stay Hurt +Stay Hustlin +Stay Ready +Stay Salty +Stay Shady +Stay Sic +Stay West +Stay wise +StayFinessed StayPulls +StayPullsBTW +StayThirsty +StayinClassy StaysmallEZ +Ste Gerrard +Ste f SteSkillalt Steady +Steady Loot +Steady Mobn +SteadyFlexin SteadyetiBTW +Steak Ramen +SteakWitWiz +Steaked +StealCutOats +Steals +StealsThings Stealth +Stealth Ownz StealthChop Stealthcy Stealthi Stealthweed +Stealthwolf Stealy Stealy Boi Steam +SteamboatJim +Steamed H4ms +Steamfitting +Steammm +SteamyBuns SteamyCreams Steanox +Steb Stedy Steeam Steebert Steeermy +Steekkha Steel +Steel Gods +Steel Stud +Steel Tarkus +Steel VIK SteelRoxas +Steelbird16 +Steeleos +Steelpan Steen Steenkoe Steezed0ut Steezi Stef +Stef West Stefan StefanLivNo1 +Stefar Stefen Steff SteffenHax Stefo +Stego +Steiger Steikluet +Steinbeck Steinphite Stela +Stelisss Stellacea +Stellamara StellarLG StellarWhey Stellard +Stellas Dad +Stelli StembaWalker Stenfen Stengo @@ -21841,21 +45220,43 @@ Stenicki Stenny Stenuism Step +Step Barrow +Step mummy StepBroHung +StepBroImZuk +StepDabs +Stepbro +Stepbro Paul +Steph Browne +Steph a nee Stephan +Stephand Stephane Stephen9320 StephenMoore Stephenns StephhRS +Steppage Steppaz Steppenwulf +Stepy Stereofuzz Sterlander Sterlington Sterrenstof +Stervolz15 +Stetko +Steum Stev +Stev en Steve +Steve Beanie +Steve Tobs +Steve Tombs +Steve Zissou +Steve ffs +Steve is Pro +Steve07 Steve51 SteveAustin SteveChamp @@ -21864,38 +45265,66 @@ SteveMCOG SteveMerch SteveOwner09 SteveSnow15 +SteveStamos +SteveTheGimp SteveTheMuss Stevee Stevefr3nch Stevemck Stevenrds +Steves Blade Stevet7125 Stevey +Stevie ESQ Stevo +Stevo Lad Stevo29 Stevoguy +Stewge Stexie Stian +Stian Olsen Stian2 Stibby +Stickasaurus +Sticker Stickers Stickman +Stickman Zeb Stickman0011 Stickmeister SticksNbugs Sticky +Sticky Icky +Sticky Nut StickyBeardo +StickyShroom +Sticky_BIBLE +Sticky_Lip Stickypooman +Stickz Alt StierK +Stieren Lul +Stiffies Stiffish +Stifighter X +Stiflers mum +Stigel Stigiam +Stigmaster Stijco +Stijf Stefke Stijin StikSt0f +Stikjob Stil Still +StillANormie +StillCoughin +StillFap2ash StillFarmin StillNoBan +StillNoTBOW StillRemains Stillblazing Stillen @@ -21905,16 +45334,24 @@ Stiltz Stimu Stimulated Stin +Stindebinde StingWisher Stingy Stink +Stink Soep Stinka Stinker Stinks Stinkwiener Stinky +Stinky Adam +Stinky Wench StinkyFatBoy +Stinkyfeat1 +Stinkypooper Stinkywon +Stino33 +Stiorra Stipples StirG29 Stirner @@ -21922,118 +45359,222 @@ Stitcha Stiven117 Stixc Stiznai +StlthyPanda Stnmn Stnyzky StockMyRam +StockOlm +Stocker T +Stockphish +Stocksund Stocktone +Stockyard Stodie Stoel +Stoere Kip +Stoerebos11 +Stoern Stoffer Stoffins +Stoggy188 +Stoic Christ +Stoic Sloth +StoicGod Stoke +StokedMaggot Stolen +Stolen Ego Stolen Grunt Stolen Herbs Stoli +Stoli za Stolid Stolpz Stolte +Stoltzkin +StolzRS +StompaHhh +Stompadile +Stompea +Stomped +Ston3d Arrow Stone StoneDwarf StoneOkami Stoneator Stonebeech Stoned +Stoned Abra +Stoned Sober +StonedGSXR +StonedNormie StonedTTD +StonedTurtle Stonedtime +Stoneplus Stoner +Stoner Morty +Stoner Pov +StonerGod +StonerL0ve +Stonerling Stonesoul15 Stoneyvangel Stonkx +Stony 420 Stoobie Stoogaroni Stooley +Stoolz Stoonly Stoopy +Stoopy Scape +Stoovey Stop +Stop Acts +Stop Mules +Stop Now +Stop StepDad +StopCapping +StopDabs StopDontDoIt +StopStepSis Stopitjoe Storgie Stories Storkie Storm +Storm Monkey +StormFly +Stormlight +Stormmaker98 +Stormy RS Stormzy Storn42 Stortford Story +Story Arc +Story of Man Storybot StoryofPete Storys +Stothelayer Stouse +Stouty +Stovall_9 Stover +Stovete +Stowa Stowty Stox7k +Str Owner07 +Str Takes U Str0ng8ad Str8 +Str8 Maxed Str8backatya Strabz Stradarts +Stradinger Straf Strafe +Strafes +Straffen +Strafi n StraightSimp +Straighter +Straka Stralia +StralianBtw +StrangeChris +Stranger bud Stranger56 +Strangerz Stranges10 Strangler Strap Stratazz +Straton Stratty StratusQc Straubrey Strauss Strav Straven89 +StrawHatMatt +Strawbs +Strawhat01 Straya Strayday Straynaneeee +Streakin +Streammmmmmz +Streammz Streepken419 Street +Street Fever +StreetSweepa +Strefi Stregano Strelitzea StrenIron Strength +Strength Lvl Strengthy43 Strepski Stresset Stretch +Stretch 07 Stretchy Strickend +StrictNein Strictly Striddle +Strideless Strife Strijden Strijder Strikedown +Striker796 Striker8 +Striker8 0 Strikes Strikingvipr +String Benis StringsLogic +Strip Club x +Stripa +Stripgwyn +Stripred +Strive Strix Tactic +Stro Sr Strobax Strocks3 Strocules +Strohs StrokeMuh StrokeMyWand Strokemon StrokmyGroot Stroller StrollerBaby +Stroma Stromboli +Strong Chonk +Strong Left StrongKush Stronger +Strongman613 Strongrune +Strongsad Jr Strongtank11 +Stronkmoscle Stronku +Stroople +Stroppy +Strosity +Stroxers Strr Strubber Strudelis @@ -22044,47 +45585,82 @@ StrydarGrim Strykijzer Stryneguten2 Strytegy +Stu Pid +StuartLittle +Stubo Stud +StudMonster Studderd Student +Student t +Student tort +Study Hours +Stuffe Stuffed +Stuffed Hog Stuffmypanda +StufnDatMufn Stugey Stuggo +StuieShadez Stukatz Stuksken Stumbles +Stunflame +Stunlax +Stunnaz Stunts +StunuuR Stupendous +Stupid My +Stupid Swagg +Stupid444 StupidMagnet +StupidTrader Stupidcoin2 Stupsus +Sturdy Base +Sturdy Wrist +StureStork Stureplan Sturminator3 Stussy Stuxi +Stuzington StvnSmth +Stvx Stxrbursts Stye Style StylesP +Stylistx StyxHatred SuBRifleS SuNnY_AuStiN SuRviVoR SuaNorte Suadela +Suavity +Sub Ironman +Sub Taz +Sub3Marathon SubEntity Sub_Void Subaru +Subaru Bow Subarue Subarus Subatinijo SubiSpeed Subiaco +Subiaco Oval Subie +Subie_rex22 +Subigrl +Subkultured Sublanza Sublimation6 +Submerging Submug Subnautica Subnet @@ -22095,56 +45671,103 @@ SubtleAsnTrt Subtotal Suburu Subwaysurfer +Subwooferman Subwrx +Suc +SuccMyMilk Succeed Successful Succulent Such +Such Mlg Suchislifee Sucjk +Suck At Zuk +Suck my dig +SuckItBrandn SuckMyBot SuckMyDuels SuckTube Suckle +SucksTehSuck SucksToSuck +Sucuk Sudan7a7 +Sudas SudoBash SudoFox +Sudsy Mule +Sudz +Sue Lynder Suede Suedezor +Suemi +Suephoria Suffer Sufferance +Sufferi +Suffering +Suffi Sufflavus +Suffspector Sufwah SugaNoCoffee +Sugab Sugar +Sugar Leafs +Sugar Lily +Sugar STR +Sugar Shilo SugarDaddyNL SugarFr3 +SugarFre +Sugarbunny +Sugaree Sugarly Sugge +Sugma Newt Sugmabum +Sugru Suhado Suhec Suhgundees +SuhhP Suhk +Suhkmedaddy Sui29 +SuiSaii Suikerwafel Suing Suited +Suizy +Sujn +Suket +Sukit Trevek +Sukiti SukkerLyn Sukondikis Sukoru_VIII Sukotto-Sama Sukz +Sulcius +Suldan Serar Sulg Sulliusceps Sully +Sully Bear +Sult an Sultan +SultricMY Sulu34 +Sum +Sum 1 +Sum Tuum Sum41 +SumBeers Sumh Sumin Sumlettuce +Summ Summah Summer Summerson1 @@ -22152,8 +45775,16 @@ Summit Summit603 Summon Summond +Summoneering Sumper1 Sumuinen +SumwanSpeshl +Sun Devi1 +Sun Isukki +Sun Muijja +Sun Riser +Sun Rising +Sun Sioux SunBar SunG0han SunaLAD @@ -22161,34 +45792,84 @@ Sunbite Sunchester Sunda0wner1 Sunday +Sundo +Sundxy SundyWundy SunflowerMan +Sungravel +Sunguinesti +Sunine +Sunky Kong +Sunlust Sunni Sunny +Sunny Boy +Sunny Coast +Sunny Otso +Sunnyi +Sunoric +SunriseEagle +Sunrises +Suns in 2025 SunsShine Sunsa Sunscape6 +Sunset +Sunset Riot +Sunshadow Sunshine +SunshineBus +Sunskit Sunta Sunti Sunuwar Sunyikata +Sunzz Suola Suolane Suolitussari Suomen SuomiKP Suomiprkl +Sup Mike +Sup3r Ziko +SupAndSupply SupDutch Supa +Supa Saper +SupaHottFiya +SupaWarmFire +Supaidahman Supdude Supeerbusy Super +Super 16 +Super Bossy +Super Cinos +Super Cool +Super Duke +Super Fish +Super Fr3ak +Super Katze +Super Tails Super4 +SuperChunk3 +SuperFlow SuperHeroWiz +SuperJoy SuperJuicy +SuperKoi +SuperKriss +SuperMcFresh +SuperMiika +SuperMuffins SuperNJ +SuperNinja0 +SuperPPMan +SuperPochaco SuperScaper7 +SuperSem1 +SuperShane SuperShif SuperT0aster SuperTussu @@ -22198,351 +45879,759 @@ Superb Superbigblnt Superbusy Superdoer +Superdrol 50 Superfire444 +Superguaygix Superior Superjim111 Superjoden +Superkman12 Superkoi3000 Supernate91 Supernatti Supernova Supersam142 +Superstition Superstuffz +Superwarz96 +Superzu 0ne Supo +Suppertmain +Supple Flaps +Suppp Suppression Supra Supra2JZ SupraTurbo +Supragirl94 Supreeme Supreme +Supreme Iron +Supreme NYC SupremeDanz SupremeSpike SupremeTeam +SupremeWoody +Suprie Suprreme Supsep +Supultura +Suq Maddic +SurNtly +Sura Lust +Suraato +Surah An-Nas Suraii Sure SureDeth +Sureal Surfboarder Surg1n Surge SurgeHunter +Suricate Suris +Suriv Surkastunut +Surkee pelaa Surlygrump2 Surok Surrey Suruinen +Surullista +Susan Boyle Suse +Susej Dog Susell +Sushi Cult +Sushi Eater +Sushi Life +Sushi Water +SushiToyota Sushidame Susilapsi +Suspence Suspiria +Susser Tod Susurrous Sutdellen Sutho15 Sutty +Suur L6vi +Suvalkietis Suvorexant Suxe Suzakuin Suzn Suzshenron +Sv +Svampus SvartaOdhner Svartalvheim Sveden Sveitsi +Sveka Sven +Sven med D Svenpai Svenskafyren Sverd Svetta +Svikkipedia +Svinepels +Svisha +Svt500 +Svurt +SvvK +Sw0llenPlums Sw3Frost Swaars +Swabby Swabski Swacked Swadloon +Swaffeldomin Swag +Swag Ponu Swag420 Swagbopeep Swageroneus Swagex +Swagg Goat Swagger +Swagger Pkr +Swaggy Ballz SwaggyMaggee +Swagittarius +Swagohod +Swagomancer Swagslicer Swagturtle Swagunit Swahilson SwamiNate +SwammyHolds5 Swamp +Swamp Crotch +Swamp People Swampy +Swampy Sloth +SwampyAce Swan +SwangleSauce Swanheart Swanney +Swanni D Swanny Swapski Swarm243 Swarme +Swarme d Swarmyard Swatfighter7 Swatson Swaxel +Swe Flame +Swe Fred +Swe Jerry +Sweatiest Xp Sweatscaper Sweaty +Sweaty G0OCH +Sweaty King +Sweaty Nurse +SweatyBeard +SweatyBurger +SweatyMoobs +SweatyScrot SweatySockZz SweatySunday Sweden Swedgeville Swedish +Swedish Fika +Swedish Myth Swedushi Sweensicle +Sweepstakes Sweepyjoe +Sweeqy Sweet +Sweet Boyy +Sweet Dee +Sweet Es Mmm +Sweet Guy 94 +Sweet Pigeon +Sweet Potato +Sweet Vodka +SweetSugR SweetVan420 +Sweetchild0m Sweetermanz Swell Swer Swerve Swerve0311 SwerveJ +Swervy +Swerze Swift +Swift btw Swift738 SwiftCobra08 SwiftSteps Swiftamine +Swiftly546 Swiftmend Swiggitywall SwiggyMcSwig +Swiigs +Swiim Shady +Swiirl Swild0 Swilza Swimfatman +Swimmor908 Swimpa Swine Swingers +Swip3rR +SwipeMyCard Swirly Swirly248 Swishers SwissPigeon +Switch Dropz SwoIe +Swole Milk SwoleBadguy Swolerbear SwoleyGhost +Swolga +Swoli SwollSoul Swollbroham Swolverine Swoofii +Swootz Sword +SwordBoy110 +Swordcode205 Swordied Swordillo3 Swordlord524 Swordman1173 Swordmank Swordmast756 +Swordmother Swords +Sworzis +Swoule +Swown Swtbnd +Swtshrt +Swuido Swurl Swxv +Swyxia +Sxng +Sxrup Sxstyl Sxves +Sy Syaniide Sybe Sybke +Sybr Sycamore +Syche Sycily Syck Memes Sycrem Sycther Syed +Syfer +Syh Syjac Sykeaux Sykes +Sykes Xo +Sykes33 +Sykezer Syliith Sylist +Sylite Sylixx13 Sylosis Sylph Rings Sylth +Sylthrakis +Sylvaria +Sylvee Serum Sylveon700 Sylveonn Sylvian +Sylwia Symb Symba12 Symblic Sympathize Symphonicx Symtai +Syn-er-gy +SynGates07 SynTechRS Synacyde +Synaii Synced +Synchronizer +Synchronous Syncire Syncronia Syncshot Synd +SyndaXatrix Syndalen +Syndicete Syndok Syndra Syndrious Synepxd Synepxo +Synergy Sam Synesthesian Synfidel +Synister9090 +Synizta SynkNZ Synn Synnri +Synoz Synq Syntax +Synthy Syntipukki +Synyster +Sypanite Syphikins Syracusa Syrile Syrilius +Syringe Syrius Syrups Syrus +Syrus Virus +SyrusDaVirus +SysTeM 07 Sysf +Sysfea +Syssla System_Fail +Syte +Syvette +Syzgy +SzechuanJuan +Szenarien +Szethe +Sznd Sztarky +Sztr +Szyrup +Szyther +T 0 0 A S T +T 0 B +T 1 ta n i c +T A B S +T A R O +T Bagmotion +T C 3 +T C J +T D +T E L O +T H C +T H O R M +T IV +T McLeod +T Montana +T Nymphadora +T O M M M Y +T O N N l +T O N Y N Z +T OXI C +T P I E +T R A V +T R U 3 +T S M +T S O M +T Shadow T +T T E X X +T U B M A N +T U R K 96 +T U U M A +T W I S T Y +T W l C E +T a v e r +T alon +T aqn +T emby +T enrin +T h a w +T imboy +T imm +T omahawk +T otem +T otz +T r as h +T rigger +T ripwyre +T rist an +T u m p p i +T ubby +T ylor +T yrr +T-A nina +T-BONE STEAK T-Hugs +T-Lai +T-Rabb T-city +T0 BE FRANK +T00K3N +T00LB0X T00ManyCooks T0AD +T0KEN T0KK +T0M +T0M ATO +T0RB3N +T0RlN T0bbbE +T0ber +T0fi T0ilah +T0lne T0mbak +T0mz +T0oth T0othbrush T0ry +T0tally lll T0vergasje +T1gBits +T1m +T1mmaayy +T2NK T3KT0N +T3T +T3URASTAJA46 T3mu T3vas +T4TE +T4a +T7 +T70TYE T7mon +T8OW +TA Greenie +TA xBravo TAA66 TAARA +TABS1E +TANK ED TANKTOPTIGER TANRfromHS +TARFUII +TARV05 +TATERT1TS04 TB12 +TB3N +TBA Ness +TBA Poppi +TBAC TBNinja TBOW +TBSE Stupid TBSG-Baine TBonFirstCoX TBoneZzzz TBoyd4138 +TC NeverLift TCHAMl TDPred TDRS TDie +TDog +TE Omni +TE-toimisto +TEAMFORTDUDE +TEATMCBRIEF +TEAxBAGGER +TEBA +TEDDl +TEEMUXXD TERMlNATE +TERRORHYPE +TESTOKEIJU +TFS TFreeze +TGCONCEPTION TGDekuTree +TGI Vendredi TGOBS +TGSH TGSpaghettiM +TH0MAS TH0MM0 +TH0TDETECT0R +TH5 +THA CHR0NIC THANIT0 +THANKS D0C THATSCARCASM +THAl LADYB0Y +THC BOOSTED +THC and EXP THCGods +THCiron +THE CH1N0 +THE JEDl +THE MOOR +THE MUUMI +THE TOSH THEHAZE THEIKOS THEST0RM +THEchronic 8 THICC +THICC UIM +THICCCBWANA +THIIIIITH THRALLGOBRRR +THT THUMBTHUCKER TIIIIIIK +TIM BEREN TIMBO TIOTEEE +TITr fishy +TJ UCF TJay +TKO Rage +TKOsh TKfromNC TLT5 TLTBT +TM Riddle +TM8 +TNA INUS +TNDSH +TNF +TNT Stealth TN_Biscuits +TOA Main +TOA Rebuild +TOA3 TOASTA +TOB Inc TOBplank +TOHIGH2FLY TOLIPTSET +TOM RlDDLE TOMMMY +TOMMYSHELBEY TOMMonyzzz +TONNI re mix +TOOlateN0B +TOR0 +TORVA BTW +TPD-Vladik TPGG +TR Ugur +TR0UT CURS0R TR0X +TR3N ABUS3R TR75 +TR9 +TRABZ10 +TRAJANO RD +TRAPH0UZE +TRAPorGTFO +TRAVllS +TRCSup TRESTOLONE TRI0DE TRIPPlE TRUMP +TRUMPbtw +TRUTH OF JFK +TRXeatsRAPTR TRYNAHANGWU +TSA +TSI TSMCharizard TSMsOAZ +TSR Mishiah +TST TSWRX +TSwiftSucks +TT17 TTBtw +TTT TTTSpiceTTT TTTTT TTVCardyUK TTVPandaBear +TTommynator +TTurret +TTyrannical +TULK4S +TURBOPEN59 TURDENATOR TURK +TURKISH HERO TURM0IL22 TVAnime +TWISTED T TWITTERSUPP +TWL +TWO000 +TWlNKE +TX Bonefrog +TX-15 +TXR3 +TYC Beatrice TYCANN TYEY +TYL0RD +TYTYTYYYTTYT +TZTOKHUGEKOK +T_ShelbyLtd T_Swishh +T_apka +Ta C oS +Ta ks +Ta1ntzilla Ta2kaz +Ta7e +Taakxic +Taargus Taathum Taavik6iv +Taavit Tabagie TabascoGrind Tabbed Tabbscoot +Tabby Cat4 +TabbyCat97 Taberknackle Tabesco +Tabiun +TableLamp +Taboo Tim +Tac +Tach +Tachr Tachycardy +TackTick Tacklebox Taco +Taco Bit +Taco Krydder +Taco Log +Taco Paws +TacoCat +TacoKittenz +TacoTimma TacoWithGuac +Tacos Pump Tacotueaday Tactic TacticNoodle Tactical-RSP Tactics +Tactics Ogre Taders +Tadz Taekwon-Do +Taekwondoo +Taelium Taelos +Tafboy Taffarell +Tafff +Tag 7 Tagei Taggeman Tagi +Tagz Tahdeton +Tahdonvoima +Tahfi +Tahimik +Tahm83 +Tahuna Beach +Tahx +Tai +Tai Mai Shuu Taikajim Taikanz Taikapoika +Tail Gory +Tail Raiser Tailight Tails Tailsnake Tainoo1 Taint +TaintFondler +Taintedhappy +Taintehds Tairoun Taiwan Taiyi2 Taizur +Taj Mahballz +Takalaaaa Takaloo Take +Take My Sp3c +TakeARedPill TakeNaps TakeUhSeat Takea12 +Takedown Jr Takeitilslay Takens +Takeout24 Taker Takfil Takimoto +TakingADab Takis Takji +Takobocchi +Taks Taky Talal +Talaxim Talcron +Taleah +TalenteDK Tali Talia Talito Talk +Talkamar TT +TalkingMoose +Tall Greg +Tall Vince TallChair TallPaul24 Talla +Talla Keyali Tallented TallerKiwi Tallink @@ -22551,57 +46640,104 @@ Tally Taloroar101 Talos Talppa +Talrith +Taltt4 Talu Talvedon +Talviel Taly +Talzn +Tam Ranch +Tam The Bae +Tam k0nijn +Tamacti Tamadra Tamaki Tamale +Tamara +Tamaraa +Tamayura Tamber +Tamberi Tame Tamerr +Tamis Tamjam Tamlin Tammii Tamminga +Tammy TampaTHC +Tampered +Tan Toad +TanDumb Tanatos +Tandanus Tanden +Tanduay Rum +Taneesha +Tang Eater Tangar +Tangela +Tangelo T TangerineFly +Tangibility Tangle +Tangle Root +TangleEllis +Tangleboot Tangledroot Tango +Tangshan Tangzu +Tanie +Tanime +Tanis513 +Tanjiro Tank +Tank VS Bear TankMageJon TankProphecy Tankarino Tanked TankedAgain Tanker +Tanker 685 Tanker773 +Tankerton +Tankkimies Tankky +Tanknique69 Tankster360 Tankytank +Tanna Tannnk Tanny Tanoak +Tanoak alt TanqueRamos +TantusV Tantza +Tanuliina Tanz +Tanz Mutagen +TanzFang Tanza Tanzagen TanzerReborn Tanzoo Tanzu Taozi +Tapanui Tapas Tapdaddy Tape Tapir +Tapir Squad Tapirslayer6 Taplop +Taplu +TapoinItteni Tapu Taqi Taradrial @@ -22610,122 +46746,262 @@ Tardysoap Tarezed Taringa Tarki +Tarkista Hv Tarlach Tarns Taro +Taro Buns TaronRS +Taroshik69 +Tarouco Tarpon Tarroo TarzanNinja Tarzzan Tasarorm +Tascar +Tase T Tashee Tashkas 007 TaskMan +Tasset Man Tassoth Tassy +TastefulNote Tasty +Tasty Burger +Tasty Nan TastyBoiMilk TastyKnight TastyToilet Tata Tataa Tatanchis +Tatankaa Tatapie Tatepon Tater +Tatertots Tatimary Tato +Tatorz Tattoo +TattrTots Tatuu +Tau Lord45 +Tau Neutrino +Tau Tau Taucher +TaunkChicken +Tauno Taunos Taurideum Tauros Taut Tautvif Tava +Tava Monster +Tavern slut +Tavernic Tavi Tawa Tawakoni Tawer +Tawhid Tawny +Tax Audit +Tax Wax +Tax Wizard +Taxing +Tay Lore Tay3rell TayMoney Tayleth Taylor Taylor672 TaylorMarcus +Taylort07 Tayluh +Taynq +Tayri Tays +Tayunu Taz762009 +Tazmina90 +Tazner +Tazplan Tazy420 +Tazz2006 +Tazzin +TazzoTezz +Tb knakworst +Tbix +Tbk Tbone5876 Tbow +Tbow Joe +Tbow Todd +TbowCourtois Tbrs6 +Tchambz +TdTapsa +Tdblindmonke Tddun +Tdmiro Tdogg31 Tdurocher +Te +Te Awa +Te x a s +Te zzz TeBroHimself TeCurt TeJay TeMuD +TeQuiL A +Tea Farmer +Tea Witch +Tea n Milk +Tea on Wayne +TeaIe +TeaMerchant +Teaandliquor Teabz Teachan +Teadribble69 +Teafuse Teagan TeagueDaBoss +Teal AV Tealer Team +Team Chubby +Team Flow +Team Rocket +TeamCleaned +TeamJono TeamSoloMid +TeamVaChimpy Teamwork +Teand Tears +Tears of Avo +TearsOfWeezy +Teazor Tebal1 Tebowowns +Tebus +Tec-Grind-95 +Tecc Legacy Tecco +TechCo +TechColor TechContraps +TechFreakTwo +TechOnMaxed +Techart TechiePicker Techmenjoe +Technician Techno177 Techno9 +Techo +Techtonique +Teckczar Teckerz Teckie16 +Tector OSRS +Ted Beneke +Ted Striker +TedRS +Teddii Bear Teddy +Teddy Brum +Teddybareman TeddysMoo +Tedication Tedsticles +Tedua +Tedzudo +TeeBee +TeeBee11 TeeSchaf +TeeTee1772 +Teebo 727 Teegious +Teegzie +Teekiz Teemu +Teemu126 +Teemuz +Teena +Teenagers Teenyduck +Teequoze Teeqy Teeray Teetoh +Tefak Teff +Tefilah TeflonCancer +TeflonJavon TegridyF4rmz +Tegriidy Teh Only God +Teh W H I P +Teh Yumm +TehBigNub TehBriBri +TehKillaaa +TehPum TehShowerMan +Tehebow Tehh +Teigur Teittinen +Teivanna Teixo Tejmaster +Tekannan +Teke Toucher +Tekkenfury Tekkies +Tekkies nub Tekknow Tekkuza Teknoid Tekoflex +Tekryael +TekstPlekshT Teksti-TV666 +Tektiny +Tektom Tektonio Tektons TektonsTaint +Tektos +Tel3 Telboy Teledogs Telee +Teleport 125 +Teleportoise TelesBehindU +Telesh69 +Tellum Telluric +Telope +TemBROross +Temdom Temeria +Temmer +Temmie TempVVS +Tempal2 Tempest +Tempesta1 +Tempick Templaris Temple Templooit @@ -22734,177 +47010,477 @@ Tempoctrl Temponazz TemporaryBls Temppuliina +TempstRimuru Tempthric Tempz +Ten Letters TenOnTheFlop TenPieceNug +TenPly Bud Tenacious +Tender Nuts Tenderrrrr Tendinosis TendoTheTux +Tendulkun +Tenebri TenebrisMeam +Tenenwasser Tenereus Tenga Tenh1s Tenhou Tennis +Tennis Socks TennisLad +Tennnessee +Tennu Tennysee Tenorman Tenraikash1 +TenryuuKaiNi +Tensilean Tensor +Tensor Trace +Tentacle +Tentpolepie Tenya Teotl +Tep3lstreel Tepeksi +Tepezki +Teplan +Tepoa Tepponen Teqila Teqq TeraLokien +TeraVit Teras Terbleg Terial117 Tering +Tering Tiete Term Termite Termiz TermnaLcpL Termoil Tero +TerpWrangler Terpedout +Terpily +Terppa1775 TerpySlurpy TerraToffey +TerraTony TerresFatum +Terrible Edd +Terro Terror +Terror Earth +TerrorJaxx Terrorise Terroristi Terry +Terry Munro +Terry61RS Terrybear Terva Tes Iron Tesaticles +Tesco Teserve +Tesla bot TeslaDiva +TeslaHero +Tesni +Tessei Tesseria +Tessies Bits Test +Test Dummy +Test Strip TestStrip Testate TesticularCa +Testikeln +Testoepa +Testsubject TetYun +Tete3 0 Tetragrams +Tetrah Blue Tetsu +Tetsu Nurtaw +Tetsu Steel Tetsu2 Tetsunohigan Tettekop +Tettezotteke Teurastamo Teus Teus777 Tev0 +Tevreden Tewocp +Tewty Tewwer +Tex Zen TexanzOS Texas +Texas Hou +TexasAggie TexasPlayer +Texasholdems +Texastea2 Texman Text Texx Teyrill +Teyzr +Tezyn Tflo +Tfue Tfulkyou +TgMofo +Th Abigor +Th0m +Th3 K1ng P1n +Th3 Wolf Th3IronMan Th3KiNG +Th3KiNG_Paul +Th3Tool +Th3Villager +Th3_DoN Th3uns +ThAngelSlayr ThEDievolved +ThSheerman +Tha Bser +Tha Crazy L +Tha Decided ThaDankantor ThaDragonSM +ThaGiz ThaLawl +ThaNewArcher +ThaRealRambo ThaStepBro ThaaMunchies +ThaaOne Thaerokem Thai +Thai Girl +Thai ler Thaichili Thalarios ThaliN1 Thamasta47 Thameslink ThanaWee +ThanatosRise Thangkang Thanked Thanks +Thanks Jeans +Thanks4Pet +Thankz Mom +Thano +Thanos btw ThanosIsLife Tharendel Tharic +Tharin +Tharok +Tharrogant +Thasos That +That Noob +That RNG ThatBoiii ThatBoyBurly +ThatDamAss ThatFishhGuy +ThatGuyGoob ThatGuyJordy +ThatLipGrip ThatLuck +ThatManBez ThatOldskool +ThatOlmlet +ThatOneAsian ThatSandNoob ThatSickBoi +Thatonesock Thats +Thats Gang +Thats Not PC +Thats Thicc ThatsNotRite +Thaumat +Thaurison Thav022 +Thawk410 +The 0ld Nite +The 0xymoron +The 1 liquid +The 11th +The 2nd GIM +The 3rd Main +The 90s +The Actuary +The Aegis +The Aod +The Apina +The Atreus +The BPO +The Baad Man +The Big Chum +The Bleez +The Blue Owl +The Blyat +The Bonk +The Booger +The Boys +The Brawler +The Broskie +The Bubbsy +The Buffalo +The Cage +The Catowl +The Champy +The Chopper +The Cleaned +The Cleaning +The Coffin +The Comeup The Crogamer +The Crows +The Dabbler +The Danny +The Dark +The Dark165 +The Data Guy +The Debated +The Decided +The Deli +The Diamond +The Dink +The Dipshit +The Dothraki +The DrNick +The Dragona +The Dre +The DripLord +The Duffman +The DwOrfe +The Echo +The Eco +The Effected +The Em8 +The End +The Engine +The Ex Zerkr +The Expanse +The Fat Toad +The Fe Woman +The Fig +The Fisher +The Fugitive +The Gas Tank +The Ginga +The Giznooch +The Grouch +The Grower +The Guppy +The HRE +The Half Man +The Harambae +The Havok +The Hibbs +The Hivemind The Hobo +The Hoffner +The Hundred +The Hunter +The I Am +The Iron Cpt The Iron Era +The Is0lated +The Jedi Way +The King 023 +The Kiwi +The Labfreak +The Lager +The Last Act +The Latvian +The Law 98 +The Locust +The Logger +The Long Day +The Lope +The Lord Ra +The Lovers +The Lurifax +The MCG +The Magic +The Maiden +The Marco +The Maskara +The Matth +The Mediator +The Midlands +The Mks +The Mouldy +The Mr Spec +The Murder21 +The Muscles +The Nexian +The Nicollai +The Noob +The OSU +The Office +The Old Fonz +The Old Kite +The Old Mike +The Omun +The Onceler +The Only Sin +The OnlyPyro +The Paladin +The Peak Pro +The Pet Farm +The Playoffs +The Pocket +The Proline +The Prophet +The Ptolemy +The Pwnes +The Qwinner +The Real Cat +The Sabre5 +The Sarge +The Schnopps +The Sea Bee +The Seahawks +The Serval +The Shy Guy +The Shy Oni +The Sly Fox +The Snail34 +The Snapback +The Steele +The Steward +The Stranger +The Surveyor +The Tardis +The Thanator +The Tils +The Tiny Dog +The Trap +The Triple T +The Use +The Victory +The W0rst +The Wael +The Warp +The Wraith +The Wupp +The keho +The thin 1ce +The-Wind-Up The0G The1 +The1_2watch +The1stmage +The3dge The3rdOlive +The3rdTrike The888 TheAdzz TheAirIsDead TheAlbatross TheAllSorts TheApe +TheArcheType +TheArchthief +TheAsic TheAssailant +TheAusSpade TheAvenger56 TheAvgJon TheAzimuth TheBandit777 TheBassIsRaw +TheBazaBrown TheBean TheBearJ3w +TheBeltMan TheBezal TheBidness +TheBigShield TheBigZofNYC +TheBlackest TheBlindy +TheBlueShore +TheBlueWrath TheBoltzy TheBoomfire TheBopp10 TheBotReaper TheBowJob +TheBrain +TheBranFlake TheBrundone TheBudWiser +TheC0W +TheCanada TheCapist +TheCarrotMan TheCaseAce TheCheezywiz +TheChosnOnes TheCokeFiend TheCollected TheCondemned TheCorrupt TheCuckening +TheCzarnian TheDaedalus TheDalek TheDampBush +TheDangs TheDankShow TheDawg +TheDayWalker TheDayman TheDeadMann +TheDeadTeam TheDeen TheDisgusten TheDog +TheDoubee TheDreamKing TheDrips +TheDry TheDuckChris +TheDuckDaddy TheDucksNut TheDuud TheDyr61 +TheEricShaun TheEstonian +TheEvaElfie TheEvlManRay +TheExtracted +TheF0rg0ten9 TheF1ash TheFabDabber +TheFallen1x TheFeOdyssey +TheFeres TheFizzCC TheFlammers TheFlopyTaco @@ -22916,51 +47492,78 @@ TheFunkyHomo TheFunnyLove TheFuzzball TheGaGee +TheGaryy TheGiggleMan TheGlawce +TheGodFathxr TheGodGuthix TheGodfather +TheGoodLife TheGoonie TheGordinfla +TheGoryGlory TheGr8Jimbo TheGrainGoat TheGreat +TheGreat One TheGreatThor TheGreenBowl TheGugguru +TheGurrag +TheHabduL TheHackedOne +TheHapa TheHartstopr TheHerbSack +TheHuffDaddy +TheHugLife TheHungOne TheHungRoid +TheHydra69 TheInilator +TheIronBarra +TheIronBoy TheIronDolan +TheIronHoser +TheIronMouse TheIronTwist TheIronVault TheIronZoid TheIsamaru +TheJele TheJosephe +TheKappaCorn TheKhun TheKid TheKillerB TheKnight TheKnightman TheKolector +TheKop +TheLaggyDad TheLampKing +TheLastTheef +TheLaxBrah TheLazyNinja TheLewhole TheLionn TheMaadKing TheMachine39 TheMadWolf +TheMainZeus TheMamba TheManInBush +TheManJordo TheManatee +TheMaskie TheMavrick TheMaxPvMer TheMayne +TheMetaNow TheMikkel +TheMorloc8 TheMuel +TheMuffin8or TheMursk TheNegative TheNerve @@ -22968,47 +47571,80 @@ TheNewLue220 TheNewMonkey TheNexu TheNinjaH0b0 +TheNoPurp +TheNooby +TheOG Logo TheOGGrimm +TheOGslacker +TheOSRSWiki +TheOllieBoi TheOlmlet +TheOne772 TheOneMatrix TheOperative ThePTWY +ThePaceAce ThePardos +ThePariah +ThePerfect G +ThePolak +ThePraetors ThePrenti +ThePrestiege +ThePrideS1n TheProfess0r TheProvider ThePureRingR TheRanger +TheRealBean TheRealBew TheRealDest TheRealIdean TheRealKanye +TheRealKush +TheRealKyle TheRealKyle9 TheRealMidus TheRealShway TheRedNas +TheRedRaider TheRevRip +TheRewriter +TheRobin129 TheRoulette TheRsBug TheSaggyOne +TheSaltyYew +TheSandFoxx TheShadyMile TheShyFn TheSimpSonny TheSinner TheSkippyBoy +TheSlim1 +TheSneakyOne TheSoggyOne +TheSoloHades +TheSpaceSaus TheStonedElf +TheSwumpMan +TheTKsmith TheTankProd TheTerug +TheTimeLord TheTinPeach +TheToyMaster TheTree7 +TheTrue Goku TheTrueNorth +TheTrueOOGA TheTrueSatan TheTruthOnly TheUnbeloved TheUndified TheUnluckyIM TheValuable +TheVerve TheVeryBest TheVnom TheVoodoo2 @@ -23016,313 +47652,603 @@ TheW1tcher TheWerebear TheWetDuck TheWhlteWolf +TheWiseLoner TheWitcherr TheWizaad TheWrongName +TheYahtzee TheYak +TheYimYim TheYugo TheZoracks TheZurvan The_Dave666 The_Dunster +The_Eoline The_Hillboy The_Jimmy +The_Krang +The_Man146 +The_Sturg The_Zob +Thea1a Theatres Thebano +Thebig76 Thecascade +TheclawMVP Theconn Thedriesj11 +Thedyolf +Thee Khed TheeMohican TheeSkill +Theel Theem Theeweaver Theez +Theezak Thefunto Theguymann Thejessman1 ThelAvent +Thelastmane +Thelegend4uk +Thelmacat342 Thelvereas ThemanwhoisB Themenchman Thengalin Theoatrix +Theodensa +Theodora605 Theohhhh Theoklitos Theoneburger Theophobia +Theorized +Theory Theoryist +Thepawgchamp Thepiefour +Theracords +Theralion +TherapyGroup +Theravasa ThereTheyre Therealhook Therens1 +Therer Thermynator Theruler333 Thery Thesaurusus +These +Thesonicking +Thespok Theunfrgiven Thew Thewoodle They TheyTukMyJob +TheyXciteMe +Theynika +Thgll Thibaut Thibi +Thic Glizzy ThicBudget +ThicCreature ThicKoontang Thicc +Thicc Aku +Thicc Budget +Thicc Fil A +Thicc Flair +Thicc Momma ThiccBird91 +ThiccCowboys ThiccDaddyXL ThiccDonut +ThiccQueen ThiccSkips ThiccZwans Thiccapedia Thick +Thick Cut +Thick Nick +Thick Red +Thick nun Thick2g Thick4Head +ThickGlute +ThickIronBum +ThickMike ThickSenpai ThickSpak +Thickenei Thickest Thickkmommy Thics +Thiery Thies Thiesen +Thievette ThievinKills ThievingL +Thievs Thigh Thighhighs ThiiComplex +Thiibaut Thillam +ThinBlueL1ne ThinPeepoSad ThinkB4Hit Thinkoclet +Thinxo Third +Third-ages +ThirdAge ThirdAgeBoss ThirdEye Thirdborn +Thirsty 4PvM +Thirtys This +This Gimp +This Leo +This Schmuck ThisAint +ThisAint-Wow ThisAintSkil +ThisGuySkip ThisPieIsDry Thisious +Thissa Thlrd Thoakline +Thocasu Thogdad +Thogidar +Thohon Thom Thomarse Thomas +Thomas Juice ThomasticIM +Thomastic_1 +Thombstone +ThomiusTeGr8 Thommetje +Thommyy Thonack Thonq Thoomin Thor Thoradin +Thoraldd ThoraxPurple Thordej +Thorgal +Thorgoth1 Thorkar96 +Thorn Thornet Thornnforge +ThorntonM Thorvesta +Thot Jadiels Thotalopolis Thoth +Thotmince48 Thotscape +Thoughting +Thoughtseize Thounde Thoupeppi Thrasmion +Thrax OS +Three Twelve ThreeOneNine +Threemars +Threesunders +Threkeld Threno ThreshDaOg Thrihyrne Thrilbo +Thrilerrr Thrillhousee +Thrisel Thrivaldi Thrlll Throat +ThrobGodTod Throbba Throbbing Thrombi +Thromur +Throupled +ThrownFury +Throxx Thru +Thru ur poo ThruDaStorm Thrust Thrustaxe1 Thueh Thug +Thug bone4 Thugbug808 Thugg +Thugge ThugsBunny +Thugsbunny73 Thulean Thumb ThumbBwana +Thumper +Thunadaja +ThundaBear ThundaJunk +Thundac Thundahcatz4 Thunder Dog Thunder Gate +Thunder Nerd +Thunder Zeus ThunderBirdz ThunderRolts +Thunderbo1tz Thundercat Thunderite +Thunderoxgod Thunderpb28 +Thunderst0rm +Thundito +Thur go Thurbs Thurison Thusly Thutmose +Thuvian Thuzd Thve1ivan +Thwart +Thy Odama +Thy Victory ThyIronGiant +ThyLegend ThyVonR +Thybo Thyclops Thyhead +Thyming +Thyreus +Thys Thysa Thyymabotnus +Ti Blade TiXN +Tia Tiaan Tiaeuth +Tiafoe +TiagoM Tiak +Tian Guo +Tianzi Tiazen +Tibadis Tibador +Tibaul +Tibbsyy +Tiberium SvK +Tiboot +Tic Fart +TicImperfect TicTacSensei Ticano Tice300 Ticina Tick +Tick Behind +Tick Ma Nip +Tick Manips TickAteUrMom TickTickRun Tickable +TicketGoblin Tickets +Tickflow +TicklMySickl TickleBerry +TicklesIM Tickmark Ticks +Ticks missed Tict Tictacdingus +Tid Eez Tidders Tiddy +Tiddy Winks TiddyLactate Tidev Tidy Tiedemanns +Tiedustelija +Tiegra Tielemans Tier +Tier 05 Tierney +Tierra Bella +Tiesto +TifaL123 Tiffs Tiftanlar +Tig Bittty TigTheWelder +Tigas TT Tiger +Tiger Tea +Tiger Trap +TigerYouDied +Tigerheart07 Tigerheart09 Tigerlady Tigerlady203 Tigerr Tigersrule23 Tigerwolf +TiggoGnomes +Tiggrat +Tighe Tight +Tight Helmet +TightSpace42 +Tigor +TigreRosso Tigrio +Tigy +Tiiimon +Tiimari Tiistai Tiistia +Tiit Hepa Tiivo Tijmen Tijn040 Tijs Tijskid Tijuude +Tiki Haha TikiTyrant Tikk1s +Tikka +Tikkix Tikru Tiktok +Tikwah +Tila Tilburg +Tild0 +Tilfreds +Tilidin +Till Bored Tillah +TillerPloww Tillykke Tilted +Tilted I Am +TiltedGoblin +TiltedIM Tilur +Tim Pedwar +Tim Puri +Tim The Jedi +Tim bo Slice +Tim-O-thy +TimShutDown TimSkilling +TimTheLegend +Timaaaay +Timaaay +TimbaWimba Timbeettius Timber +Timbertail Timbito Timboball +Timbuktu25 +Timbulz Time +Time For Me +Time Leap +Time Voyager Time2Lose1 TimeFlies +TimeIsStrnge TimeLion TimeMuffins +TimeSink2000 +TimeSpan TimeZebra +TimedxBeast Timespacing Timetokill19 Timew8ster Timex +Timex 07 Timka Timkempp Timm Timmay111865 Timmeh Timmies +Timmuuhh Timmy +Timmy Jim +Timmy Tbow +Timmy Tiime TimmysCoffee TimnI2 +Timonkey Timos TimosTime +Timotej Timsey +Timzee +TinFoil Joe TinHongetech +Tincoinco Tincup +Tindatinn Tindell23 +Tinder lvl 1 TinderMatch Tine Tinfawn TinfoiledHat Tinfoilhat +TingSumWong TingleTingle +TingleUwU Tingot +Tingsha Tink Tink200345 +Tinkelsia +Tinmanone +TinnMan +Tinnitus Tinsmith +Tinted Rock Tintti218 +Tinuvial +Tinvicuna Tiny Tiny Bagel +Tiny Buddha +Tiny Bun +Tiny Dck +Tiny Feet +Tiny Lego +Tiny Secrets +Tiny Teemo +Tiny Triceps TinyFnRick +TinyIronDong TinyLebowski +TinyLes +TinyTaro TinyToasters TinymafaRick Tinypns +Tinytiger17 Tinzo +Tip Zee +Tip of Dik TipTheTank +TipeF Tipeeee Tipit +Tipoz Tipper +Tippin_toes +Tips Iron +Tips Touched +Tipsy PvMer TipsyBeard +TiptopSundae Tipu Tiqu +Tiques TiramisuTart +Tire Slayer Tired +Tired Hero +Tired of IRL TiredPidgeon +Tirex Tiring TirmenaT TironTula Tironade +Tirpz +Tirsonek Tisalia Tisch +Tism Tanner +Tit Bow TitMcghee TitPoker Titan +Titan Luke TitaniteSlab +Titanium Mag +Titas P TitePoire Title +Title Fight Titsferdayz Tittsnbiches +Tittyboi5 Titus +Titus Furius +Tivaaa Tivinter +Tizra +Tizzcdn +Tj Russo +Tj Watt +Tj0epert +Tjaa Tjabalabaaa +Tjalo Tjar Tjarie +Tjarles Tjbakel +Tjf12 +Tjob TjockaBengt +Tjs109 Tjuv Tkit Tksquad Tktn +TlCK LOST +TlLLER +TlLT +TlME 2 QUlT +TlNUS +TlTANlC Tliltocatl TmSmT Tman Tmarvy +Tmilllz +Tmm +Tmmm Tmoe Tmoe123 +Tmoneyyyy123 +Tmppa +Tmtiger16 +Tnt Harder +To Ashes +To Be Sure +ToAPlanker +ToBeAgile +ToLoNi +ToMyTailFin +ToNFisKrs07 ToTheRanch +Toad Pond +ToadKar TV Toas20 Toast ToastBTW @@ -23335,125 +48261,239 @@ Toasterly Toastie Toasty Toat +Tob Braidy +Tob Carvery +Tob Marley +TobOnMyCox +TobSpoon69 +Tobak +Tobbeloba Tobias TobiasFate +Tobiasz Toboogan3 Tobser Toby +Toby Raz3 +Toby1889 TobyMcCrier +TobyRax +TodWhispers +Todays N00B Toddlet Toddo25 +Todt Thot +Toe Biden +Toe Crocs +Toe Moss +ToeRag +Toedeloe Toedels +Toeh5 Toek +Toekie +Toeristje +Toermalijn +Toes B4 Bros Toets Toews Toffenboi Toffer_99 +Toffffu +Toffoli Tofs4pk +Tofthagen Tofu +Tofu Python +Tog +Toge6343 +Togepi +Togica +Togo Mouri +Tohni Tohveli Toilet +Toilet Humor ToiletBowlbb +Toiletpaper9 Toimin +Toiney Toinzy Toivo_43 Tojosodope +Tok in +Tok-xik-Hex TokHaar Tokah Toke +TokeSevere Tokedaddy Toker11 Tokin +Tokin Ranarr Tokin99 TokkHaar Tokkie01 +Tokonu +Tokoya Tokoya11 +Toktz-ma-cox Tokyo +Tokyo Ghost +Tokyo Prose +Toldasor +Tolhuis ToljaSo +Tolo Ar Nin +Tolweg +Tom Adamo +Tom Cx +Tom Decoene Tom Fn Brady +Tom Is Huge +Tom Shanks +Tom T +Tom Vercetti +Tom Waited +Tom btw +Tom from 561 +Tom is Sharp +Tom o_0 +Tom vs Log Tom9 TomAce +TomFlinn7 TomHilRigour TomMazilian TomMerrilin TomOfTheWest TomTehCat Toma +TomaMvp +Tomaatti169 Tomarti Tomastor TomatoAndEgg +TomatoFarm19 TomatoTom9 Tomazon +Tomb +TombLooter +Tomba Tombalo Tombat Tombazv2 Tombea09 +Tombing Tomdabom +Tomdoc14 +Tome TomehhG TomiL Tomiee Tomik85 +Tomlyn Tomm TommieSalami +Tommm Tommmo Tommy +Tommy 901 +Tommy J C Tommy2Tick TommyGunn204 TommyJay TommyTalapia Tommyhawk Tommyyy +Tomn Tomo Tomori Tomorrowlxnd Tompi +Tompp1 Tompsi Toms +Toms PvM Tomsdk Tomson177 Tomtastrophe Tomtom032 Tomukas +Tomvdh Tomxi +TomzodoubleO +Tond3 TondeK18 Tone +Toneez Tonester03 Tonfa Tong Tongo +Tongue Baths Toni Tonicto +Tonk A Wiz Tonka742 +Tonkade TonkatsuLife +TonkerEx Tonnie Tony +Tony D +Tony Gwynn +Tony P +Tony Soap +Tony Soprano +Tony Tuna Tony23 TonyCaawk Tonyde Tonydidles Tonyhood Tonzaww +Too Original TooBasic TooBigDad +TooManyCooks +TooManyKinks TooMuchIan +TooMuchSauce TooMuchTuna TooReal TooShredded +TooWhite4You Toobias +ToogaTank Tooheys Tookkul +TookyDaWooky +Tool Toolbox +Tools1977 ToomBooii Toomas +Toomi Toonic Tooodley Tooomo Tooq +Tooqan Toortles Toostie Tooterz +ToothhurtyPM Tooves +Top Gear +Top Glock Top Milk +Top Monents +Top Mong +Top Trends +Top V1 +Top Vestain +Top10 Top5Worst TopGlitch TopGunt @@ -23463,34 +48503,58 @@ Topez Topgolf Topi Topkeklethal +Topkerel Topman Toppavenger Topstad TopsyTurve +Tor Btw +Tor Cool Guy +TorBlueJays +Torag Tony Torags +Torags The C Torchbringer +Torchfall Torchiclover Torchpix +Tordenknold Tordue Toreador Toreransu +Torgonis Tori Torille +Torille btw Toris Torkoal +Torky Bow +Torlux +Torm TormentingU +Tormood +Tornaddyne +Tornado Kim TornadoGang TornadoShit Torned Tornjak TornoDB +Torok Toronto TorontoOVO Torr3nHunt3r Torra +TorraTwo +Torretto +Torrfly +Torrud Torsinclair Torso +Torstol +Torstol Todd Tortanium +Tortcher Tortelini Tortew TortiIIa @@ -23498,36 +48562,68 @@ TortillaChip Torttunaama TortugaBooga Tortugas +Tortuous +Torture Iron +Torva boy Torvee Torvesta +Toseki Tosetti +Toshiba +Tossin Tostada +Tot1 Total +Total Lvl Total Pede +Total Spoof +Total Vanity +Total Zero Totaldumm Totaled3 Totalis +Totallymad20 +Totalxq +Totato Tote +Totemas Totnum +Tott +Totti +Tottie Toucan Toucann +Touch My Wii +Touch Tablet +Touch Touch TouchPinis TouchedByXp Touchkin Touchmydig Touchpad +TougeAttack Tough Tounii +Tour Guide Tourino Tournx72 Tousart Tove Towely +Tower Guard TowerOfGod Towerbros Town +Town Square +Towney Stark +Towton II Toxic +Toxic Dice Toxic Emre +Toxic Joda +Toxic Mfer +Toxic Outlaw +Toxic Waltz Toxic017 Toxic125 ToxicBropipe @@ -23535,12 +48631,22 @@ ToxicDroPipe ToxicKenny ToxicSimp ToxicWaster +Toxzeek +Toy Chest Toycutter Toyger Toyne Toyocoma ToysRusK1d Toysalami +TozzinSalads +Tpc Chris +Tpc Jimmy +Tpc Ken +Tqnks +Tqrb +Tr Farmer +Tr hoca Tr0gdor Tr1ckyD1cky Trabelco @@ -23548,85 +48654,164 @@ Trac Trace Tracing Trackpad +Tracktix Trackzor +Tracto r +Tractorjoe13 +Tracxy Trad Trade +Trade Me 70k +Trade Parade +TradeForItem TradeMeBro TradeMeUWont +Tradebarrier Tradeophobia Trae47 +Traenalai +Trage Gast Tragick +Tragon33X +Trail +Trail Sodas +Train Track +Traincore Trained Fish Trainer +Trainer Bad Trainin +TrainingDay TrainingDays +TrainingWill +Trainor BTW Trains Traiths +Trakiya Traktori Traktorman Tralan +Tralfamadori Tramayne Tramfix Trample +TranSfused Trance +Trance Heals +Trance666 TranquilGod +TranquilHaze Tranquillia Transfer Transgressor +Transpiler Transpose Tranziztance Trap +Trap Carrot +Trap the Cat +Trap-A-Holic TrapGdReptar TrapGood93 Trap_Capo Trapking +Trapking 8 +Trapping Trapski Trasclart Trash +Trash Pvmer +TrashBlast +TrashManJ0hn +TrashServers Trashu2 +Trashua Trashy Traumahh Travee Traveler Travis +Travis 42 +Travis on Rs Travis473 TravisThott Travish Travor TravviePatty +Travy D Law +Traxic Traxxed +Trayvon King Trazaeth +Trazez +Trazza001 Trebluh Trebyzond +Tredderss Tree +Tree Form +Tree Oils +Tree43210 Tree50 TreeNut11 +TreeTopsFart +TreeWeasel +Treedemption +Treehuggers Treelo23 Treenut Treinen +Treio Trejoracine Trekhoar +Treldinn +Trem Bao Uai +TremensInc Tremorx00 +Tremzy +Tren Culture +Tren Hex TrenbolonAce +Trench Ben Trendeon +Trendiness Trenl Trennel7 Treno Trensational +Trentovitch Trentt Trepa Trepador Treps Tresemmee Trev +Trev the dog +Trevenant Trevo Trevor +Trevor Died +Trevor M +Trewg +Trews Trey +Trey is Treygor +Treyvor +Trezler +Trgx2 +Tri Flamingo +Tri-P0LAR Tri9py_J TriBalanced +TriFlea Tribe +Tribe Leader +Tribe of 123 +Tribryd26 Trick +Trick Blue +Trick Statue Trick-demon TrickRoom Trickiac @@ -23636,11 +48821,15 @@ Trickster122 Tricomb Trictagon1 Trictagon2 +Tricx +Trid3nt Trieuce Triforce13 Trigamy +TriggaTriggs Triggadd Triggahappy +Trigger Me TriggeredMot Triggeredm3 Triggering @@ -23650,22 +48839,44 @@ TrikiRiki TrikyCutworm Trikzed Tril +Trilipe Trilligy +Trillogy Trillotani +Trilogies TrilogyXO Trim +Trim Jr +TrimmedGrimm Trimmedguy TrinitySauce Trinix Trio +Trio MF Trip +Trip Machine +Trip Masta +TripEatIRL +TripJaw +TripSquad Tripachu +Tripae Triple +Triple Deuce +Triple Dex +Triple U TripleBeans +TripleHyphen +TripleNeck Triplet +Tripod Neb TripodDog +Trippie Damo Trippinsac +TrippleMarty Trippy +Trippy Mind +Trippy Troll TrippyReefer TrippyScapee TrippyTony @@ -23673,78 +48884,155 @@ Trips TripssAcid TrisomyMumky Triss123456 +Trissee TristV1 Tristann +Trito +Triton Blue +TritonSaber Triumph Trivi Beast Trivit Trivium Trixcet Trizak +Trizepz Trldude7 TrocSan +Trocko4 TrodOnLego Troetelbeer Trogbite +Trojan Steed Trok +Trolerage +Troll Chad +Troll464 TrolleVV TrolletTruls +Trollevv +Trollface +Trollheimusk +Trolli TrollinPony TrollinTony +Trollinguy +Trollzzy Trololosaur Tromal +Trombalski +TronixxIM +Tronkshark Tronse +TroopIronman Trooperke Tropec TrophieTyler Tropical97 +Tropicaux +Tropidelics Tropsic Troskals Tross Trottero Trottington +Troulis180 Trounced TrousrMonstr +Trout Wallet +Trovam +Trovo +Troxicus +Troy Buckle +TroyPolamalu +Trreezz Trrrpfosdss +Tru3Iron +Trublians Truce TruckTruck +TruckYacht +TruckerLiam +TruckerTuck TruckersLife +Trucki +Truckstop0 True +True Flex +True Joker +True Knight +True Owl +True Repent +True Tones TrueBadApple TrueBank +TrueNorth95 +TrueSatan TrueTech TrueTeller77 +TrueYoshi Truegreed Trueplaya +Truers Trueth Trulieve Truly +Truly Lifted +Truly Loving TrulyForever +Trump TrumpCard TrumpSuxAss Trumpet +Trundholm Trunker Truno Trunsa +Trusejageren TrustIssues +TrustNoOne Trusted Trustifarian +Trustin +TrustyThrust +Truth Hurtts +Truth Seekir +Truxy One +TrveKvlt +Try Casual +Try Die Cry +Try Hard Jr Try2IronMan TryAltF4 +TryCBD TryHard +TryHard DBag TryHardi Tryactin +TrygveLegend Tryh4rder Tryhard +TryinNot2Die +Trym +TrymeTrick +Tryna Max +Tryna match +Tryndaflex77 Trynottodie2 Trypophobia Tryptamind Tryptopane Tryuumph +Trzaskowski +Ts Danne +Ts Me +TsMaxed Tsakahele Tsar +Tsar Douglas Tsarina Tschinelas +Tshotz Tsiquli Tsitika Tsjok @@ -23758,27 +49046,42 @@ Tsudo TsukasaS TsukiAkuma Tsukihi +TsukikoBaka Tsukiya Tsuku +Tsuku yomi Tsukuyomi Tsumikitty +Tsumoso +Tsurubebi Tsyphoid Ttffdd Ttoz TuNxZbs Tubbless +Tubbzzy +Tube Dood021 Tubmasta95 +Tubs Tubz Tuca +Tucker Jeb Tuckerajf +TudoLaDentro Tuerro Tuexo Tufan Tuffa Tufs +TugBoatWilly +TugMeTwice +TugMyCox +Tuga9 Tugboat Tugboats +Tuii Tuisku +Tuittu Tukeldaja Tukkairti Tulf @@ -23786,20 +49089,37 @@ Tulip Tulipssonmyd Tully03 Tulppu +Tumbdownlog TumbleDore +TumblingSand +Tumeken Tay +TumekenToday +Tumma Paahto +Tumms +Tumnus Tumppe Tuna +Tuna Juice +Tuna Shuffle Tunaking420 +Tunasafarii Tunda TunderBog Tundra +Tunez Tung +Tung Fu Rue Tungsten +TungstenGyro +Tunguska +Tunkky Tunnellord Tunppii Tunsberg Tuntun +Tuoppi1230 TupacShaakur +Tupackid Tupakki Tupeli Tuperrovski @@ -23807,97 +49127,188 @@ Tupla Olut Tuplaa Tupoopsa Tuppu +Tuppu hoitaa +Tuppuu +Turambarr TurangaNui +Turb0uu Turbanator Turbie +Turbin Turbo +Turbo IM +Turbo Junkie +Turbo Toes +Turbo Zero TurboMilf +TurboSoak +TurboTez TurboWet Turbokjepp +Turbotymer +TurdFlicker TurdFurgeson Tured Turha Turiak +Turjilin +Turk Monsta Turk3H +Turk3HH TurkBird +TurkeyPro +Turkije +Turkish One Turkish3agle Turkled Turkos06 Turks Turn +Turn It Left TurnMagic +Turnalar Turner Coach Turnii Turnt +Turqish +Turqy Turri Turrner Turrox Turskaa Turso +Turssuttaja +Turtelloo Turtle +Turtle Rak +Turtle s Turtle7 +TurtleCoddlr TurtleWurdle Turtlebutt +Turtledog Turtleliest +TurtlesClimb TurtlesRdank Turtleso3o +Turts +Turuii +Turvasana Tusc +Tushy Taker +Tusk Tusondude25 Tusseladd92 +Tustea Kossu +Tutorial Tutoroo +Tutta Tutter TuttiFruttii +Tuttimango Tuttimelon Tuuri +Tuuri haukka Tuvisitt +Tux Tuxy Tuya Tuzle +Tvanderlaan1 +Tvb Cx +Tw0Pack Tw1ZzT +Tw1nsPurr +Tw1nz +Tw1st3d_B0W Tw1stedBow TwTv Twald10 Twas +Twas Xmas +Twashua Twaste +Tweaky +Twee Barkie Tweeder Tweek +Tweekzor Tweetart Tweeznap +Tweezr TweezyShadow Twelvyy +Twenie Wan Twerk +Twerk 132 Twet Twice +Twice Flo TwickerTweet Twiddle +Twiddle Twix +Twidgets +Twiested +Twift Twigbert +Twigglet TwiggyC Twigler Twigster Twiisted Twilight2326 TwilightPony +Twilly Spree +Twillz Twin +Twin Fists +Twin Kinz +Twin Peaks Twinflip +Twinick Twink +Twink Ripper Twinkle +Twinkle Park +Twinklekat Twinr0va Twins +Twist2K Twist3d Twist3dData Twisted +Twisted Boog +Twisted Bung +Twisted Cook +Twisted Echo +Twisted Jam +Twisted Matt +Twisted Titt +Twisted cat +Twisted garg TwistedAegis +TwistedBeer +TwistedBerns +TwistedBowlz TwistedBr0 TwistedCybrg TwistedGuard TwistedHeart +TwistedLike TwistedOffer TwistedWally Twisted_Ry +Twisted_newf TwistedxAura +Twister Nips +Twister XIII Twistertjeee +Twistie TwistsedT Twitch +Twitch Fear +Twitch simp +Twitch507 TwitchFear TwitchSeljan Twitchen004 @@ -23905,220 +49316,491 @@ Twitchin TwixTM Twizler74 Twizzlers88 +Twnkles Twntythree23 +Two Elites +Two Fears +Two Ounces +Two2SixToo TwoBeerBuzz TwoDigitJish TwoDogs TwoInTheSink TwoLegedDog TwoShue +TwoTwentyTwo +Twoconch TwojaStara +Twoy Twpz Twstd Twum +Tx2 +Tx3 Poseidon +TxNock +Txbor Txfr Txga +Txh +Txlon +Txture +TxunaTuna Txyus +Ty +Ty God +Ty Sit Idiot +Ty bears Ty1er Ty4F +TyDyPooFingr +TySkidmore23 Tybear575 Tyberium +Tybones Tycen +Tycens Tydigidy Tydollatree TyeMeUp Tyelca Tyepo +Tyfr Tyfus +Tyfus Bende +Tyfus Mug Tyfuslija Tyga +Tygr +Tyhger Tyin Tyixx +Tyjes1 Tykaman +Tyl r Tyler +Tyler Razz +Tyler Stone Tyler051095 +Tyler4rs +TylerGBR +TylerMike Tylerknight3 Tylerton +TylerxBandit +Tylo54 Tylooor Tylz2 Tym8 +Tyney +Tyngre Tyom Tyontaja +TypOneg4tive Typhose Typical Typisch +Typto +Typtooka +Tyq +Tyr 24 +Tyr Ara Tyra Tyrael +Tyrael OG Tyran88bid +Tyraniana Tyranids TyrannicaI Tyrants Tyrep +Tyrex Tyro Tyrus Tysh Tyskie +Tyskie38 Tyskn Tysn +Tyson11 Tysonn Tyst +Tysterisk +Tytastic Tythle +Tytto Tywonia +Tz-Kek-Twang Tz-Ket-Kush TzCal TzCok +TzCok-Hard TzDeez +TzDeez-Nuts TzHaar +TzHaar Meej +TzHaar-Doug TzJal TzKal +TzKal Majora +TzKal-AszZuk +TzKal-Cumsok +TzKal-Gio +TzKal-Mike +TzKal-Thad +TzKal-Zuky TzKalFatkok TzKalSuk TzKarl +TzSuk-Dis2 TzTok +TzTok Chris TzTok-1gBud +TzTok-Fart +TzTok-Flame +TzTok-Izzy +TzTok-Jaffa +TzTok-Jahd TzTok-Jeffy TzTok-Joka +TzTok-Kekw +TzTok-KetKok TzTok-Ladz +TzTokTitties Tzek +Tzhaar Bower +TzhaarThicc +Tzhaar_PvP Tzharzh +Tziis Tzikit +Tzkal Edgar +Tzkal Tygo +Tzkal-Reborn +Tzok-GigaKok Tzoski +Tztok Shaz Tztok-Met-Al +Tzuyuwu +U A E +U A V Online U Ded Boi +U Mirin Brah U N X +U R Lame +U Tele Nub +U W8 +U n I f Y +U nknown +U0Q U0sunpeh +U3 +U4 +U71Q +UAWMAN UB02 +UBWare UBaKr UConn UENDELIG +UFOTurtle UFOs +UG Juppa UGLYMONKEY58 UGLYS UGam3zOS +UI GOKUU +UI UC +UIM BUTT3RS +UIM Brah +UIM Burni +UIM Chayula +UIM Illusive +UIM Liar +UIM Lobotomy +UIM Log +UIM Loki +UIM Nathanjb +UIM Ori +UIM Paperbag UIM Pepper +UIM Pidbull +UIM Shen +UIM Shrift +UIM Sorrows +UIM Spongie +UIM Stephan UIM Thor +UIM Vas +UIM Warriorh +UIM Wedey +UIM Yoga +UIM Zoldyck +UIM whozan +UIM7 +UIMBerg +UIMaqtpai +UIMatti +UIMpostor +UIPanda +UIdahoKush +UIspice +UKB0b449 UKnowItsB +UL7RA +ULF Tagger ULTMA ULTRA +UN-FAZED UNBIQUOUS UNC0RN UNCEL UNCLE +UNCLE LEET +UNCLESAMY +UNDEAD EXO +UNDERURBED4 UNEX1ST UNH0IY UNLOADED999 +UPDOGS +UPSdeliveree +UQV +UR 0 HP LMAO +URA Noob URAMESHl URSS +US Postal +USA Tyler +USA USA USA +USA is Dev1l USAF USAO USBthe2th USMARINEHYDE +USNavyOver70 USSpaceForce USTreasury +USirHaveLost USirHaveWon +UTT +UWCB +UWF +UWUSTELIJA +UZY VS JAD Ualt +Uamee Ubar Ubaru Uber Uber Logan +Uber Yeats Uberamazing +Ubersmind +Ubicorn +Ubu +Uccino Uchiwa +Uchr Udacity +Uderp Udhariol2 Udingus +Ufda +Uffe Persson UgliestIncel Ugly +Ugly Hipster +UglyFishArm Uglybeetle27 Uglyyo93 UgranDag +Ugursuz IT Uh-0h Uhbove UhhhBoneless +Uhhhh Wut +Uhm Uhscended +Uidi One +Uim Beyonce +Uim IRL also +Uim Zuko UimSian +Uimakoulu +Uk Bristol +Uki Kukkamaa +Uki Skillz Ukko +Ukko-Pekka +Ukkonenn +Uknow Yunho UknowPotter Ukonvasara +Ulappa +Ulibarri +Ulik madiq +Ulillillia Uliss Ulizius +Ullerton UltDrewes +UltRise Ultebor +Ulthane120 Ultima +Ultimat3ly Ultimate +Ultimate Low +UltimateH0bo UltimateSami +UltimateWare +UltimateZe Ultist Ultistic +Ultorman Ultra +Ultra Miami +Ultra Primal +Ultra Sloth +Ultra Space +Ultra Teuz +UltraGOOP UltraJack UltrasTomi +Ulvhilde Ulyanyx UmUUmuUmU +Umarra Umbr +Umbrah Umea Umek Umiland +Ummfufu +Ummz +Un Dutchable +Un M-U-T-3-D +Un puma +Un-Expected UnDutchable +UnGoof UnHoLyxMaTTy +UnOrderly +UnRuleD +UnTaMed X +Una999 Unacceptab1e +Unaclogger +Unaided Unass1sted Unassisted Unavailable Unbacked +Unbann me Unbiased +UnboundLeaf Unbreakable Unc1e +UncJo Uncaged1776 UncagedOne Uncandled Uncel +Uncel Ben +Unchunk Man Uncle +Uncle Exci +Uncle Julien +Uncle Rocho +Uncle Solo +Uncle Somnus +Uncle THC +Uncle Trippy +UncleBigBob1 +UncleCuckle UncleDaddiii +UncleGuy UncleSlappy1 +Unclear +Unclesam137 Uncode UncomfyPants Uncommon Uncooker +Uncool Gary +Uncr3at1ve Uncut Uncy Duncy Und3r0ath Undead UndeadElk333 +UndeadYenny Undeadd Undefeated +Undelivered Undeniable Under UnderExiled +Underaged Undercatt Underlogged Underoos +Underrs +Understars Understated Underverse Undine Undoubtful +Undra +UndrgrndSalt Undulaatti94 Unemati02 +Unemployed Unequalized Uneven +Uneven Mango +Unexposed Unfair Unfearful +Unfergetable +Unfi +Unfollowing Unfriended +Unfunno +Ung Ungolianty Unhappy +Unhapytuna Unhealthy UnhingedE Unho +Unholy Bains +Unholy Cult UnholyAbyss Unholybucket +Uni Mike +UniQ +Unicat Unicorn Unids Unii +Unik +Unimaginary +Unimander Unintended +Union Dixie +Uniqlorn Unique +Unique Am I Unit +Unit Tests United +UnitedLeeds Uniteds +Unity 3D Universe Unkh +UnkindPastry UnkindledTwo Unknown UnknownBeing @@ -24127,158 +49809,353 @@ UnknownValue Unknownchuck UnkoPlayer Unkoly +Unkownkiller +Unlash Unlds +UnlimitedHC +Unlit Sky +Unlock Death +Unluckerdile Unluckers Unlucky +Unlucky Jord +Unlucky lmp +UnluckyNGay +Unlugy +Unluigi Unmasked Unmaxed Unmerkable Unmoist Unnerving Unohdettu2 +Unomia +UnorthodoxGF Unorthodoxfo +Unown397 Unprofitable +Unread +UnrealGecko +Unrecord +Unreliab1e Unrot Unruliest Unsafest Unsainted +Unsavior Unscuff +Unscythed Unseen +Unshaven Cat +UnstopFork Unstrung +Unsubscribed Unsung Untameable +Unthinkable Until +Until Dawn +UntrimCraft Untrimmed +Unus Divinus +Unv +Unwiii Unzkiboi +Uoziz +Uozu +Up N Ur Mom +Up ya mom jr +UpBad +UpNorthCha UpRoaRs UpThaSaints Upeo +Upi Uploading Upluk +Upper Four +Uppercuts +Uppotukki Upriser Uprising Uproot Upsi +Upthe1rons Uptime +Ur Exp 2 Me +Ur Obsession +Ur a baish UrASalmon UrBabyzDaddy +UrBoyBilbo +UrDadsLov3r UrJustXpToMe UrMomLvedIt UrNansMan UrPureSucks UrScr3wed +UrSuchaShita +UrWifeLuvzMe +UrYe UrZoggy +Ura Juanker +Uragaan Dude Urakas Urakkapallo +Urallia +Uran ium Uranus UrbanMerza Urbdayy +Urbn Urdead Urea Urek +Urekzera +Urfe +Urheiluiatka Urheilujatka +UricOddball Urkchar +Urked Urkerhard Urock16 +Uross +Urotsuki +Urpokarhu +Urri +Urs +Ursa UrsaOmega +Urskog Urthron +Uru p +UsainBloat +Usb Doe Tin +Usb Flies +Use +Use Tongue +Use2bHC UseToBeGood Useable +Usecsythang UsedApplePie +UsedLube +UsedRubbers Useless +Useless Main User +User 27 UserApproved +Useranme Usos Ussin +UsualAntZ Usva +Utahime +Utahn Utareita Utca Utini Utis Utsuwu Utter +Utter Pleb Utvisa Uunijutsku Uunipelti +Uuo +Uus kaust Uuti3 +UwU Leo +UwU Noctis +UwU Otaku UxXwpasdiioa +Uzin Uzot +UzrielBoi Uzunar +V 3 +V 9 +V A K U +V A L O R +V A M 0 S +V A N D E R +V Bros +V E F +V E H +V G +V GK +V Gamemaniac +V L A D Y +V Lestat V +V NL +V Nichushkin +V OO D 00 +V R I L +V e c n a +V endetta +V ergetend +V ezi +V i 1 e +V r o l ij k +V the Victim +V-43 +V-EuSoto +V-SaTanjiro9 +V-Tek +V-olRagnarok V043 +V0F +V0WZ V0XA +V1BER V2int5 V3NQM +V3XY V3lnias V3n3natis +V3rtigo btw +V43 V4SKI V4sia +V5C 2 +VAIDUOKL1S +VALH0WLA +VALHA LL A VALK0 VALORANTJETT +VAMPYRlC +VANlLLA RUM +VB TINNS VB24PK +VBoss +VBsec +VDB Niffooo VDHG VEDA VEGANDIET +VEN0M13 +VENNY MOCKER +VERYBANANNA +VET DMaZ +VHS or DVD +VIAL1 +VIE T +VIII +VIIVII +VIKING0 +VILIONKKAA VLEERMUlS +VODKANATOR +VODs VOIVID VOREVERAIONV VORKl +VOlDWAKER +VP N1ck +VPSxSam VPixels VRScotty +VS0P VS367 VUNDA VVDance VVJuul +VVS1 VVStoner +VVade VVanderer VVarden +VVeems +VVhite0ut VViggle +VVilson +VVinni +VVintage VVona VVoods +VW Crafter Va1ynx Vaal Vaalbara9 Vaalberg Vaandah +Vaca Preta +Vacant V Vache +Vache Verte Vad3 Vader Vado Vaealin +Vaelleruen +Vaelte Peter Vafan +Vafthruthnir +VagAngler +Vagabond +Vagina Bad +Vahagis +Vahagn Vahidil Vahlyte +Vahn +Vaikuttava Vailokas Vain Vainis Vaishe +Vaisu VaitkiS Vaizki Vajengo +Vakstu +Val Rex +Val3ntlne +ValOnMyChest Vala Valadrak +Valarfax Valaron Valdevon Valdomiro B +Valdyra Valendale +Valentijn +ValentinaLuv Valete ValgeHunt09 ValheraUK Valhk +Valiant Nite +Validde Valiente +Valienton Valiiim Valiralith +Valirion Valivill Valknir +Valkore +ValkyraeS1MP +Valkyrie btw +Valkyrie xo ValkyrieNora Vall Valliance +Valliate +Vallies Vallk Valluu Valmar +Valmora Valo +Valo Ville Valo247 Valor ValorantJohn +Valorate Valorisatie Valorise Valorpoint @@ -24288,10 +50165,17 @@ Valverdi Valxn Valzar Valzor +Vam pire Vamo Vampeodia +Vampire Bob +Vampirehunt Vampiress Vampurr +Vampyire +Vampza +Van Cold +Van Hellsing VanDarkhome VanHeisma Vanaic @@ -24300,79 +50184,154 @@ Vandahl Vandaine Vandalize Vandalized +Vanderhek +Vanderstorm +Vandery Vandetto Vandmand Vandorann Vanek +Vanek-26 Vang Vanh0 VanhaKettu Vanhal000 +Vanilla Best +VanillaBum VanillaDonut +VanillaFlow VanillaSwirl +Vanillawaifu Vanity +Vanity Fair +VanityNugett +VanityRS +Vanityyh Vanja +Vanja M +Vanki +Vannara +Vanskid Vanss Vantiron Vanupimphi Vape99 VapeMonster +VapeNGapeLLC Vapeape1 Vaperan +Vapid H VapinTiger +Vaping Cloud +Vaporion Vaporized +Vapsjeeee +Var Laslore +Varamyr +Vardorfister +VardorvisSux +Vardovish +Varduka +Varenagan +Vargen96 +Vargo +Vargrmoon Variabulls Varixed +Varlanazz +Varlot4 Varm +Varm Kaffe Varnilla VarockVirgin +Varonom +Varpu +Varradero +Varro Varrock +Varrok Obama Varroq +VarrrokObama Varsa Varsii +Varthon Varulven Varxas Varz +Vas R Vasar23 Vasd Vasdeffernce Vaskapotti Vaskekort Vasquez +Vassaa +Vassago0111 +Vassal +Vastrakal +Vastuuvapaus Vasuki Vasyliev +Vatenkeist Vatix Vauderus Vaull +Vault Hunter +VaultTecs +Vautumn +Vaux +Vava471 Vaveti +VawnHeuf Vaxx +Vaxxil +Vaxyr VayQ Vayda Vaynon98 Vaz0r +Vazier Vazoo +Vb Panther Vbiqve +VeKnow +VeZuu Veas Vector +Vedam Veddunreal Vedroulian +Veecal Veeena +Veekkuu +Veerpalu Veersnof Veetu76 Vefsn +Vegabond Vegakargdon Vegan +VeganFemboy VeganVibes Veganstho Vegard Vegas +Vegas Gunman +Vegas Mike +Vege-Mauri Vegeetta Vegeta +Vegeta MF Vegetah +Vegetas Veng Vegetunks +Veggie Mate Veghan +Vegitation Vegito +Vegito Meta Vegolse +Vehicle Tech Vehms Vehru Vehtamin @@ -24382,205 +50341,416 @@ Veinin VeitiKKa Vekie Vekuuu +Velaynx Velbain +Veld Velegro Velek Velgreed +Velhote Veli +Veli Iron +Velikan Veliki +Velite12 +Velkennar Velkija Velli Velmir +Velociraptor +Veloxrapt0r Velreth +Veltsi +Velvetgunner +Velvsy +Velzun +Vemba Venado Vendall2 +Vender Venderik Vendet27 +Vendum +Venemies Venenatis +Venerate Venereology Venetsia +Venfour Veng +Veng BTW +Veng Me Not +Veng Waster VengDeezNutz Venganza +VengeMeDaddy Vengeance013 VengeanceTR Vengeancen Vengeful +Venizar +Venkeltje +Vennythizer Vennyv99 Venom +Venom Vegeta Venom00 VenomTears +Venomousbite +Venomxkillz +Venous Cobra +Venpu +Ventia Ventile +Ventril0qist +Venus Nyan +Venzdroid Venze +Venzy +Veo s Veos +Verac Obama +Veracs Veracthus +Veraen Veratyr +Verbac Verbal +Verbal Kint +VerbalIrony +Verbia Verbo Verche Verdantys +Verdoemd Verdux Vereco Verelya +Vereoris +Vergi Drakan +Veri Eze +Vericity +Verify Verin +Verissimum Veritasium Verity +Verix Verjj Verkyz +Verliax +Vermachelen +Vermeill +Vermithrax91 +Vermont Iron +Vermyapyre +Vern29 +VernalTDevil +Vernon +Vernossiel +Vernyxitas +Vernz1337 Verokostaja +Veromnis +Verrario Verrtus +Verruckt VersGrind +Versacce VersaceGuap +VersaceSofa Versalix Versatio Versily +Vertigo 2 +Vertsu1 +Vervyyy Verweel VerxaRS Very +Very AFK +Very Typical VeryAvgRNG VeryGoodRNG +VeryLarry VeryLilRng Veryloo Verysharp988 +Verywarm2246 +Veryx +Verzicky Verzik +Verzik BTW +VerzikDaCuck +Verzika Verzy +Verzy Werzy Vesal Vesaris Vesipiisami Vesku Vesley +Vespina Vespulia Vesqu Vesryn +Vessim Vest +Vesta 1000 Veste Vestergaard +Vestorius Vesture +Vesunna +Vetala +Vetem VeteranGamer +Vetilation +Vetinari37 +Vetionarian +Vetr Skaoi +VettePolle +Vex Viper +Vex Virus Vex8 Vexare +Vexed Viper +Vexeed +Vexer07 Vexero Vexers +Vexflame Vexing Vexinity Vexrip +Vexstrom Veylantz Veyron Veysel +VezTa Vezka +Vezon +VgbndUnicorn ViIlageIdiot +ViaCarter Vial +Viallinen Viat +Vib3Ch3ck3r Vib8 Vibby Vibe +VibeCentral +Vibeke Gar +Vibeology +Vibes Check VibesTooWavy +Vicardi +Viccuri Vicente1111 +Vicenza 173 Vices +Vicious Dest +ViciousPawg Vickies Vicky VictheStud +Victimised Victor +Victor Dogg +Victor Lim VictorHo +Victorize Victorp75 Victoryw Victreebel +Victur Vida Vidacks +Viddii +Video Gamic +Videogam3r +Vidlmao Vidy Vidz Viech +Viema Vienas Viera22 +Viesker Viesty Vietnamees View +View Bot View0 ViezNegertje +Vieze Jongen Viggi +Vigi V Vigilamus +Vigly Viguro Vii23 +ViiPV ViiViiVii Viiduus Viiiral +Viikonloppu Viiksi-Vallu ViinaGoblin Viinakramppi ViisYsiKuus +Vijand Vijay +VikSC Vikat VikeSkol Vikerne666 +VikiLord Viking +Viking Dad +Viking768 +VikingKnees Vikingfe +VikkiVance +Viklok Vikram Viktor +Viktor Wins VilainLutin Vilbeee Vile +Vile Arcane +Vile Cabbage +Vile Squid Vileblood Vilemaw +Viliant Vilke +Villagers +Villain Dude +VillainLife Villanovaguy Villanovan Ville Ville921 +VilleGallle Villix Villllu +Villosa +Villrix Vilnis Vilty Vilunki Vimpsen Vims +Vin 08 +Vin B +Vin Vista +Vinaegre +Vinbum43 +VinceBennett +VinceOfMince Vincen Vincent +Vincent D2 VincentDaMan +Vincentimetr +Vinceut Vinchops +Vindi IM +Vindruen +Vindruva +Vinh y +Vinish Vinkmaster1 Vinland +Vinloxx +Vinn28 +VinnFrazz Vinneh Vinnie +Vinnie Dabs +Vinnie Pazz Vinnie526 Vinny +Vinny NZ +Vinny Speedy +Vinny Verac +Vinny-G +Vinnyaldo Vinoloog Vinopenkki Vinostondis Vinous VinsanityGG Vintage +Vintages Vintner +Vinxe +Vinxion x Violas Violent +ViolentPudd +ViolentToxin ViolentWaves Violetti +Violon +VipStar Viper +Viper Aurora +Viper G40 VirZuk Viral +Virbatum Virbliud +Virelith +Virgin Creep VirginGirl +VirginHunteh +VirginUwU +Virginism +Viriato VirtRemnants Virtaheepo Virtuaalnuss +Virtual +VirtualAhri Virtuart Virtuous Virunypel Virus +Virus LSSZ +Virus Soup +Viruss ViruzTehNub +Virzik VisagePlease +Visarin +Viscera Viscx Visdom Vishno +Vishnu Visibly Visigothic Visine Vision +Viskan +VismaBlet +Vister Vistopherson VisuallyMatt Viswiel Vita VitaLemonTea VitaMineralz +Vital Dude +Vital Hope +Vitality +Vitamin See +VitaminCnote +Vithark Vito Vitor Vitreous @@ -24588,260 +50758,533 @@ Vitrified Vitruvio2 Vitryssen Viturbio +Viturscy Viva +Viva La Vida +Viva Mehico VivaLaWeegee Vivalask8r Vivalla Vivants +Vivascape VividAzura VividDreamss +Vivido +Vivij +Vivk +VivziePop +Vixca Viyrew Vize Senpai +Vizima +Viziyo +Vjee +Vk VlBZ +VlGGAN VlNTER +VlRAL +VlVID VlaamsePlank Vlad +VladTheBwana +VladThePaler Vlada Vladek284 Vladi Vladibjoern Vladictorian +Vlaendren Vlambare Vlast1n +Vlncent Vloxx +VnG Lynds +VnG Zenyte +Vnce VngH +VnilaGorilla +Vo Amice VoHiYo_Nami Vobia Vocaloida +Vodbi Vodec Vodka +Vodka ice VodkaBarrage +VodkaBreath VodkaPlz Vodke Voekus Voetbalkous +Vogefur Vogues +Vogven Void +Void Fanatic +Void XVII +Void-Cho VoidMySoul +VoidOnyx22 VoidWraith Voidberg Voidnight Voidpaw +Voidrow Voidseeker +Voidwaker op Voittosumma Vokon +VolProMan Volai Volantis Volary +Volblaffen +Volc Volcan +Volcance Volcanos +Volco Volcwinder Voldesad Volgos Volkert +Volkie Volkrah Volkzy Volmortt Volrod Volrum +Volt +Volt OSRS +Volt268 +Voltimolt Voltz Tzkek Volucris +Volunteer Volux +Volzo Vomiting +Vomiting Cat +Vompiainen Vomuao +Vomure +Von Beck +Von Dylan 2 +Von Locke +Von Reibnitz VonThaine Vonbony Vonderhaar +Vondoom Voneirus +Vonhinten Vono +VonoV +Vonschatten +Vonsv Vonty VooLis VoodooCells Voodookie Voorheees Vopi +Vor The Ape VorGole Vorare +Vorarlberg +Vorcan +Vorikar Vork +Vork Planker VorkathsMate Vorki VorkiAlt Vorkscaping +Voroth Vorpal Vorpeo +Vorsamu Vorschlag Vorso Vortob4 +VosOc +Voss V2 Vossen +Vosty Voted4Kanye +Vovi +Vox Whoppa +VoxMachina Voxna Vozion Vpdz +Vrauri Vrayl Vreaper Vref +Vrekked Vriendschap Vriix +Vrijer +Vrillionaire +Vroeg VroomMasheen Vrzn +Vsian Vsmoke +Vsnn Vt Flavourss +Vt99 Vuallis +Vubbe +Vud Vueko Vukodlak +Vuku Doll Vulcan Vulcant +VulcunLogik Vule Vuleka +VulfRS +Vulia +Vulklet +Vulm +Vulo Lives Vulp Vulpes +Vulpes Audax +Vuohi Vurx +Vurzik +Vus Vusa +Vuto +Vutox +Vuuln Vuur Vuuututututu +VvM Otter +Vxmm +Vxus Vyagruhh +Vyaraeaen Vyaza Vyby +Vycarious Vycos +Vyluxian Vymera Vynlx +Vyrelady Em Vyrza Vysaraine Vyseri +Vytauts +Vyukris +Vzb Vzla +Vzr +W 0 R D +W A G M I +W A G U S +W A V V E S +W E K A +W I L L 777 +W I N 3 D +W J M V +W L F +W O W S K I +W Rizz +W a r s +W ak +W amo +W anted +W e r +W elfare +W i i +W i l l y +W i r e tap +W ifi +W iggly +W illy +W ilmer +W l S E +W ll Z A R D +W olf +W orx +W-2 Form +W-A-M-M-E-R W00D +W00DINTUNA +W00X W0N DMM W0MB +W0RY W0SS W0fle W0nderwall +W0t Rng +W141 Legends W1ETzakje +W1F +W1G +W1Z3 W1ldman W1nch +W1ngedDragon +W2P_III W330 +W349 W370 +W3SL3T7 W3ST0RZ -W4R3 W420 +W489 +W4R3 +W4T3RM3L0N +W4Wumbo +W56 W5O7 W7uQs7s1uKyS W7uQsTu1KsyS +W8 letme pot +W88SSKILLER +W8ST UF TIME +W8ing4Name +W91 forever +WA2 WAGINGWARS +WAKANDA JEFF WAKEUPSWEATN +WAP Munchlax WAQQQ WARLOCKTIN WAZABl +WAxsTAche +WBA Scott +WBMA WE0DEND +WE3N +WEEDY Woody WEGOINGCRAZY WEGSIR +WFH Gains WFTWP +WG Affect +WGWGWGW6W6W6 WH1TECHAP3L +WHITE E92 WHITEEEEEEEY +WIMThighs WINNINGrng +WIRE PULLING +WISEB0LDMAN WIZRD6 +WLJ +WM94 +WMAF +WMATA WO0DSIE WOMYNAREDUMB WONT +WOOGLlN +WPNsuper +WS Hubris +WS Kyzza +WS Phora WSOP +WTB GF 1 GP +WTF No Luck +WTF S7VEN +WUDS +WV Boi +WW I +WW2 Champs WYBD +WYD STEP M0M WYDStpSis +WZ95 Wa Wa Master +Wa t WaIk WaVy +Waaaan WaaduuuHek +Waafty Waager +Waai Do +Waassssaaap +Wabanaki Son Wabbert +Wabo +Wack Sparrow +Wackabie +Wacko Swaami Wacoltx Wadbot Waddlez +Wadee Wadeo369 Waderik +Waem Wafcfreak +Wafe +Waffle Cone +Waffle Jr Waffleboy +Waffleici Wafflekin Waffles700 +WafflesYo Wagada +Wagblump Waggit WaggsTheDog Waggz Wagit Wagn1984 WagnBurner +Wagyu Wahb Wahido11 +Wahpahp Wahpan Waifuism Waifus WaikatoChris +Waikit8 +Waine Wait +Wait Quick +Waitforit14 Waitin Wajiiro Wake +Wake Boarder WakeAndDrake +WakeUpF1lthy +WakeUpGirl +Wakey Wines Wakisaka +Walborg Waldrin Waldron530 +Wales164 Walker Walkin +Walkofshane WalksOG Walktellfox +Wall-enberg Wallabies +Wallabillah +Wallace D S WallaceTusk +Wallah Krise +Wallah Lag +Wallahper Walleen Wallerz Wallex Walli +Wallstboi69 +Wallsunny WallyGator Walnutt +Walo CZ +Walpie6 +Walsamer Walshy Walshy00 Walshyo +WaltJnr Walter WaltersPub +Waltt Waltzz Waluigi +Walzu Wampire +Wan E WanPanMan Wandel +WanderTheSee Wandereer +Wanem WangSohLong +Wanhe +Wanistan Wanna WannaBeE-tje WannaGetABag +Wannebet Want Want2beIron Want2mess2 +WantTacos Wantsome909 Waonnman Wapf +WapitiSmackr +Wapol Wapsi +War Dwr +War Force War12Ready WarFawk +WarHawKVJ WarOnOurMind WarSoc +War_x Warbler Warchee +Warcloud Warclown +WarcraftOSRS Ward988 Warden +WardensFe Wardle101 +Wardless 1 +Wardy791 +Ware Adelaar Warfeh +Warforged Warg Wargames11 Wargowitto Warhammer70 WarheadZ91 +Warking 5099 +Warlolz Warlord +Warlord Kek +Warlord Oli +Warlord Papi +Warlord Sage +Warlord Tyth Warlordjinx Warm +Warm Alfredo +Warm Beer +Warm Donuts +Warm Pasta +Warm Pillows WarmLoaf WarmManBlast Warmantus Warmly +Warmongoloid Warmouth Warnac +Warney Warnings +WarpedDeath WarpedMatrix Warph +WarrgMG Warrior Warrior09rs WarriorHome @@ -24853,41 +51296,66 @@ WartoysCandy Wartt Warwick 1080 WasHcimLoL +Wasabeh Wasbeertjes WashMachine +Washed CJ Washyleopard Waspoeder Waspraa Wasserohne Wassillie +Wasstyr WasteOfBond Wasted +Wasted Toxic +Wasted Xj +WastedSpecs +Wastedgr1ny WastingTicks Wastinmylyfe Wastoid Wasup725 +WasupMyBwana Watafara Watamei Watamelun Watanuki +Watardid Water +Water Skink +Water-T Waterbender Waterbury +Watercress +Waterdrop WatermelonTV +Waterpijp +WaterrBoy +WatersHorse Watertodt +Waterwraith Watr Watschi Watson92 Wattledaub WattsyMain +Wattua Waugh Waulez Wauzemaus +Wave 10 +Wave 69 WaveBtw WaveSkill WavesOnMars Wavezz Wavy +Wax Dabs +Wax i +Waxby +WaxbyDonkey +Waxing Sun Waxtap Waxy WayOfKings @@ -24898,113 +51366,240 @@ Wayabove Wayde Waydens Wayert +Wayfarers +Waylor Wayne +Wayne1149 WayneBretzky +WayneMain WayneStated Wayney WayofWonder +Waypanaator +Waypastfear +Wayt Wayward +Wayward Soul +Waza131 Wazp +Wazup31 Wazupfighter Wazzlewop +Wbd +Wcrocks Wcs139 +Wct +Wdfamidoing +Wdges +We B Trollin +We Feed +We Grinded +We Guard +We Love Dogs +We Tad +We The Team +We sley +We0921 WeAllGucci WeAreDoomed WeAreGroot WeAreMoksi WeChat +WeDaBstMusic +WeLoveToB WeRageAsTwo +WeTheCha WeTsHaDoW +WeWanking +Wea Boo WeaIth +Weak Iaugh +Weak Mindset +Weak Terror +Weak Wrists WeakLesss +WeakSpot Weakcob Weaker +Weaki Weaky +Wealth herbs Wealthy +Wealthy Pro +WeaponNovice WeardBeird Wearwolf +Weasel07 Weasel09 WeaselToast +WeaselTuco Weaselious +Weatherwatch +Weatherzx Weav +Web 2 Webb +Webbe +Webben Webdow Webs Webstar +Webtastic +Weby Elite Wecanmakeit Weddn Weder WedhusGembel +Wee Wee Seapy WeeDRekT +WeeMissSlays +WeeWab Weebcrusher Weebmurderer +Weed +Weed B0Y +Weed Barrage +Weed Farm +Weed OG +Weed PhD +Weed Strains +Weed VIII +WeedWizzard +Weedbucks +Weedle +WeeeWoooo +WeekndWorior +Weel Weelechts WeenieHut Weeperz +Wehby3K Wehsing +Weichey Weight Weighting +Weightless Weighty WeirdChimp Weirhere +Weis578 +Weistalief Weki WelbyBree +Welcome +Welcome Beck Weld +Weld Arc Weldar +Welfare PvM +Well Done +Well-ChromeD +Wellar21 Wellbutrin Welld Wellfence18 Wellpower10 +Wellrun1076 +Wellschit WellyWonka +Weloy +Welp Welsh +WelshNProud +Welshfuryy Welshhy +Welshy 07 +Welshy94 WelshyRhys +Wemh Wench +Wendy Page Wendyy Weng Wenja Wenkey +Wenzhou Were2341 +WereWaffles +WereWolf II Werebanana +Werk +Werknemer +Wermin +Werrett Werrtus +Weru Wery Wesh Weshzz Weslee Wesley +Wesper Wessen +Wesss West +West Mids +West Shiv +West Tigers +West Varrock WestFlag WestTxBoyz +Westardythot Western +Western PA WesternBlot Westleafer Westly Westwich Weszie +Wet Boxes +Wet Garbage +Wet Land +Wet Ottr +Wet P +Wet Sneeze +Wet Thot +Wet and oily WetDreamMeme WetForPet WetRnG Wetex Wetone2880p +Wetta +WetterPussy +Wevile Wexom +Wexs +Wexxorz Weyerbacher Wezl Wh1teSox WhaIe +Whaddup +Whag Whai Whale WhaleeWatch +Whaletorsk Whalkatraz +Whamburgers +Wharebadjer Wharncliffe +What Da Hell +What if WhatASpoon WhatAThot +WhatAreDrops WhatItDoBoo WhatSheZed WhatTheDaze Whatapure101 Whats +Whats Canada +Whats Good +Whats Reddit +Whats Trade WhatsHonour WhatsRsSober WhatsThisRNG @@ -25015,262 +51610,501 @@ Whavenlad Whaxt Wheatleyprop Wheel +Wheelchair +Wheelied Wheelies152 +Whees +Wheesnaw WhenPigsFly WhenRuneLife WhenYouCute Whensfull +Where is GE WhereAreWe WhereIsPeace WhereTheCats +Whereisme7 Wheremeballs +WheresMyPets +WheresMyPurp +WheresMyRNG WheresPurple WhersMyPet Whesh Whey +Whey God Whiff +WhiityTosser WhilyWhip Whimsicat Whina Whinnie +Whipppy WhippyMong Whipsaw Whiskerfish Whiskey +WhiskeyWhip WhiskyVault Whiskywayne Whisp3r +Whisper_2 Whitah White +White Beauty +White Mage +White Mantis +White Owl +White Rnger +White Shark +White Soul +White Wolf WhiteBTW +WhiteBoySam +WhiteChuky +WhiteCricket +WhiteDeathh1 WhiteMagnet +WhiteMaleHre WhitePilled WhiteProdigy WhiteWolf216 WhiteZephyr +WhiteZetsu Whiteboy016 +Whitecoast +Whitecrow Whitefire68 Whiteflashh Whitemambz +Whitetoes +WhizsuJr +Who Add +Who Nose +Who8mypaint +WhoDatDJ WhoDoICatch +WhoHaxdMe WhoLikesIron Whoa +Whoa Gaming WhoaKemosabe Whole +Wholelottabk Whomp923 Whomst +Whomst MD Whoratile +Whorrid Whos +Whos Jo3 +WhosUrDabby +Whosjason Whosyourmac Whothehell +Whruum +WhtKndofCake +WhteLilySeed Whuo Whurse +Whurse Meat +Why 8ank +Why Play +Why SkaR +Why Try Guy +Why are u ge +Why u N0ob WhyBank WhyCantIPick WhyIsRumGone +WhyNevaLucky +WhyRyan WhySleeping +WhyTryAtOsrs +Whyase Whydoubother +Whymcie +WhyteGoodman +Whyto +Wibbity +Wibbix Wibbleforce Wicea +Wichel +Wick3d Bet Wickaboag WickedKlowns +Wict Widdly Wide +Wide Client +Wide Vibe +WideBoyy +WideDommy Widmee +Widows Kiss +WiebMasterly Wiebah Wiebere Wiebren Wiebstar003 Wiegedood +Wielebny WienerWipe +Wierie +Wiesel Wife +Wife or RS Wifi +Wifi Beater +Wig Lops WigWacker Wigan WiganRS +Wiggety Wiggie WiggleMcTuff Wiggly +Wiggly Woo Wiggoth Wigins +Wihbane +Wihwy +Wiigg Wiiillllsonn Wiiize +Wiji Wijji Wijsheid +Wikas1337 +Wiki Flipper WikiLiex WikiWorm +Wikinger Wikingpedia +Wikked +WikuliK +Wilby Wild +Wild Bill 71 +Wild Clicks +Wild Cowboys +Wild Fury +Wild Orange +Wild Pump +Wild Raven Wild Rivers +Wild Spirit +Wild Squidi +Wild Stylez +Wild Whim +Wild chickie WildAbandon +WildGothGirl WildSF WildSnorlax +WildTokes Wilda83as7 Wildapple +Wildbasher Wildboy181 +Wildcard +Wildcatface +Wilde Bizon Wildfire Wildlands Wildly Wildmon +Wildy Back Wilk Will +Will Compton +Will Smif +Will T +Will i b +Will man +WillCosby69 WillG WillNight +Willasaurus WillfulNomad WillfulTiger +Willi William +William Way William2133 +WilliamChris Williamsburg Williamwrc +Williamwurld +Willianderma Williard Willicous Willie WillieP +Willlowsap Willo +Willoweeper2 Willowisp +Willsy_828 +Willy WillyMonka +WillySneeze WillyTickles WillyVanilly +Willyams Willymule WilmaCokfit Wilnafe +Wilpu Wilro +Wilso Wilson +WilsonBro WilsousGoods Wilters Wiltings +Wilwork4stuf Wilza Wilzy Wimm +Wimpy Hulk +Wimpy Worm +Wimpy127 Winanda Wind +Wind River +Wind the ham Windal +Winded +WindexWipes Windi +WindmillBoy Windowpie Windows10 Windrun +Winds Tormnt Windwaker Wine Wineapple Winedolphin WinexBlaze +Wing Daddy +Wing Reaper +WingToe Winged +Wingedlemur Wingednebula Wingknutts Wingless +Wingman Skre Wingmanfire +Wingmastah Wingnut Wingnut277v2 Wingoficarus WingsXIcarus Wingz Wink +Winner Chris Winner1Class +Winning Life Wintage +Wintaj Winter +Winter Slays +Winter Woede +Winter mage Winterpropht +Wintersaurus +Wintertarwe Wintertoad +Wintis WintonOS Winze Wipe WipeRZ +Wippy Spuds Wipzed +Wirbi Wirusas14 +Wis +WisCheez Wisdem Wise +Wise Andy +Wise Bloke +Wise New Man +Wise OlMan +Wise Old Guy +Wise Old Roy +Wise Pug +Wise Up +Wise9884 +WiseBlackMan WiseOIdMan WiseOld +WiseOldCam WiseOldNeef +WiseOldNut WiseOldSimp +WiseOldWench +WiseYoungOne +Wish +Wishen Wishidie +Wishies +Wishwandewe +Wishy Walshy +Wispa WispyWolf Wissfx1 +Wisundaz Witch +Witch Aileen +Witch Elf +Witchkraft +Witchprick +Witchz +Witeout LLC +With Skills Witha +WithoutAGe +Witicism Witkus WitlessFox Witness +Witte WitteLijnen Witteri +Wittu Wittytoad Wixsie Wixxa +Wiz master WizKaleeba WizKawifa Wizard +Wizard Weird +Wizard of Oz Wizard012345 +WizardFish11 WizardIRL WizardKing WizardMascot WizardSleeve +Wizardmatt6 Wizardspike Wizfujin Wizidross Wizreefer +Wizyweirdo +Wizz Z Wizzie Wizzti Wizzy Wizzypoop Wizzzard +Wkd Br0 +Wkedjester +Wkndr WlLDERSGEERT WlLL +WlLL1am WlLSO +Wlatt Wlsperingeye +WnB WnnB +Woadblu Woah +WoahDudeNice Wobbery WobbleyPOP +Woblin Wobos Wobs Wobufett +Wocka Wocky Wodka +Wodka Orange +Woe Woe Woedipoe Woemance +Woeppa Woes Wofford +Wofford2 Wohdii Wohrx Woikaz +WojoWins Wojtas WokeUp Wokkel +Wokki Wolf +Wolf Berserk +Wolf66 WolfApple WolfLucifer Wolf_Hunter Wolfazal Wolfboi70 Wolfboy1209 +Wolfden95 Wolfeena Wolferno +WolfgarrX +Wolfgarth Wolfhearth Wolfie +Wolfie Daddy Wolfiezzz Wolfinger +Wolfique Wolfjob Wolfman +Wolfman00777 Wolfmans WolfnBane WolframOxide Wolfshade Wolfshook +Wolfspeed Wolfy3777 Wolfz Wolles +Wolq poW +Wolverine V8 Wolvesy Wolwa WomanScorn +Womaz WombatLife +Wombats Wombayaga +Won Alll Day Wonderful +Wondo +Wonka44 WonkyShlonky +Wonn Wonnawonka +WonterJodt +Woo Hee +WooGGi Wood Wood Choppin +Wood Scimmy +Wood Style +Wood VI Wood4all WoodJablowm3 Woodcrest +WoodcutterQw +WoodenSword Woodland Woodsboro Woodson @@ -25279,31 +52113,57 @@ WoodsySmells Woody Woody540 Woody8 +WoodyBussy69 Woodys Woodz90 Woof +Woof Woof Jr Woofaire +Woofi Wooh +WookieBread +Wool Socks +Wool Tar +Wooli +WoollffMain Woolly +Wooo Blod +Wooody125 +Woooglets +Wooogy Wooooo91 +Woopie Doop Woopsx10 Woopuh Woord +Woorm +Woos Woosa Woot Woowoh Woox +WooxFromWlSH Wooz Woozie400 +Wophel Iron Word +Word Bird Word2urmum +WordCheck Work +WorkUkko Workahaulix Workath Workin +Working Man +Working Poor Workouts Worland World +World 46 +World 526 +World 61 +World Of Rat World345 WorldEndero WorldWarTwo @@ -25313,23 +52173,39 @@ WormInHeaven Wormeater66 Worms Worning +Worning Mood +Worst Herp +Worzo Worzord +Wosby Woshy +Woste Wostyn WotCee +Wotnel +Woudie Woul Wounded +Wounds Wourlow WouterMarni +WouterPils Woutertje +Woutertje 93 WowAUnicorn WowDerek +WowoW Woww Wowzer1482 +Woz X +Wozi +Wozol Wozzah +Wozzy Wqlq Wrackbar WraithSx +Wramn Wrap Wrap5 Wrath @@ -25337,11 +52213,17 @@ Wrath0fMath Wrathchildxd Wrathinsea Wrav +WreXham AFC +Wreck These +WreckAndRoll Wreckage Wreckanism +Wrecked Wreckfull Wreckin4Days Wreckon +Wreckonize +Wrecky Wreckzu Wrekdum Wrektem @@ -25349,139 +52231,283 @@ WrektumRalph Wrenny Wrigzer Wrijo +Wrinkle Meat Wripzi +Writing +Written +WrmFzy +Wroggi Wrong +Wrong Focus Wrot Wruumze Wryd +Wrynn Wsupden +Wtf Burgers +Wtf Bwana +Wtf Is Elmo +Wtf Michael +Wtf happen WtfJad Wtf_Stake +Wtfisabook Wtfxcreepy1 +WuLuZ +WuTangDan WuTangRs WuTangflame WubbaRs Wubj +WuceBrayne +WuckFeeaboos Wucky Wudkip +Wueu Wuhan Wulfeh Wulkanaz Wullets +Wulong Rush +Wumbolii +Wumox Wundy Wungz +Wunk +Wuntch Meat Wur1 Wurgon Wurj +Wurrior Wurstfest Wurtziite Wuteng Wutlife Wutru +Wutta Beast Wuuluu +Wuz H Wuzn Wuzzi99 Wwalrus Wwarlock Wxll Wxyz +Wy 1853 Wyan Wyard +Wyatte +Wybb Wybo Wyborowa +Wyd +Wydie +Wyel +Wyiphi Wyldar WyldeKirito Wylie Wyliecoyote2 Wylyne Wynand +Wynmao123 WyoFletch WyrdStoned +Wyrlor WyvernBreath +WyvernMage WyvernSlayer +Wz Karmish Wzurd +X X V +X 0H +X Axis +X Bullet X +X Dsmith X +X E L E X +X E R 0 +X Ecuted +X I L E +X Ice Break +X Japan +X King Jehu +X Landie X +X Mars +X Mathiew X +X OUT OF RS +X Robert X +X Rorschach +X SUR X13 +X Seabridge +X WAVE 70 X +X Y P O +X e x i m +X pelz +X-Jabs +X-Man +X-SVNTH +X0N3X X1xfa11enx1x +X65 +X9Y XAAXAA +XANSEB XARlQUE XATHD XAyYxlMaOxX XCShark XCortezX +XD XDDD XD XD8D +XDQ XDamage XERTNARG +XESPIS XEback XFitVapeVegn +XHCD +XI J +XI5 +XIBT +XIX Zeta XIX +XIXIXIXVXIVI XIXXI +XJT XKappaX +XL Peen +XL Succ +XL itikka +XMR XNovaCainX XOBLIN XORB +XP All Night +XP Hobo +XP Jake +XP Thieving +XP for gains +XPOQ +XRP Glandorf +XRV +XRay Mike +XS3X XSandwich18 XSapocalypse XTCarlo XTIF XUJ0RKI XV73J +XXGrimorgXX +XXTENTACI0N +XXaan XXera +XZoTicTB +X_Ouchies_X +X_Streetz +Xa1e Xaaan +Xaberphel +Xadia +Xaeram Xafirox +Xahp +Xaint +Xalastar +Xalcvs Xalrir +Xam Renz Xambitz +Xamphion +Xan Gogh +XanaxXR +Xandahr +Xandayn +Xanefeo Xanfan Xann +XanomaliuX Xanq Xanthophylle +XanthousKing Xantiasto +Xar Xarigue Xarious +Xarkus Xarnathos Xarov +Xarpus Decay +Xarqn +Xartes253 Xaryn Xasdaxs XaskiM +Xasthur Xatar Xaud Xaussiemaulx Xav777 +XavieerL Xavieerr Xayrus Xazz12 Xbalan Xbogaerts Xbox +Xbox Account Xbox kid 99 Xboxkid33 +XcL ady kila XcalenX Xcalumet Xcelorin Xcuuuse XeNeaxaxa +Xebstrika Xecki +Xedoria Xeem +Xeet +Xefi +Xefn Xelas Xelcab Xeldin Xelian Xelzathar +Xemnax +Xems Xen0phyte XenaDior Xenastry Xendel +Xenece Xenethos Xenfire +Xeniat +XenithStar +Xenlon +Xennofobia +Xeno X Human Xenofiel Xenofile Xenoforms +Xenorith XenoviaZhao +Xenria Xenses Xentric +Xentric I Xenzah XeoX Xeqo Xeretus Xeric Xerics +Xerics Exile +Xerics Solo +Xerile +Xerkom Xerloz Xero XeroBlitzAce @@ -25491,14 +52517,25 @@ Xerona Xeroscape Xerosenkio Xeroso +Xerphias Xertrius +Xerxe Xestox +Xetarillos +Xewne +Xeyler Xfebruari23X Xflowerz1 Xgen2004 Xghostbx XgodofironX +Xhaloz +Xhq +Xi Bogan iX +Xianerth Xiang +Xiang Ying +Xiao Wei Xick XicoDoAnzoL Xidos @@ -25506,19 +52543,37 @@ Xielt Xiff XigZig Xiglaan +Xijaxer Xilamz Xillious Xilphy XinXani +Xinbonddon Xinck Xiom +Xiphoz +Xipi +Xipo +Xirenia +Xists +Xityx +Xiuol Xizor +Xizxuka Xizz +Xj Driver +Xl Pat lX +Xl Tango lX +Xlarss +Xlogmore +Xmas Santa +Xmasvibes Xmerl Xnam Xname4 Xniklaz XoXdr34mzXoX +Xofal Xoir Xojix Xolca @@ -25530,319 +52585,739 @@ Xoobs Xorphas Xorx XoutofRunes +XpTitan +Xpect2die Xpelz Xperiencer Xplay2slayX +Xplicits +XploitClamps +Xpm +Xpreme +Xqcksilverx Xqkiller Xquick +XrRipple Xsk1l3rX Xskillz XslayerkingX +Xsy +Xtan +Xterioz +Xternal VZ +Xteven +Xth +Xtian Xtopheris +Xtreme Kill +Xu +Xu3 +XuRuP1Ta Xufo Xuhe +Xulaa Xulreh Xupafion +Xuro Xutera +Xuxe Xuxy97 XvalQ +XvalentiX XvegetaX Xvim +Xwb Xwoprl +Xx A ll y xX XxBABY +XxBriDGExX XxCoponerxX XxJIMBUSKID XxKaakkimusx +XxMOPExX XxPoliSwagxX XxRXZ60xX Xxhel +Xxpyroxx20 XxrigourXx +Xxsagexx30 +Xyagrius +Xylarium Xyler Xylr +Xylym_pilot Xymox Xynamic Xyrath Xyresic Xyryu Xyzcanon +Xz8 Xzavier Xzil +Xzpect +Y DynHaearn +Y Fx +Y O D A +Y O K A I +Y arok +Y ellow +Y em +Y ogo +Y oshi Y0N3 Y0l0mees Y0usless +Y2K38 +Y2k Survivor Y3RBA +Y3w +Y4P +YBank_Mak 69 YCTH +YCantPigsFly YChewyYOS +YEEEZY YERTII +YExTI +YGBigTank +YKW +YM +YMJFL +YNW Kushy YOKiki +YONGWOLRANG +YOOOH JOEE +YORJ YOTJ YOUR +YOURMOMSMAN +YS3 +YSL +YSL for Life +YSinpo +YTPO YTrySoHard +YUSEF +YWM +YYST YYose +Ya Boi Suff +Ya Boy Lachy +Ya Boy Ryan +Ya9 +YaBoiCrisps YaBoiSlip YaBoiWestie +YaBoiiQuan YaBoyCosmo +YaDoons YaLilMupp1t YaOldHeffer +YaSillyRAT +Yabai +Yabbary +Yabbie Pump Yaboiskee Yaboitrek Yaboku Yabui Yachi Yachiri +Yackill +Yackity Yak +Yackley C Yagyu +YahRamen Yahe +Yahtz3 Yahtz3e Yahya10100 Yaik +Yaimzz +Yak Elves +Yak of Iron YakYak +YakimaValey +Yakka Stacka Yakosu +Yakushima +Yakuza +YakuzaDragon +Yalazar +Yall Raycist Yalloune Yalomi +YamR6 +Yama XCII Yamagata Yamakato Yamata Yamda Yami +Yami Bakura +Yammed Yampay +Yampay II Yamsaretasty YanKeDooDle +Yand0 Yang Yangtze Yangus456 Yani +Yanilla +Yanilla Kush +Yank on This +Yankee Run C +Yann78 Yannick177 +YannickH Yannis +Yao Yo Xin +Yaossynx +Yaprakdal +Yardi +Yarfn Yaribel Yarne Yarny +Yaseki +Yasen 105 Yasuos +Yasuuo Yato1God +Yaukai Yauz Yavrum Yawan Yawgg YawningPanda +Yawp +Yaxfe YayItzTyler +Yaysuo +YazanBates Yaziro Yazo Yazuki Yberi Ycinho +Ydmyk Ydorog Ydrasil +Ye Magekilla +Ye Mate +Ye Olde Ace +Ye Olde Ned +Ye Olde Oak +Ye ti YeBankWicked YeBoi +Yea UrBanned YeaAiiiiite +YeahIMineIM +Year YearUp YeastPocket YeastyPussey +Yeblehs +Yechs Yeda YeebusGeebus +Yeeeeehaww Yeeetist Yeeetscape +Yeern 101 Yeeska +Yeet Dan +Yeet Thyself YeetMemes +Yeeted One +Yeetu +Yeezy YefTalks Yefim Yehtii Yeiw +Yek Yekhsad Yekouri +Yeland YeldariIII Yeli +Yelling YelllowFlash Yellow +YellowForest +Yellowfox21 Yellowhoody +Yem o +Yemly +Yemrisnen +Yenie +Yenil +Yenofthunder Yenom Yentelke1998 Yeoubi Yeoz +Yep Cup +Yerbal Tea Yerd +Yeren Yerico Tsu Yerkinoff +Yerm da worm Yeroen YerrBanned +Yerradu Yerrrrey +Yes Click Me +Yes No Maybe YesOk +YesYesYall +YeshuaShalom +Yeso Pro Yessir Yesze Yetagaindied Yeteri Yeti +Yetiburger +Yetlon +Yetsu Yettiez +Yetzne +Yevop +Yevral +Yew 2 +Yew Burn +Yew Root +Yew Tree +Yew Wu +Yew a sloot +YewEssBee +YewGnomeSayn Yewcutter Yewise Yewna Yewp +Yews pnas Yewsanity Yewse YexaC Yezzi +Yfer +Yffud Gnik +Yggdrasverre +Ygh +Ygritte8686 +Yharnam Soul +Yhul Yiatse +Yid +Yidy +YikesScooby +Yilver Yimo +Yinlin Simp +Yippe +Yippiyak +Yisabela +Yja +Yke Ykeykey YlFF Ylikivaa +Yllattyneet Yllig Ylmn +Ylpistynyt Ylulawo +YmanThe1 Ymbyydi +Ynastra +Yne +Yng Xehanort Ynka +Ynun +Ynza +Yo Caspian +Yo Griff +Yo Its Devo +Yo Kev +Yo L +Yo MyUSD +Yo Soy Dios +Yo lm Lee +Yo uu +Yo wyd Yo2021 +YoCerberus +YoGurttt YoHighRoller YoHimiiTsu YoIronManBtw +YoKristaps +YoMomsFavBF YoSinNow +Yobew Yocan +Yoco Yoda +Yoda Adf +Yoda Corona +Yoda NSZ YodaEvans +YodaPen +Yodamainn YodasNo1Fan YodasYoda +Yodasquad Yodati +Yoder +Yoerias Yoeshua Yofang Yoghurtis Yogi +Yogi DMT +Yogi Surfer Yogololo +Yogusun Yohny Yoink +YoinkYoink Yojak3 Yokie Yolkysky YoloSwagHope +Yolp +Yolvert +Yomaku YomamaPro +Yomashu Yomdo Yondu +Yondu Poppin +Yonemid +YongZhong +YongZonnegod +Yonkers +Yonko Buggy +YooDuragon Yoodish +Yoooord Yoorah +Yor Yorga Yorick Yorick095 YorickMorty +Yoriichan Yorinky +York Rite +Yorkson Yoru Yosep Yoshi +Yoshi Man17 +Yoshi Suyumi Yoshi6380 +YoshiThick YoshisStory +Yoshiwaptor +Yoshizilla +Yotaak +Yoteii +Yotsuya Miko +Yottie +You Are Zach +You Be Love +You Die71 +You Ok Fam +You R D3d +You rc 2 +YouGotRoll3d YouGotSeabed YouNeverKnow YouOverThink +YouSuck YouWontScare YouareGE Youbartonz +Youme +Youncies Young +Young Bucket +Young G4 +Young Guthix +Young Krazed +Young Logic +Young Mooch +Young Mullet Young Tango +Young li +YoungCatFish +YoungNoot +YoungScuba YoungThugga +Youngdaddyg Younge +Younglleff +Youngn Youngsters Youngvet Your Your Amity +Your Future +Your Idol +Your Misery +Your Mom67 +Your Mothers +Your Toilet YourDadIsMe YourFrenRen YourObsessed YourPaperm8 YourSolution +Youre Joshin YoureAllTalk YoureIgnored YoureMySam +YoureSoCool Youssef +Youthful +Youtube Luck Youwillcower Youxi37 Yowarrior40 +Yowch Yowzer Yoxz Yoyaaaaaaaaa Yoyo Yoyo502 +Yoyoma007 +Yrasa +Yrdna +Yre Yrjo +Yrok +Yrrabo +YsL Dom Ysdragos +Yshtola +Ysr Yster +Yster Bunny +Ysuke +Yu Seolha Yu Stinkipu2 +Yu Zi Jiang Yubs +Yubz +Yucan Tucan Yuck +Yuck Aroma YuckFou YuckMouth +YuckNoYums Yudi Yuengllng +Yug Cipe +YugiMoto666 Yugioh +Yuhai Yuigahama +Yuiii Yuiku Yuipster +Yuka Takaya Yukaiaiaiai +Yuki Kitsume Yukika Yukionna YuliusCaesar Yulong +YumYumSauce +Yuma az +Yumaad Yumbo +Yumhumgao Yumiko Yummy Yumped Yundastan +Yundruh +Yuneekh Yunery Yung +Yung Belial +Yung Birdie +Yung Deaner +Yung Nice +Yung Swoosh +Yung Zach +YungDuski +YungIron YungMoistGod YungPredator +YungSlick YungThimothy YungTungsten YungWhiteBoy Yungsik +Yungsingteng +Yungyonder YunnT Yuno +Yuno Yuno Yunozen +Yunq Yunthers +Yuoni YupImMadBro Yupal +Yuqi Dab +YuqiShuhua Yurimo Yurix +Yuriy Yusaris Yusko Yutorgborm +Yutzz Yuudachi YuugeJohnson Yuuki Yuuma +Yuuuuurd me Yuuzu Yuyevon2003 +Yuyu Shirai +Yuzumii Yveaux Yvno +Yvoa100 Yvon +Yvuar +YxY Yxskaft +Yzanagi +Yzap Yzyszn8 +Z 0 D I A C +Z 84 +Z A D O +Z A R G O T +Z A V Y +Z Barrows +Z E R0 +Z I K O +Z Jay +Z M P +Z O R 0 +Z O V K O +Z O ZO +Z Rowz +Z a a K i R +Z aeBae +Z e e e f +Z e x +Z ebak +Z edd +Z erkie +Z ev +Z i g g e h +Z igZag +Z ion +Z y g y +Z ygis Z-TheReaper +Z00STER +Z0K0 Z0MBI3 +Z0MBIFIED +Z0NK3D +Z0RB0 Z0bbey +Z0deac Z0ops Z1pn Z1pn_xD +Z26 Man +Z3 +Z3FRAN +Z3R0 CO0L Z3ds +Z3ymour1 Z4phy +Z4ppie +Z6L Z7Z7Z777ZZ +Z900 +ZACCU +ZADDYGANG ZALCANOPET ZALGlRlS +ZAQWRY ZB84 +ZBLOCKA ZEIVI ZEKEDAFREAK ZELDRIS0 +ZER0 EHP +ZER0Z +ZGJ +ZHG ZIARED ZILLER +ZJ +ZL1 Devin +ZLA +ZMB Scary ZMithrilMan +ZNDX +ZOEY 101 FAN ZPWUZZLED122 +ZRegi +ZSYD230207 ZUCK +ZUK EXPERT ZUwUBI ZWUNK +ZWlFT +ZX +ZY +ZZ +Z_Z ZaPhiRoxD +ZaadTeef Zaane Zaanstad +Zaatar Zabe +Zabey Zabini +Zabky Zaboxi ZacAynsley +ZacBallsHard +ZacFx +Zacariah D +Zaccchhh +Zaccy Zacflame Zach +Zach 6464 +Zach Patino +Zach est13 +Zach q p +Zach07 Zach314 +Zach4211 Zachagawea Zachatank Zachawy @@ -25852,38 +53327,86 @@ Zachs ZachsAccount Zachulous Zack0ry +Zack504 +ZackAsch ZackSparrow ZackWasTaken +Zackary +Zackle Berry Zackly Zackman Zackology +Zackree +Zacky P Zacoron +Zacxion +Zaddy Bully +Zaddy Zo +ZaddyCool +Zadek Zadior Zadok +Zae +Zae The Bae +Zaenil +Zaeres +Zaf Zafaron Zaff +Zafrot +Zaga911 Zaggly +Zagustin +Zagz +Zahard Army ZahellGlarus Zahellina +Zahiirr Zahnae +Zahrani +Zaion Zairin Zairx Zaitsev Zaixii +Zaixyyuu +Zakhary +Zakje Pep +Zaku Rs Zakz +Zala +Zalaberto Zalar +Zalat Zaleander Zalen Zalerae +Zalotus Zalphyrus +Zalse +ZaltyPretzel Zalux +Zam ZamPeZu +Zaman Zamanr Zameul +Zami Zamianx +Zamicxus +Zamirok +Zammiy Slay Zammy ZammyHasjta +ZammyJesus +Zammys Flame Zamorak +Zamorak 61 +Zamorak Book +Zamorak Tome +Zamorak808 +ZamorakBrews +Zamorakz Zamorangue Zamorizzle Zamowrecked @@ -25897,225 +53420,461 @@ Zandous Zane Zanfew Zanfis +ZangKeera Zangola Zaniels +Zanimoto Zanithic Zanity +ZannyBatsbak Zanryu +Zans Zansus Zantaril Zanwk +Zanzu +Zaom Zaon ZapWasTakn +Zapah +Zapatos +ZaphiasTM Zaphyan +Zaphyrim +Zapii Zapio Zapky Zapleno Zapletics +Zappaforever Zappman123 Zapppy +ZappyLand +ZappyWabbit +Zapsticle +Zaque +Zara Mobile Zaraffa Zararod Zarasth Zarathos +Zarchonias Zardee Zardua +Zarf +Zarfa +Zargole +Zariah +Zariky +Zariser +Zarke +Zarkh +Zarkino Zarkoz +Zarley Zarliona +Zarmos Zaro +Zaro M +Zaroki Zaros +Zaros2k +ZarosCorrupt +ZarosRex Zarosadomin +Zarosian Rat +Zaroux Zarov Zarpedon +Zarrix +Zarude +Zarvern Zary +ZaryteKnight Zasi +Zasilus +Zastava Ak47 +Zastavo +Zatailex Zatch175 Zathul Zatom +Zattix Zauberbiest +Zausbaus Zav555 Zavaren +Zavikk Zavilia Zavinor Zavorak +Zavvia +ZavylonCG +Zawro +Zawts Zaxbys Zaxcord Zaxm1 Zaxxxsoldier Zaza9119 Zazian +ZbraCakez Zbychu +Zch +Zcj +Zcu +Zdawg +Ze Golo +Ze Own +Ze Punheteir +Ze Ubernoob ZeOblitz +ZeProx +ZeQuYaa +ZeZienMaar +Ze_balla Zea1ous Zeal +Zealandra Zealea Zealicious Zealoe +Zealous Love +Zeals Max Zeather +Zeb Slays +Zebak +Zebak Acid +Zebaks Jugs +Zebedank +Zebede Zebest605 Zebras +Zebs main +Zebs scuffed Zebu Zebuck +Zecca Zechuchith Zect +Zed7 +Zed_11 Zedanko Zedar Zedd +Zedd UwU Zedd180 Zedek Zedirex Zeds +Zedz ZeeAyyBee ZeeEL +ZeeZeeZee Zeebers +Zeec Zeedith +Zeehox +Zeeke Zeekheart Zeelmaekers +Zeemoee +Zeeny +Zeeon +Zeep +Zeepra Zeerkel +Zeeshan Zeeshan01 +Zeeuun Zeeuws +Zeeuws Tuig Zeeve Zeeyk Zeezki Zeffe +Zefrank +ZefyX3 Zegetable +Zeh Zimah ZehThijs Zehbu +Zehf +Zehiret Zehv +Zeilex Zeinovyah +Zeious +Zeitgaist Zeithex Zeken +ZekimusPrime Zekje Zekkels +Zekley Zeko01 +Zekrom ZelaooReturn Zelatrix +Zelcano Zelcode Zelda +ZeldaHaxor42 +Zeldamen Zeldaza +Zelle Me GP Zelnite53 +Zelroy2 +Zelten +Zelzoy +Zemiak +Zemie Zemix Zemmy Zemoko Zemps +Zemyrrah +Zen Moments +ZenBtw +ZenRaidz +Zena96 +Zenathrius +Zenci Zenearys +Zenemz +Zenez Zenfor +Zeni Master Zenithina +Zenitsu +Zenium Zenki +Zenki RS +Zennyte Zeno +Zeno Pho Bia +Zenoce Zenpep +Zenqii +Zenqs +Zenrrrr +Zensai +Zentae Zentra Zenytum Zenzulo +Zeon Clone +Zeorun +Zeos Zeoth +Zeou Zeoxr Zeph Zeph0s Zepher138 +Zepherahs +Zephere16 Zephrinne +Zephxa Zephylius Zephyren +Zephyrhills +Zephyric Zephyrot Zeplin1 +Zeppelin IV Zer0Requiem Zer0Tw0 Zer0mancer ZerUmh +Zeratul259 +Zerbeh +Zerberous +Zeref Zerendipity +Zergonaut +Zeri0n ZerkByDay ZerkInDaYard +ZerkMeOffPlz +Zerkeee Zerker +Zerker fe Zerkism +Zerkom Zernag Zero +Zero Cals +Zero DeNiros +Zero Saber +Zero Scope +Zero Suit +Zero Talent +Zero Tol +Zero Zeros Zero92922 ZeroAltruism +ZeroBodies +ZeroCake ZeroDignity +ZeroFox8576 ZeroGravity3 ZeroHour +ZeroHpAgain +ZeroTheKing +ZeroValor ZeroXJD ZeroZero Zerobeat Zerodareborn Zeroeagle +Zeroen +Zerololz Zeroly2 Zeroswift Zerq Zerrilis Zerro +Zersers +ZertUwU +Zerx +Zesima Zeskater Zesty +Zesty Bvrnsy +Zesty Senpai +Zesu +Zet_RS Zetani Zetardo Zetore Zetreker Zetrus +ZetsuboSekai Zetsubou Zetta Zettty Zeug Zeus +Zeus Jr +Zeus07 Zeus09 Zeusvdl +Zeven Zeveria +Zevlim Zevosh Zevvo Zewx +Zexma +Zexra +Zexual +Zeyora Zeyzima +Zezaroth Zezergz +Zezima Simp +Zezima W q p +Zezimaislife Zezimas Zezime +Zezus +Zezynx +Zfg Fan Girl Zfsalt +ZgeL Zgrite +Zgzi Zhacarn Zhaoyl Zhava Zhinky Zhiqth +Zhonny +Zhops +Zhotaro Zhqith +Zhuxiong Zhuzh Zhykiel ZhyloFlex +Zi P +Zi1y +ZiNikor Ziauze +Zick Licker +Ziclone Zidi Ziega +Zierikzee +Zigfrid +Ziggey Ziggurattus Ziggydog7 +Ziglar +Zigory Zigzagoonfmt Ziicatela +Ziidz Ziinc Ziincor +Ziisus69 +Zijwiel Iron Zikry +Zil o Zilandraa Zilanthra Zilex +Zilexion777 Zilly ZillyLovesMe Zillya Zilpha Zilvia Zilyana +Zilyana Gr +Zilyana Jr +Zilyanas Dad +Zilyarma +Zim Zim8 ZimFlare0003 +Zimak +Zimaterasu +Ziminiar +ZimnyZielony +Zimpathizer Zinc Zinct Zing Zingg Zinixon Ziniy +Ziniyy +Zinnialexis Zinogre Zinoto +Zinryia +Zinshaw +Zinsmaster +Zinsmeistro Zioh Zion +Zion Fire Zioo Zip Lynx +Zipacna Zipit +Zipm Zipo259 +Zipp0Fluid Zippeeee +ZipperTrout Zippy909090 Zipsap Ziqs Zireal +Zireb ZireneV2 Zirkisi Zirn @@ -26123,74 +53882,143 @@ Ziron Zirrub Zirvzaa Zisam +Zitaya +Ziti Sauce +Zitri Zittte +Zivile Zixcon +Zixon Zixxen +Zixxty Ziyai +ZizZazZuz Zizarius Zizou Zizzle +Zji +Zjoelini +Zkilrr +Zkyui ZlGGEY ZlNQK +ZlZl Zlap Zlatan +Zleek +Zleepeey +Zlig +Zlomble Zlote +Zlug Zmancool Zmanonthego +Zmazek ZmaziWasHere ZmbieShepard Znked Znorox +Zo e y +Zo m B +Zo nk Zo7al Zoak Zoanthids Zoaxyl Zobbe +Zobny Zoca +Zoca BD +Zodden ZodiacIsTed Zoeh Zoet Zoey Zofad +Zofu +Zogggii +Zoggy Marley +Zogloid Zogurk +Zohi +Zoik Zoil +Zojun +Zok +Zoka Zolarf +Zolaris Zolcome +Zoli +Zolkaria Zollo Zolon Zolrisma Zolruh +Zolt09 +Zoltan Uk Zoltanious +ZolwoZ Zombah +Zombi Zack Zombie +Zombie Cody +Zombie Zack Zombiezparty +Zomboide +Zombrons Zomi Zomimi +Zomp +Zone Red ZonichGG Zonir +ZonlyAlex +Zonnedael +ZonoxRS Zonse Zonum_Knight +Zoo Orleans +Zoo scape +Zoobloo7 +Zookly +Zool +Zoolander Zoolander011 +Zoom Zooming Zoommaster97 +Zoonix +Zooted Zoppy Zorb49 Zorblet +Zorcar Zoret Zorg Zorh +Zorie Zorin +Zorkath +Zorkz ZornDurag ZoroRoronoa Zorolith +Zoros son Zorrac Zorrita Zorros Zoryov +Zos +Zosc +Zossssssssss Zostok Zoteize Zotetz Zoudrex +Zoumok +Zouns +Zown Zowski Zozma Zozo1232 @@ -26198,54 +54026,116 @@ Zpizz Zqw6 ZrankerF Zrax +Zriv +Zrr Zsedcx +Ztneff +Ztxch9 +Zubaritis Zubora +Zubsolv Zuburus Zucchini3 +Zuchini +ZuckZyZock Zucu +Zuel +Zuened Zuggster +Zugmah Zugs Zuibbi +Zuiox Zuipe Zuipen Zuipvlek +Zuk +Zuk Bot +Zuk Cucker +Zuk Meh Off +Zuk My Toa +Zuk Sucks +Zuk a Duck +Zuk a Zik +Zuk at 1 Kc +Zuk my Tzok +Zuk was here +ZukDisLilZik ZukMadic +ZukMyAss ZukOrBust +Zukala Zukmos +ZukoTheMcoon +Zul-Bot Farm +Zul-tul +Zulandra Zulax Zulex Zullander Zullrah Zullu +Zulrah Goat ZulrahSlave Zultora Zulu ZuluLippen +Zum Zumaz +Zumaz Her +Zumb ass +Zumwalt Zundaddy Zundix Zunkrah +Zup Rap1 +Zurad +Zuramaru Zurcal +Zure Kut Zuremate Zuriel Zuriel321 Zurius Zurkzes +Zurlofix +Zurn +Zurper Zurqos +Zurthur +Zurvan +Zurxas Zuubi Zuuqe +Zuurkast +Zuv Arik +Zuviel +Zuwy Zuxar +Zuxf +Zuxi Zuzad Zuzas23 +Zvg Zvirbulis +Zvotne Zw3d Zw4rtjoekel +Zwabba +ZwangerePapa Zwanneke +Zwartbeast Zwartbest +Zwarte Iron +Zwarte dief +ZwarteAapMan +Zwartehand Zwef ZweiBeer Zwei_02 Zwerky +Zwette Draak +Zwieber Vos Zwiers Zwift13 Zwijg @@ -26254,65 +54144,152 @@ Zwimps Zwinter Zwlr Zwwwnbeast +Zx J3SS3 xZ +ZxFe +ZxbeeRs +Zxeon Zxirl Zyad Zybes +Zyck o Zydecolarry Zyer Zyev Zygalo +Zygameux Zygy Zykaite Zykonic +Zyl Zylco Zyleta +Zyliana +Zyllami +ZynGardener Zynerith +Zynnocent Zynthe Zyntixero Zyper89 +Zyrem +Zyrinth Zyrok +Zyrona Zyrpex ZyruviasDied +Zyunkko Zyxla Zyxuz Zyyra +Zyz Zyzziima +Zyzzy +Zyzzz brahh +ZzJ0SH Zzanoss Zzayo +Zzuma6 Zzyzzx Zzzax Zzzd ZzzzG +[#OA0VWICQ8] +[#SG1JGUXTW] +_Untrimmah +_gria +a Cairn +a Dang +a Mikey +a Miko +a Venenatis +a Venny +a Weeman +a bean +a black cod +a casul +a cooky +a cool bug +a eu +a f o +a gentle sir +a little sad +a ndo +a p p l e +a pathetic +a pb +a sad kraken +a sad waffle +a sk y +a the sniper +a tnQ +a trash cat +a zo +a-x51 +a07 +a1liez +aDome +aDuNaz aGamerGaming +aGamingDad +aGlasgowGrin +aGoober aIIfather +aKSU nPC aKash20i3 +aMiniDude aMiniGorilla +aMiniHitMark +aNERDYsloth +aNStarFury aPinkBunny +aPinkKitten +aShavedLlama aSnowTiger aSunnyPotato aSwiftyBoi aThoms aTinyChimp +aVolk aWildSeal a_SaladKing +a_duncan +aa noob +aaah aaahChu aalucard +aanmaken +aanwaz1g +aaron_diaz aaronparkerr +aatudoz aaty ab0u +aballer09 abbruzi +abbruzie abcz abdi abdimajiid abdulllah abis +abk144 abni +about blank +abqve abra238 +abrakadabraZ +abray +abu hassani acceleratism +ace375 acecoffee2 acerkush achillies +achillies zy +achinadav +achterkamer acidshit +actionbob actlater actofvalor24 acuile @@ -26320,41 +54297,78 @@ adPEXtwinDnG addaaam adem6792 adizzle444 +adogg0323 adopt adrian adsdadasdgtd +adsh55 +adue adul +adul t +advia +advies +advit +aeej +aeggefar aejfd aenpaa aezthetic1 +afatslug aferraro +afh +afk 2 2277 +afk a bunch +afk n doc +afkayscape afkdontattac afkingRS afkvsehp afkwarriorzz +aflk afraid2boss afraid2boss2 african aftrthxght +afzi agent_of_fox +agentdd_4 agfdbzIM +aggain agic +agieee agoraphob1a +agrovation agsmanpro11 +agytagtyewaq +ah oh uh ahahah ahippi3 ahokusa ahrlhyrslylh +ai eye q ai3i42Iaaskl +aiden m8 aidibohx +air strikes +airborn frog aito +aitoBeruna +aiwodas +ajiffniff ak47afghan1 +ak47ags akaExploit akaNorman akae akbennyboy +akeep +akidnamdjrmy akipf +akirahokuto +akkha kum akleb +akq +akspew aksta aku aku1212 al0x @@ -26362,82 +54376,139 @@ alabasta1 alabthemoola alans alaric +alaxam +albersson albiguerra albioon alchetraz alders0n +aldi warrior aldrichmax +aldrichs alecbeats aledank +alexjberroc +alexlangman alexs99s alfafa20 alfhershey algotrader +alias ilias alic3 alimpweenus +alipandh alittlepig alkaizerx alkkis +alkkis pk +all en +all love man +all to 200m +all4four20 +allcaps +allebylund +allenbb3 allez allluminati +allomax3 allora76 allowitplz allzeras +almoggar +alpacathund1 +alpacka toke alpalmchal alpha alphabosss already also alsobosspros +alt f4 gn +alt maxed +alt rite alt0n +alt2kil +altairanezio altehnub altforusa altiarblade +alvexn ltu alvinhic +always win alwayscurius alwrighty +am sam +am so sleepy amagna amay +amfoine +amheeh +amikiri ampharos61 amputated anasacez21 ancapistan anchor +anchor seb +ancientskye andhetakes andhim +andmcadams andreable andriuttt +andtony +andynew2007 anelebar +angel995 angeline1711 angrydump +aniki chan anime +anime member animeF3tish animegirl73 anizan +anklez ankou4smokin annie +annie r u ok +annni annull anoldfatdude +anon1606 +anotha 0ne +another gay anti antibully +antituuri antivaxer antombomb anttimage3 anxiousbeef +any1 anygirls +anyma +ao aocha +aod retired ap1ska +ap3xOo apacifist789 +apcays apeman8731 apenzoon +aperts aphr0 +apikeppa apina apiph3ny apou appelkneuz applee +aqp frog aqqqqqqqqqqq aquafx +araenel13 aratsanus arcane archyiop @@ -26445,342 +54516,692 @@ arcvnaxiii arcwriter ard lad 06 ardazz +ardy guardy arengs +arevles arexmeister arfih argilah +ari abdul +ari shaffir ariman22 +arkoudaphile +armadyyyyldo armyofkids +arnr +arocardo arookie +arouze arreydn arrowbob2 +arrows2ashes +art possible +artaax artan arteefact +arth urr +arthadeos arthurdebart +artist_60 +artizia +artogoes arvothepure arzier ascipulus ascott1 +asdafsdafsd asdasdasdf1 asdsdafsa asedviss asfand ashster25 asian +asianbunnyx asibioass +asidb +asm asmoooo +ason jones aspiraring1 asrdftgffdsh +ass chance +ass jiggles assaultvests +assbergerler +assdrag +asseaterbtw assoffire +astgferallah asuhcuh +at0 +atob7 atoma619 attaaM attack2much auditore28 audrey +audrey horne auma +aus j osh +aussieyobbo +aut AT5 aut1st1c autism +autisticboiz autumnbound +av av av8bgo +avantoeftish avewy +avg Jake +avg gov ee +avg idiot +avikntos avmech31 avocabo awple +aww fk +axeleebob +axeli +axetoons axoblaster axperson808 ay96 aydrian +ayeetbix +ayhank aynull +ayos ayo +ayrball +ayshinx ayte ayvz ayyreynolds +az3r bulbul +azerke azilim +aziz8mohd +azj azura +b a g +b a l r a j +b b o x x +b eeg +b enjy +b ezos +b i g s t +b kuz +b l u rr +b oe +b on ass +b oo n +b ooth +b u l +b ulba b-ray29 b00ty +b00tyw1zard b0btehcool +b0dybilder +b0gadu +b0nelesstofu b0rn2grill +b0urb0n_kid b1ackcoxdown b1apoody +b2bpurple b2bs +b33 rabbit +b39 b3cc b3kiy +b3rgo b4Mb00B0NGG0 +b4iforget b4nyluckypvm b5 s4 0000 bIack bIrkaoff bLeatzker +bMat95 bSteeezy bVibin bZabii +ba1tti +baIIs3 +baami +babewatch babstah babugo baby +baby peb +baby peba babybuffalo babygirl +bacbacbacbac +baccybaxter backseatrs bacodie +bacroonX +bad bee +bad boy602 +bad british +bad data badalts +badass1337 badat3tick badcold611 +badderjari +baddspella +badger fan 7 badsadmac +badtechnique +bag milk +bagans baglokale bahumat baile +baile y baitmem8 +baj69 +bajadam bajj +bak3d bean5 bakareru bakesome bakkerbtw +bakplaat11 balding +balisongg +balkare ballenbak +ballerbebbo +balsson +bam bam-BA-lamb +bambera2 bamfhitman bamsi banana bantrok +baraho barakozle +barcrestboy bard006 bardj +baritone888 +bark bark +bark f0r 0ff +barkirion rs barkmeat +baron nash +barou barry +barrybacon +barrysdad barryslet bartelbarel based +based af +bash the rat bashong basic basically basiel +basispiloot6 bassdrum bassfortexx +basspro76 bastukid +bat flack di batchela batdog183 bates batje vier batseflats +batterym1es +battlebus33 +battleman36 battried +baumannii bawky bawsi +bawz zaa +baywest +bazjunior +bbALL +bbb bbest bblood bbones bboyalec bbtarget +bbully +bc22 bcguppy +bck2kickass bcollier94 +bdb +bdw reborn +be one +bean flick3r beanbag173 beanieton beanskunk bear +bear66881 +beard3djesus beardZy +bearia +bearitimus bearjack +bearplusiron beatthebot +beaver 5 +beaver 9 +beaver win beaverboy47 +beavitte bec0me +becarius +becca boooo beckles96 +bedabin247 bedburn bedtime beech beeelake beenus42 +beer sweats beggar +behaardezak belanin belgibeer beliver96 +belizianos bellatarius +bello0 +belub bemw bendy +bengnomonkey +benisbutts69 benjabii +benjimin benjiswaggod +benlovesyo +benrj91 +benroxo bent benyatta1 berd007 berkut87 berrytrials +berseke1 +berserkbear +berserkku bertucci2575 berzerks +besouley +best bojji +betleejuice betonimuna +betty wu +beverlly beverwijk bevh +bevsteve +bff +bfxrturbo +bg s +bgs ownez +bhicks bhuffs3434 +bibilush +biccc bicycle666 +bidls bidof bieltanman +biffsy bifket +big bad jon big bojangle +big gay ig +big gmbino +big head +big man shaq +big rart +big snood big tree 63 +bigbadheskey +bigbig horse bigbigbone bigblade28 bigbluntbrnr bigbologna31 +bigboybarlow +bigboybrips +bigboymansir +bigbud bigchoocher +bigcuff bigdbandioto bigdoggtrock +bigdoinks42 +bigg bud biggest +biggest fan +biggiefries7 biggledoinkz biggreenbush biggs +bigjuicybutt bigl +biglenz +bigmoney jim bignipnik bigonevs +bigotslayer +bigpkz bigpompom +bigreddogV2 bigschlong33 bigsee bigshrub45 +bigsnail +bigspick +bigsugoi bigtuner bigz4p bike +bike trail +bilbsay bilet92 bilfey +bill one +billbosaurus +billsta46 billy +billy g0at +billy mayne billy5454 +billyj0e billyjoewo +bilo xd +bimblebunk +binaev12 binch bincho420 bindi bingels binkie +biolemonhaze biozahard +bipiee bipoc +biq pp gamer +bir d birbeon +birdieputt birkki +bisp +bisse bitten by bitter +bitter fruit bj0rne bjassen bjpc bjuti +bkguytd6 +bl00d twinz bl00dShaman bl0wMyPlpe +bl0wdakushh +bl4ckdelt4 black +black coffee +black cum24 +blackbelt blackburrito +blackedmarko +blacksshad0w +blackstyl579 blackzranger blade2k9 +bladee blaktraksoot +blame war blankownz blarbydoo blastmaster6 +blawblaw +blazar58 blaze59 blazed blazekin blazertrail4 +bleenkie +blessn bleu +bleu kayn +bleubeard bliksem269 bliksuiker blind +blind idiot +blink 1 8 2 blinkforlife +bliss blky bloQQe blobaman +blockteleing blonkie45 bloo37 blood bloodhound96 bloodrain202 +bloodredd bloodtheatre blothbather +blowmebuddy blubbystr699 blue +blue in vain +blue max6 +blueTofu blueWagonMan bluebean2596 +blueberry624 +blueberrydev bluebop blueergon059 +bluetrane bluewahfull +blunt tank +blurite boy4 blutorch blvckstvrr blynaas +bman5 +bmeow bnff +bnndt +bnqy +bo bby +bo nk bo2Lawrence bo3rke bo4r +bob bob bob +bobby ross bobby45153 +bobbyb727 bobbyitsme bobbyjoe +bobbytronx +bober vvittu +bobfish66 +bobness +bobondowski bobsushi6396 boer bofflepopper bogie297 +boglad4ket boident +boinkerton +boke smowls +boksepoeper1 bollocksed +boltwitdick +boltzy bombadinski bombycrahan +bon4ri +bonSwole bond bondalorian +bonderoeven +bonelessrice +bonfire bongohonkers bonkbonk bonna1994 boo0ty +boob boob +boob nut boodje1993 boofablebass +book of ra boom boomboom booogieoogie +boooosh +boorito boosbear +bootn98 bootylips +bootyters boratas13 bored +boring hobby born +borpxatu borsi boss +boss hogs +bossboy420 bosscat56 bossdemon88 +bossdog99 +bossedit123 bossy momma +bostonbb2g +bosway bota2 +botchx +botilabaca +botleFluff bottleo +bottn bottomfeed bottypedpsw +bounsn bouquetboy bouwjaar1990 +bouzouki +bowfa bob +bowfaaddict bowl +bowl loaded +bownd boxofbears +boxtrapgod boycie420 +boydi +boydt +boyuniverse +boz pwr boze +bozo +bp9 br0wnardRS +braap poster +bradrian bragegutten brain +brain rotted brainbusterr +branderx gim +brandom lol +bratnt brawling +brb cigy bread breakingood brecht181 bree breeze +brejch +brekitutta brett +brett 69 bretthomas3 brettiz +brettjournal brettmanx7 brettrae +brew bar brewi brexit +brian285 +brian289 brianknight7 briareus brics bridder bridgeport brika +bring ya ass brisingr vin briskyhot +brixs22 brizzle +brky +bro its hamz +brock soup broetie broker +bronson99 +bronzelvl bronzenohkie brooskii +brotha in Ra brotherkjell brothervoid7 brown brownell brownjesus brtsbrg +bruh +brushy1 brutals bryike +bsavs bsct +bskiller10 +btc btd6king +btw Im Nakey +btway +bu bla buIberpikmin bubbakush +bubbith +buckethead12 bucketnipple buckul budabaii @@ -26789,47 +55210,108 @@ budge1 budhato buds buff +buff T O N Y +buff boobies +buff outlaw +bug salesman +bugeye freak bugsijs +buhm buldog89 +bulk up +bulpster +bumbler bumpyD +bumpywalnut8 +bunchface0 +bundun bungaku +buornos +burakaflocka burlyman +burnedpotato burnindank burnsalinas +burntfish55 +burntjar burt buryafriend bushkada +bussi eater bussmandrew +busta bunny butlesha +butt boobies +butt chug +buttercord +buttonbags buup buurman111 buurtvader2 buwatt +buying ai gf +buying girls +buying sloth +buzzin +bwaby +bwana69man bxhxdir +bxmbi little +bxther +by Juddy byEmilsson +byll byrny bzerk +c a b l a y +c bro +c greyson +c h u c kles +c hainz +c j t +c m l +c o 1 a +c onorr +c rompt +c ubic +c xx zz c c-ross93 +c00lbeer c0vid c11ntz +c3h8 seller +c95 c9hype cBold cDive cFraze cMehuu +cMoney62 +cRunX +cT Kura +ca mel caaaazaaa cache cad5112 +cadeano +caesarsantin cafedenbas +cagasaurio cage cagesitter caiomhinnn cajzbi +cakemusclez cakepillows +cala mity +calamity meg calgore10 +calisme call +call me Q callbackc4ll calliott14 +callmedaddie callmeqel callmeson callum23394 @@ -26839,148 +55321,291 @@ calvo camberland cameindamail cameliorate +camellia +camognome camrbidge +can o fish +can smoker canadian canalope cancer +cancer ruin canman794 +cannot pvp cantgetdrops cantsoloraid +canyon crab +cap byakuya cap3r +cape kraken capinkiller1 cappin captn_dabbin car oussel +carbonclock carefree +carltonking1 carlwalter +carmito999 +caronita carrotepic carrus65 +carti stan +carx casadri cash pls +cash19400 +cashcache +cashflowgwap +caspar O8 casperium +cass cassa +cassidyz +cast iron 99 +cat goose +cat the rat +cat yo quack catJAMgif catdog183 +catdog184 caterpie +catgirl41 catlice catlover68 +catmummy18 cats +cats go meow catsandogs catsarepeopl catsntatts +cattierjam cattsts causey +cave zizil5 +cawn man caykk +caza +cb0ndy cbbr +cbf maxing +ccc kyle ccc kyle BTW +cccccc +cce ccoinstar ccvtw +cdryz +ceef ceerial ceh9 +celery dog +celestial177 celliott94 certifythat +cestlez ceus +cgmirin +ch oi ch00s3n chach47 +chadduker +chadmaro chadsmurfin chaffedlips chakra +chamby +champagnedon champinjon champions champrocks chamskillz +changa nndmt changemyrng +chao keng +chaossarim2 +chaoswizzard +chaps on chargnar +charliety charlyzard charred chat chatandinan +chats 0ff +chavito_mobi +chazsti +cheapscape +cheddaphile cheekraider +cheenos cheese +cheese nuts +cheese tots cheesed cheesesmoka +cheesesnake +cheesie odst cheesy05 cheetboyx90 +cheetodust cheffffffa chemistmike chemistryphd +chenswok chent +chenzoh +cherenkov +cherrygrove chessboxing chewbacca814 chewybeaver +chi huahua chickenarise chickenbyrd +chickennug74 chicknsoup3 +chicknugets chicom +chicom shill chidavantlez chie +chie na wei +chien belge +chigas +chilli chillimayo chillscape99 chillummen +chimney15 chimonas +chinchinchin chinkbox chinorondon1 +chipndale chirspls +chisssa +chlodotexe +chnged +chobby bong +chochise +chodell13 chogey chokemepleas chokemysword choobocka5 chook1e choppers +chopsbwana +chorkin +chr0nic k0 chris chriscrossz3 chrisdude +chriskr9 chriskys christimgood christinet +chronicwax +chuI2ch +chubbybeagle chubysack +chum god +chumboyee chunkydaddy +chvi123 chxpo +ciggen3 +cigonusargas +cimmins cimsoK +cindur +cinekmiszcz cinim circa445 cityless +civo cjim +cjrr3 cjskillet cjuul clabby clac clancy272 +clangy544 +clara ravens +clardiiii claryzz class1k clayylmao cleanhell +cleann +cleanscape clevesbch2 click +click boss +clickin boss +clickrtraind +clienting cliff clifsideGang +clig fish +clini +clippng +clodoaldo23 +clogs please closeline194 cloudroamer clouds +clownViking +clownsrus2 +clownzeta +clrjones964 clubsammich cluemaster7 +cmer cmiite +cmiuehye cml852 cmm6364 coatria +coats cobrafrost cobraslyer coco +coco coconut +cocoa codga123 +cody curls +coffeechuno coffeemug91 +coin btw +cojestkuwa colafanboy +colb 45 coldumber coleeeeee colekay +colemak +collect butt collessin colon colt +coltsfan1287 +coma afk come +come in bum commandrsnow +compact cat +concert conig connie connor420sit +connorkwl +connorosrs containment +content days +continental contra145 conww cookiedunker @@ -26988,59 +55613,118 @@ cookiekill cookieman cookys cooldude2845 +cooleodotty coolgasje +coolish funt +coop ananas +coozin_killa copy166 +cor tapijt cordnog core63 corgi +corgi fan73 +corgsterr corn +corn8holio7 +cornstarchII coronel coronieverus corps corpse +corpslave18 +correctmymis cosec cosineeee cosmic cosmicphetus +coszzyy +council pops +count beck +countjupiter covid covidconvict covidsurvivo +cow abandon +cow cede +cow20 +cowpigj cowpker4life cowpoo99 +cowpuncher19 +cows +cox finisher +cox for cash coxchambers +coxfcksme +coxnass +cp5 cpenn +cpt-cujo +cr ai g +crZbY crab +crad le guy cradleguys +crag slayer +craigbr-1994 craigobaker +craigs cool craj +crajan crannerz crash +crayfish1 crazecanuck crazycow913 +crazyloLer14 +crcsofkrks +creamiboi crenux1st crep +crevebach crew-z +criddd +crimsonlily +cringis khan +crinkes cripsystrips +cris7ronaldo +crisVao +crisp damage criticism critiode cross crownchyfled +crppironman +crueship cruisingmap +crumble +crunchyfrog6 +crunchyrolll crushsquid crustymcfk cruuton crybcdry +cryme wave crypto crystalbluu crzybrady cskt csramsus +cstk1Ng ctmv +ctq ctraltdelet +ctsRasta +cu ck cub1c +cubbear cubezor99 cucked +cumstrosity cunnys +cupma cupofpeepee cups cursed_angeI @@ -27048,43 +55732,94 @@ curt cus345 cuscusrevers cute +cute lil elf +cutekitten7 cutemuppit +cutepenguin9 cutetoeluvr +cutie cat24 +cutie frog cuvae +cvbj +cvl +cwazi +cxir cyanicide cyberdunk cyberyux +cyi +cynth ia cyxxa czaroiemao czechit +d arb +d e v o u t +d io r +d urrin +d y lan d00she +d0g eat d0g +d0gz +d0nnie-d0rk0 +d0rq d0ugjudy d1sh +d216 +d2n d3mot10n d4rkk +dDillyDilly dEAd0dhz dEdjamaL dXLeamXb da jiin +da ph da6god daMteG daPeanut +dab 710 +dab face +dab marino +dabaig +dabbtheslab +dabilitation daboiz daboo420 +dabs n smoke dabtchel +dad bad +dad tree +dadcoma +daddds +daddy sharky daddyfred daddys daddywaluigi +dadfcker69 +dads bulge +dadsbelt dadsilou +dadson23 daedbent +daft plonker +dagens mand dagg3 +daggry daghostwoman +dagond0rk1 +dahhyunnee dahlinho +dahlukeh dainiux2014 daisyfletchy dallas +dallee +dalunk +dalvik +damage222 damo332 dan73 +dan_hua dance dancol00 dandy man @@ -27092,51 +55827,89 @@ danielccm danielfanacc dank dankburgers +dankin606 +danklingt0n danliciouso +danny4490 dannytjuhhh danthebankid danzing +dapipelaya daprincess21 +daredevildog +daretodair darigondeath darius_v_2 dark +dark binding dark hays dark0hawk dark5pac3r darkandrew7 +darkanimal86 darkclues +darkcyco darkgirl79 darkhex0 darkjustin54 +darklordc +darkmagicdad darkness darkstagx darkxaotik darkyChao +darranegobli +darranekarhu +darranekives +darth gainz darthbuddatv darthv102 +darvan +dascarytaco dasor012 dataenz datakrash +datalogi datboi2456 +datstonerlal datweekaz74 davadof daveb123 +david442p +davidpain dawaj dawningofwar +dawnsend4 +dawocar +day of dog dayoh +daysack daystar +daystar 0-0 +daz 11 +dballl dbau +dbopp dbow4pking dbstf94 dc22000 +dc22000_1509 dcPain dcfgs4 +dcing always +dcross9999 ddSyndrome dddvlot +ddr ddsfornubs +de Fe Dad +de em tee +de metro de00 deZoet +de_rono dead +dead tee dead2Mfarmer deadazztec deadcentred @@ -27148,69 +55921,157 @@ deadmeadow deadskiller7 deadwildyXD dear +dear sleeper +dearlola1 death +death to wdr death0183 deathax10 deathchecks deathe +deathlifiron debbie +deci m8 deciphered +declaw3d +decoy110 +ded R N G +ded pixels +dedarec +dedcell +dedfin +dee2801 +deeezey deep +deepbowlz deeptechouse deesnertz deetuu +deez ntz +defblacklawl +defender3355 defenderCX degie degraded degryser dehula deivvn +dekane deketamingo deku delboy1964 delete +delete from demaguz +demerstrand +demon ssss +den n y dennyispro densenuggett denver +denver nugs denzarr +departusa deperni Jr +derdepoging derrybarry +des9re +deserved pet +desksitter despotroast dessverre desteezdubs +detnia f +detrater mi +detzy detzy +deuce +deuzzz +dev rat devils +devilz ashes +devious man devonjt1 dewsh dexaur +dexless 4eva +dfkjgkjfdgj +dfordumm1ed dftba +dg eco +dgndfgjnmfdh dgro dgwiD dharokjonsin +dhe +dhl +di Trevi +diK +diabolicdth diamanda +dibdabber33 dibdibdibdib +dickchair +dickenbals +dickfield +dickin +didak +diddy420 +didnt rwt dids didugetrekt +dieHosen +dies at zuk +dietc0ke +difjnuee +difluenz +digigrind +digimonOtis digthat diguper dikke +dikke vogel dikorbut +dilaudid dad +dilbertt dill +dill picklez +dinklebrah +dinwy dioshka +dirt box dirty +dirty rat420 +dirtyman +dirtyydan +disc o +disco sminny disdudsauf disease disrespected distracktion diti +diu nay lomo dive divibee +dixon butts +dizma +dizzee l0l +dj en sander +dj poolboi +dj roblox +dj1111 dj_khaaaled +djawns djerfen +djfistingodx djhonnyc22 +djk1168 +djm 0813 djschaum dkb15 dkirk +dlaldus +dmersaregay dmgx dmh3464 dmmeguy @@ -27218,170 +56079,340 @@ dmmskuhled dmolishall dmorgzz dmxbutwhite +dnb demon +dnd5 +do o d +dobiz +doctorbeefus doctorkrysis doctorstig +doeby1 doesnt +dog dog baby +dog rs +dog yum dog22 dogGoesMeow +dogluvr42 dogofrivers +dogongod +doing time +dokus dom1nation97 dominicpm008 +domiuxas52 dommegekke1 +don paro +don3tsc3m donaldsocool +dong bone +dongiebong +donkeykong2 donmaners donnyr2hcim +donohuey dont +dont eatass +dont relax dontiane dontpickme24 +dontsmokemid donzy doobie8 +doogleweed dookie +dooknukem3d doom +doomcat67 doominizer +doomsiclepop doorbel doors +doot scooter +dop dopa +dopey smurfy dopeyaf +doris negra dorkSine +dormanth +dorschbag10 +doteslintrrc double99 +doublecross +doublegulps +dox +doyc15 dp23wnnabe +dpi +dps7 +dr crabman +dr evil +dr smurf dr3wst3rk dr4ll1m drag drag0n dragneel132 +drago_jr555 dragonizer11 dragtom draind drakh drakonic +dratini213 draxyboo drazil7 drdrinkwater +dreads iron +dreadthefate dreamworld +dreiph +drewsky drickvatten drillbit_10 +drink toilet dripcuck drogodon +droidfarmer +droopsnoot drotRS drouzeee drphil10203 +druedainx drug_yew +drugandbass drugs drugsrbad drunk +drunk f00l drunkwpants drwilly92 drxfluffeeee dssctn +dstortion +dstroyd dsvgs +dt duby dtfmslogan duMonster +dubba z dubdubbronco +dubies4days +duckwax +dudash dude duder +dudrid BTW dudylson +duedeman duel duets +duette duff skill duffman1992 duhpho +duked +dulitrai +dull needle +dulle00 dumb +dumbass hick +dumbweeb22 +dumple +dumpy rat +dun2kaz +duncan c +dunerat +dung_eater31 dunnman +dunt +duo virgin +durche93 durgs duriol +durkology +dusp dusq +duve melker +dvnny +dwarFcaNnOn4 dwayne +dwvb +dylaaan6 +dyldied +dylodide +dymo dynastyzero +dyrachyo +dyskletiker dyssection +dyzi dzhy +dzmomolungma +e LFa s +e acc +e c e e R +e c stasy +e ji +e med e pinke +e rui +e031420e e1ght +e50 e500 +e621net eKaleb +eL Lavish eLSD +eLas eLeeT eLqTLN +eM Be +eMiSk AU eNCH ePicnicEMan +eSquence eStimatic +eXtr3Mer btw eacy eaekk eagl393 +eagle shed +easily thero easterparty easyMEDIA +eat a udon +eat my bewty eatcox +eathelwulf eatmyfries +eatmypie +eatsdonut49 eazybreazy eazygps eazyws ebbe ebijah +eboy gamer57 ecce +echo cooks +eckla ecofine +eddimunstr +eddyb edgarsjm edible +edilot +edmu edunit +eegor +eek monkey +een pint aub eenzeven eerx +eesau eetbordleeg eetuliiniBTW +efcherry +efci effectiiveRS +effing sheet +efog efren189 egcept +eggo death +eggu eggzotic egirl qt314 +egirl tile egirl73 egyptasaurus egypton egzoff ehdion ehoov92 +eiaraieaieai +eid ola +eiflA eigh +eilhart +ein Bier +einu miegot ejobs ekcivtec +ekhoplex +ekm +el Laneo +el is +el peppo +el tubo elDiablo666 elGoblino elbo +eldanari +elden lawd elder +elder jam +elder poul elderimpking eldestlance +eldrich ball elefantsrul elfje185 elitharion +elkcarc elkcark +elletwo elli +ellias elliott1650 +ellipsism ello ellran elm43 elmeroguero elon eloquent +elsieh +eltomatero12 elyaxo email12345 emaq emaru emaw emiiru +emilviperHIM emilyqt666 emiracle +emm +emokid93 emopapi emptyhead emptysoul88 emuulzzz emvelienenko +en Au encoa endless endlessgrnd +eneco enemy2mad269 +enen energymango +eneslannn +engrave +enjoiskate enlmatek enlot enneUni enormousguy +enservices +entiesman envetoids eocri +eow btw epcr epic epickayle @@ -27390,62 +56421,123 @@ epocsorekiM eptul er2002 eraie +eric verdonc erik020 +erikj20 +erilthil +erk is cute +erlendolstad ermer +ermer fudd ernestoch ernieK erniethecat +erpponen +errikkold5 error +error nope +error occurd errorwithnam +ersatzquatch +eruze ervin +erzin +esc eseeg +esportspker espress +esssaa +estra jen +eteelS eternatus etha +ether-or +etsii naista eujamaispkei euonym +euphoria btw +eureka 4 euxy +ev tesla evanthenewb evidencez evil +evil drudge +evils ault evirgin evoL evol evonaabi evoshiva +ew its paul +ewechoose ewenn ewgility +ewmu +ewvn ex0dus ex_shadow +excalipoodle excitedz exila exile exodiass exolyte +exp waste 07 extesyturtle extinct extra +extra big pp extrastark extratrippy +eyes fire2 +eygs +eyniss ezAce +ezzychu ezzymc +f a r r r +f ainted +f c gee +f d b +f ern +f q +f r o st y +f reS +f0r 20 f0rbes f0rev3r +f0rzca f1elder +f1ll2 +f1oh f1sh1ngcrazy f2pkiller420 f3n1kz +f41r unknown +f4az +f4fight +f8 fEmshi fJack fa1lure fa2kil +fa43ws5gdrxn fabsku +faceeee +facehunt fadc2 failbloug +fairies ring +faiyaz911 +faka jackson fake +fakeironman +falcon30040 fallen fallenbouse fallengodxx +falneek falsehope familypp22 famousbutnot @@ -27453,61 +56545,123 @@ famousdavies famwell fan3to fancybunny84 +fanof +fanook kill +fansh +fapital one +fapyqt +farmerman111 +fart taker farth4lyf +fartypants64 farva5001 fastfoodguy fastsalmon34 +fat bish0p +fat miso +fat n ugly fatalzgs fatalzick92 +fatblimp +fatcactus99 fatdabz fatdemon +fatfish44 fatiimma fatpapi fatrat095 fattig fatty +fatty_ironmn fattymcthick fawaka fawkin fawnt +fayeuk +fayfey fazIlioilol fbah2 fcemee +fdegierr fdsd +fe ar none +fe fi fo ben +fe fleton +fe hessu +fe shredder +fe unclefro +fe-ma-le +feaker fearma +fearnooaths +fearsome +fech +fed the rat +fedposting +feed water +feet r neat +feet shui +feet tickle fegorus feinkostbob fellman250 felsic +femaledhide +femboy fever femiketyson +femra fems +femtanyl fenimore133 fenty +ferbies +ferderb fergina ferkyy fernezi fernibosh +ferric thane +ferrotopamin +ferrousnub fettnerd +ffa btw +ffakin roids +ffs cmon ffun15 +fghtmeirl +fhb ruby fhisher9 +fi1LMma8Ke7R fiddy +fieryflame55 fifiz +figey figgiolly +fighter8888 fighttme fighu figit fiiks +fiishcat +fijurgt fiko +fikset fill2 +filma kraken filnrozdor +filthiefrank +fin hupu final dip finalkid finally +finance king +findmydog4 finerunes7 finfinnegan fingerd finn fire +fire skull99 firebrotha2 firebwan fireflyman3 @@ -27515,67 +56669,145 @@ firemadara firewood149 firework19 first +fish cook +fish slut34 +fishbomb83 +fisher ilhan +fishfiinger fishnforfun fist +fist n twist fistaah fistofzeuz +fitjamal +fixedmanose +fixx ur face fizoo +fjuc +fk bud +fkMushrooms +fkWiiz fkn seale fl0ppy +fl0rals fl0xen flabb flaccid +flacidzillaa flaggeding flaminstu flapdrol2000 flatbroke +flatjack78 flatpancakes +flawskee flax somker +flenis +fleqk +flexmaniac flexxygreen +fligh4 +flimzzy +flink1145 flint +flip555 flipdoggydog flipouu +flipperkid69 +flke +floch floie floopily +florda v2 +florida1992 flow +flow states flowerbunz +flowers +flowerworks +flowing past +flpi fluffy +flunkerr +flwc flyingfather flyingpigs +flymouse +flywoodhead fmPalm fo77y +fo_of +focusor foldoutchair +folklore +fondledeez fonytergus0n food +foom +foookz +foot fe tish footbag +footgobble9 +forbiddengol forcestealer forevercc +forfun_ED +forgeleader4 forkheals formazion +formerly act fornitesweat forrestriley forss1 +fortnitecuum forty7 fortyfour44 fortyfours fotjonxd +found RS2019 fourecks +fowrt +foxMcCl0udd +foxair foxdude909 +foxi melissa +foxmr2 foxological +foxspirit foxxit +fp fp4bank +fpsbowser +fr0stys fr0zen46 +fractalion +frail +franklinzule frantam +frawzti +fredz1 free +free rations +free willy1 freeheadbutt freekek freekface99 freepknub337 freeze4peeps +fremenik +freshaf +freshdougie fressi +friccSailing +friday x +friedz friendlyduck +frierenfan69 +frodobaggens +frog37 frogggggggg7 frogmeme420x +frogposting froock frootl00p233 froppy @@ -27583,147 +56815,269 @@ frosteazz frostfire089 frostkatss frothsy +froxey frozen +frqke fruitdeeps fryguy20 frzen +fskencha219 +fsnack fsteve fsxd ftSlimShady +ftmg fubar fudkingname +fuhrari fukduelarena fuktig +full service +fullrune383 +fun e name +fund issues +funk +funk it funky +furi xD +fusarium5 +future days +fuwamoco fan fuzzlumpkin fuzzydenuts +fuzzypapi +fuzzypupz +fwapdokopjai fwft09 +fwip fxck fxck versace +fxdb fyfaen +fymcgee +fyr irony fyromaniac +g arre g o o w +g u L z g00dz +g0_ober g0d slayer19 g0dofgames +g0dz target g0ld +g0rgelzak g1g1tyg0o0 g1n00tj3 +g1o +g4rug gIassy gOWObs +ga zr gaaaaleth gabba-jabba gabbe314 gadnukB0W gaffel +gagifidi gaht +gaht dangit +gainsaid gainz +gainz no gainztrain9 +gallidogs galx37 game +gamedbroski gamelsbad gamer gamer63738 gamerb0y +games sheet gamesalright +gamin hard gang +gang gastino ganger34u +gangstersfly ganjah lord ganoze +gapin ass gaping +gargalonmyd +garn woolies +garra +garymunzen +gas watah gasang +gatekeepr gatlin +gatorbait61 gatorclue +gaumcat14 gavin +gay avenger +gay corn +gb +gd5 ge1uk gecs gege4646 +gegebee geitz4 general_a3 generalneos genghys +geniuswinner gentle geof +georgemonger gerdlol gestrikt +get pregnant +getcance +geten30 getpixelz getsnipez getting geusjj +geve en neme gewoonhendri gezzle +gfink +gg Goombas gggochu ggremlin ggroeffus +ggrr ggwpezgame +gherkins +ghliz ghost +ghost of CVS +ghostlyjon ghostmahi +ghrimdrakan +ghxstboy gianni025 +giannnnni giant giantsbane10 +gibsons dog +giffnamepls giga +giga zooted +gigglybear gijsro gilgir +gim Bam +gim ajuin +gim so bad +gimeurgpm8 +gimhunter02 +gimmey bear +gimpeta gimpinator14 +gimtmbj gingasnapz +gingerr ginjembre gino2315 gintoki +gioh girl girlbossed girthing +git gud kekw give +give in +give me 1 gp giveortake givmemonyplz giza gizmywizz +gkfdhsjbgpas +gl barnie +gl gf i win +gl lmfao gl0s2n gladwin97 glazura glgl glica glingster +glitch1128 +glooboowhoo glotis420 glowmastree glumburger +gmac33 gmtn +gnak +gnariska gnarrrkilll +gnobo +gnome pegger goGETTER87 +goal freak goat goated goatflocker goblinsyler +gochugaru +godblaster69 godfreeB godmiljaar godmysavior godofvoid +godsdankweed godss godver +going2maxnow +gokhanakan goku9172 +gokublk409 golaguard +goldenN0B0DY +goldest cat2 goldiepawper gomikasu +gon dor gonarusinat +gone numb +gonewild gonsoLE +gonz o good +good duck 4 +goodboi525 goodlife +goonette goontuna gooootsby +goos fah bah goose goosegoose22 gor7 goranfr +goreshitfan +gorgewkush goroka +got u m8 +goth mime gothic gothorian gotmadtree gotoschool gottaRash gotzeeben +goy street gp294 gpawshaft02 gpf93 gr0ve +grace life graftosh gramss gran chorizo @@ -27731,310 +57085,648 @@ grandp4 grandpa grandpashome grandslamhit +graphist II grashuis +grass hopper gratis gravetheif +gray ass +greasy bacon greasyguy19 great +great bazza +great blazer +great keith +great pkay greeaf green greenbuds1 greenfleet1 greenie14 greenm4444 +greeny4 +greenzyyy greg +greg btw grey +grilecheese +grillmagnum grindin2bond grinschen grizzy77 grlobe +grogybog12 gronoc +groogns +grossedinde6 gruelsipper grumpyy +gt i gtrpower3 +guardy gucci guirkle gularies gulibleidiot +gullefjun2 +gulp my knob +gumlord54 +gunbladelh gungaman3 +gunnmoses gunnybear gunzip +gurz guttsberserk +guuvin +guy123452345 +guyfranklin guyhasaname +guyladriel +guzzlinbrews gvfsa +gwaph gwaphics +gwapo gwapo +gwb +gwen-artemis +gwiffdakid +gwogoudain +gwug gypzys +gyxl +h ertog +h re +h ugo h00cares h0rseZ +h2j h3hz +h4h4h h4ndshake +h8browns +h8dip +h9g hEAdGLiTcH hOsujaRS +haaksrikko +haam habitus hacchi hackening hadibaam hagerlund haha +haha lul +haha nice xd hahaLMAOlol hahaa +hahaa-ukko hahaha +hahaha LOL +haiiG hairahcaz hairy hairyraccoon hairytaent haistavittu9 +hakuikoyori +hakumaata halal +haldfhaklfa2 half halfcrimp +halfrightfox +halftimegod halling1 halo +halo 238 +halo is scum +haloumbs hamdrip hamers +hamhand hamilton +hamilton fan hammade hammershark hammu666 hamptonboys hamthenoob hamzullah +han sohee hanakarjala +handi 0_0 handi9900 +hangry_bird hanh +happi hippo +happyprophet +har tig hard3x hardcoresque +hardstuck gp +hareby harelmatlaw1 +haringbata hark harleybegum +harmony +harmony w harrpp harryderoest harts0295 harziol +hasansahl hasbulla hash hassbulla +hat foe +haterz havfherter +haw yee hawkesbay +hayati enta +hayden787 +hazel nut +hc pilszen hc_karadeniz hcgoldslaye +hcim gud btw hclirongang hcmemekiller hdhd +he a 1O but +he box jonge +he r b +he4 +heGee hear +heartvessels +heast oida +heatassnugs +heatwave305 +heavygim +heavyiron94 +hebrewmytea heca +heckn frick hedonismbot +hee haw 255 heegeer heel heelgoed heeming heeyhallozeg +heftZEUS hehe +hehe heheh hehehehe hei954 +heimy22 +heizenberg11 helgrimm +helicopta12 hellobob488 hellofpures hellohello +hellohelloom +hellper hellreaper25 help +helpa +helpusobi1 +hembree +hemmingsson +hemmy +hemran +hemstad henkka89 henkleingeld +hennnin henson23 henzer heppytako +her mager herb +herb btw +herb l0rd +herb l0rd jr +herbdean0981 herbi herbibear here herezacsko herkimer heros +herrasmies68 +hesedona +hesi pullup hestakuken heuj speulen hexergeralt +hexxers +hexzie +hey axel hey im ben +hey its zul +hey_imgrump +hey_rayray heywoodya +hh r hhdrgu +hi im miku +hidde xddd hiddeboven +hidesonkush hidlandboys high +high bonsai +highIMcamila highelolux +higher man +highfly94 highprice +highright highsassy hightoo99 highventure +hih hiiggiinss_5 hiirihaukka hikipastori +himokarpaasi himself99 +hiphopdied hipocracy +hippofart27 hippyjump3 hippytrippin hirai +hirai momo his7 +hit trees hitchclimber +hitsumabushi hitto hitz hiunknown +hiya jase +hizli maymun +hlen +hlua hlucky hm08 +hn k +hnge +hnsky hoangbach456 +hobbit head hobosloveme +hodl link hoffnungslos +hol my got holdadoor holden21 holdenrulz6 +holidayRus +hollowking holonomy holy +holy chad +holyfizz holypalo +home2get homestank homie jaquan +hompkerbenis honda honeywheat honourpig48 +honscy hooah89D hooliganism +hoonyboony hoosier4life +hootis4 hopOFFbish +hopeakettu25 hopeless0ne horkkaaja18 horror horsedik7 +hos hoswoo +hot otter hotdogmum99 hotpants +hotyogapants +how 2 use ge +howliett +hows it garn howyadurrr +howyodo hrby +hrsn +hs +hs3 huang9296 hub154 +huealldaym8 huge +huge cog +huge man big +huge ovaries hugh +hugh_who +hugheberto hughjazznut hugjam02 +hulikopteeri hulikopteri hulken532 hulrikon94 +human6000 humble +humjiboi +hummerspeck humpmedumpty hunden +hungrypit hungwonglow hunter +hunter01132 +hunter123216 +huntin_pets +huor4hansu +huoranpenska hurt +hurtswhenip +husbandowo hush huskyheaven +hut dugs huutonaurua hwow +hxdd +hxzy +hyasf +hycoda hydra hydraboss hymke hypez hyphynx +hypofelix10 hyprmax hyung +hyvintoimii hzpascal i 0nly skill +i Bmx +i C D +i Kelly +i Kyle +i Shin Chan +i Spartan i Stugbert i +i URNn +i Val +i am hua ren +i am iron xd +i am nickle +i am rocky +i am soo dry +i bets i +i ch i +i dandy +i dcd 4 sets +i disabled +i done +i eat noods +i forte +i gag +i gor +i got dust +i grug +i hit 420z +i i i i +i john 96 +i look great +i lose gp +i need top +i need you +i ok can win +i poop alone i praise mvp +i preferhead i r muffin +i r4piid i +i ronman +i vexterr i-i-istutter i0 strength i0am0the0one +i126 i2eally i2legit i2me +i3nd Legends i420u +i50 +i8 2 l8 +i9 iAIex +iAM Pleb iAUSSIE iAdriaan iAlch +iAlchedNieve +iAmNotSharky +iAmaWiseAss +iAmen +iAttributes +iB Lifted +iBal iBallr +iBeDorky iBeanie93 +iBeast It Up iBeatNavy +iBeleti +iBelg +iBlack Zeus iBlake +iBloodsicle iBlurks iBosstastic iBoughtpizza +iBrandon iBukowski iBunZoot +iBurkie +iBurn Dro iBustedaNUT +iCakoRS iCarna iCbarr93 iCh0sen +iChangeling iCheezedOff iCheze +iChugBleach +iCiga +iClickYellow iCmurda +iCoconut +iCrank a lot iCrash +iCreeme iCyantist +iD Dazza +iDH +iDabbedOut iDan iDanny13oy +iDark Jesus iDashio iDashwood iDeathlok iDerpo +iDez +iDo RageQuit iDoNot iDontWinSad iDrankCOFFEE iDrebin +iDrexler +iDrops +iDuiveltje +iElfy +iElysian iEmery iEpa iEprod iEuph iEuroVamp iEvergarden +iFailAgainnn iFatalFoei iFerrumMan +iFeud +iFightClouds +iFightGiants iFire +iFoRGe +iFrads +iFradz +iFrogs +iFry +iFucter iFukFatChix +iFurb +iGab +iGarns +iGlaceon +iGlitched iGlowGreen iGohan iGotDds +iGotDibs iGotem iGreig +iGuDMaFF iGuessUrNew +iHazelnut iHenrie iHenry +iHiyori iHockey +iHolydude +iHuntie iHydroxity +iHydroxityv2 +iIimesiahiIi +iJ0HN +iJ4CK +iJCLEE X iJake iJamPancake iJesslag +iJezuss iJfr iJohn +iJomm iJwu iKHole iKILLN +iKantu iKarl1s iKerry iKitch iKitz +iKonijn iKoning +iKozak iKristov iKurko iKushUp +iLEFTmyGF4RS +iLL Sushi iLeftHer4XP +iLegacyX20 +iLetaker +iLeveled +iLikeTacos +iLikeTheStok iLikeuAlatte iLoner iLove iLoveYou3OOO iLuckout +iLuv Pandas iLuvMyMilf +iLuvSubs +iM3RKEDu iMADbro iMahjarrat iManiac +iMark 1 +iMarkl iMartin11 iMatey +iMeeger iMellek iMelli iMementoMori iMeo iMichaelN +iMikail +iMikey +iMogUCope +iMotown +iMushh +iMuste +iMx +iN3K0 +iNamaste iNeedBeaver iNeverMor3 +iNewbcake +iNewton +iNibble Bats +iNz +iOPeveryday +iOhmz +iOsmumten iOwl iOwnU4ss +iP1 iPIMP iPKedEpstein +iPacmanSam iPaki +iPalumor iPatrickN iPegAsians iPerfectoX @@ -28044,6 +57736,7 @@ iPink iPlugg iPod iPop +iPray_Guthix iPresident iPunchCones iPure @@ -28056,9 +57749,14 @@ iQuittedCiao iRNGizzed iRekt iRepToronto +iReue +iRev Man iRezlo +iRoN cUj0 iRoNmAnSdUmB +iRonAllot iRunTh3World +iRunzo iRycoda iSaran iScape @@ -28068,115 +57766,255 @@ iScream03 iScrewedUp iShibby iSkane +iSkript +iSlay Girls iSlayCookies iSlayGoat +iSlenderMann +iSmashUrGF iSniffCows +iSnowmobile iSoulReap +iSpare iStayLowKey iStealSex iSteele +iStick2Corp +iStonee iStream +iStreetfight +iSuhdle +iSwarly +iSweat +iSweatyYeti iT0m +iTan +iTaz +iTed2cold +iTheDarkKing +iTheodore iThiellie iTisk iTmetzo +iToxic +iTrin iTunder +iTw1sted iTyler +iTz Command +iTz Frosty +iTzBoo420 iTzGoinInDry iTzKATalyzt iTzSnypah +iTzTurtlexD iTzTyLeRXD +iTzWho iTzZ +iTzxLiMiiTz iTzzVanquish iUberz +iUsed2Corp +iUub +iWilkel +iWillz +iWithdrawal +iWonderWhy iWyatt iYahwehi +iYellowClick iYunis +iZ4 iZorru +iZuny +i_murder_bh iafx iain19 +iamAFK iamCanadian iamMcLovin iamfish +iamfried247 +iamfuss0100 iamgreatrng iamlucas19 iamonfiyaaaa iamperkins +iamvery cool iamvoldemort iamzed +iandrehehexD iateyourpie ibbenbueren +ibn Mukhtar +iboomedyou +iboyfly ibr4him iburythebone +ibushmani ic3d +ic3d c0ffee icannotboss +icanread321 icantspelle +ice beam +ice juice +ice-nine9 +icearrow28 icebolt19987 iceburg189 icecoldbreez iced +iced marbles iceman000 icewall0w +ichau icjbi +icky sticky icutL0g5 +icy ded ppl icyT icydilldos +icypolarbear +id c +idc idcy +idfap +ididnthither idiedonmyhc +idiom idiot +idiot game +idiothead +idk chill idkgoogleit +idkh0wtoplay +idkwhatdo idle +idle1 idpoen idrater +iduggz10 +iekaajer iekgaa +ienai +ifantomas6 +ifarted42000 ifootfondle +ight igiddeni +iglo16 igotdaice igotnolifee igotnoolife +igotwingz igotyour99s +igrimmerz +igugh ih8urkind ihan ihaveanxiety ihavitunopee +iheartBBLs +ihopeurope +ii Sahir ii +ii am teaser +ii bad guy +ii forte ii +ii love cats +iiBink iiDivine iiDrackidii iiGallardoo iiHydra +iiLuckyVibes +iiTz Nothing +iiZEEii +iiZno +ii_TrYHaRd iiaannaa +iiiRood +iijelloo +iikasoyi +iimperious +iinsin iioktb iirc +iitsjarod +iitz deebz +iitzz Ryan +iivy11 iizoneout +ijoinedthis ijust ijustgotreal +ijzer ali +ijzer vreter ijzeren ijzerenman1 +ik itachi ik ikea ikeep1r0lled ikil ikillstuff +ikiroz ikoyouboy +ikp0wnsz +ikpakjou ikrr iksalion iksd iksdeee +il malocchio ila161 ilapaR ileax ileft ilegalTruble ileppmot +ilfi ilike2gamble ililliiilli +iljang +ill Dottore +ill-Money illadelph illenials +illisible +illmaister +illmatic +illmindhop5 illotex illumea illuminavi illyreia +illysia +ilookcat +ilovecitywok +iloveinsulin ilswisa +iluvducks +ily stepmom ilyakchuk +im 1v9 +im Jaacob +im Noah +im Z a n e +im a learner +im a pur8 +im at cox +im bad tbh +im batu kham +im cats +im dandy +im huge +im in charge +im nlf +im pogging +im ruben im solo rose +im tr4vis +im ttZ im4everxerox imAcidic imAhri @@ -28187,123 +58025,302 @@ imJT imNEWY imPapu imStef +imWebby +im_pro_jones +ima big boy +ima survivor imafatscaper +imago loop imaybegod imbaconyum +imbueddog212 imbutz imbuyingf +imcahos imcaprise +imdrunkashit imdud +imfi imflavio imho imhuge iminarager +imjeremy +immachin +immaturely immef +immirgant +immmersion immortalz +imnotdani impaired impartial +impek +imteddybear +imthenalls +imtogepi +imusualyhigh +imzahikell +in africa +inSec inTheSlum +inb4elysian +inc incant +incci +inclibe includingtea +ind +indebara +indian inescate +inf nick infamousO775 +infamousjew +infare +infernalwhen +infessian infragant +initiator iniu inject +injinourme inked inldgwetrust innSink +inner child insaned17 +instagation +intja intwystis +invictecum +invirtua +invisybl +invynsable inwervs inwnucleus inxx +ioe iojerfwpjiof ioliteKnight iolm +ioncolliderz ionlypickadc +ionman iorcsrox +iownw69 +ipfreely939 +ipwnkthxbai ir0nschlong +irak93 +ireq +irhunter +iriee NZ iron +iron Dreven +iron Fapkin +iron Nido +iron bewts +iron carly65 +iron chur g +iron colb iron cuzibro +iron d +iron dafanek +iron dumle +iron evoo +iron gariks +iron grogu +iron hobbs +iron i jad i +iron imp 89 +iron kobe +iron kydrol +iron mazie +iron nakno +iron nkellen +iron sfgiant +iron snak3s +iron snurd +iron t2 +iron te_un +iron to main +iron tylerr1 +iron veil902 +iron veyydot +iron vow iron xarcher iron0 +iron0_o ironBellukka iron_day54 ironandwhine +ironarrow ironbandiit ironbass +ironclimber1 irond ironderp +irondude686 ironerextion ironerrosann ironeye +ironfara +ironfather8 +ironfyi irongaga +ironguxiz +ironhuuu +ironic it is ironikcronik ironinfinite ironjssnoob ironjustice +ironjutku ironkendall +ironkevkev ironlizrdfuk +ironlog215 ironlooks +ironly fans ironmortel85 +ironoujust ironpower29 ironpuni ironskunkki ironspig irontsuki +ironvesku ironwortel ironwoss irradiated +irregularly +iruste +is an option is skimpin isCatrileo +isayswegyolo +isleepwith3s islugo +isoPROpain istealyoloot isuwu +it do be +it ends now it0b it0keup itachi9113 itbch +itirof +itisdre +itmeautim +itroy v2 +its Bread +its DeFib +its Hoffman +its Knetter +its Mewtwo +its Polle +its a ginger +its aight +its an 8th +its leviosaa +its not mine +its the game +its20after4 +itsAden +itsErnie itsGLD itsJayy itsNoki +itsSt1cky itsWum +itsa itsa_feature itsaart +itsalic +itsbenny itsbill +itsdanlol +itself +itsfwhobar itsmewarreng +itspajamaday itsthedirtyJ +itwasme ityttmom +itz Ozie +itz Tastey itzjaycie +iua iusedtosleep ivao ivibondivi ivlr +iw iwearIRONirl iwinulosety +ix7 +ixGOONIExi ixJake ixoniron iyyghg izmibence j e company +j o h n z +j o l s +j sta1in +j uddr +j wet j0rd +j0rdb98 j0shNZ +j0shwah +j1nx +j311yb311y97 +j3ffmaill3 +j3rvi5 +jDaledge +j_mes jaakqoo +jaatzy +jackBingus jackLonghorn jackancoke7 jackjester +jacksonlol +jacola +jae Bee jaguarundi +jagweed +jagweedle jahan +jahmelo jahnjo jahrezeiten +jaitt075 jajademonrat +jajajad jake92 +jake9549 +jakeyvil jakkaru +jakste r +jaksterlite +jakuusa24 +jallon jalucox jamaz8 james1234269 james68889 +james9f +jamesishere +jamesw jamflex +jamgyo +jamie g 456 +jamjarrrs +jamm janguy jani jankywanky @@ -28312,50 +58329,104 @@ jaq0 jargonship jarjoevis jarngrimre +jarno561 jarnrisi +jarnwoodchck +jarsko jaseDemon +jaseGrumpy +jauu +jaxta +jay 6ird +jay oh kay jayst1n +jayveedee jaza +jaza maxwell +jbirrd +jd345 +jdockett jdore jdsgsxrthou jeREEEEEmy jedi +jeeeeee jeep jeezuschrxst jef skhiii jeff jeffswifty jegerklog +jehhjr jekkujaba +jellyfish49 +jellyjam20 +jem is cool jerome +jerrys ags +jerrys venny jerzieboy18 +jesjesjo +jesus bone +jesus vape jesus4gives jesusiswrong +jesust +jetalexnder +jettyrr +jewellski jewjoking jfan +jfc +jfrank127 +jfy +jfyulopaef jheezuz +jhinnessy jhonlee48 +jhoog +ji mbo jibmask +jicc +jigx +jihal2020 +jikrak jiksee +jim jones it jimbob1 jimbobeda jimdeut06 jimmy +jinjji jippey +jjammerweer +jjerrbo +jjingx jjpanther95 +jkfizzy jkre jkthekilla jman +jmjmjt01 jmusilli11 jmw1031 +jndi +jnjo +jnlffs +jnyjny jobljobl123 jochieboy6 jodilynne +jodo wollos +joe the pop4 +joepie +joestrider2 joey0343 joeyisstoned johhny2hatz johndalton95 johnnyandtam +johnsmain johnstaLoL joje9 joji @@ -28363,7 +58434,10 @@ jojojo357 jokaranpampu jokemyster jokerpoker +jokkefar jokr +jolle rmm +jon ko jonehehe jones jonk33n @@ -28371,56 +58445,106 @@ jonko jonnetuinen1 jonnywormy99 jonppeli +jonwillbert +jonyants jonzii90 joohhhnnn joop59 jordaaaan jordlar jords +jordy8878 jordymans jorfee +jorge w +jorma heat jortdebonobo +josheroni joshuab98 joshy872 +joshykun uwu jossejoks123 +jossiee9 joster jouv +jovic joygi +jozn +jp s +jpjpj +jpmonkeyboy +jpruee jqzz jre257 +jrwheelyy +jslashk +jsoaksdawg jstrot jta1992 ju1ce +juaco torta juan09gon jubbediah juggernaught +jugiwow juice juicy jones juicyj03 +juicyjoystic juju +juke +juketsauli13 juks +julia uwu jullbrew +jumpe jumpshot7 +junami14 jungle jupi234 +jupilersjuk jupl234 jusiuke just +just living just2own justJust justLush +just_willie justamemerr +justaname20 +justapethunt +justarabbit +justin5454 justinttu +justluxy +justsomenoob justsomewood justwin4head +justwitching jutku22 +jutmeister juwgrehg8w30 +jwalty jwheaties900 +jwles jwov +jxdoo +jye2014 jynzziii jyripetteri jyyhnas +jzhua jzm0 +k a p s +k anao +k e y e r +k ep +k ian +k oga +k u h +k um man +k-rock94 k0ed y0oh k0mpact k0nch @@ -28428,73 +58552,145 @@ k0nna k0rrup7i0n k1dnam3dcud1 k1lla +k1lla cam +k1ngzinho k3yblade +k6k3 k80may k9k9k9k9k9k +kFlipsta kMikey kUwUmiko +ka r el +ka vi +ka wa +kaaf kaali +kaalwo +kaaskrok3t +kab000m123 kabal1995 +kacy +kaffetime kahleparta kahvi kahzaohimark +kaii tangata +kaimann kaiokenz +kaizen1 +kajyboyyy +kak_kis kakes kako666 +kako666 ii kaksaking +kalacaodan kalev +kalijaveikko kaljunaama kaloopsia +kalusto123 +kalxb kamekazi +kamerucito kamika kammoooooon +kanchazi +kandids +kanga roe kangaroo kannv +kanyefan22 kapot +kappachino +kappaross420 +karambamb karelzzzz karibola karil +karilol +karma dies kaspery0 kasperyo1 kasrug +kassi kastekann007 +katelate +katiegore katinnekke +kavachi kavinskie +kawasakki kawi kawkky +kawnoz kaynori kayxiv kbest777 kbuns +kc3 +kc3477 kcaaJ kcmeatlover +kdani +kdawg710 +kdog420 kdw27 +ke-bun keatonboss keegan789 keekluulz keep +keep going33 +keepitdedpls keesie7 keezuth kehd keilaaa +keiniks keittokinkku keittoo +keiwa kejo kekipua +kekw9001 kela +kela fan kelan +kelan miehii +kellapea +kemabi kembot +kemosabi kempster8 +kenchuuuu +kendawwwg kennynoodles +kenya help kepijuku keppuli +kepwontmax +kerekewere +kerm jump kerwin +keskorian567 +ket boof +ketaccino ketawuss ketchupfles kevin +kevinator41 +kevinnnggg kevpurp +kevvaGG +keyggre +kferd +khaki cuffs khxn +kiba420 kibeleza +kibsy kica7 kickback kiddoseta @@ -28502,98 +58698,166 @@ kids kidsteve18 kidz kiefcheef +kiiiiiiiiid kiix +kil lu a kill kill3r killah +killakristy +killanoob93 killconey84 +killcrab killd0zer666 killedual0t killerline +killnack killthatree +killwi5e killy kilpi +kimachevich kimbo kimpsa +kinako4 kind +kinda soft kindaanxious kindocool king +king lee166 +king stuff1 +king-tiddus +king420smokn kingblackops kingcuzh +kingdaddyIM kingdale26 kingerr +kinghrvatska kingknightf3 kingkong1211 kingofmanse +kingprince kingrizzla2 kingsardine9 kingsje kingswood kingvoid1083 +kinky fish kinobi kinzyyy kipitril kirby421blzt kire7693 +kireeh kiri2kun kirkaye +kirky 127 kisipicka kisses kissmyassetz kitashan +kitkats GIM kittymeow556 +kivipaska kiwidson kiwie +kiwiibear +kjekken kjellingeee kjeltringen kjems +kk lol kkDV kkangaroo +kkarl kkjamin +kkman45 kl00g +klaaore klacen klampo +klankerclean +klassiskt klef +kleshkebem kleu klexosfox +kleyver22 +kliefhead kll4mee9 kloh510 klojo103 klojo105 klopt +kluizen klycko +kms fast lol +kn0ws beers knani +kneel2me kneeslapperr +knickname +knifemanha knight +knightnate8 knobb knobby knokro +knotts knox kodakid98 +kofelad +kojak1211 kokimon kokinaattori kolb komari_k +konar simp +konb konch99 +koning broer +kontiainen +kony +koo de gras +kooks only +kooltrickz +koronaaaa kosiq koudekroket kounga koyhalaulaa kozel +kozina +kozzuu +kpt +krZ +krab meat krad +krakenwsn +krakim krakkakkak krakodile krat +krazyfaken +krb 16 +kreepingdeth kreeq +kreesie +krewl krikenator krillzscape krioyo kripu krisaaferfi +krnjellytv +krobi kronicganj kronopes kroodjebaass +krp +kruizin kruuuuben krxW kryptocookie @@ -28604,157 +58868,344 @@ ksubbi ktsmo kudryavtseva kugelencas +kujan2 kukerino +kukko soosi kukrishikari kulers +kulers bsk kulju kulso kulwreck +kumiko okada +kurasaki21 kurdikana kuro 275 +kuromi irl +kursa sucks kurtebener kush +kush tacos kushmas +kusib kusimursu24 kuvaiti +kuwait333 kveemanne kwak +kwak eend +kwall +kwarkje kwdn kwinus +kwuantadyme +kxde +kxlle +kxzu kyaa +kyanochaites kybl kyfu +kyle of pvm kylehwog kyles kylop kyssysmeua kyurem_vrah kyykanhenki +l Am Mathew +l Athena l +l BTW Envy l +l Buzzsaw l +l Click l +l Darken l +l EARN MAN +l Elija +l Fam l +l Feather l +l Floss Fish +l Flynn +l Freya le +l Hav Ligma +l Kelpie l +l Killua l +l Lena +l Love You +l Macca l +l Misfit l +l Mister l +l Nz l +l RSK l +l RWT l +l Snus l +l Steph l +l Succubus l +l Sw0rdm3n l +l Viz l +l am RON +l am cute +l cup +l failex +l lemon +l u g i a +l unchbo x +l-LEVY-l +l-Relinquish l-l-L_l-lL_l l0H3aV3nLy0l l0ne0ne l0rellai +l21 +l23 +l33tsuperh4x l3LACK l3W0FPI +l3en l3enjie +l3ezzerk l3ishop +l4n +l5h l7ivine l8tenite l995 +lA R R O Wl +lAlexAnderl lAnomaIy lBaecob +lBakin lBrent lCEBERGSLIM +lCONlC +lD an +lDFC lDalel lDanilo lDark +lDark ice lDerby lDezzy lDokkum +lE N VY lGuess lHawke +lHong Kongl +lI pv M l +lIHlIIlHIllI lIIIlIlIIII +lIIlllllIlll lJWl lJoynerl +lKanel +lKeith +lKenna lKenny +lKlas +lKyle +lLY Harambe lLewis +lLupo +lM Nathan lMarkl +lMercyl +lMr Zaros +lNSF +lNTOXlCATlNG lNerdRage lNolan +lPaleHorsel +lParticle lPeriphery +lPoe +lR0NM4N lREKEEN lReece +lRossy lRyan +lSaiyanl +lSellFeetPix +lSkinny +lSlayer +lTS ON SIGHT lTedl lTextbookl lTlTITlTlTIT +lVIaGiK +lVictorl lVitor lVlango lYungPlaguel l_Thunder_l +l_wyverns_l +la flare +lacefield07 lactosebad +ladsquiron lady-yoshi78 ladyspartin +lafreniere13 lagamuffin +laggium lakde99 +lakey peak +lakeypooh laks +lala u death lamJason +lambar lambreturns lame lamepun3than +lamironben lamshnarf +lamyourdeath +lanch +lanthe +lapyflapy laqsative lardosio large +large moist +large nob large pox +largewillow9 larinen larl larsbeuk99 larsnjun larvacorium lash209 +lasseboi last +last katana lastdcplz lasyy +later gater +latinamx +laughingbisc laura +laura froggy lawkingkong +lawo1 +laxasia lazarus +lazy aiz +lazydj2 lazyhitman lazzabazza11 +lcefiend lckle +ldy +le iromax leafsevens +leak organs +learncox123 +least toxic leather74 +lebanon d0n lebusoft +ledy0710 leech +leech iron +leech keeper leechyGP +leehi +leesha locks leetjojo leevi22 leewhiffy leewi leggie +legioen +legionbgbear leglizeRanch +legn-dary legohuis99 leipo leivonnainen lejhobs +lejhonni +lekkergras +leld +leliwuz1837 lellikedraak lemeborrowgp +lemm y lemon +lemon 25 +lemon cookie lenda +lengf +leo vzla +leos main +leoshnoire lepetitpois +lepicklenick lepo +lerpledore +lesel +letos111 lettam +lettername level-103 +level-125 level-596 levendi levon lewcifer8118 +lewdogg lexi +lexi bell3 lexicon7 lguana +lhommerun liamde +libad5343 libertador liberteeeee +licdik21 lick +lick my chad +life is shit lifesalaugh lifter light +light year lightfader96 lightningess ligma ligma2 ligmadich lihaamm +lihavadino69 +liisa liisankissa +lijkenpikker +like cooking +lil Mamacita +lil b +lil bumpp +lil chris 15 +lil ducky69 +lil gay cat +lil jev +lil mage013 +lil zeze +lil zuck lilBula +lilJacklil lilNoob2341 lilbitlifted lilchiken +lilcjay11 lilcuff lilgreasy +lilkcough +lill misfit lillelar92 lilliilillil lilmasterOG +lilmuscles lilseveron lilsquiddy liltunechi @@ -28763,195 +59214,385 @@ lima limona5 lindyman line +linebacker linkhg100 linlithgow lintydeer linusforsman lionking-PVM +lip length +liquidchese +lirska lisher100 +listentonano +litger litheum +litranmaito +littering an +little boat +little man 9 littlebumble +littleman699 littlemc14 liubei4444 livdumb livil liz_mar70 ljjjt +ljzerenmes lkalgo lkaoz +lkeaurhteakl lkigai lkjhsdfglkjh +lkkle +ll Grey ll +ll Zeke ll +ll jacob ll +llBear llIIIlIIIlII +llIIllIIll llKaren +llKris +llNephilim +llRevenantll llStevell llab +llaurune llerb +lletya sugma lleygo llhan +lliigmanuts +llitl +lllIlIlIll lllPeppalll lllnifflll lllogical +lllustrious +llo yd +lloytronn lluH +lm Drunk69 +lm H o T +lm Jard +lm Scoob +lm Stark +lm Washed +lm batman +lm scrub lm2fas4u lmAddicted lmBarryAllen +lmTrash lmao +lmaonation +lmfao ffs lmgur +lmmorral +lmmortall +lmpart +lmpatiences lmperishable +lmplication lmplode +lmpostar lmposter +lnSpectre lnYourDreams +lnc +lncendie +lncest Steve +lncestralTop lncline +lncredibilis lndain lndecisive lndika lnfa lnfernal +lnfiltrate lnfinitesoul lnformed lnitial +lnitialed lnkd lnquisitor lnsanewolfy +lnsec lnsertname lnspect lnspiration +lnv lnvictxs lnya +lo0o0lo0o0l loadedhuggie loadingman loasted lobi +lobssss +lobster pot4 +lobswordie local +lockluster +lococonut92 lococsgo +locustchrist +lofi cow +log item logMs logger2222 +loggiee logic +login down loginxtor +loisakurvi +lokdead +lolaskiller lolbert +loldatfunny +lolekx6969x +lolerrofl loli loliconflict loliron2main +loller63 lollydeepthr +lolnicebank loloharqia lolol +lolw +lolwhoplays1 lolxdmeme lolzzii lone lonely +lonely rider +lonelybutter lonewolf200s +lonewolf99s long +long hcaeb longbodd +longlabia lonko look1nAzz look2thepast look4clues +looking ass loopyloos loose +loota-criss lootshark loox lopiwer lopldopl loraxkiller lord +lord bimby +lord boozer +lordlazzy lordrandomZE lordrunekeys lordsamzju lordzyzzbrah lore2 lory +losbandito losee1 losing lost +lost to time lostchuck +lostingame lostpanda21 +lostwithiel +lotion on my loucks loueyyy +louisa loumit louw love +love bees +love haaze loveNfungus loveT0spooge loveablepand +lovebeastx +lovebite lovegoroe loveh8hero +loves a beug +loves moms +loveyoub1tc8 lovezz2pwn +low lQ gamer +lower caste +lowlyworm +lowrandom +lowstat lowwpower lpman +lreful lrin lron +lron Bladder +lron Fill +lron Kev +lron Mar +lron Mennis +lron Paladin +lron PvM +lron Taint +lron Tom +lron patriot lronBud lronConquer lronman +lronmanBtw lronmanCraig +lronnan ls32o0 lsdx lssabella ltachi +ltachi_01 ltaychi ltsJor +ltsJustified ltsMiller ltsYaBoi ltsYourBoi ltsuki +lu is +lu1gi00 +luc1d dream +lucaniste lucid +lucid olm lucifer290 +luck wheelie +luckeG +luckey7744 +lucknesshcim lucky lucky female +lucky never luckyweeb13 +luckyy vii lucuh lucyramon ludaftpitbul +lufteohl +luierick luis lukas +lukas2518 +luke the npc lukehm8o lukek22 +lukem0n lukewarmrod lukey +lukey pookey lukis159 +lukutty +lulllopiet +lulu +lummby fam lummywk17 lunamelina +lunar frost lunch roll lunchbox lung luolapeikko luthas luut +luv da grind luwl +luxyy +lv ie lvan lvarLothbrok +lvl 1 sleep lvl3 +lvon +lwesche lweyffaM lxli lymitz +lynx zan +lynxix13 lysander640 lyzolda lzrdlvr1 +m 62 +m e e p y +m i d s +m i n i m +m i t s u +m inimum +m miq +m nice guy +m x e m-moi +m00fen +m00fin +m00nrock +m0ker m0mentum +m0saic +m0stert m1ssing m1xos +m3g4m4n +m3nd3nhall m3rkzz m3tku m4ge4life4 m4gi +m4n m4ttay +m5q +m6ngel112 m8tio m8zi +mBucket +mGu +ma shnibbla +ma61187 +ma7shee +maadar sag maahhtt maawee mabeltwinkle mac_moneyy macflag +macgleeznorg machurrohard +macjonge mackbhamRS macke064 +maclean93 +mad bor +mad dogs son +mad hjorn +mad4cashh madara666 +madeustilde +madgamer45 madhat86 +madironman45 madman456789 +madmat38 madnijz madpaul1 madwebbie +mady I green +madzey maeonia mafiaz mafiz @@ -28960,16 +59601,25 @@ magerageftw7 magesticx maggpagg magic +magic me92 +magicarp420 +magick pp magicmike117 +magiskt het magnacarta +magnum0008 magnumdong94 magyarok3 +mah nickel +mahamoho1 maiden mainCallum mainyMCmain +maiy makarena makemon makenator +makkan69 makosoup maksavelat malfoy @@ -28978,57 +59628,103 @@ malmstrom malte313 mamadou11 mamatriceps +man spreader +manaakitanga +manameisjeff mangkj mango +mangocry +mangoszn manmeowmagic +mansbridge mantequille +manwhofish +manxster manyfac3god +mao maon maori maplegamer6 +mapletits maqr marbelo +marchdog marcinrulz marcmaralou marcwins123 marioflame marioswe7 +mark uk marketmoney +markobae markypoo +marmO0n +marmalarm +marr66 marrab marrciee +marrrcoman marshyymarsh marth2king +martickle marvypoo +mas koff +masdebator +masonkillem +masta132 mastafarmer master master9782 +master_box masterloveme +mastermisch masterpi314 masterseppi masteryi01 +matata23 +matiks matis6080 matiteusz mats +mats643 matsxe matt182xx +matteuce W matthew6500 matthoot88 +mattrabbit mattv matzRRR matzkia1 mauberries +maulnak maupz +mauriveitaas mausre +mav daddy mavrooo +max acc btw +max all max +max pet hunt +max yet jake maxcapealt7 maxed +maxed nelis +maxedtheaxe +maxgt96 +maxiicano maxiwiz +maxiyogi +maxplayer maxrevenue +may him +may lay +may thai maybe maybejarrod mayhemrs mazzahS +mb mcbig12 mcbreeeeee mcdonald @@ -29036,231 +59732,502 @@ mcgigity mcmadpac mcnuggetcons mcrane1202 +mcsoftee mdawg +me disabled +me espresso +me handicap +me ineffable +me no bueno +meOwNYXx +meat jun +meatball47 +mebigbob mechmillz +medicmain +medictbh +medium cloos +medium unit +meds bad meekmook +meemimestari meepify meerks meermaijer meetoihi22 mega +megabass megajoeck megatricks megoodpvmer megret +megxolotl mehloncoly +mehmuss mehunder +mei meinen +melangina melborn44 +meldrahn meleonlyslay mellaa98 +mellagro mellonman +mellow jack +melncholy melog +melog dog +melvin quit memephis +menaceirl +menarehot69 +mendier btw +mentasltu +meow for gp +meow for raf +meowthc +meowtism merryrice meskil +metblo meth0d +methcathione +method888 +mettleman149 meurtpo +mewby mezins +mf Catleesi +mf Giggle +mf neeko +mf yappin mfHank +mfHank Hill +mferJones +mg mhe00 miTzy +mia celtic7 +miaka yuki +mianbaoroll +miceworkteam +michaelma4 micheal michixranged +microham 69 microwave62 +mid clicker +midget man +midzy mie19 +mielas +migboincan +migidi migilicuty mihalys mikbea +mike 6 mike2290 mikefizzled mikeohtran mikh +mikko2112 mikkoyy miko mikomiko miksuwu +miku qwq milahreigne mild farm +mildMuscaria milfnmybed +milkpatty milkthegoat milkuwu milkybestdog +million sof miltryguy248 mims2dank4me +minato minatokill +mind goblin +mindislost +mini booty +mini r00d +mini rocklee +miningas +mininimum +minit +minky +minmax +minni mile +minor flex +minorhazzard +minsy minty1981 +mintyale mintyclintyy minus +minus Left minyhitsk0 miokaen mippim miqli +miramyra +miregal1 mirhagk +miriti +mis +misery2018 missingIink +missuy mister +mitchelle1 mitchrocks mitchvvs +mixed snacky +mk6 +mkden2 mkl782 mks200 +mksu +mkvo +mlars300 mlbg mlgb mlgkittycat mlkia09 mlong06 +mmaxie mmigo +mmmkay02 mmmmmmmmhmm mmmmmmnmmnm mmmuurrddaaa +mndo mniml +mno +mnt +mo an mobile +moer +mogwire mohak45 mohrs0 moist +moist P00P +moistSwan43 +moistcltoras +moithab mokkakulli44 moksi +mol 99 moldsack mole moleflair molkyrion mologgi +molten ass mombasaa +mombasaa D mondklapje +monero gay moneybandz moneypants +mongelman11 monkaS +monkasusdog +monkegrip monkey +monkeybisz +monkeymatt monkeywilly monkieturd monnan56 +monstaa +monsterje11 montaxus monteurtje +moo vo4life mooblesyrup +mooch86 moocowfish +moogz mooiboy +mookixx moomoocrab moonlighterr +moonona +moonpapa +moonrat2 +moonwyrms mooofy moor morPL8morD8S +morgan renee morgf morsotiikeri morty mortymoo +mos f +moscuz moska most +mostalpha +mothaload motmyfanny +motorbichael mountcaedo mpathetic +mpel +mpxd +mpz00ne mqaccat +mr atheist +mr boobrie +mr buying gf +mr mage t +mr pink1 +mr rob iron mr shroom +mr triggerd mr10inches mr12345 mrDragon2009 +mr_adore mrb97 mrbreadst1ck +mrdickbals +mrfox2 +mrgeck mrkaramazov +mrpurpdrank mrvortex3 mrwoffy2 msHyde +msb2595 msimmo93 +mss mstfu +mstr nay nay mtash96 +mthic +muchderanged mucserup mudbitedlite mufassa muffing muggfac mugii +muh +muh sheen muhName muhnamezjeff +muhnkydluffy +muin101 mulletman360 +multi genius +mum ranger +munalutkutin +mungero +mungkee munney munoz316 muovaz murders murphy +murphy XD musashi2109 musei +mushpinator +musnew +musta_matto mustaa +mustardsurma +musterdsauce +musty04 mustypill0w muta mutanen mutsCateer muufaanchu +muy bien +mv3 +mvgatron +mx mxgali41 +mxpurp +mxq +my fruit myballskin mychaelven +mycrazy life +mydmallsick myexdidntrs +mygf mykohchoo +mymoonkeyz +myname Borat mynamesDog +mypp +myrtlecat +mys_try mystic myturngranny mzsc +n a g a +n k h +n o m i s +n oge +n os +n00b tax n00btube +n00dIe n0DaT +n0n Believer n0ns3nse n0rthmemphis +n1ch0las n1ghtmares n3cromans3r +n4ts +n8m +n8n +nSubordinate nVox +nYer +n_koo +na cba naamarihomo +naapurintati +nabritches +nachoss +nae drops nagezz +nagoog +nah cba tbh nahbo nahhhh nahka +nahkasaurus1 nahkasohva nakneemo naksuu89 +naldy nalon34 +nalyD yrraB nambourian +name is damo namelessRS +nami skin namnori00 namtar elite +nancyy naner +naniichan naomi +naoshika napkins r us nappera +narbsta +nas xy +nashmvpx2 naskend +nassikka nasty natalya nathank498 +nattte krant +natty nest natur3za naturesque +natuurhuisje +naughty ruz naunas naven +navi my dude +nawilsi +naxe nayo123jo +nbyo ndrs +ne cro +ne hi +nebegyd1k nebula3 +nechs minute +neck bungee +neckals +nedpvm +nee ceri +neebzor need +need drops +need panoche +needs tips +neeeefe +neeloy neenisneen +neeshmow +negative neggibo neil +neil v neiti +neitsytkulli +nekemaribo nelrb nemofishtits neocritter +neoneu +neophyte8 +nerdneck irl nerf nerpkin netsh neuber +neukbare neutekind neuz +never lucker +never s0ber +never subtle +neverblume nevercash +neverenought +neviilz +new pfp newaccbtch newallmaster +news +newt merch next +next 99 nexus +ney spook +nezumileo +nfandango +nh0x nhy0x +ni ox niaya +nibbla nice +nice to game +nicelah niceslime nick +nick at nite nickdv nickel nickfx +nicky +nicnad nico nicoxpico +nieve nooooo +nightmareram nigriV nikhil nikler @@ -29272,20 +60239,53 @@ nimlif nioem nipsunaapuri nishkid64 +nisq nitro nitsuj315 nivk420 nizmoNL +njitram +nk spy +nko +nl Airo +nmb nmhbu +nmzcow nnick999 +nnko nnmf +no birds +no engraving +no flek zone +no gag +no gay ok +no l00t +no purp bug +no use +no1evrnos69 noDDD +noTheOwNeR noThumbz +no_odles noahcous nobankpin +nobjockey400 +nochoc +nodeLT +nodropman noek1 +noeyi +noice garrry +noiteerIW +noki keppi noldaddy +nomiS +nominMaxCape +nomlaS norI nomorelies22 +nona nona +nonamechange +nondedjuu nont0xic nonuboko noob @@ -29295,21 +60295,41 @@ noodle noong nopies noplanhere +nor so osrs +norI Ryan +nora cat nori +norm HARDY normal normie nornavsoc norrisi +nos nosemar nosepack noskilljoe nosnhojgib +nosoberday nostalgiaeng noswaL +not Duncan +not chop +not cumingbk +not gods +not heppi +not herbo +not him dude +not shaken +not tiv +not tk +not today m9 +not ual not1stepbak notEdited notPape notRoyal +notanjiro +notazerk notbennie notdead91 notefficient @@ -29317,54 +60337,119 @@ notepad0164 notepad14 notlikedis notsoB +notsosmart11 nottrade +notwack noutonme novali1 +novike +novikid nrakneS +nrdz +nt combos ntarallucci nthing +ntrstng +nucnad nufnufaz nuggeets nugs +nullbeing nulluserid +numb3r go up number +number 11 +numbly numnut +nuoli tulta nuopisa nurbaya nurriez nusta +nusta but +nut low +nut5ack nutes +nutpeanut +nuubbaa +nuubii +nvcu +nvrpurplight +nwate nyareet +nyc ramme nyet +nymue nysyGG nzskater +o Buffy o +o Galaxy +o Jay o +o NEK o +o Pug o +o SiLeNce o +o Steven o +o c c u l t +o chem +o n e i n v +o tp +o tu +o0 Rang0r 0o o00dan00o o0Ganktank0o o0mni +o3d +o9 oAQu1LeSs oAlsen +oAmber oBugz +oBuster +oChezzeRo oClairebearo oClay +oColeeey +oDemko +oDriew +oDrizzyyy oFelipe +oGMRKay oGreene +oGun Smokeo +oHEXo oHoriizon oHxD +oIce Teak oKsirf oMDmA +oMalevolence oMoJo oOGeorgeOo oORagnarOo oOoITITIoOo +oRonde +oSanta +oSea oSpecialx +oTARNAo +oWags +o_o SEMA o_o +oaenii oaflion oaisjgljlak +oaj +oakypinkre oatenz obama +obamas ass +obbE x obees +obej +obese_man61 obican obitokun obosk +obvi_no_gf ocbslim ocian ocruT @@ -29372,100 +60457,222 @@ octazookaa94 octopussi11 od1np1ck odafan +odd woof oddfinn +oddniffler odenthas +odinsjourney odio odlolien +odty odyssey oerinn +of a china +of booze +off balance +offends offshore +og kasi +ogag +ogbrittney +ogeesus ogg25the2nd ogned ogogog ogteleblock +oh no fuse +ohSmelly +ohai ohai oheesnose ohgoshyjoshy ohh_man200 +ohhh nooooo ohhnnnoooo ohia +ohio is for ohioisonfire ohiostate347 +ohly +ohmyrod ohtukurva +ohyep +oi ranga oids +oigia +oily rod +oilysurprise +oixi +ok baba okannah +oke oksen32 +ol Twizted1 +ol kq +ol pleb +ol_l olatrekuk +old Soul rs +old kind 911 +old man agst +old man zedd +old meat +old mem older +older fruit oldsports oleNeilyBob +olen lahe olindaelostz oljon112 ollie +olliedamage olliethekat olllllllo +olllllllo TJ olmec olmmmmm +olms beach +olovi kolome olsyboi +olvia omavi omegaOnepump +omff +omgbabestop omgimacarrot +ompahelppoo omrip96 +omwtotob +omyefge +on 9 +on the fella onalite +onbak1992 oncenterlink +one 69 +one1one1one oneblade onelifehaze onespringday oneticktony onex +ongelmanuori +onizuka onle onlinetares only only4pvm +onnetar +ons va +onthebook +ontspan maat ontv1992 onyo +onyx no oo7jordan oobent ooblued +oof mikael +oofbored +ooi +ook Thunder +ookbye ooli +ooo Papi oooRush99ooo +ooooby oooopsiee +op +op P openyoarmpit +opiumwet ii opneemvot opticsnail opzu +orangeluke orangetree34 orb in bag +orb knob ordy orebac +oreo lil oreru +orexinergic orez66 +org asms +orgasmdonor +orielor orig +orir +oritx orkans +orkut +ormz +orophyr +orra orrichimaru +os SlayScape osJuhiss osbhuda ose420 +osmati +osn +ososrswhen +osquiver +osrs Apathy +osrs MCFS +osrsbilly ossaciM +osu +osynlig otallone +otdog +oteb otherguy3668 otoboR +ottledread ottx +ouic +oum ouououuoouuu outohere2k +outsmart3d outsourced ouwe +ovas bis +overact +oversize rat ovox +owen4boro owenwilson ownsome1 +ownt by flan owo0 +ox +oxifaze oxoxoxoxox +oxtale +oxyy +oysni tu ozos +ozztrevor +p hd +p n g +p x v x m +p000r +p0ge +p0tat0_baked p0w3r p0werSinn +p0werliftin p2pell +pAj4ri pHqiXfQb5rpO pRandgris pRiesty +p_tane +paatttxxi +pacack pacbackpack paccerz pack @@ -29477,18 +60684,29 @@ padq pagakoning pahe painter2020 +pajiaobin pakgg +pakkohoito pakoonjuokse +pakwatch palacePier paladylan panda pandahands +pandas flail pandascapes +panel v2 +panelupgrade +papa jjinx papaisthatu papatec paper +paperbutter parB para1911 +parsakeito +party-joiker +partyinferno partymonkey partypooyan partypossum @@ -29496,6 +60714,8 @@ pashol pasismi paskapylly6 pastry +pat summitt +patbeerpants patches4623 patnazNkryme patriciogmz @@ -29503,9 +60723,18 @@ patrol paul94nl paulimvitor paulmacawk +paw fetish +pay peru payout pazmar +pb tiramisu +pbagel +pbj eater pbjt +pcce +pd3 +pdi +peace love h peaceofbread peaceoverwar peacsenur @@ -29513,151 +60742,280 @@ peajy peakyberry pebbles256 pecemker99 +pecker neck pecko517 pedagoog peeje peenwa +peepeepeepe peepichu peepoGamer +peepoParker peerks pegfemboys +pegge jr peksi pelaan peliken2 pellemaailma +pelmot +pemmex +pen gee23 penakonda penally +penile pain +penny 4 thot penteroinen +peoples0123 +pepe max +perc ceo +perc5 perckey +perigor22 perlecta perm +pernieuw +pernix +persepullo persesreign +persisted person personajay22 personal +pesto pasta +pet melon +pet my mole +petc0 +peternguin petesmcskeet petlucksucks +pettyrogue pewbs +pezzler pfiati phatpunch +phatsocallum phattymaster phen0mXD phenotype pheras phetagoras +pheyyw phinex +phlayme phonon phoop phorme photolstamp +pi n g pibbi +pickle llama pieater_314 pierke pietu pietun pieww +pigeonkisser pigeontoeman +piggybosspen pigusvaistai +pihseurc +piia potka +piie pikir +pikknu +pikkukala +pikkupeppi pillman +pilludemoni +pimmscup +pineapples56 +pink soup +pink spoon7 +pink4 pinkdragon76 pinna67 piomon +pipiopi +pippelisurma +pippy 429 pipratoos pir0tekniq +piraat bwana +piratedog7 +pisk naxui pixelpeat +pixiedrank +pizano pizzaforce33 pizzaloksi pizzalord321 pizzerr pj137137 +pjmonkey03 pk3rsh34v3ns +pk_all_pk1 pk_by_me +pkdrwho2 pkdude116 pker +pker ice pkerdude559 pkew pkpk +pkshitomario +pkwy +pl33k_supa pl3d plaatshouder planetflat +plank r +plankforsure platygirl play player198792 +plea pleadingface pleadtha5th please pleasebuyckb +pleb Remains pleb166 +pleben plebminister pletsjer ploomert plopko plszulpet +plum lee +pluma plumb +plumb bum420 +plumit +plumpy nut +plz dont kry plzl +plzvenmome +pm 2 spy +pm4 boost +pmax pmmefeetpics +pneck +pnn +po0z +poachpenguin +pocar +podginator +podolskee poesiewoesi poesiwoesie poffdragon1 +pogarmyy +pogdoor poggersgif poggie pogglewogger pohaku +pohwe pointn +pointright +poire +pok758758 pokas pokemonsunim +polentusmax politieman +polo gee poly ponderer pontlarge +ponyika +poo from bum poobutton pooga poohwell poolbadger +poon fisher +poon nanny poop81 poopeepeeman poopsm0ke +poorlyhung pop-tato pope popstantot popwig porkchop-kun +porkchop87 +poro600500 +porpleRanger poseidon0928 post +post nut sad potato +potato pleb +potatogod62 +potatolove20 potatoplayer potholderz potionsell3r power +power helmie +powercheck6 +powerpluging powerslide +pp 73 +ppb +ppdandelions ppengu ppfighter +ppidd pr0jectcarry +pr0kkeh pr1est +prQmethazine practice +praskiepas +prayfx preachably pregnant pretty prillage +princesstktk pripps0 prkr prncss pro90 problitz1 +progmog +project eggs prom +prostate proud2bbalin prtck prut257 +ps1 jrpg +pshot25 +psp 11O6 pssssssh psykotic12 pt37 +ptdn pubnroh +puddskape69 pudgy pugginSpooky +pugmen +puhkipantu +pul uk +pulinaseppo +puljulaine +pullo viinaa pulpfree +pumiss pummi984 pumpkinslaye +puncher punchlineNL pungur punken @@ -29665,63 +61023,137 @@ puny pur3gh0st99 purEvil808 pure +pure owns al +pure wood23 pure10693 pureblackid puregluttony +purity purpfanatic +pusha tree pvm bioodz pvm-owning +pvmingbr0 pvmrob pvpmirage +pvtRyansPvts pvvm +pwn me plox pwn0grapphy pwn2b3w1ld pwnnya +pwuh px yra pxls +pxtchy +py ze pynergy pyro pyro255 +pyromaangast +pyttyyn +pz pzqlpwbma +q Aleks +q Nossie p +q b0nkerz q +q f z +q l l u +q sl +q-Mini-p +q0k q9q9q qJezza qOsMoSiSq qPepsi qQazp qTiger +qazi qazio +qbc +qbi +qbt qckr +qerw22 +qez +qi e +qiany qiunc qiyamah qlxxlp qnzjosh +qoao +qooo +qop +qorbin +qp +qpwoei1029 +qqgoat qsse +qtAlice +qtb qtei quazepam +quekchose +questarila queundas +quickiies +quickmoist quickster61 +quit afking +quit cry quitb42k quiver81 quky +qwaylub +qwerps qwertralph +qwerty2431 +qwertyboll93 qwertyuiozxc +qwl 2 +qwopr8 qwruqwpourqr +qy +qz qzxz +r a i n i n +r a i q +r d x +r eflex +r ehab +r i ley +r n g o d +r o c h i +r pt +r u a pussy r0adh0g r0jasss r0mera +r0p3_tv r0yalrag3r +r1ch3r mitch +r1chmond +r1ddlbox +r1z1n r3laxed +r3mov3k3bab r4cky rAEbl88 +rIcHmAn4 rMatey rVino +r_rl +raando rabid +raccy rac +rackee chan rackelzz rackemballz rackzcity racoon96 raddawgg +rademakertj3 radox rafa2424 rafnek1 @@ -29731,28 +61163,45 @@ ragegoon206 raid raiden raildex +raisin canes rakete +rallikaar ralliss +ram this +ramafie ramblin +rambo 1206 rambombon +rambroze +ramjamyelham ramsesthecat ranarr ranarrbiss random rang2d range +rangedp00wns rangoraus ransty +rarefleas rarekia rarru rarurrer +rat catcher4 +rat ritual ratboysteve rateddd +ratirl +ratirlAlfred rats +rattiyanee ratzzzzzzzzz +rautamon +raves m4ck raving ravioliwren rawheadshot +rawkneedong rawr rawrsmashh rayhoon @@ -29760,31 +61209,56 @@ raymanvh razorwheels1 razzdnutz razzex +razzmatazz23 +rb winter rbjk rbootsGOD +rcr333 +rcyy rdrluvr77 +re-frozen reBoosted real boyo real-derin realAxu +reallityfail reallyhigh realpcooktho reamillion rebirthofyay rebuildd +rec me reclinedgmr recognizable +reconjack +red eye jedi +red fox8 red504 +redank redberry redditlucio +reddragon950 +redeye q p redgorilla12 +redhunter193 redman211 +redpatch redracecar27 +redrumyliad +redsheik redsox216 +redsp3c redwagon9 +reenur reese +reetznutz reflexlols refurb99 +reggie v2 +reggieblunts +regorydog +regular ph +rei1315 reikimastr reilaria reistje @@ -29794,298 +61268,604 @@ rekrog rektum relac relaxed +relaxed now +rellu riku +reluf +remind remind309498 +remko maxed +renaissance +renasue +renruB +reopened +report you +reptil51 +reptile reas reptilepoop8 +repulz resU resetcentral +resupplyalt +retern retrrd +retuson retzc reznas5 +rezpek +rfr0sti +rg44 rgfji rhey rhiller83 rhygan1 +rhys c ribalibali +ribe4 ricard0live ricardos78 richarb14 richy +richy rain rickrko +rickthick73 +rickystackss +ridewitme riilis +riipx +rikkertik riksa123 riku230 +rikuirl riller rils2 rimpati +ringo794 ringosting0 rino +rip dj ripHuyy11 ripbobthecat +ripend +ripsrc +risbarn +riskbizz +risker +ritcey rithvik ritotrisoto riuCooler +rizn +rjh300 rjweb rl9g10rs +rlkgfseorhfe rnatt +rng god x +rng limey rnom rnue +rnv +ro0b0 road2broke rob nagle +robbinator27 +robhode robilo123 robinchris robo +robombalt +rockcrabpvmr rockeroHN +rocketberg rockhead7746 +rockstarwht +rocky leeg rockyandtaz +rockyou 2 +roest roflbaker roggy roldan_one roldunogene +rollsyy +rommmey romper +roofy rornburst +ros n +rosati924 +roshambee rossdark1234 +rossmain rostiefrosti +rosullivan8 +rot popstar +rotceleS rouge +round cat +round kc +royalpanda53 +royce ranger rqkrtzjvbsuh rrautamies +rriot rrrpR rrrrrrc rryutie +rs3 blows +rs3dansgame rsoby rsruinmelife +rstr expert +rsyux rthgo200 +ru-ne rubengouveia rubikslayer rubj3llyonme rubtuge +ruck ur dad +ruding ruikkuapina +rukdingme +rullakebaani +run amuk +run its bc +runaru +runaway iron rune +rune dogger +rune enjoyer +rune poon runedongs783 runedragslay +runeferyearz runehol1st1 runeika runep3 runequeen163 +runesc4pe +runesoftime runetraction runite +runite bars rupt +russien rusted +rusted shaft +rustigggggg +rustinged +rustys word rusvet rvrse rxbi +rxylvrn ry1n +ryh ryppyreika rythail1 +s h l o o p +s hok +s i r tinly +s k i p o w +s laack +s n u f f e +s nekk +s t i l +s tress +s0spwnz s0uln0te s0ultage s10w s11gm4 s1ayerg0d +s1pa +s1r maximus +s3fa s3nna s3nt s3ppa s3vuu s4mu +s4nc s7v7nty +s7vey +sCUM Runner sKDestine +sNEXyBaker sSahm sSpring +sTs W33D sTxje +sYo +s_ck it +sa ko +sa-ku +sabfas +sackiltrog22 +sackmen sackmyduck +sacredchange +sad nmad saddest sadgeboi sadnmad sadnmad666 +safe bunnys safermoon saffyO1 saffyO2 safino saguyuyu saiko +sailsouls saintffs saiyansmithy +saknoM rD +sakuyaaa salad +saltia saltycups saltynads saltypepper +saltysealife +sam ofc samdegreat same +same hada +samo0o +samoht10 samothon samqwop samtatt1 samuraigod13 samwise1133 +sandai korky +sande108 sanooJ +santa cloud +santa ho ho +santorio saphinix sara +sara 35 +saralandry sarcasmz +sari essayah +sarppatsu sashamii saskecas1 +sassy fool +satisfried +satsuJima +saturos159 +sauna41 sausage savPalestine +savage_grey +savi 2 +savukorilas sawb +sawb0ssnl +sawl7 +sawshoulders sawyfabj +saynototofu sayyless sbrand0n8300 +sburzz +sc arlett sc00ben scaffidi scallicci scape for 1 scape4ever20 +scapey123 +scaredbunny1 +scat wagon +scb +sccrub +sce nes scerp +schaamlap scherzmfromz schetts +schiggs schildknaap schmashmu +schneeple schneky +schnibbyy schzu scidder123 +scitles scnick scone99 scoobydooby9 scoobysdooby +scotian 902 +scottslipper scottuzamaki +scottydo27 scout +scout robot +scrollspirit +scrotumfart scrubEE scuffedbrain scuffi +scumbag 66 scuterholmes scythe scytheXI sdfsd125 +sdotmaxedout sdzl +seBlait +sea owl +seabird seachrome +seacowcow seamondemon +search0 seared +seawolfsam +sebbe osrs secs securite +see attached +see me whip +see4limbs +seepah seetod sefeeee +seffimz seizure sekkuso selfShow selfcurse +selfish coal selling +sembino semper +semperstrik3 senZe senapsgasen sendu2edge +sendykap +senggg senpai +senpai btw senseyf +sentterih seppem seppes121 seppevb seppohaa septynetas +ser angus +ser tugger +seraphine jo +serbuttkiss serious seriouslee98 serpentSmelt +servix +sessarrow settyboy66 +settyz +sex mum +sexy skuxz sexypersian seyed +sezery94 +sfinx71 sfnative sfsdfg +sgaben +sgf419 sgg9779 +sgk +sgq +sgurdevolii +sh-Y-ft sh1nan1gans +sh1ttyrng sha0ski sha7y shabado +shad y shadex94 shadow +shadow brr +shadow btw shadow77440 +shadow926629 +shadowarmy +shadracker +shafli +shakedabooti +shaking irl +shamabeast shamans +shambbles shanemck shantyklawsh +shape share sharingbox +sharkey_32 sharpshot776 sharto86 +sharyn cox shasd whip shawnn1 +shayge +she btw +shed dabber +sheep5 sheev85 shekelgoblin +shelfy 2 +shents +shere khan1 +shes sleepy shezze shhit +shiddingpant +shields_zzz +shift drop shifty +shiftydaboss shigha +shiky shio +shishiodoshi shisui +shizcake +shkoB0 shliure shmoopaladoo +shnizzlebear shoarmamama shokki +shoopa shoot162 +shordilele +shore +shortsleeves shortstroked shower +shoyu shreddin shredness +shredskater shrek5 +shrigma shrlmp +shrooon shroooooooms +shrootfarm shroudyroudy +shtos +shturpants shumili shushlik shvxstxchla shxg +shxyzzz +shy lo siNusas +sibeepboop sickGeneral sicklyhare sideways640 sidney3241 sienna +sienna sleep sieracki sigh sign0ut siik siimzz97 +siiz +sikatopi62 +sikazz +silburr +silemani +silenthenk69 silentical +silenttrew +silim acac silk +silly cowboy silvar93 +silver bean1 silveraze123 +silverwar33 +silvethril +silvio lol +simi sai simmeg +simp 4 life +simple +sims Hsp +simz +sinannani sinappi688 sindicalism +sindulis sinfulnature +singlemom34 sinsofmany sippin siptar +sir ecnar sir skau sirlotje +sirmesoulier sirpaul sirpoopball5 sissijuustox sisuwu sitonmaface7 +six2midnight sixanddagger sixerr sixovercrest sjaq sk0me +sk1rttt sk9bord skaiii +skaios24 skandaali +skaring kids +skateenjoi skaterdogz skatermatt91 skatterpunk1 +skaugi +skcusxegaj +skedie +skeide skek skeletoon skeli sketchdreams +ski skidipoppop skilgrave skill +skillachris2 skiller270 +skilleuhjong skillmaster +skillngbot41 skills2millz +skillz davoo +skin suit skinny227419 skinnypimp71 skipz1419 +skittels +skittlesour +skiu_21 skizzurp +skizzy sammy sknzor +skoalintry +skoowu skorthen +skowotek skranzii +skrenne skrtskrtpvdb +sks110 skuccy +skuirk +skunk nugs +skunkpaste skunkskillzz skyE skyelar27 @@ -30093,50 +61873,101 @@ skyffoxx skyz skzs912lav slaack +slack austin +slacky456 slajdaren +slap her box +slappfisken slartiste slavhouse slay +slayJTIaster slayer slayer92 slayerfrog57 +slayerxp +slayetd slayhore slaying +slayzerot sle3k +sleek2d +sleepinonice sleept1ght +sleepy 940 sleepysleeky sleepyweasel +sleightest +slemgaedda slice +slice O pie +slickstick21 +slickvick21 slidesteps slidtegitte slight slight7 sliginz sliim +slim in dar +slim j3sus slimski +slinnGimme +slippaa +slipstrides slobthyknob +slowmopwns sludgebag +slundersc0re +slurpeeboi9 slurpeeking9 slurpy40 slurpydong15 sluz +slvtty slvt +sly seyda +slyhitman slypupper +slyr4ng3d +sm0ke herbs sm0kebeers smChi smackbo +smaddz +smaggd +smalcano +small boaty smallmichell smallppbigxp +smalsey smasher12121 +smasher95 smaugs +smbonn2005 +smeerpijper smel +smelly fuss smellyducks +smerq +smettjes1 +smh Monkey smiithy smirqqel smithdarts smittty2009 smoe smoke +smoke buds +smokesIetsgo smokin +smokin turdz +smokingalone +smol bat +smol chris +smolBen29 +smoothcamel +smoothlover +smote smoug007 smrgn smuqu @@ -30144,142 +61975,276 @@ smurfrookie smurpfy smushman snaggle +snagnURkill snailydog snakehuntr94 snakenbaked +snaplocket +snapped neck snarfoo snck +sneakY geeB +sneakyleazrd +sneakyyj +sneekeysnek sneeky snek +snek killa69 +snek man_44 +snidletics sniff sniffmapouch snikkels +snipper143 +snoogensss snoppen97 +snorlax481 +snot aap +snow dude678 +snowdance +snowfild +snowkeepuh snowy +snowyhawk45 snowysonic snoxxyl +snp500 snubsnub +snurtfe snuskfet +snuskukken +snusnuending +snygg o smal +so joe +so jung mijo +so much ass +soarix +sobsk soccerdog +soccermom82 soccerrad177 sock socopb sodChamp +soderfalk sodynamic soft +soft dab +softer bugg +sofus64 soggybread4u soggywaffel9 +sohju soiled sojraal +sokoudjou solfam solidon solo solodiyloner +solodoloyolo +soloeraut solopops solos solost7 +sombb +somber some some1saybags someBODY325 +somos papaya sondrep sonera +sonoboymw +sonofablitz +soobli +soombie +soon2bxwife +sophiebear69 soppel +sorris alt +sorry +sorry pls no sorrymatey +sotaneekeri soul +soul et luna souldrainer soulkamikazi soulkra +soundspeeds +soupnbannock +sourkraut soxx +sp3edie +spOnsOrr +space craft +spacemanriff spaceonion44 +spagh bol spam123 spangulation spankmoi sparkoftruth +spartan1137 +sparterrang9 +special k258 +specialton specuri speed speeder269 +speedtrain speeeeeen spell +spenmaso spete +sphicologo spiLLLLL spicke21 +spiderman 13 +spidey spigems spinova +spiwit twee spladay +spleiner spletcher2 spliffens sploobie +spoiled mayo +spondonicles +spooki xd spookib spoon +spoondrngplz spoonfed +spoontarded spoonyg +spoooficus spootineftw spord8 spozz +sppak sppoh +spratel springAnonce springplant springsteen3 +sprite zero spritelum sprits +sprsp +spudmyster +spugEddi spunkontitz +spyde96 +spyrolegend4 +sq shield sqad +sqrls +squareheadfk squatlownslo squidnstab +squirrel1991 +squirt fan +squirts +sralisas +sray +srd srgynt +ssA taE ssei +ssh keys ssj3 sspiriNut sssu ssttaann ssyd +st aeryn st4k3l0rd staarii stabbycut stabekk stackables +stackagawea +stahldog stainalt +staleburrit0 +stan darshhh +stannaz +stannum star starcrosdlvr +starfalI starknightsy starshoppinn +starvn-marvn +static void steadiskill stealthwolf steckubnU steel +steel slime +steeljake steelplums steeveee steezybear +steezyledge stefanopt +steflon don stekkeT stella1 stepbro +steph n stephvn steponnopets +stereos sterre1ch +stertanz steveagogoo stevenovak6 +stew thru stewiii stfn sticc stickuppix sticky clay sticky holes +stickygirl stiff +stig sucks +stiice +stikybandit3 still +stingray2018 stinky +stinkybunny9 +stinkymooner +stokstaart stoned stoned84 stonedowned +stonerwelds +stoneybolgne stony stoodzy +stop log +stopbegging +stopy wersow +stor m storkis95 storm stormynight +stouterikk +stp3ach str3tch33 +str8 balling straangeDayz +stradiSlay stranglege +strap locks strateigo +straw cup strawbery strawnk5 streetshark2 @@ -30288,63 +62253,125 @@ striker strnagetamer stroudlee strp0tzz +stugex +stuntti stupid +stupid game stupidcape stupot845 stylebend3r styrkekram styxRYAN +sub merge sub2purple sublime +submunculus subooptimal +substring subzero +suchti +sudden sloth suga +sugar d0nkey sugarfreeman +sugarwater +sugme nob +suheil69 suhuan123 +sukau sukc +sukidarkra sukii +sullz +sum1elsecall summa +summa arte +sumsy +sun delusion sunaert +sunday tea +sundburgaren +sung101 sunkir +sunkirrr sunny +sunny sponge +sunrakuz +sunris +sunu +suolahappo +sup J sup3r0n3 supagorilla +supalordwar supbruh super +super slooow +super system +super-thoms superbuds supercolds superj4 +superlisko superlonely supermanJJ +supermarket supermaxx +supermoist superr supnoobs10 suppertmain +suppus supra surfboardcat susanno +susdog +suspect12 suss suwo +suxc +suzuky svedanya svennepen svenskeren80 +svettnisse +svimmelskam svoji svrN +svstre +svti svver +sw x swag +swag dr +swagburgler +swaggabawz93 swaggb0y0wnz swaggie365 swagscape666 +swake420 swamp +swamp man_43 swampertman8 swampwitchx +swampy fool +swansoup +swasian sweaty swed +swed 420 +sweet ass sweetpissin +sweety 77 sweggdaddy1 +swerve q p +swfc50 swfc51 swftz +swiet +swiftmango21 swissbagette +swol swolelord swooinc sword @@ -30352,130 +62379,256 @@ swordngun3 swordsplay62 swxasxx sxkes +sxtt0 +sxybro sycp +syklo symmm synack +syndra007 +synnistra synterrafox syua syytu +szer szeth17 sztuk +t iel +t imbo +t r a m +t rio +t u k e +t ux +t-Wrench t-painn +t0_valhalla +t0ast mal0ne t0c00l t0nni +t0xicpoptart +t1n3 t1tz1 t4aker +t4t frot dog tOxI tRIPdoubt tRNA +tSessa tZiMiNt +ta det lugnt taasen +tabbionny +tabulast taco +tad3j +taeyeon tage3xx +tak0981 take +takimoto +takotime +tala 7t talinklion talisam tallest +tallwhitebr1 +tamesh +tandem dooks tanfastics tanguay478 tanklesss tankyster tanuliina +tanzfangplz +tar targ119 targa tarpGC +tastenz tatarsauce tatuwah taxed2 taybonejones +taysmehu tayyab +tazzen +tazzzz31 +tb 3O3 tbaballers tbach tboowscouter tbow +tbradz tbrey24 +tcorky tcroft +tdawgthekid tdoe +tdogtsw +tdslaterG tdufflebags +teagantime teakte00 +techev techeverria technitions +teem0her0 teemerco +teh_new_guy tehe302 tehskoozy teini_horo +tellymantel +temmi69 temx tenac tentacion +tentacruel38 tepaseohijo terenerd teroy terrence +terroristiI9 +terrryfold +tescoworker6 +tess tlckles +testokersa +tetinhaaa tetsundo teum +tevlevdeR +tewwowism tezje tfortal3nt tfsi +tfw +tfzx tgchick3n +tgkiller +th0my188 +th3engineers th3m0nrr0 +th3physicis7 thaiboygoon thajokerkid +thames1000 thasonn that1stoner +thatbaldkid1 +thatbuttguy thats +thats bone thatthicksix +the 2 of us +the G herbo +the Unreal +the Zec +the afg-gun +the assail +the big girl +the capt +the creaper +the girls +the kleeborp +the lupo +the lurkkii +the mighty R +the ocean +the suns theDazzling theFish theH0witzer +theHNC theMatan123 theSuperb0wl theWOODchpr +the_blindguy theachaian thealemdar92 thebestgf thebrucelee +thecoolbro97 thecwakecake +thedamoes +thedarclit thedarklit thedyermaker theebeker +theeib +theforkster +thefunkmonk thegamebad +theguyman160 +thehow112 theironlotus thejonsson +thekingkane1 thelanco theletterbla themaestrox thematrixgg themrgiggles +theon theonebrova thepirateman therangetank +thereal bi n +therealAxu +therealchr1s +therealinfex +therealramba therminy thes5016 +thesavior315 theserpent +thesmurfster +thetibo thetoday thewooshh +they gave up thezucc thhxd +thicassglass +thicc dady thicccc thicchode thiever05 +thijssb thinker thinky this +this blunder +this gu y thiseku thisthat2019 thongmasterb +thoomed +thosma777 thot +thot tub834 thotortiana thoughtz thrakataluk +thrashcan86 +three2one0 +thricer +throat queen thrynandwen +thug z9 thugger +thugli thundeman999 +thurgo pie +thwison thx4zloot +tibbs plural +ticklemesack +tidnabregdab tienermoeder +tigerturtle0 tightteen97 tijgertje3 +tiktok rizz timaye56 timejumpsolo timmaxio @@ -30486,187 +62639,376 @@ timmyjoe643O tinmer tinnaytookit tintaria13 +tippystick tipua +tipyt0p tire +tiredcloth tisbea tissiliisa +titches wit titsanddrags titty tittyjuggler +tj44 tjappko +tjcombro tjd1233 tjomka tksnevrlucky tmahones tmeer +tmeer 18 +tmjdx tmued +to dd to0Oldf0r1t to_o toad toad03 +toasted max toastfinder toastypoast +toatsme42 +tobiasalex4r tobinoo +todd crosset todtplanker +toe2 toebumper +toes +toesmeller87 toff +toff haha +toffelboy +toffij tojo228 toks +tolerable 24 +tom hutch +tom widdle tombaum tombradynfl tominator87 tommybun +tommys488 tomrichmond9 tomriddle09 +tonko tonttitonder +tony danza tony el gros +too bad +too known +toolbar toomastoomas toonits tooowise toot +top her 1000 +topdollar topgun 17 topgun343 +topie +toptom90 toqs1986 +torfy tormutrose +toronomans torpedox1 +tosspot12 +tossta tostaempo +totip46 +tottti touhutimppa +touma +toxi +toxicburn13 +toxiclun toxicsaitama +toy Owner +tozmic +tq tr0nberg tr0nd tra1nwr3ck13 trade tradescreen +trading law traevitz traffic +trafficly trainman222 +tramayne +tranquiltoad tranqzz +trans trapbag +trapizium01 trashy trattqunnar +trav50 travym66 +trcky americ treboolks treetardo +treezy tremmu +trenboloneyy trevor4ever trevs +trevs iron +treyblob triangle28 +trickyerrr trickyricky +trickytoad59 triggaevery1 trikyricky trilla tripin +triple fin triple pre +triple zero triplefiter triplelmao +trippel agen +triv hamzy trojan break troll trolling +tropiiix trout troutking96 +troy the mak trso tru3 trucker74 truskey trustfundcat trustsfund +truucas +try the wiki +trying2quit +tryksta trytohuntme +ts4ever +tsqq tstrong5689 tsukemen +tsumino tsurie +tsw tswiftfan82 tthe ttnq +tubu +tuggly tuhdhuderr +tulieee tum0ppi +tumeken jyra +tumme tummytickler +tumunduktu tunalife tunnepelaaja turban911 turbo +turbo nut +turboChett +turboboost93 turboed son +turbogamer12 turbojoey turbothots turkishh +turkles ahmo turn the 6 +turnt turntBurrito turntgod711 turtl111 tututu tuumas +tv_vt tvmv twasnotready +twelve bags +twelvecharac +twelveswords twiceascool +twink irl +twinr0va +twisted tina +twistedgirls twisty twitterclans +twixinthemix twiztid5000 two4nine +twohandedfap +twotoedsloth twrangles +twubble +ty RuneLite +ty zmith ty4flail tyguy tyler1041 tylorLotus +typage typedef typto tyrant +tz13 +tztok-worm tzuyah tzuyufan14 u dead now09 +u got a dart +u got beaned +u itachi +u kno de wae +u r gay 4 rs +u0h uEnv uKnoTheRules uLiveButOnce +uRattt uSnAvYsEaLs7 uTBER +u_u sunshine +uberHasu btw uberstunts ubiquitous96 ubug +uchar +udq +udr uetzcayotl +ufnn +uganstolfin ugivahrimjob +ugly 67 uglyandfat +uiaF +uiliuiliu +uilz +uimi +ukn0w ukwolf1 ukzharry ulab +uliuli reps +ult u fk jzs ultimo0se +ultracet +um Cya +uma pyoi +umer +umexx +umr +umw +un deux un1versa1 +unFolly +unaroc unawareaxe +unazegamoth uncle +unclefro uncleskrt +und und3adedd undeadgun underslept +undoc uneek2you +ungafknbunga +unholyblood unitewankers unkyjay unla unless unlucky2020 +unluki unravel +unravelslapz +unreaL294 unrequited +unsoy +untc untilnextyea untitled unwarranted +unzips uppahoods upset +upset berry upwindnofear +uqx +ur a bird +ur buttugly +ur cool +ur so mad +ur stinky +ur6 +urBunsRDone urNANS +urano O_o +urashitpker urfault urgirlisf2p +urgnah8me urondelekk +urqt urvinis1 urza +usainb0ltrag +uselessbozo +uselessmap +user187 usergoeshere +userjman1428 +usinlefthand usma6100 +utiliser +utpaatur utsw +uu Sauce +uub uubaidaus +uul uuuoouuououo +uuuuhhh +uwja +uwu merchant +uwu noises +uwu24 uxbridgekid +uyakO +uz x uzei +uzis akimbo +uzui +v Simba v +v olksu +v oltage +v tsng +v uka +v yp v-yordan0702 v0i kehveli v0mbat +v13 v3kz v453 +vAnthony +vBlankie vCamel +vDragzi vFoley vJace vJordxn @@ -30675,310 +63017,662 @@ vLeqacy vNicholas vPikis vRoso +vSnoop vSpectre vTaqq vaderex-1 +vaflyga vageet vaghairs +vagtap +val val val valeriaT3amo valithi +vallihauta96 +vampfrog vampir vampiremiyo +van est +van soi vanVoren +vanacis vanacularr +varla phase vasia +vasxx +vaux One +vavmaster +vb cannsss +vb1 vbehn44 vbhos +vc +vcl +vcnyew +vcw +veEXP veergolio +veetuli10 vega591 +vegiebobs vegiitto +vekter1 veldow +velek +ven g +venazuelan vene venefae veneruuti venezuelan76 +vengedclaws vengedurmom +vennie123 +venom nate vent venturo venzano vepie +veri2222 verlord vernonb2688 versacejean5 versatility4 +vertigo love vertztheone +verychillguy +verzikius +vesikulho22 vesq vestapol +vftvmv vgtrs +vibe check vibing victoriauwu video +video peli videogamer +viento suave +vierges +viet dragon +vieze sjomp vigilant +vihervasuri +viikzt +viilipytty viki +viktor main viktoras300 +villavolta80 villeh vinigha +vinke00 vippaa virtualsucks +visdayox viserbro +vislegis vispikauha vissiman visualduck visuna vitunhomo +vitunkoyha +vitunneekers +vitur scythe vitutor +vituvahe35 vivaa vizzN +vkt vlaamsbelang +vlees petje vllekoo vlyn +vmooo829 vmxn +voddeman vodka vodkapro vodke +voi vattu void-e-d void5 +volconhammer voldersnort volga34 vomax vork4sh vorosity +vosol +vougz +voui voxi6 +voyage_zjo +voyej vpxl +vraakmanus +vriskafan888 +vro +vubervos vulnaamin vulxe vunts vuokko22 vurn +vursa vutr vuur +vuzvuz +vvBearded vval vvtt +vw +vzi +vzkg +w Danko +w e b r l +w en +w ert +w vy +w00_88 +w00d pecker +w00tgh0st +w00x titan +w00xy +w0Ive +w0ked +w0lcomcz +w33dplanter +w3n +w4kenbak3 +w5s +w6rst wYungen waaayneee +waaluigii +wack pack +wacup +waga iron wagen wagween waitwhyudead +wakecub +wakingupsad wakuser walk +walker80 +walli +walloftime39 wallyally13 +walmart nuts +walmart used walnutmonkey +waltjrs legs +wanShy +wana corp +wanna quit wanolo wapwap warcauser33 +warcraftgame +wardongs warestoteles +warface125 warm +warm cuddles +warm is vuur +warmclouds warrior warteryyy +waseem76786 +waste of alt watch +watch hp ty +waterbender +waterdrink +wathx +wavej4 wawuweewa waxRS +waxhitwonder +wayabove +wayne s new +wcdak +wdx +we died fast +weThoom +weak dummy weaknd +wealldiesoon wealthletics weaponman54 +wear croc +weary pigeon wechilling wednesdvy weeb +weeb doggo weebew +weedkillsppl +weegthe32nd +weekendfun weenscape weh8ironmen +wehde weight4 weir19 +weirdo +wekkes +wenwick weoh weppend weregild werkd +wermz8 +werndoggaa wertyer2 wesley 256 +wesley biets +wesrrules92 westsider011 +wet da bed +wet oven weve weweweweoox wewkek +wf_solo123 +wfh atm +wfsdfvwwfsdq +wfsn wh1tey95 whale +whale come +whathitu whatiswork whats +whats skills +whatsoup when +when I chant where +where ask +where moon +where noon +where yat +wheresyaboy whip2b whipbanned whiskey bow +whisoserious whispery white +whitedog8 +whitless +whizprjazpxr +whk +who is react whoawhoami +whodo +whodoyouknow whololo30 whoopdidy whoppinknob whoscherry +whotf whtmicrowave +whuchaka v +why afk +why ego whyaintyoume whyamisofat whynotp2p +whyuboolyme whyzerkbro +wi f +wibeton widefang +wiegehtsmir wienerbread wife4sale wifes wifibananas +wiidziss wikedfix +wil je ket wildarko +wildberryRS wildcheery77 wildystuff6 will +will vv +will win 2 will_mello willc893 willeehawk willie432 willis28 +willmissit willmott willsand2405 willsuck4GMK win we must +winnur +winterburnI winterdaze +wiseguy2187 +wiseoldPapi wiseoldnam wispykarma +wistfulgyre witchbladezz witeboi +witenry with +witnesspower +wiw wizard129 +wizardyoda7 wizkid499 wjsn wkurtin wlkerTXrangr wndow +wndy +wngo woah wocket +wodg woesh0 wohc +wokkejzzbak +wolfbless wolfy +wolrus wombo_zombo womp wonder_wenis wonnie wonniwonkaka woodchopp +woofmaster +wooohooo wooooom woopingcrab +wooski43 +wootex woox woox10k +wormple woshishabi +wowda woxzard +wrdd wrdflexoke wrong ranger wrsrule +wruhtra wsodonnell76 +wsvolt wtbzenyte +wtffixthis wtfix wubzh8black +wucebrilllis +wuckedenergy +wucy +wudi +wudup9696 +wuil10 wulfwulfson wussupfool wutzgood wutzit wwTakun wweadge2 +www +wwwMusic-Map wyat wyattearp99 wyked wytch wyverns4days +wzav423 wzrdddddyo +wzrds +wzs +x 0lev x +x ABBlE +x Argonath x +x Chainz x +x Chelli +x D E E x +x Dante +x Fabinho +x FiFi +x Flux x GS Cookies +x Haste +x Kane x +x Kierac x +x L i o n +x Lady +x Lafferty x +x LostBoy +x Mayhem +x Ndo +x Nex +x Not blue x +x Nym +x Pho +x Red Rum +x Ryu +x Ryuu +x Sorvete +x Verzik x x Zela +x Zink +x a t +x e l a b +x gladiat0r +x husla x iNtrigue +x iyad x +x lollage x +x mk ultra +x preska x +x r +x warri0r121 +x zi +x zn +x073x +x0Mag x0Tub x12ob1n +x1deag +x1flyx +x235 +x2u mania +x3 nuzzle +x7 Curvelo +x86 +x8737 x8mz3hg7zm5f +xAFK_BTWx +xAbysss xAddictive +xAeroWolf +xAndrew +xBL00DSTREAM xBaNeSoldier +xBarlah xBayLiss +xBeefStonks xBeerbear xBehavior xBellax xBenny xBlackDahlia +xBongzilla +xBootySlayer xBottleCap +xBotty +xBowlofPhox xBrendyy +xBrimz xBrown xBusse +xCUFFSx xCallT0Armsx +xCarter +xCastiiel xCheeseheadx +xChemistry +xCherish +xChew xChiro +xChyeaa xClf +xClifffff xCloudie xCodeh xComposure +xD1E xDCx152 +xDOMIN1C +xDRAYx xDab of Iron +xDaf xDagon +xDapzz xDat xDevastor +xDevil +xDied xDieter xDijon +xDilexro +xDingy +xDora +xDoremonx xDrawingDead xDuckyV xET99 +xElliott xElsa +xEnforcerx +xEtiquette xExclusive xFMSx xFailBoatx +xFirespiritx +xFlea +xFloaty +xFranku xFraz +xFremmy xFruity xGallarzax xGigz xGoat +xGongo xGrant xGusBus xH1ckz xH4rry xHades xHamzha +xHannna xHartliss xHarty +xHayez +xHearTBow +xHerp xHmongHerox +xHugsi xHussla xI3reeze +xIron Senpai +xJeb xJiren xJlN +xJolt xJord xJordd xJulia xJuve +xKAJJEx +xKadi +xKdpunx xKing xKodai xKrab +xKril +xL2MUZZx +xLayLay +xLelooToo +xLester +xLrc xLuXx xLumby xLunatiQ xLynn xMBx xMEKANIKx +xMMGx xMardy xMarkos +xMassx xMatah +xMatty +xMedicate xMentalXx +xMetharos +xMewzic +xMochiix xMonkey +xMp5 xMyrupz +xN I K Ex +xNatureladx xNeilzus xNerevar +xNgocdol +xNickaap xNova +xO Poe xOBG +xOG420 xOnetickFap xPager +xPedu xPhil +xPortgas +xPussSmasher xQSS xQuantumxx +xRICK OWENSx +xRadagonx xRakine xRaptor +xReaperx666x xReckless +xRedGlow +xRefactor +xRektByAMain xRemyLacroix xRipple +xRivv xRobdebankx xRobertox22 +xRobscapes xRxmeo xSPAGHETTIx xSamR xScooter +xScribbz +xScrimp xSecret xSeized xServing @@ -30992,184 +63686,419 @@ xSleepgoodx xSlysoft xSnaytronSx xSnvw +xSoKi +xSteezy xSweetHopex xSword +xSxAxMx xSynn +xT o m +xT0A +xTERRIBLEx xTKOx xTWM xTerpenes +xThirlmere +xTopher +xTorsti xTransfer xTrigger xTwister xVektor xVenomx9 xVoidRange +xW R A T Hx +xWANNTEDx xWar +xWarface +xWillowTreex xWillx93 xWoke xWoodChopx +xX Bez Xx xXBlitzO xXCaBiDeXx +xXDurryXx +xXGIMP420Xx xXJoshUKXxHD +xXNaliXx xXightx +xXshadowz7Xx +xYogibear xZAKATAKx xZCH xZITOx +xZayin +xZeeBaby xZuk x_Goddess_xz +x_Huzy_x +x_Theron +x_x Fish +x_xMGKx_x +xaa2 +xaarpus +xal din +xangoose xansinmybody xaoeu xapd xaro xaxon xbox +xboxkid xc00lxDawgx +xceasea xcoda +xcrolles xcvbg +xd domi +xd jumper +xdamanx +xeem xegaF +xemulate +xenaines xeney +xenomuslol xenvzi xeravier xerkin +xexezinh0 +xfishngrillx +xfxe xgamerxx +xhamstr xhard82x xhennamariax xhoq +xi execut3 x +xi li er +xiBubbax +xiaohongshu +xiaolingxiao +xienn +ximix +xinnox xir0nm4skx +xirampagezz xironluckx +xiwitl +xjawz1e +xl Aaron lx +xl cook +xlDerekxl +xlReversal xlibrio xlr704 +xlxl xlxl +xmikeyx +xmxxmx56 xoKt xoRUNExo +xolindseyxo +xoreZ xoro xoxoverdose xozoca +xp per hour +xp waster 47 +xpabax +xpcumsez xpeyotex +xplo it +xpoltergiest xpwast3d +xrated xslayerbobx xson xspacedoll xstic xtcoming999 +xthn +xtothedee xttx xtyrantix +xvn +xvrph +xvza +xwl xwuwesxdrf +xx darkminer xxDerp xxMatter +xxREVENANTxx xxSabin09xx xxSe7en +xxTanner +xxcombatgodx +xxero +xxok xxpurskillxx xxx2sp3cuxxx +xyujah xyzn +xzanle +xzb +xzizzil +xzn 2 +xzwu +y om +y w +y0y0keepitup +y37ir y8s1 +yTried +ya pig +ya winning yaJaeT yaboyalex +yack daniels yacobson99 +yae yahn yai5 yamawaro yamb0 +yamsauze yankmynads yannickal4 +yanq10 +yaowiee +yapgragim +yasuo god69 yayasquirrel ydoc +ye idc +ye ki M +ye p to +yea im jebus yeahboi9992 +yeahsmitty +yeahyoink yearstowaste +yeastyboys yechu +yedgy +yee yee fkrs yeet +yellow car01 yeniL yerrakunt yesigame yesy0u yinghao870 +yinksnerf yinonormal yipcl yippie1317 yispaulcute +ykO Ops +ykeL +yleisradio ylliB +ym y ymca4life +yna +yo eight +yo itz ep +yobacha yodA yoda +yoda monkey yohaha123 yolomuffin +yonex +yonghoon +yooyeet +yorkietootz +yorue +you died123 +you r a qt youb1n +youneedcope +young meows +young vibby +youngbriks +youngmachine youngtrop21 youngwings +youpougou your +your thrall youralterego +youssef +yowasupppbra +yoyoma361 +yp +ypoodle +ypool4 ypools +yragbackward +yrhs yribbles +ytsud mi +ytuiop +yugiiii +yuh huh +yum +yummybolete yung +yung chubs +yung cody yung inf +yungando +yungcozzy +yuor yurciq +yuy165 +yves klein +yvonko ywwoT yxow +yyknaH J +yyttam yyzanadu94 +yzyzyzy +z 5 x +z Animal z +z Rhodes +z Shane +z a p s +z iQ +z man827 z0nk +z0rrda z0up +z1ftz +z4np +z5p +z7co +zBando zBenn zBonesz zCocoa zDaremeth zDeku +zFalcone68 +zGameDance +zGanj +zGibao +zGz zJenga zKamp +zKno zKuru zLost +zMonchi +zNico +zNolan zRarePiece +zRavenx zSlay +zSpaceRaptor +zSwanny zTitan zWxLF +za2 za33erklan zaaza zacharial +zachhy zachte zack +zack_0333 +zacyy zaddy +zak vol zalm zakaru9999 +zakk0 +zakrey +zakrobgames zalem +zancan2 +zapper920 zarehp zarfeq +zartrez zasmaru +zatch_maxed +zazalover69 +zazazhizhma zazyga +zbra zclx zcollins +zdi zdin +zdya ze4l zeKrD zeachh zeall +zeano +zebakkes +zeccStake +zedabranca zeddicus676 zedisabled zedzke +zeeebak zeeno zegg +zehstee +zeikhannes zeker zeldris +zelo1 zendendead zenmort zensix +zenuk zay zenyte4money +zeppelin0ooh zeqha zer0 zer0sirisFHK +zerker seven zerkhelm zero zero gd +zero u_u +zeroblade81 zeroixx zerotwonine zeroz136 zerzerz +zeus energy zewos +zewzh +zhengu +zhipowerz zi0n1 +zibri7 +ziewe zifyr +ziggy man 77 +ziily zilyana zina +zina n chill zinax +ziniD +zinzares +zionari +zippp zirreael +ziurbtw +ziuys ziyi_wangler +zjh +zjind +zlayaa zlink10 zlliM +zlowden zmakisan znap5 zoan @@ -31177,35 +64106,55 @@ zoboz zohcysp zokunashi zole +zomff zomgrick zonares zookwaa zoomixd zop1 zorkie +zoro u zouse +zparky +zppr +zq0 zreL +zrl zruG zrzwns ztaM ztkfps +zu b zubor1 zuenzima +zujal +zuk a cuck +zuk xd +zuk2hard4me +zukushichi +zulogz zulrah zuluTriPig zummorak zurcc zurlond zuuryy +zw zwah zwint zwolle zwqpDEOXl23 +zxpf zyeetz +zygarde92 zygis323 +zykez zymaa zymz zysuna +zzad zzai +zzben zzero zzigma +zzzJonno diff --git a/Server/data/cache/main_file_cache.dat2 b/Server/data/cache/main_file_cache.dat2 index 5658efe32..245fa6081 100644 --- a/Server/data/cache/main_file_cache.dat2 +++ b/Server/data/cache/main_file_cache.dat2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2985b1eac72527b3badb3316d61ba0043310f3c38dd6a5626ae2bfb082fee200 -size 91621090 +oid sha256:b5431211b019b9403b4cfca933f4c9635c1d5278d3730995dced0d8672b1cc91 +size 91702293 diff --git a/Server/data/cache/main_file_cache.idx19 b/Server/data/cache/main_file_cache.idx19 index e46de80f4..853d55e14 100644 --- a/Server/data/cache/main_file_cache.idx19 +++ b/Server/data/cache/main_file_cache.idx19 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4de110552ac35ab5395e44b79133450ba04eac1fa36c4eceb7271cda858024f1 +oid sha256:3e8d1f3dd00aed753029371a37eb75f2068a6bfb902d481891a2e15afd3a9f34 size 348 diff --git a/Server/data/cache/main_file_cache.idx255 b/Server/data/cache/main_file_cache.idx255 index 4c00d4443..6e504259a 100644 --- a/Server/data/cache/main_file_cache.idx255 +++ b/Server/data/cache/main_file_cache.idx255 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad2c31c2f1ce536aee407c59c733ece2809e4ee9b930108f13e149744521ed56 +oid sha256:83a2292c515596af0423764c48e41dfe1aac482920dca0b89ecb343db6dd4c30 size 174 diff --git a/Server/data/cache/main_file_cache.idx5 b/Server/data/cache/main_file_cache.idx5 index dad8dfec7..f19fd00c7 100644 --- a/Server/data/cache/main_file_cache.idx5 +++ b/Server/data/cache/main_file_cache.idx5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ef6807f6653e09be9b6e2299055f1132c34dc6108e8b92fbae5add2233c2d64 -size 22092 +oid sha256:32bda84b31731cd60f7bc4e90caf7d55671e966fa958d60f2ec91da16340743d +size 22188 diff --git a/Server/data/configs/ammo_configs.json b/Server/data/configs/ammo_configs.json index 4cc7dbc0a..47da425c3 100644 --- a/Server/data/configs/ammo_configs.json +++ b/Server/data/configs/ammo_configs.json @@ -125,7 +125,7 @@ "start_graphic": "232,96", "darkbow_graphic": "", "projectile": "226,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "813", @@ -133,7 +133,7 @@ "start_graphic": "233,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "814", @@ -141,7 +141,7 @@ "start_graphic": "234,96", "darkbow_graphic": "", "projectile": "228,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "815", @@ -149,7 +149,7 @@ "start_graphic": "235,96", "darkbow_graphic": "", "projectile": "229,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "816", @@ -157,7 +157,7 @@ "start_graphic": "236,96", "darkbow_graphic": "", "projectile": "230,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "817", @@ -165,7 +165,7 @@ "start_graphic": "237,96", "darkbow_graphic": "", "projectile": "231,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "825", @@ -221,7 +221,7 @@ "start_graphic": "206,96", "darkbow_graphic": "", "projectile": "200,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "832", @@ -229,7 +229,7 @@ "start_graphic": "207,96", "darkbow_graphic": "", "projectile": "201,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "833", @@ -237,7 +237,7 @@ "start_graphic": "208,96", "darkbow_graphic": "", "projectile": "202,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "834", @@ -245,7 +245,7 @@ "start_graphic": "209,96", "darkbow_graphic": "", "projectile": "203,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "835", @@ -253,7 +253,7 @@ "start_graphic": "210,96", "darkbow_graphic": "", "projectile": "204,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "836", @@ -261,7 +261,7 @@ "start_graphic": "211,96", "darkbow_graphic": "", "projectile": "205,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "863", @@ -325,7 +325,7 @@ "start_graphic": "219,96", "darkbow_graphic": "", "projectile": "212,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "871", @@ -333,7 +333,7 @@ "start_graphic": "220,96", "darkbow_graphic": "", "projectile": "213,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "872", @@ -341,7 +341,7 @@ "start_graphic": "221,96", "darkbow_graphic": "", "projectile": "214,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "873", @@ -349,7 +349,7 @@ "start_graphic": "223,96", "darkbow_graphic": "", "projectile": "216,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "874", @@ -357,7 +357,7 @@ "start_graphic": "222,96", "darkbow_graphic": "", "projectile": "215,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "875", @@ -365,7 +365,7 @@ "start_graphic": "224,96", "darkbow_graphic": "", "projectile": "217,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "876", @@ -373,7 +373,7 @@ "start_graphic": "225,96", "darkbow_graphic": "", "projectile": "218,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "877", @@ -389,7 +389,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "879", @@ -421,7 +421,7 @@ "start_graphic": "19,96", "darkbow_graphic": "1104,96", "projectile": "10,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "884", @@ -437,7 +437,7 @@ "start_graphic": "18,96", "darkbow_graphic": "1105,96", "projectile": "9,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "886", @@ -453,7 +453,7 @@ "start_graphic": "20,96", "darkbow_graphic": "1106,96", "projectile": "11,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "888", @@ -469,7 +469,7 @@ "start_graphic": "21,96", "darkbow_graphic": "1107,96", "projectile": "12,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "890", @@ -485,7 +485,7 @@ "start_graphic": "22,96", "darkbow_graphic": "1108,96", "projectile": "13,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "892", @@ -501,7 +501,7 @@ "start_graphic": "24,96", "darkbow_graphic": "1109,96", "projectile": "15,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "2532", @@ -565,7 +565,7 @@ "start_graphic": "273,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "4160", @@ -725,7 +725,7 @@ "start_graphic": "19,96", "darkbow_graphic": "1104,96", "projectile": "10,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5617", @@ -733,7 +733,7 @@ "start_graphic": "18,96", "darkbow_graphic": "1105,96", "projectile": "9,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5618", @@ -741,7 +741,7 @@ "start_graphic": "20,96", "darkbow_graphic": "1106,96", "projectile": "11,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5619", @@ -749,7 +749,7 @@ "start_graphic": "21,96", "darkbow_graphic": "1107,96", "projectile": "12,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5620", @@ -757,7 +757,7 @@ "start_graphic": "22,96", "darkbow_graphic": "1108,96", "projectile": "13,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5621", @@ -773,7 +773,7 @@ "start_graphic": "19,96", "darkbow_graphic": "1104,96", "projectile": "10,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5623", @@ -781,7 +781,7 @@ "start_graphic": "18,96", "darkbow_graphic": "1105,96", "projectile": "9,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5624", @@ -789,7 +789,7 @@ "start_graphic": "20,96", "darkbow_graphic": "1106,96", "projectile": "11,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5625", @@ -797,7 +797,7 @@ "start_graphic": "21,96", "darkbow_graphic": "1107,96", "projectile": "12,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5626", @@ -805,7 +805,7 @@ "start_graphic": "22,96", "darkbow_graphic": "1108,96", "projectile": "13,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5627", @@ -813,7 +813,7 @@ "start_graphic": "24,96", "darkbow_graphic": "1109,96", "projectile": "15,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5628", @@ -821,7 +821,7 @@ "start_graphic": "232,96", "darkbow_graphic": "", "projectile": "226,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5629", @@ -829,7 +829,7 @@ "start_graphic": "233,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5630", @@ -837,7 +837,7 @@ "start_graphic": "235,96", "darkbow_graphic": "", "projectile": "229,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5631", @@ -845,7 +845,7 @@ "start_graphic": "273,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5633", @@ -853,7 +853,7 @@ "start_graphic": "236,96", "darkbow_graphic": "", "projectile": "230,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5634", @@ -861,7 +861,7 @@ "start_graphic": "237,96", "darkbow_graphic": "", "projectile": "231,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5635", @@ -869,7 +869,7 @@ "start_graphic": "232,96", "darkbow_graphic": "", "projectile": "226,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5636", @@ -877,7 +877,7 @@ "start_graphic": "233,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5637", @@ -885,7 +885,7 @@ "start_graphic": "234,96", "darkbow_graphic": "", "projectile": "228,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5638", @@ -893,7 +893,7 @@ "start_graphic": "273,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5639", @@ -901,7 +901,7 @@ "start_graphic": "235,96", "darkbow_graphic": "", "projectile": "229,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5640", @@ -909,7 +909,7 @@ "start_graphic": "236,96", "darkbow_graphic": "", "projectile": "230,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5641", @@ -917,7 +917,7 @@ "start_graphic": "237,96", "darkbow_graphic": "", "projectile": "231,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5642", @@ -925,7 +925,7 @@ "start_graphic": "206,96", "darkbow_graphic": "", "projectile": "200,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5643", @@ -933,7 +933,7 @@ "start_graphic": "207,96", "darkbow_graphic": "", "projectile": "201,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5644", @@ -941,7 +941,7 @@ "start_graphic": "208,96", "darkbow_graphic": "", "projectile": "202,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5645", @@ -949,7 +949,7 @@ "start_graphic": "209,96", "darkbow_graphic": "", "projectile": "203,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5646", @@ -957,7 +957,7 @@ "start_graphic": "210,96", "darkbow_graphic": "", "projectile": "204,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5647", @@ -965,7 +965,7 @@ "start_graphic": "211,96", "darkbow_graphic": "", "projectile": "205,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5648", @@ -973,7 +973,7 @@ "start_graphic": "206,96", "darkbow_graphic": "", "projectile": "200,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5649", @@ -981,7 +981,7 @@ "start_graphic": "207,96", "darkbow_graphic": "", "projectile": "201,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5650", @@ -989,7 +989,7 @@ "start_graphic": "208,96", "darkbow_graphic": "", "projectile": "202,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5651", @@ -997,7 +997,7 @@ "start_graphic": "209,96", "darkbow_graphic": "", "projectile": "203,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5652", @@ -1005,7 +1005,7 @@ "start_graphic": "210,96", "darkbow_graphic": "", "projectile": "204,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5653", @@ -1013,7 +1013,7 @@ "start_graphic": "211,96", "darkbow_graphic": "", "projectile": "205,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5654", @@ -1021,7 +1021,7 @@ "start_graphic": "219,96", "darkbow_graphic": "", "projectile": "212,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5655", @@ -1029,7 +1029,7 @@ "start_graphic": "220,96", "darkbow_graphic": "", "projectile": "213,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5656", @@ -1037,7 +1037,7 @@ "start_graphic": "221,96", "darkbow_graphic": "", "projectile": "214,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5657", @@ -1045,7 +1045,7 @@ "start_graphic": "223,96", "darkbow_graphic": "", "projectile": "216,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5658", @@ -1053,7 +1053,7 @@ "start_graphic": "222,96", "darkbow_graphic": "", "projectile": "215,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5659", @@ -1061,7 +1061,7 @@ "start_graphic": "224,96", "darkbow_graphic": "", "projectile": "217,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5660", @@ -1069,7 +1069,7 @@ "start_graphic": "225,96", "darkbow_graphic": "", "projectile": "218,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5661", @@ -1077,7 +1077,7 @@ "start_graphic": "219,96", "darkbow_graphic": "", "projectile": "212,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5662", @@ -1085,7 +1085,7 @@ "start_graphic": "220,96", "darkbow_graphic": "", "projectile": "213,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5663", @@ -1093,7 +1093,7 @@ "start_graphic": "221,96", "darkbow_graphic": "", "projectile": "214,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5664", @@ -1101,7 +1101,7 @@ "start_graphic": "223,96", "darkbow_graphic": "", "projectile": "216,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5665", @@ -1109,7 +1109,7 @@ "start_graphic": "222,96", "darkbow_graphic": "", "projectile": "215,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5666", @@ -1117,7 +1117,7 @@ "start_graphic": "224,96", "darkbow_graphic": "", "projectile": "217,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5667", @@ -1125,7 +1125,7 @@ "start_graphic": "225,96", "darkbow_graphic": "", "projectile": "218,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "6061", @@ -1133,7 +1133,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "6062", @@ -1141,7 +1141,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "6522", @@ -1293,7 +1293,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9287", @@ -1301,7 +1301,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9288", @@ -1309,7 +1309,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9289", @@ -1317,7 +1317,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9290", @@ -1325,7 +1325,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9291", @@ -1333,7 +1333,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9293", @@ -1341,7 +1341,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9294", @@ -1349,7 +1349,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9295", @@ -1357,7 +1357,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9296", @@ -1365,7 +1365,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9297", @@ -1373,7 +1373,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9298", @@ -1381,7 +1381,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9300", @@ -1389,7 +1389,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9301", @@ -1397,7 +1397,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9302", @@ -1405,7 +1405,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9303", @@ -1413,7 +1413,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9304", @@ -1421,7 +1421,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9305", @@ -1429,7 +1429,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9335", @@ -1557,7 +1557,7 @@ "start_graphic": "1116,96", "darkbow_graphic": "1114,96", "projectile": "1120,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "11228", @@ -1565,7 +1565,7 @@ "start_graphic": "1116,96", "darkbow_graphic": "1114,96", "projectile": "1120,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "11229", @@ -1573,7 +1573,7 @@ "start_graphic": "1116,96", "darkbow_graphic": "1114,96", "projectile": "1120,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "11230", @@ -1589,7 +1589,7 @@ "start_graphic": "1123,96", "darkbow_graphic": "", "projectile": "1122,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "11233", @@ -1597,7 +1597,7 @@ "start_graphic": "1123,96", "darkbow_graphic": "", "projectile": "1122,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "11234", @@ -1605,7 +1605,7 @@ "start_graphic": "1123,96", "darkbow_graphic": "", "projectile": "1122,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "13083", @@ -1621,7 +1621,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "13085", @@ -1629,7 +1629,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "13086", @@ -1637,7 +1637,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "13280", @@ -1661,7 +1661,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "1837,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "13881", @@ -1669,7 +1669,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "1837,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "13882", @@ -1677,7 +1677,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "1837,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "13883", @@ -1701,7 +1701,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "1837,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "13955", @@ -1709,7 +1709,7 @@ "start_graphic": "1837,96", "darkbow_graphic": "", "projectile": "1840,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "13956", @@ -1717,7 +1717,7 @@ "start_graphic": "1837,96", "darkbow_graphic": "", "projectile": "1840,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "13957", diff --git a/Server/data/configs/door_configs.json b/Server/data/configs/door_configs.json index 8d6a61db5..4dd902862 100644 --- a/Server/data/configs/door_configs.json +++ b/Server/data/configs/door_configs.json @@ -301,7 +301,7 @@ }, { "id": "2025", - "replaceId": "1534", + "replaceId": "2026", "fence": "false", "metal": "false" }, @@ -312,7 +312,7 @@ "metal": "false" }, { - "id": "2036", + "id": "1530", "replaceId": "1531", "fence": "false", "metal": "false" @@ -925,7 +925,7 @@ }, { "id": "3747", - "replaceId": "1534", + "replaceId": "3748", "fence": "false", "metal": "false" }, @@ -943,7 +943,7 @@ }, { "id": "4148", - "replaceId": "4248", + "replaceId": "4246", "fence": "false", "metal": "false" }, @@ -1439,6 +1439,12 @@ "fence": "false", "metal": "false" }, + { + "id": "11470", + "replaceId": "11471", + "fence": "false", + "metal": "false" + }, { "id": "11483", "replaceId": "11708", @@ -1759,7 +1765,7 @@ }, { "id": "14245", - "replaceId": "14248", + "replaceId": "14246", "fence": "true", "metal": "false" }, @@ -1984,16 +1990,14 @@ "replaceId": "28518", "fence": "false", "metal": "true", - "autowalk": "true", - "questRequirement": "Icthlarin's Little Helper" + "autowalk": "true" }, { "id": "28514", "replaceId": "28518", "fence": "false", "metal": "true", - "autowalk": "true", - "questRequirement": "Icthlarin's Little Helper" + "autowalk": "true" }, { "id": "21065", diff --git a/Server/data/configs/drop_tables.json b/Server/data/configs/drop_tables.json index 3794ccbe0..474c5bbe7 100644 --- a/Server/data/configs/drop_tables.json +++ b/Server/data/configs/drop_tables.json @@ -3262,7 +3262,7 @@ } ], "charm": [], - "ids": "73,74,75,419,420,421,422,423,424,1826,2714,2863,2866,2869,2878,3622,4392,4393,4394,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331,5375,5376,5377,5378,5379,5380,5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408,5409,5410,6099,6100,6131", + "ids": "73,74,75,419,420,421,422,423,424,2714,2863,2866,2869,2878,3622,4392,4393,4394,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331,5375,5376,5377,5378,5379,5380,5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408,5409,5410,6099,6100,6131", "description": "", "main": [ { @@ -5858,205 +5858,165 @@ "charm": [ { "minAmount": "1", - "weight": "100.0", - "id": "12158", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "12159", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "12160", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "12163", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", + "weight": "1000.0", "id": "0", "maxAmount": "1" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "4986.5", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "12.5", + "id": "10976", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "10977", + "maxAmount": "1" + } + ], "ids": "114", "description": "", "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "7844", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "10977", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "10976", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5104", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "12176", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5293", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "3.0", + "weight": "39.0625", "id": "6666", "maxAmount": "1" }, { "minAmount": "1", - "weight": "3.0", + "weight": "78.125", "id": "6665", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "13.0", + "id": "14422", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "13.0", + "id": "14430", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "156.25", + "id": "345", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "156.25", + "id": "327", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "156.25", + "id": "371", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "156.25", + "id": "372", + "maxAmount": "5" + }, + { + "minAmount": "1", + "weight": "156.25", + "id": "359", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "156.25", + "id": "360", + "maxAmount": "3" + }, + { + "minAmount": "1", + "weight": "39.0625", + "id": "383", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "39.0625", + "id": "349", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "39.0625", + "id": "331", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "156.25", + "id": "313", + "maxAmount": "15" + }, + { + "minAmount": "1", + "weight": "156.25", + "id": "1511", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "39.0625", + "id": "1383", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "78.125", + "id": "407", + "maxAmount": "1" + }, + { + "minAmount": "4", + "weight": "78.125", + "id": "402", + "maxAmount": "8" + }, + { + "minAmount": "5", + "weight": "4.0", + "id": "555", + "maxAmount": "18" + }, + { + "minAmount": "3", + "weight": "78.125", + "id": "6664", + "maxAmount": "6" + }, + { + "minAmount": "1", + "weight": "39.0625", + "id": "401", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "39.0625", + "id": "6667", + "maxAmount": "1" } ] }, @@ -11426,47 +11386,11 @@ "weight": "100.0", "id": "526", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "1506", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "6065", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "6067", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "6069", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "6068", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "6070", - "maxAmount": "1" } ], "charm": [], - "ids": "347,348,357,369,370,371,372,717,718,719,2349,2350,2351,2373,2374,2784,3216", - "description": "", + "ids": "370,357,348,369,347,371", + "description": "all combat mourners", "main": [] }, { @@ -18161,7 +18085,7 @@ "maxAmount": "1" } ], - "ids": "1183,1184,1201,2359,2360,2361,2362,7438,7439,7440,7441", + "ids": "1183,1184,1201,2359,2360,2361,2362,2373,7438,7439,7440,7441", "description": "", "main": [ { @@ -20143,6 +20067,208 @@ } ] }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [ + { + "minAmount": "1", + "weight": "55.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "13.0", + "id": "12158", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "6.0", + "id": "12159", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "25.0", + "id": "12160", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "12163", + "maxAmount": "1" + } + ], + "ids": "1338", + "description": "Chaos Tunnels / Lighthouse Dagannoths", + "main": [ + { + "minAmount": "1", + "weight": "5.0", + "id": "1237", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "1239", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1243", + "maxAmount": "1" + }, + { + "minAmount": "15", + "weight": "4.0", + "id": "555", + "maxAmount": "15" + }, + { + "minAmount": "15", + "weight": "2.0", + "id": "886", + "maxAmount": "15" + }, + { + "minAmount": "3", + "weight": "1.0", + "id": "828", + "maxAmount": "3" + }, + { + "minAmount": "1", + "weight": "18.0", + "id": "14428", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "12.0", + "id": "301", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "4.0", + "id": "345", + "maxAmount": "3" + }, + { + "minAmount": "5", + "weight": "4.0", + "id": "327", + "maxAmount": "5" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "311", + "maxAmount": "1" + }, + { + "minAmount": "15", + "weight": "2.0", + "id": "314", + "maxAmount": "15" + }, + { + "minAmount": "50", + "weight": "2.0", + "id": "313", + "maxAmount": "50" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "377", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "359", + "maxAmount": "1" + }, + { + "minAmount": "10", + "weight": "2.0", + "id": "402", + "maxAmount": "10" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "413", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "411", + "maxAmount": "2" + }, + { + "minAmount": "56", + "weight": "29.0", + "id": "995", + "maxAmount": "56" + }, + { + "minAmount": "25", + "weight": "9.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "44", + "weight": "8.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "41", + "weight": "6.0", + "id": "995", + "maxAmount": "41" + }, + { + "minAmount": "12", + "weight": "2.0", + "id": "45", + "maxAmount": "12" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "405", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "5733", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "14426", + "maxAmount": "1" + } + ] + }, { "default": [ { @@ -25464,8 +25590,8 @@ "maxAmount": "1" } ], - "ids": "1665,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025,6212,6213", - "description": "", + "ids": "6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6212,6213", + "description": "Werewolves (and human form for Canifis)", "main": [ { "minAmount": "5", @@ -26042,12 +26168,6 @@ "id": "686", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "25.0", @@ -26328,12 +26448,6 @@ "id": "1129", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "50.0", @@ -28355,60 +28469,10 @@ "maxAmount": "1" } ], - "charm": [ - { - "minAmount": "1", - "weight": "85.8093", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "10.9113", - "id": "12158", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.6775", - "id": "12159", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.6019", - "id": "12160", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.0", - "id": "12163", - "maxAmount": "1" - } - ], - "ids": "2044,2045,2046,2047,2048,2049,2051,2052,2053,2054,2055", + "charm": [], + "ids": "2044,2045,2046,2047,2048,2049,2050,2051,2052,2053,2054,2055,2056,2057", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "1.0", - "id": "1", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "124.0", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "3.0", - "id": "7848", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -31596,7 +31660,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "532", + "id": "526", "maxAmount": "1" } ], @@ -31632,26 +31696,6 @@ "maxAmount": "1" } ], - "tertiary": [ - { - "minAmount": "1", - "weight": "9973.0", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "10976", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "2.0", - "id": "10977", - "maxAmount": "1" - } - ], "ids": "2783", "description": "", "main": [ @@ -36414,12 +36458,6 @@ "weight": "100.0", "id": "532", "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "100.0", - "id": "526", - "maxAmount": "3" } ], "charm": [], @@ -37667,12 +37705,6 @@ "id": "1129", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "50.0", @@ -39709,7 +39741,20 @@ ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "532", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "9080", + "maxAmount": "1" + } + ], "charm": [ { "minAmount": "1", @@ -43345,280 +43390,6 @@ } ] }, - { - "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "2859", - "maxAmount": "1" - } - ], - "charm": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "12158", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "12159", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "12160", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "12163", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" - } - ], - "ids": "6028", - "description": "", - "main": [ - { - "minAmount": "5", - "weight": "50.0", - "id": "2138", - "maxAmount": "5" - }, - { - "minAmount": "5", - "weight": "50.0", - "id": "2132", - "maxAmount": "5" - }, - { - "minAmount": "5", - "weight": "50.0", - "id": "2136", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1993", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1325", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1353", - "maxAmount": "1" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "886", - "maxAmount": "50" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1157", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1329", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1181", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1109", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1147", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "830", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "10", - "weight": "50.0", - "id": "995", - "maxAmount": "10" - }, - { - "minAmount": "90", - "weight": "50.0", - "id": "995", - "maxAmount": "90" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "6814", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "958", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "7868", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "227", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "245", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "983", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "239", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1925", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] - }, { "default": [], "charm": [], @@ -58922,9 +58693,6 @@ ] }, { - "charm": [], - "ids": "1676", - "description": "Experiment (level 51)", "default": [ { "minAmount": "1", @@ -58939,6 +58707,9 @@ "maxAmount": "1" } ], + "charm": [], + "ids": "1676", + "description": "Experiment (level 51)", "main": [] }, { @@ -58954,5 +58725,53 @@ "ids": "3672,3673,5168,5169,5170", "description": "Ram", "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1583", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "6108", + "description": "", + "main": [] + }, + { + "default": [], + "charm": [], + "ids": "795", + "description": "", + "main": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "1580", + "maxAmount": "1" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "592", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "8127", + "description": "Dark energy core", + "main": [] } ] \ No newline at end of file diff --git a/Server/data/configs/ground_spawns.json b/Server/data/configs/ground_spawns.json index 2e49e5d29..7ddcf821d 100644 --- a/Server/data/configs/ground_spawns.json +++ b/Server/data/configs/ground_spawns.json @@ -1,654 +1,686 @@ [ { - "item_id": "28", - "loc_data": "{1,2807,3450,0,7864410}" - }, - { - "item_id": "33", - "loc_data": "{1,2547,3114,0,983050}-{1,3208,3395,1,3932190}" - }, - { - "item_id": "39", - "loc_data": "{1,2672,3433,0,7209050}" - }, - { - "item_id": "41", - "loc_data": "{1,3125,9997,0,7209050}" - }, - { - "item_id": "53", - "loc_data": "{3,3240,3940,0,300}-{2,3237,3948,0,300}-{3,3235,3949,0,300}" - }, - { - "item_id": "83", - "loc_data": "{1,2637,9819,0,30}" - }, - { - "item_id": "84", - "loc_data": "{1,2638,9906,0,30}" - }, - { - "item_id": "85", - "loc_data": "{1,2628,9859,0,30}" - }, - { - "item_id": "88", - "loc_data": "{1,2654,9767,0,4587583}" - }, - { - "item_id": "90", - "loc_data": "{1,2570,9604,0,5898270}" - }, - { - "item_id": "185", - "loc_data": "{1,2467,3176,0,7209050}" - }, - { - "item_id": "223", - "loc_data": "{1,3128,9956,0,7864410}-{1,3129,9954,0,7864410}-{1,3126,9958,0,7864410}-{1,3117,9951,0,7864410}-{1,3118,9948,0,7864410}-{1,3119,9949,0,7864410}-{1,3179,9881,0,7864410}-{1,3177,9880,0,7864410}" - }, - { - "item_id": "229", - "loc_data": "{1,2809,3342,0,7864420}-{1,2808,3343,0,7864420}-{1,2588,3090,2,7209050}-{1,2587,3090,2,7209050}-{1,3195,3849,0,6553660}-{1,3186,3836,0,6553660}" - }, - { - "item_id": "231", - "loc_data": "{1,2905,3297,0,1310730}-{1,2907,3295,0,1310730}-{1,2553,3751,0,7864410}-{1,2553,3754,0,7864410}-{1,2552,3757,0,7864410}-{1,2546,3763,0,7864410}-{1,2542,3765,0,7864410}-{1,2540,3765,0,7864410}-{1,2528,3716,0,7864410}-{1,2501,3727,0,7864410}-{1,2502,3726,0,7864410}-{1,2501,3729,0,7864410}-{1,2500,3730,0,7864410}-{1,2500,3731,0,7864410}-{1,2501,3732,0,7864410}-{1,2503,3733,0,7864410}-{1,2504,3732,0,7864410}-{1,2506,3731,0,7864410}-{1,2529,3735,0,7864410}-{1,2531,3736,0,7864410}-{1,2527,3746,0,7864410}-{1,2525,3751,0,7864410}-{1,2532,3762,0,7864410}-{1,2503,3757,0,5898270}-{1,2502,3756,0,5898270}-{1,2502,3754,0,5898270}-{1,2502,3752,0,5898270}-{1,2503,3751,0,5898270}-{1,2505,3753,0,5898270}" - }, - { - "item_id": "239", - "loc_data": "{1,3217,3812,0,196610}-{1,3210,3808,0,196610}" - }, - { - "item_id": "243", - "loc_data": "{1,2910,9809,0,30}-{1,2908,9805,0,30}-{1,2902,9806,0,30}-{1,2906,9796,0,30}-{1,2905,9801,0,30}" - }, - { - "item_id": "245", - "loc_data": "{1,2931,3515,0,4587570}" - }, - { - "item_id": "247", - "loc_data": "{1,2512,3080,0,5898270}-{1,2517,3082,0,5898270}-{1,2509,3088,0,5898270}-{1,2508,3084,0,5898270}" - }, - { - "item_id": "272", - "loc_data": "{1,3108,3356,1,10485900}" - }, - { - "item_id": "273", - "loc_data": "{1,3097,3366,0,10485900}" - }, - { - "item_id": "276", - "loc_data": "{1,3111,3367,0,4587570}" - }, - { - "item_id": "277", - "loc_data": "{1,3092,9755,0,4587570}" - }, - { - "item_id": "278", - "loc_data": "{1,2604,3358,0,4587570}" - }, - { - "item_id": "301", - "loc_data": "{1,2608,3397,0,7209050}" - }, - { - "item_id": "303", - "loc_data": "{1,3245,3155,0,10485900}-{1,3244,3159,0,10485900}-{1,3429,4784,0,75}-{1,3432,4783,0,75}-{1,3414,4768,0,75}-{1,3429,4769,0,75}-{1,3431,4772,0,75}-{1,3415,4786,0,75}-{1,3412,4784,0,75}" - }, - { - "item_id": "305", - "loc_data": "{1,2605,3395,0,7209050}" - }, - { - "item_id": "307", - "loc_data": "{1,2860,3335,0,13762750}" - }, - { - "item_id": "311", - "loc_data": "{1,2605,3396,0,7209050}" - }, - { - "item_id": "347", - "loc_data": "{1,2563,9511,0,5898270}" - }, - { - "item_id": "357", - "loc_data": "{1,2820,3453,0,7209050}" - }, - { - "item_id": "401", - "loc_data": "{1,2810,3387,0,11796640}-{1,2809,3388,0,11796640}-{1,2809,3385,0,11796640}-{1,2807,3386,0,11796640}-{1,2804,3385,0,11796640}-{1,2807,3384,0,11796640}-{1,2805,3384,0,11796640}-{1,2802,3382,0,11796640}-{1,2805,3381,0,11796640}-{1,2804,3380,0,11796640}-{1,3829,3053,0,11796640}-{1,3813,3066,0,11796640}-{1,3814,3064,0,11796640}-{1,3808,3060,0,11796640}-{1,2693,3727,0,11796640}-{1,2698,3729,0,11796640}-{1,2700,3731,0,11796640}-{1,2708,3728,0,11796640}-{1,2725,3731,0,11796640}-{1,2721,3730,0,11796640}-{1,2719,3733,0,11796640}-{1,2714,3733,0,11796640}-{1,2712,3732,0,11796640}-{1,2896,3119,0,11796640}-{1,2914,3111,0,11796640}-{1,2926,3110,0,11796640}-{1,2939,3102,0,11796640}-{1,2938,3073,0,11796640}-{1,2945,3068,0,11796640}-{1,2975,3013,0,11796640}-{1,2955,3010,0,11796640}-{1,2959,2959,0,11796640}-{1,2943,2950,0,11796640}-{1,2866,2976,0,11796640}-{1,2854,2977,0,11796640}-{1,2839,2975,0,11796640}-{1,2757,2947,0,11796640}-{1,2755,2952,0,11796640}-{1,2786,2960,0,11796640}-{1,2782,2987,0,11796640}-{1,2771,2995,0,11796640}-{1,2761,3000,0,11796640}-{1,2755,3008,0,11796640}-{1,2754,3017,0,11796640}-{1,2756,3060,0,11796640}-{1,2755,3070,0,11796640}-{1,2756,3074,0,11796640}-{1,2806,3128,0,11796640}-{1,2864,3195,0,11796640}-{1,2764,3131,0,11796640}-{1,2753,3125,0,11796640}-{1,2757,3109,0,11796640}" - }, - { - "item_id": "444", - "loc_data": "{1,3195,9821,0,11141270}-{1,3231,3739,0,19661050}-{1,3236,3741,0,19661050}" - }, - { - "item_id": "480", - "loc_data": "{1,3298,3313,0,5898270}" - }, - { - "item_id": "526", - "loc_data": "{1,2917,9796,0,7864410}-{1,2914,9796,0,7864410}-{1,2910,9797,0,7864410}-{1,2908,9794,0,7864410}-{1,3103,9953,0,7864410}-{1,3104,9950,0,7864410}-{1,3110,9952,0,7864410}-{1,3111,9956,0,7864410}-{1,3110,9958,0,7864410}-{1,3111,9959,0,7864410}-{1,3116,9950,0,7864410}-{1,3127,9957,0,7864410}-{1,2926,9801,0,7864410}-{1,2924,9801,0,7864410}-{1,2924,9804,0,7864410}-{1,2927,9805,0,7864410}-{1,2929,9807,0,7864410}-{1,2938,9792,0,7864410}-{1,2938,9796,0,7864410}-{1,2935,9799,0,7864410}-{1,2930,9794,0,7864410}-{1,2932,9792,0,7864410}-{1,2966,9772,0,7864410}-{1,2968,9771,0,7864410}-{1,2903,9826,0,7864410}-{1,2906,9823,0,7864410}-{1,2906,9825,0,7864410}-{1,2907,9824,0,7864410}-{1,2910,9825,0,7864410}-{1,3101,9825,0,7864410}-{1,3107,9823,0,7864410}-{1,3109,9823,0,7864410}-{1,3110,9825,0,7864410}-{1,3138,9880,0,7864410}-{1,3141,9879,0,7864410}-{1,3142,9880,0,7864410}-{1,3143,9878,0,7864410}-{1,3097,9902,0,7864410}-{1,3094,9907,0,7864410}-{1,3093,9884,0,7864410}-{1,3098,9886,0,7864410}-{1,3093,9879,0,7864410}-{1,3122,9891,0,7864410}-{1,3116,9891,0,7864410}-{1,3119,9894,0,7864410}-{1,3120,9894,0,7864410}-{1,2831,9766,0,7864410}-{1,2829,9764,0,7864410}-{1,2510,3080,0,5898270}-{1,2516,3084,0,5898270}-{1,2509,3090,0,5898270}-{1,2512,3084,0,5898270}-{1,2510,3084,0,5898270}-{1,2505,3114,0,5898270}-{1,2507,3116,0,5898270}-{1,2503,3118,0,5898270}-{1,2510,3114,0,5898270}-{1,3238,3606,0,9830500}-{1,3236,3605,0,9830500}-{1,3238,3603,0,9830500}-{1,3240,3603,0,9830500}-{1,3242,3604,0,9830500}-{1,3244,3611,0,9830500}-{1,3247,3614,0,9830500}-{1,3238,3610,0,9830500}-{1,3238,3608,0,9830500}-{1,3232,3948,0,9830500}-{1,3237,3938,0,9830500}-{1,3250,3952,0,9830500}" - }, - { - "item_id": "528", - "loc_data": "{1,3182,3848,0,6553650}-{1,3178,3851,0,6553660}-{1,3179,3853,0,6553660}-{1,3185,3857,0,6553660}-{1,3187,3853,0,6553660}-{1,3211,3822,0,6553660}-{1,3212,3819,0,6553660}-{1,3217,3816,0,6553660}-{1,3218,3813,0,6553660}" - }, - { - "item_id": "542", - "loc_data": "{1,3059,3488,1,7209050}" - }, - { - "item_id": "544", - "loc_data": "{1,3059,3487,1,7209050}" - }, - { - "item_id": "554", - "loc_data": "{1,3303,3311,0,5898270}-{1,3026,3636,0,19661000}" - }, - { - "item_id": "555", - "loc_data": "{1,3297,3316,0,5898270}-{1,3028,3636,0,19661000}-{3,2960,3899,0,19661000}" - }, - { - "item_id": "556", - "loc_data": "{1,2938,3158,0,8519790}-{1,3030,3636,0,19661000}" - }, - { - "item_id": "557", - "loc_data": "{1,3032,3636,0,19661000}" - }, - { - "item_id": "558", - "loc_data": "{1,3206,3208,0,5898280}-{1,3023,3640,0,19661000}" - }, - { - "item_id": "559", - "loc_data": "{1,3089,3866,0,5898270}-{1,3021,3637,0,19661000}-{1,3233,3573,0,19661000}-{1,3234,3578,0,19661000}-{1,3230,3582,0,19661000}" - }, - { - "item_id": "561", - "loc_data": "{1,2671,3737,0,19661000}-{1,2672,3738,0,19661000}" - }, - { - "item_id": "562", - "loc_data": "{1,3021,3640,0,19661000}-{1,3137,3833,0,19661000}-{1,3139,3814,0,19661000}-{1,3145,3814,0,19661000}-{1,3144,3826,0,19661000}-{1,3149,3823,0,19661000}-{1,3151,3831,0,19661000}" - }, - { - "item_id": "564", - "loc_data": "{3,2947,3898,0,19661000}" - }, - { - "item_id": "590", - "loc_data": "{1,2368,3135,0,10485900}-{1,2431,3072,0,10485900}-{1,3112,3369,2,10485900}-{1,3209,3734,0,13107300}" - }, - { - "item_id": "677", - "loc_data": "{1,3369,3378,0,11141270}" - }, - { - "item_id": "687", - "loc_data": "{1,2655,3424,0,6553680}-{1,2665,3424,0,6553680}-{1,2669,3423,0,6553680}-{1,2668,3419,0,6553680}-{1,2671,3420,0,6553680}-{1,2677,3423,0,6553680}-{1,2676,3425,0,6553680}-{1,2676,3422,0,6553680}-{1,2679,3424,0,6553680}-{1,2678,3426,0,6553680}-{1,2679,3423,0,6553680}-{1,2680,3424,0,6553680}-{1,2672,3427,0,6553680}-{1,2676,3430,0,6553680}-{1,2673,3428,0,6553680}-{1,2670,3437,0,6553680}-{1,2649,9854,0,6553680}-{1,2643,9853,0,6553680}-{1,2644,9851,0,6553680}" - }, - { - "item_id": "708", - "loc_data": "{1,3363,9831,0,5898270}-{1,3370,9826,0,5898270}-{1,3368,9770,0,5898270}-{1,3364,9764,0,5898270}" - }, - { - "item_id": "712", - "loc_data": "{1,2887,3412,0,5898270}-{1,2903,3441,0,5898270}" - }, - { - "item_id": "767", - "loc_data": "{1,3245,3385,1,4587570}-{1,3243,3383,1,4587570}" - }, - { - "item_id": "882", - "loc_data": "{1,3135,9916,0,10485900}-{3,3130,9903,0,10485900}-{1,3205,3227,0,10485900}-{1,2957,3205,0,5898270}-{1,2944,3332,0,5898270}-{1,3104,3599,0,13107300}-{1,3103,3596,0,13107300}-{1,3100,3593,0,13107300}-{1,3096,3595,0,13107300}-{1,3094,3598,0,13107300}-{1,3100,3609,0,13107300}-{1,3104,3610,0,13107300}-{1,3107,3611,0,13107300}-{1,3108,3603,0,13107300}" - }, - { - "item_id": "946", - "loc_data": "{1,3218,9887,0,6553680}{1,2700,3407,0,6553680}-{1,3205,3212,0,6553680}-{1,3215,9625,0,6553680}-{1,3224,3202,0,6553680}-{1,2903,3148,0,6553680}-{1,2820,3450,0,7209050}-{1,3218,3416,1,7864410}-{1,3106,3956,0,5242940}-{1,2566,9526,0,5898270}" - }, - { - "item_id": "952", - "loc_data": "{1,2566,3330,0,100}-{1,3120,3359,0,8519790}-{1,2981,3370,0,9830490}-{1,3572,3312,0,3276830}-{1,3571,3310,0,3276830}-{1,3218,3412,1,5898270}-{1,1951,4964,0,5898270}" - }, - { - "item_id": "954", - "loc_data": "{1,2094,3152,0,11141270}" - }, - { - "item_id": "960", - "loc_data": "{1,2846,3384,0,11796640}-{1,2848,3383,0,11796640}-{1,2851,3239,0,11796640}-{1,2847,3238,0,11796640}-{1,2845,3232,0,11796640}-{1,2856,3231,0,11796640}-{1,2857,3236,0,11796640}-{1,3216,3665,0,16384200}-{1,3224,3668,0,16384200}-{1,3245,3678,0,16384200}-{1,3230,3686,0,16384200}-{1,3216,3677,0,16384200}-{1,3219,3680,0,16384200}-{1,2550,3575,0,16384200}-{1,2553,3576,0,16384200}-{1,2554,3575,0,16384200}-{1,2556,3573,0,16384200}" - }, - { - "item_id": "966", - "loc_data": "{1,3237,3696,0,13107350}" - }, - { - "item_id": "970", - "loc_data": "{1,3290,3033,1,7864410}" - }, - { - "item_id": "983", - "loc_data": "{1,3131,9862,0,7209050}" - }, - { - "item_id": "995", - "loc_data": "{1,2909,9800,0,11141270}-{4,2912,9801,0,11141270}-{8,2910,9803,0,11141270}-{5,2907,9807,0,11141270}-{6,2913,9806,0,11141270}-{1,2922,9820,0,11141270}-{1,2934,9834,0,11141270}-{1,2917,9850,0,11141270}-{1,2914,9849,0,11141270}-{4,3088,9898,0,11141270}-{1,3088,9899,0,11141270}-{1,3091,9899,0,11141270}-{3,3195,9834,0,11141270}-{4,3195,9820,0,11141270}-{66,3191,9821,0,11141270}-{56,3190,9819,0,11141270}-{26,3188,9819,0,11141270}-{26,3188,9820,0,11141270}-{35,3189,9819,0,11141270}-{384,3224,3830,0,6553650}-{384,3220,3824,0,6553650}-{2,3106,3547,0,13107300}-{1,3104,3558,0,13107300}-{1,3106,3534,0,13107300}-{2,3101,3564,0,13107300}-{4,3103,3579,0,13107300}-{2,3234,3560,0,13107300}" - }, - { - "item_id": "1005", - "loc_data": "{1,3014,3227,0,3276830}-{1,3009,3204,0,3276830}" - }, - { - "item_id": "1009", - "loc_data": "{1,3122,9881,0,3276830}" - }, - { - "item_id": "1059", - "loc_data": "{1,3242,3385,1,7209050}-{1,3097,3486,0,7864410}-{1,3148,3177,0,5898270}" - }, - { - "item_id": "1061", - "loc_data": "{1,3244,3386,1,13762750}-{1,3208,9620,0,13762750}-{1,3210,9615,0,13762750}-{1,3111,3159,0,13762750}-{1,3112,3155,0,7864420}-{1,3302,3190,0,5898270}" - }, - { - "item_id": "1069", - "loc_data": "{1,3249,3740,0,7209050}" - }, - { - "item_id": "1119", - "loc_data": "{1,3084,3859,0,6553660}" - }, - { - "item_id": "1137", - "loc_data": "{1,3242,3688,0,13107350}" - }, - { - "item_id": "1139", - "loc_data": "{1,3122,3360,0,13762750}" - }, - { - "item_id": "1171", - "loc_data": "{1,3217,3514,0,2949150}" - }, - { - "item_id": "1191", - "loc_data": "{1,3250,3797,0,13107350}" - }, - { - "item_id": "1203", - "loc_data": "{1,3242,3383,1,17039600}-{1,3248,3245,0,13107380}-{1,3216,3695,0,13107350}" - }, - { - "item_id": "1205", - "loc_data": "{1,3213,3216,1,13762750}" - }, - { - "item_id": "1207", - "loc_data": "{1,3213,3682,0,13107300}" - }, - { - "item_id": "1217", - "loc_data": "{1,3180,3821,0,6553660}" - }, - { - "item_id": "1265", - "loc_data": "{1,3081,3429,0,6553650}-{1,3288,9442,0,7864410}-{1,3288,9431,0,7864410}-{1,2963,3216,0,5898270}-{1,3229,3218,2,100}" - }, - { - "item_id": "1321", - "loc_data": "{1,2965,3211,1,5898270}" - }, - { - "item_id": "1351", - "loc_data": "{1,2795,3161,0,17039600}-{1,2970,3376,1,5898270}" - }, - { - "item_id": "1385", - "loc_data": "{1,3099,3862,0,58983299}" - }, - { - "item_id": "1422", - "loc_data": "{1,3320,3137,0,11141270}" - }, - { - "item_id": "1510", - "loc_data": "{1,2576,3334,0,100}" - }, - { - "item_id": "1511", - "loc_data": "{1,3205,3226,2,11796640}-{1,3205,3224,2,11796640}-{1,3208,3225,2,11796640}-{1,3209,3224,2,11796640}-{1,3106,3160,0,11796640}-{1,3106,3159,0,11796640}-{1,3105,3159,0,11796640}-{1,2958,3205,0,5898270}-{1,2959,3205,0,5898270}" - }, - { - "item_id": "1523", - "loc_data": "{1,2614,9570,0,5898270}" - }, - { - "item_id": "1550", - "loc_data": "{1,2714,3478,0,5898270}" - }, - { - "item_id": "1573", - "loc_data": "{1,2559,2975,0,327690}-{1,2560,2974,0,327690}-{1,2561,2973,0,327690}-{1,2561,2976,0,327690}-{1,2740,3637,0,5898270}-{1,2736,3638,0,5898270}-{1,2735,3636,0,5898270}-{1,2743,3636,0,5898270}-{1,2739,3634,0,5898270}-{1,2743,3640,0,5898270}-{1,2741,3639,0,5898270}-{1,2736,3641,0,5898270}-{1,2738,3641,0,5898270}-{1,2734,3640,0,5898270}-{1,2738,3636,0,5898270}" - }, - { - "item_id": "1590", - "loc_data": "{1,2900,9766,0,7209050}" - }, - { - "item_id": "1592", - "loc_data": "{1,2935,3283,1,7209050}" - }, - { - "item_id": "1595", - "loc_data": "{1,2928,3290,0,7209050}" - }, - { - "item_id": "1597", - "loc_data": "{1,2932,3287,1,7209050}" - }, - { - "item_id": "1599", - "loc_data": "{1,2931,3287,1,7209050}" - }, - { - "item_id": "1607", - "loc_data": "{1,3169,3887,0,32768400}-{1,3169,3887,0,32768400}-{1,2679,3740,0,32768400}" - }, - { - "item_id": "1641", - "loc_data": "{1,3196,9822,0,11141270}" - }, - { - "item_id": "1654", - "loc_data": "{1,3192,9821,0,11141270}" - }, - { - "item_id": "1734", - "loc_data": "{1,3286,3491,0,4587570}" - }, - { - "item_id": "1735", - "loc_data": "{1,3192,3272,0,10485900}-{1,3126,3356,0,10485900}-{1,2930,3285,1,7209050}-{1,3152,3306,0,5898270}" - }, - { - "item_id": "1755", - "loc_data": "{1,2935,3286,0,7209050}" - }, - { - "item_id": "1785", - "loc_data": "{1,2822,3355,0,11141270}" - }, - { - "item_id": "1856", - "loc_data": "{1,2638,3292,0,11141270}" - }, - { - "item_id": "1887", - "loc_data": "{1,3141,3452,1,7209050}" - }, - { - "item_id": "1917", - "loc_data": "{1,2799,3156,0,11141270}-{1,2796,3165,0,11141270}-{1,3080,3438,0,4587565}-{1,3286,3033,1,7864410}-{1,3077,3439,0,5898270}-{1,3077,3443,0,5898270}-{1,3049,3257,0,11141270}" - }, - { - "item_id": "1919", - "loc_data": "{1,2798,3156,0,11141270}-{1,2799,3155,0,11141270}-{1,2795,3160,0,11141270}-{1,2798,3160,0,11141270}-{1,2798,3161,0,11141270}-{1,2795,3165,0,11141270}-{1,2794,3165,0,11141270}" - }, - { - "item_id": "1921", - "loc_data": "{1,3291,3034,0,7209050}" - }, - { - "item_id": "1923", - "loc_data": "{1,3208,3214,0,6553680}-{1,3140,3452,1,7209050}" - }, - { - "item_id": "1925", - "loc_data": "{1,2564,3332,0,100}-{1,2428,3079,0,6553680}-{1,2428,3080,0,6553680}-{1,2371,3128,0,6553680}-{1,2371,3127,0,6553680}-{1,3225,3294,0,6553680}-{1,3121,3359,0,6553660}-{1,3216,9625,0,6553680}-{1,3026,3289,0,6553680}-{1,2958,3510,0,6553680}-{1,3221,3497,1,6553680}-{1,2958,3510,0,6553680}-{1,3222,3491,1,6553680}-{1,3307,3195,0,5898270}-{1,1941,4956,0,5898270}-{1,2633,9908,0,100}" - }, - { - "item_id": "1927", - "loc_data": "{1,2685,3683,0,1966}" - }, - { - "item_id": "1929", - "loc_data": "{1,2820,3452,0,7209050}-{1,2823,3449,0,7209050}" - }, - { - "item_id": "1931", - "loc_data": "{1,3209,3214,0,6553680}-{1,3144,3449,2,7209050}-{1,3166,3310,0,7864410}" - }, - { - "item_id": "1935", - "loc_data": "{1,3211,3212,0,6553680}-{1,3211,9625,0,6553680}-{1,2905,3146,0,6553680}-{1,2936,3292,0,7209050}-{1,3142,3447,2,7209050}" - }, - { - "item_id": "1939", - "loc_data": "{1,3193,3181,0,11141270}-{1,3194,3168,0,11141270}-{1,3191,3162,0,11141270}-{1,3189,3163,0,11141270}-{1,3185,3161,0,11141270}-{1,3182,3165,0,11141270}-{1,3172,3166,0,11141270}-{1,3170,3167,0,11141270}-{1,3164,3169,0,11141270}-{1,3171,3177,0,11141270}-{1,3173,3178,0,11141270}-{1,3164,3180,0,11141270}-{1,3165,3187,0,11141270}-{1,3171,3191,0,11141270}-{1,3178,3190,0,11141270}-{1,3181,3193,0,11141270}-{1,3182,3181,0,11141270}-{1,3493,3408,0,3932190}-{1,3494,3402,0,3932190}-{1,3494,3396,0,3932190}-{1,3499,3401,0,3932190}" - }, - { - "item_id": "1944", - "loc_data": "{1,3229,3299,0,3604515}-{1,3226,3301,0,3604515}-{1,3191,3276,0,3604515}-{1,2853,3370,0,3604515}-{1,2852,3369,0,3604515}-{1,2851,3372,0,3604515}-{1,3015,3295,0,5898270}-{1,3016,3295,0,5898270}" - }, - { - "item_id": "1955", - "loc_data": "{1,3141,3447,1,7209050}-{1,3140,3447,1,7209050}-{1,3141,3447,2,7209050}" - }, - { - "item_id": "1963", - "loc_data": "{1,2907,3146,0,11141270}-{1,3009,3207,0,11141270}" - }, - { - "item_id": "1965", - "loc_data": "{1,3217,9622,0,7209050}" - }, - { - "item_id": "1971", - "loc_data": "{1,2843,3369,0,11796640}" - }, - { - "item_id": "1973", - "loc_data": "{1,3143,3453,0,5242920}-{1,2384,4440,0,5242920}" - }, - { - "item_id": "1982", - "loc_data": "{1,3085,3261,0,11141270}-{1,3039,3706,0,1310735}-{1,2584,2966,0,327690}" - }, - { - "item_id": "1985", - "loc_data": "{1,3083,3260,0,11141270}-{1,3039,3707,0,1310735}" - }, - { - "item_id": "1987", - "loc_data": "{1,3237,9761,0,7209050}-{1,3144,3450,2,7209050}" - }, - { - "item_id": "1993", - "loc_data": "{1,3291,3033,1,7864410}" - }, - { - "item_id": "2025", - "loc_data": "{1,2449,3510,1,7864410}" - }, - { - "item_id": "2026", - "loc_data": "{1,2483,3482,1,7864410}-{1,2489,3489,1,7864410}" - }, - { - "item_id": "2126", - "loc_data": "{1,2661,3478,0,100}-{1,2638,3480,0,100}-{1,2632,3492,0,100}-{1,2648,3498,0,100}-{1,2669,3497,0,100}" - }, - { - "item_id": "2140", - "loc_data": "{1,2971,3382,1,5898270}" - }, - { - "item_id": "2142", - "loc_data": "{1,3080,3443,0,5898270}-{1,3077,3441,0,5898270}" - }, - { - "item_id": "2150", - "loc_data": "{1,2418,3511,0,3932190}-{1,2417,3512,0,3932190}-{1,2416,3512,0,5898270}-{1,2412,3511,0,5898270}-{1,2411,3519,0,5898270}-{1,2415,3518,0,5898270}-{1,2417,3516,0,5898270}-{1,2417,3515,0,5898270}-{1,2421,3519,0,5898270}-{1,2424,3517,0,5898270}-{1,2424,3514,0,5898270}-{1,2428,3510,0,5898270}-{1,2407,3516,0,5898270}-{1,2907,3393,0,5898270}-{1,2903,3400,0,5898270}-{1,2908,3410,0,5898270}" - }, - { - "item_id": "2162", - "loc_data": "{1,2425,3515,0,5898270}-{1,2415,3517,0,5898270}-{1,2414,3518,0,5898270}-{1,2419,3511,0,5898270}-{1,2415,3511,0,5898270}-{1,2414,3510,0,5898270}-{1,2427,3508,0,5898270}-{1,2426,3507,0,5898270}-{1,2408,3518,0,5898270}-{1,2896,3414,0,5898270}" - }, - { - "item_id": "2313", - "loc_data": "{1,3142,3447,1,7209050}-{1,2820,3455,0,7209050}-{1,3222,3494,0,7864410}" - }, - { - "item_id": "2347", - "loc_data": "{1,2934,3286,0,7209050}-{1,2579,3463,0,5242920}-{1,2975,3368,1,5898270}-{1,1953,4974,0,5898270}" - }, - { - "item_id": "2357", - "loc_data": "{1,3192,9822,0,11141270}" - }, - { - "item_id": "2398", - "loc_data": "{1,2517,3049,0,6553650}-{1,2511,3045,0,6553650}" - }, - { - "item_id": "2407", - "loc_data": "{1,2935,3460,0,5}" - }, - { - "item_id": "2408", - "loc_data": "{1,2903,3471,0,5}" - }, - { - "item_id": "3109", - "loc_data": "{1,2893,3565,0,1441794}" - }, - { - "item_id": "3110", - "loc_data": "{1,2893,3564,0,1441794}" - }, - { - "item_id": "3111", - "loc_data": "{1,2893,3561,0,1441794}" - }, - { - "item_id": "3112", - "loc_data": "{1,2893,3563,0,1441794}" - }, - { - "item_id": "3113", - "loc_data": "{1,2893,3562,0,1441794}" - }, - { - "item_id": "3138", - "loc_data": "{1,3463,9478,2,3932205}-{1,3461,9480,2,3932205}-{1,3461,9482,2,3932205}-{1,3461,9484,2,3932205}" - }, - { - "item_id": "3711", - "loc_data": "{1,2660,3676,0,196}" - }, - { - "item_id": "3803", - "loc_data": "{1,2658,3676,0,1966}" - }, - { - "item_id": "3805", - "loc_data": "{1,2659,3676,0,196}" - }, - { - "item_id": "4707", - "loc_data": "{1,3571,3312,0,3932200}" - }, - { - "item_id": "4199", - "loc_data": "{1,3492,3474,0,0}" - }, - { - "item_id": "5523", - "loc_data": "{1,2935,3282,1,7209050}" - }, - { - "item_id": "6291", - "loc_data": "{1,2681,3111,0,5898270}-{1,2673,3112,0,5898270}-{1,2674,3094,0,5898270}-{1,2671,3089,0,5898270}" - }, - { - "item_id": "6662", - "loc_data": "{1,2984,3112,0,8519770}" - }, - { - "item_id": "6663", - "loc_data": "{1,2984,3114,0,8519770}" - }, - { - "item_id": "11065", - "loc_data": "{1,2928,3289,0,7209050}" - }, - { - "item_id": "12494", - "loc_data": "{1,2762,2973,0,7864380}" - }, - { - "item_id": "5008", - "loc_data": "{1,3230,9609,0,300}" - }, - { - "item_id": "7510", - "loc_data": "{1,2867,9875,0,300}" - }, - { - "item_id": "1913", - "loc_data": "{1,2863,9875,0,300}-{1,2861,9878,0,300}" - }, - { - "item_id": "4619", - "loc_data": "{1,2713,4913,0,2}" - }, - { - "item_id": "2964", - "loc_data": "{1,3437,3337,0,100}" - }, - { - "item_id": "2957", - "loc_data": "{1,3443,9742,0,100}-{1,3443,9742,1,100}" + "item_id": "20", + "loc_data": "{1,2577,9655,0,100}-" }, { "item_id": "21", - "loc_data": "{1,2613,9639,0,100}" - }, - { - "item_id": "23", - "loc_data": "{1,2583,9613,0,100}" - }, - { - "item_id": "24", - "loc_data": "{1,2564,9662,0,100}" - }, - { - "item_id": "20", - "loc_data": "{1,2577,9655,0,100}" + "loc_data": "{1,2613,9639,0,100}-" }, { "item_id": "22", - "loc_data": "{1,2574,9633,0,100}" + "loc_data": "{1,2574,9633,0,100}-" + }, + { + "item_id": "23", + "loc_data": "{1,2583,9613,0,100}-" + }, + { + "item_id": "24", + "loc_data": "{1,2564,9662,0,100}-" + }, + { + "item_id": "28", + "loc_data": "{1,2807,3450,0,90}-" + }, + { + "item_id": "33", + "loc_data": "{1,2547,3114,0,10}-{1,3208,3395,1,30}-" + }, + { + "item_id": "39", + "loc_data": "{1,2672,3433,0,90}-" + }, + { + "item_id": "41", + "loc_data": "{1,3125,9997,0,90}-" + }, + { + "item_id": "53", + "loc_data": "{3,3240,3940,0,44}-{2,3237,3948,0,44}-{3,3235,3949,0,44}-" + }, + { + "item_id": "83", + "loc_data": "{1,2637,9819,0,30}-" + }, + { + "item_id": "84", + "loc_data": "{1,2638,9906,0,30}-" + }, + { + "item_id": "85", + "loc_data": "{1,2628,9859,0,30}-" + }, + { + "item_id": "88", + "loc_data": "{1,2654,9767,0,63}-" + }, + { + "item_id": "90", + "loc_data": "{1,2570,9604,0,30}-" + }, + { + "item_id": "185", + "loc_data": "{1,2467,3176,0,90}-" + }, + { + "item_id": "223", + "loc_data": "{1,3179,9881,0,90}-{1,3177,9880,0,90}-{1,3128,9956,0,90}-{1,3129,9954,0,90}-{1,3126,9958,0,90}-{1,3117,9951,0,90}-{1,3118,9948,0,90}-{1,3119,9949,0,90}-" + }, + { + "item_id": "229", + "loc_data": "{1,2588,3090,2,90}-{1,2587,3090,2,90}-{1,2809,3342,0,100}-{1,2808,3343,0,100}-{1,3186,3836,0,60}-{1,3195,3849,0,60}-" + }, + { + "item_id": "231", + "loc_data": "{1,2905,3297,0,10}-{1,2907,3295,0,10}-{1,2553,3751,0,90}-{1,2553,3754,0,90}-{1,2552,3757,0,90}-{1,2546,3763,0,90}-{1,2542,3765,0,90}-{1,2540,3765,0,90}-{1,2528,3716,0,90}-{1,2501,3727,0,90}-{1,2502,3726,0,90}-{1,2501,3729,0,90}-{1,2500,3730,0,90}-{1,2500,3731,0,90}-{1,2501,3732,0,90}-{1,2503,3733,0,90}-{1,2504,3732,0,90}-{1,2506,3731,0,90}-{1,2529,3735,0,90}-{1,2531,3736,0,90}-{1,2527,3746,0,90}-{1,2525,3751,0,90}-{1,2532,3762,0,90}-{1,2503,3757,0,30}-{1,2502,3756,0,30}-{1,2502,3754,0,30}-{1,2502,3752,0,30}-{1,2503,3751,0,30}-{1,2505,3753,0,30}-" + }, + { + "item_id": "239", + "loc_data": "{1,3217,3812,0,2}-{1,3210,3808,0,2}-" + }, + { + "item_id": "243", + "loc_data": "{1,2910,9809,0,30}-{1,2908,9805,0,30}-{1,2902,9806,0,30}-{1,2906,9796,0,30}-{1,2905,9801,0,30}-" + }, + { + "item_id": "245", + "loc_data": "{1,2931,3515,0,50}-" + }, + { + "item_id": "247", + "loc_data": "{1,2512,3080,0,30}-{1,2517,3082,0,30}-{1,2509,3088,0,30}-{1,2508,3084,0,30}-" + }, + { + "item_id": "272", + "loc_data": "{1,3108,3356,1,140}-" + }, + { + "item_id": "273", + "loc_data": "{1,3097,3366,0,140}-" + }, + { + "item_id": "276", + "loc_data": "{1,3111,3367,0,50}-" + }, + { + "item_id": "277", + "loc_data": "{1,3092,9755,0,50}-" + }, + { + "item_id": "278", + "loc_data": "{1,2604,3358,0,50}-" + }, + { + "item_id": "301", + "loc_data": "{1,2608,3397,0,90}-" + }, + { + "item_id": "303", + "loc_data": "{1,3245,3155,0,140}-{1,3244,3159,0,140}-{1,3429,4784,0,75}-{1,3432,4783,0,75}-{1,3414,4768,0,75}-{1,3429,4769,0,75}-{1,3431,4772,0,75}-{1,3415,4786,0,75}-{1,3412,4784,0,75}-" + }, + { + "item_id": "305", + "loc_data": "{1,2605,3395,0,90}-" + }, + { + "item_id": "307", + "loc_data": "{1,2860,3335,0,190}-" + }, + { + "item_id": "311", + "loc_data": "{1,2605,3396,0,90}-" + }, + { + "item_id": "347", + "loc_data": "{1,2563,9511,0,30}-" + }, + { + "item_id": "357", + "loc_data": "{1,2820,3453,0,90}-" + }, + { + "item_id": "401", + "loc_data": "{1,2959,2959,0,160}-{1,2943,2950,0,160}-{1,2866,2976,0,160}-{1,2854,2977,0,160}-{1,2839,2975,0,160}-{1,2757,2947,0,160}-{1,2755,2952,0,160}-{1,2786,2960,0,160}-{1,2782,2987,0,160}-{1,2771,2995,0,160}-{1,2761,3000,0,160}-{1,3829,3053,0,160}-{1,3813,3066,0,160}-{1,3814,3064,0,160}-{1,3808,3060,0,160}-{1,2945,3068,0,160}-{1,2975,3013,0,160}-{1,2955,3010,0,160}-{1,2755,3008,0,160}-{1,2754,3017,0,160}-{1,2756,3060,0,160}-{1,2755,3070,0,160}-{1,2896,3119,0,160}-{1,2914,3111,0,160}-{1,2926,3110,0,160}-{1,2939,3102,0,160}-{1,2938,3073,0,160}-{1,2756,3074,0,160}-{1,2806,3128,0,160}-{1,2764,3131,0,160}-{1,2753,3125,0,160}-{1,2757,3109,0,160}-{1,2864,3195,0,160}-{1,2810,3387,0,160}-{1,2809,3388,0,160}-{1,2809,3385,0,160}-{1,2807,3386,0,160}-{1,2804,3385,0,160}-{1,2807,3384,0,160}-{1,2805,3384,0,160}-{1,2802,3382,0,160}-{1,2805,3381,0,160}-{1,2804,3380,0,160}-{1,2693,3727,0,160}-{1,2698,3729,0,160}-{1,2700,3731,0,160}-{1,2708,3728,0,160}-{1,2725,3731,0,160}-{1,2721,3730,0,160}-{1,2719,3733,0,160}-{1,2714,3733,0,160}-{1,2712,3732,0,160}-" + }, + { + "item_id": "444", + "loc_data": "{1,3231,3739,0,250}-{1,3236,3741,0,250}-{1,3195,9821,0,150}-" + }, + { + "item_id": "480", + "loc_data": "{1,3298,3313,0,30}-" + }, + { + "item_id": "526", + "loc_data": "{1,2510,3080,0,30}-{1,2516,3084,0,30}-{1,2509,3090,0,30}-{1,2512,3084,0,30}-{1,2510,3084,0,30}-{1,2505,3114,0,30}-{1,2507,3116,0,30}-{1,2503,3118,0,30}-{1,2510,3114,0,30}-{1,3238,3606,0,100}-{1,3236,3605,0,100}-{1,3238,3603,0,100}-{1,3240,3603,0,100}-{1,3242,3604,0,100}-{1,3244,3611,0,100}-{1,3247,3614,0,100}-{1,3238,3610,0,100}-{1,3238,3608,0,100}-{1,3232,3948,0,100}-{1,3237,3938,0,100}-{1,3250,3952,0,100}-{1,2966,9772,0,90}-{1,2968,9771,0,90}-{1,2831,9766,0,90}-{1,2829,9764,0,90}-{1,2917,9796,0,90}-{1,2914,9796,0,90}-{1,2910,9797,0,90}-{1,2908,9794,0,90}-{1,2926,9801,0,90}-{1,2924,9801,0,90}-{1,2924,9804,0,90}-{1,2927,9805,0,90}-{1,2929,9807,0,90}-{1,2938,9792,0,90}-{1,2938,9796,0,90}-{1,2935,9799,0,90}-{1,2930,9794,0,90}-{1,2932,9792,0,90}-{1,2903,9826,0,90}-{1,2906,9823,0,90}-{1,2906,9825,0,90}-{1,2907,9824,0,90}-{1,2910,9825,0,90}-{1,3101,9825,0,90}-{1,3107,9823,0,90}-{1,3109,9823,0,90}-{1,3110,9825,0,90}-{1,3138,9880,0,90}-{1,3141,9879,0,90}-{1,3142,9880,0,90}-{1,3143,9878,0,90}-{1,3097,9902,0,90}-{1,3094,9907,0,90}-{1,3093,9884,0,90}-{1,3098,9886,0,90}-{1,3093,9879,0,90}-{1,3122,9891,0,90}-{1,3116,9891,0,90}-{1,3119,9894,0,90}-{1,3120,9894,0,90}-{1,3103,9953,0,90}-{1,3104,9950,0,90}-{1,3110,9952,0,90}-{1,3111,9956,0,90}-{1,3110,9958,0,90}-{1,3111,9959,0,90}-{1,3116,9950,0,90}-{1,3127,9957,0,90}-" + }, + { + "item_id": "528", + "loc_data": "{1,3211,3822,0,60}-{1,3212,3819,0,60}-{1,3217,3816,0,60}-{1,3218,3813,0,60}-{1,3182,3848,0,50}-{1,3178,3851,0,60}-{1,3179,3853,0,60}-{1,3185,3857,0,60}-{1,3187,3853,0,60}-" + }, + { + "item_id": "542", + "loc_data": "{1,3059,3488,1,90}-" + }, + { + "item_id": "544", + "loc_data": "{1,3059,3487,1,90}-" + }, + { + "item_id": "554", + "loc_data": "{1,3303,3311,0,30}-{1,3026,3636,0,200}-" + }, + { + "item_id": "555", + "loc_data": "{1,3297,3316,0,30}-{1,3028,3636,0,200}-{3,2960,3899,0,200}-" + }, + { + "item_id": "556", + "loc_data": "{1,2938,3158,0,110}-{1,3030,3636,0,200}-" + }, + { + "item_id": "557", + "loc_data": "{1,3032,3636,0,200}-" + }, + { + "item_id": "558", + "loc_data": "{1,3206,3208,0,40}-{1,3023,3640,0,200}-" + }, + { + "item_id": "559", + "loc_data": "{1,3233,3573,0,200}-{1,3234,3578,0,200}-{1,3230,3582,0,200}-{1,3021,3637,0,200}-{1,3089,3866,0,30}-" + }, + { + "item_id": "561", + "loc_data": "{1,2671,3737,0,200}-{1,2672,3738,0,200}-" + }, + { + "item_id": "562", + "loc_data": "{1,3021,3640,0,200}-{1,3137,3833,0,200}-{1,3139,3814,0,200}-{1,3145,3814,0,200}-{1,3144,3826,0,200}-{1,3149,3823,0,200}-{1,3151,3831,0,200}-" + }, + { + "item_id": "564", + "loc_data": "{3,2947,3898,0,200}-" + }, + { + "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}-" + }, + { + "item_id": "687", + "loc_data": "{1,2655,3424,0,80}-{1,2665,3424,0,80}-{1,2669,3423,0,80}-{1,2668,3419,0,80}-{1,2671,3420,0,80}-{1,2677,3423,0,80}-{1,2676,3425,0,80}-{1,2676,3422,0,80}-{1,2679,3424,0,80}-{1,2678,3426,0,80}-{1,2679,3423,0,80}-{1,2680,3424,0,80}-{1,2672,3427,0,80}-{1,2676,3430,0,80}-{1,2673,3428,0,80}-{1,2670,3437,0,80}-{1,2649,9854,0,80}-{1,2643,9853,0,80}-{1,2644,9851,0,80}-" + }, + { + "item_id": "708", + "loc_data": "{1,3368,9770,0,30}-{1,3364,9764,0,30}-{1,3363,9831,0,30}-{1,3370,9826,0,30}-" + }, + { + "item_id": "712", + "loc_data": "{1,2887,3412,0,30}-{1,2903,3441,0,30}-" + }, + { + "item_id": "767", + "loc_data": "{1,3245,3385,1,50}-{1,3243,3383,1,50}-" + }, + { + "item_id": "882", + "loc_data": "{1,3205,3227,0,140}-{1,2957,3205,0,30}-{1,2944,3332,0,30}-{1,3104,3599,0,100}-{1,3103,3596,0,100}-{1,3100,3593,0,100}-{1,3096,3595,0,100}-{1,3094,3598,0,100}-{1,3100,3609,0,100}-{1,3104,3610,0,100}-{1,3107,3611,0,100}-{1,3108,3603,0,100}-{1,3135,9916,0,140}-{3,3130,9903,0,140}-" + }, + { + "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}-" + }, + { + "item_id": "952", + "loc_data": "{1,3572,3312,0,30}-{1,3571,3310,0,30}-{1,3120,3359,0,110}-{1,2566,3330,0,100}-{1,2981,3370,0,90}-{1,3218,3412,1,30}-{1,1951,4964,0,30}-" + }, + { + "item_id": "954", + "loc_data": "{1,2094,3152,0,150}-{1,2785,3279,0,150}-{1,2786,3287,0,150}-{1,2786,3286,0,150}-" + }, + { + "item_id": "960", + "loc_data": "{1,2851,3239,0,160}-{1,2847,3238,0,160}-{1,2845,3232,0,160}-{1,2856,3231,0,160}-{1,2857,3236,0,160}-{1,2846,3384,0,160}-{1,2848,3383,0,160}-{1,2550,3575,0,200}-{1,2553,3576,0,200}-{1,2554,3575,0,200}-{1,2556,3573,0,200}-{1,3216,3665,0,200}-{1,3224,3668,0,200}-{1,3245,3678,0,200}-{1,3230,3686,0,200}-{1,3216,3677,0,200}-{1,3219,3680,0,200}-" + }, + { + "item_id": "966", + "loc_data": "{1,3237,3696,0,150}-" + }, + { + "item_id": "970", + "loc_data": "{1,3290,3033,1,90}-" + }, + { + "item_id": "983", + "loc_data": "{1,3131,9862,0,90}-" + }, + { + "item_id": "995", + "loc_data": "{2,3234,3560,0,100}-{2,3106,3547,0,100}-{1,3104,3558,0,100}-{1,3106,3534,0,100}-{2,3101,3564,0,100}-{4,3103,3579,0,100}-{384,3224,3830,0,50}-{384,3220,3824,0,50}-{1,2909,9800,0,150}-{4,2912,9801,0,150}-{8,2910,9803,0,150}-{5,2907,9807,0,150}-{6,2913,9806,0,150}-{1,2922,9820,0,150}-{1,2934,9834,0,150}-{1,2917,9850,0,150}-{1,2914,9849,0,150}-{3,3195,9834,0,150}-{4,3195,9820,0,150}-{66,3191,9821,0,150}-{56,3190,9819,0,150}-{26,3188,9819,0,150}-{26,3188,9820,0,150}-{35,3189,9819,0,150}-{4,3088,9898,0,150}-{1,3088,9899,0,150}-{1,3091,9899,0,150}-" + }, + { + "item_id": "1005", + "loc_data": "{1,3014,3227,0,30}-{1,3009,3204,0,30}-" + }, + { + "item_id": "1009", + "loc_data": "{1,3122,9881,0,30}-" + }, + { + "item_id": "1059", + "loc_data": "{1,3148,3177,0,30}-{1,3242,3385,1,90}-{1,3097,3486,0,90}-" + }, + { + "item_id": "1061", + "loc_data": "{1,3111,3159,0,190}-{1,3112,3155,0,100}-{1,3302,3190,0,30}-{1,3244,3386,1,190}-{1,3208,9620,0,190}-{1,3210,9615,0,190}-" + }, + { + "item_id": "1069", + "loc_data": "{1,3249,3740,0,90}-" + }, + { + "item_id": "1119", + "loc_data": "{1,3084,3859,0,60}-" + }, + { + "item_id": "1137", + "loc_data": "{1,3242,3688,0,150}-" + }, + { + "item_id": "1139", + "loc_data": "{1,3122,3360,0,190}-" + }, + { + "item_id": "1171", + "loc_data": "{1,3217,3514,0,30}-" + }, + { + "item_id": "1191", + "loc_data": "{1,3250,3797,0,150}-" + }, + { + "item_id": "1203", + "loc_data": "{1,3248,3245,0,180}-{1,3242,3383,1,240}-{1,3216,3695,0,150}-" + }, + { + "item_id": "1205", + "loc_data": "{1,3213,3216,1,190}-" + }, + { + "item_id": "1207", + "loc_data": "{1,3213,3682,0,100}-" + }, + { + "item_id": "1217", + "loc_data": "{1,3180,3821,0,60}-" + }, + { + "item_id": "1265", + "loc_data": "{1,3229,3218,2,100}-{1,2963,3216,0,30}-{1,3081,3429,0,50}-{1,3288,9442,0,90}-{1,3288,9431,0,90}-" + }, + { + "item_id": "1321", + "loc_data": "{1,2965,3211,1,30}-" + }, + { + "item_id": "1351", + "loc_data": "{1,2795,3161,0,240}-{1,2970,3376,1,30}-" + }, + { + "item_id": "1385", + "loc_data": "{1,3099,3862,0,131}-" + }, + { + "item_id": "1422", + "loc_data": "{1,3320,3137,0,150}-" + }, + { + "item_id": "1467", + "loc_data": "{1,2784,3289,0,0}-" + }, + { + "item_id": "1469", + "loc_data": "{1,2766,3277,0,100}-{1,2766,3289,0,100}-" + }, + { + "item_id": "1510", + "loc_data": "{1,2576,3334,0,100}-" + }, + { + "item_id": "1511", + "loc_data": "{1,3106,3160,0,160}-{1,3106,3159,0,160}-{1,3105,3159,0,160}-{1,3205,3226,2,160}-{1,3205,3224,2,160}-{1,3208,3225,2,160}-{1,3209,3224,2,160}-{1,2958,3205,0,30}-{1,2959,3205,0,30}-" + }, + { + "item_id": "1523", + "loc_data": "{1,2614,9570,0,30}-" + }, + { + "item_id": "1550", + "loc_data": "{1,2714,3478,0,30}-" + }, + { + "item_id": "1573", + "loc_data": "{1,2559,2975,0,10}-{1,2560,2974,0,10}-{1,2561,2973,0,10}-{1,2561,2976,0,10}-{1,2740,3637,0,30}-{1,2736,3638,0,30}-{1,2735,3636,0,30}-{1,2743,3636,0,30}-{1,2739,3634,0,30}-{1,2743,3640,0,30}-{1,2741,3639,0,30}-{1,2736,3641,0,30}-{1,2738,3641,0,30}-{1,2734,3640,0,30}-{1,2738,3636,0,30}-" + }, + { + "item_id": "1590", + "loc_data": "{1,2900,9766,0,90}-" + }, + { + "item_id": "1592", + "loc_data": "{1,2935,3283,1,90}-" + }, + { + "item_id": "1595", + "loc_data": "{1,2928,3290,0,90}-" + }, + { + "item_id": "1597", + "loc_data": "{1,2932,3287,1,90}-" + }, + { + "item_id": "1599", + "loc_data": "{1,2931,3287,1,90}-" + }, + { + "item_id": "1607", + "loc_data": "{1,2679,3740,0,144}-{1,3169,3887,0,144}-{1,3169,3887,0,144}-" + }, + { + "item_id": "1641", + "loc_data": "{1,3196,9822,0,150}-" + }, + { + "item_id": "1654", + "loc_data": "{1,3192,9821,0,150}-" + }, + { + "item_id": "1734", + "loc_data": "{1,3286,3491,0,50}-" + }, + { + "item_id": "1735", + "loc_data": "{1,2930,3285,1,90}-{1,3192,3272,0,140}-{1,3152,3306,0,30}-{1,3126,3356,0,140}-" + }, + { + "item_id": "1755", + "loc_data": "{1,2935,3286,0,90}-" + }, + { + "item_id": "1785", + "loc_data": "{1,2822,3355,0,150}-" + }, + { + "item_id": "1812", + "loc_data": "{1,2747,3579,0,100}" + }, + { + "item_id": "1813", + "loc_data": "{1,2746,3578,0,100}" + }, + { + "item_id": "1856", + "loc_data": "{1,2638,3292,0,150}-" + }, + { + "item_id": "1887", + "loc_data": "{1,3141,3452,1,90}-" + }, + { + "item_id": "1913", + "loc_data": "{1,2863,9875,0,44}-{1,2861,9878,0,44}-" + }, + { + "item_id": "1917", + "loc_data": "{1,3286,3033,1,90}-{1,2799,3156,0,150}-{1,2796,3165,0,150}-{1,3049,3257,0,150}-{1,3080,3438,0,45}-{1,3077,3439,0,30}-{1,3077,3443,0,30}-" + }, + { + "item_id": "1919", + "loc_data": "{1,2798,3156,0,150}-{1,2799,3155,0,150}-{1,2795,3160,0,150}-{1,2798,3160,0,150}-{1,2798,3161,0,150}-{1,2795,3165,0,150}-{1,2794,3165,0,150}-" + }, + { + "item_id": "1921", + "loc_data": "{1,3291,3034,0,90}-" + }, + { + "item_id": "1923", + "loc_data": "{1,3208,3214,0,80}-{1,3140,3452,1,90}-" + }, + { + "item_id": "1925", + "loc_data": "{1,2428,3079,0,80}-{1,2428,3080,0,80}-{1,2371,3128,0,80}-{1,2371,3127,0,80}-{1,3307,3195,0,30}-{1,3225,3294,0,80}-{1,3026,3289,0,80}-{1,3121,3359,0,60}-{1,2564,3332,0,100}-{1,3221,3497,1,80}-{1,3222,3491,1,80}-{1,2958,3510,0,80}-{1,2958,3510,0,80}-{1,1941,4956,0,30}-{1,3216,9625,0,80}-{1,2633,9908,0,100}-" + }, + { + "item_id": "1927", + "loc_data": "{1,2685,3683,0,174}-" + }, + { + "item_id": "1929", + "loc_data": "{1,2820,3452,0,90}-{1,2823,3449,0,90}-" + }, + { + "item_id": "1931", + "loc_data": "{1,3209,3214,0,80}-{1,3166,3310,0,90}-{1,3144,3449,2,90}-" + }, + { + "item_id": "1935", + "loc_data": "{1,2905,3146,0,80}-{1,3211,3212,0,80}-{1,2936,3292,0,90}-{1,3142,3447,2,90}-{1,3211,9625,0,80}-" + }, + { + "item_id": "1939", + "loc_data": "{1,3193,3181,0,150}-{1,3194,3168,0,150}-{1,3191,3162,0,150}-{1,3189,3163,0,150}-{1,3185,3161,0,150}-{1,3182,3165,0,150}-{1,3172,3166,0,150}-{1,3170,3167,0,150}-{1,3164,3169,0,150}-{1,3171,3177,0,150}-{1,3173,3178,0,150}-{1,3164,3180,0,150}-{1,3165,3187,0,150}-{1,3171,3191,0,150}-{1,3178,3190,0,150}-{1,3181,3193,0,150}-{1,3182,3181,0,150}-{1,3493,3408,0,30}-{1,3494,3402,0,30}-{1,3494,3396,0,30}-{1,3499,3401,0,30}-" + }, + { + "item_id": "1944", + "loc_data": "{1,3191,3276,0,35}-{1,3229,3299,0,35}-{1,3226,3301,0,35}-{1,3015,3295,0,30}-{1,3016,3295,0,30}-{1,2853,3370,0,35}-{1,2852,3369,0,35}-{1,2851,3372,0,35}-{1,2453,4476,0,1}-" + }, + { + "item_id": "1955", + "loc_data": "{1,3141,3447,1,90}-{1,3140,3447,1,90}-{1,3141,3447,2,90}-" + }, + { + "item_id": "1963", + "loc_data": "{1,2907,3146,0,150}-{1,3009,3207,0,150}-" + }, + { + "item_id": "1965", + "loc_data": "{1,3217,9622,0,90}-" + }, + { + "item_id": "1971", + "loc_data": "{1,2843,3369,0,160}-" + }, + { + "item_id": "1973", + "loc_data": "{1,3143,3453,0,40}-{1,2384,4440,0,40}-" + }, + { + "item_id": "1982", + "loc_data": "{1,2584,2966,0,10}-{1,3085,3261,0,150}-{1,3039,3706,0,15}-" + }, + { + "item_id": "1985", + "loc_data": "{1,3083,3260,0,150}-{1,3039,3707,0,15}-" + }, + { + "item_id": "1987", + "loc_data": "{1,3144,3450,2,90}-{1,3237,9761,0,90}-" + }, + { + "item_id": "1993", + "loc_data": "{1,3291,3033,1,90}-" + }, + { + "item_id": "2025", + "loc_data": "{1,2449,3510,1,90}-" + }, + { + "item_id": "2026", + "loc_data": "{1,2483,3482,1,90}-{1,2489,3489,1,90}-" + }, + { + "item_id": "2126", + "loc_data": "{1,2661,3478,0,100}-{1,2638,3480,0,100}-{1,2632,3492,0,100}-{1,2648,3498,0,100}-{1,2669,3497,0,100}-" }, { "item_id": "2128", - "loc_data": "{1,2648,2963,0,327690}-{1,2647,2956,0,327690}-{1,2638,2949,0,327690}" + "loc_data": "{1,2648,2963,0,10}-{1,2647,2956,0,10}-{1,2638,2949,0,10}-" }, { - "item_id": "4615", - "loc_data": "{1,3479,3092,0,0}" + "item_id": "2140", + "loc_data": "{1,2971,3382,1,30}-" + }, + { + "item_id": "2142", + "loc_data": "{1,3080,3443,0,30}-{1,3077,3441,0,30}-" + }, + { + "item_id": "2150", + "loc_data": "{1,2907,3393,0,30}-{1,2903,3400,0,30}-{1,2908,3410,0,30}-{1,2418,3511,0,30}-{1,2417,3512,0,30}-{1,2416,3512,0,30}-{1,2412,3511,0,30}-{1,2411,3519,0,30}-{1,2415,3518,0,30}-{1,2417,3516,0,30}-{1,2417,3515,0,30}-{1,2421,3519,0,30}-{1,2424,3517,0,30}-{1,2424,3514,0,30}-{1,2428,3510,0,30}-{1,2407,3516,0,30}-" + }, + { + "item_id": "2162", + "loc_data": "{1,2896,3414,0,30}-{1,2425,3515,0,30}-{1,2415,3517,0,30}-{1,2414,3518,0,30}-{1,2419,3511,0,30}-{1,2415,3511,0,30}-{1,2414,3510,0,30}-{1,2427,3508,0,30}-{1,2426,3507,0,30}-{1,2408,3518,0,30}-" + }, + { + "item_id": "2313", + "loc_data": "{1,2820,3455,0,90}-{1,3142,3447,1,90}-{1,3222,3494,0,90}-" }, { "item_id": "2333", - "loc_data": "{1,3042,3952,0,100}" + "loc_data": "{1,3042,3952,0,100}-" + }, + { + "item_id": "2347", + "loc_data": "{1,2934,3286,0,90}-{1,2975,3368,1,30}-{1,2579,3463,0,40}-{1,1953,4974,0,30}-" + }, + { + "item_id": "2357", + "loc_data": "{1,3192,9822,0,150}-" + }, + { + "item_id": "2398", + "loc_data": "{1,2517,3049,0,50}-{1,2511,3045,0,50}-" + }, + { + "item_id": "2407", + "loc_data": "{1,2935,3460,0,5}-" + }, + { + "item_id": "2408", + "loc_data": "{1,2903,3471,0,5}-" + }, + { + "item_id": "2957", + "loc_data": "{1,3443,9742,0,100}-{1,3443,9742,1,100}-" + }, + { + "item_id": "2964", + "loc_data": "{1,3437,3337,0,100}-" + }, + { + "item_id": "3109", + "loc_data": "{1,2893,3565,0,2}-" + }, + { + "item_id": "3110", + "loc_data": "{1,2893,3564,0,2}-" + }, + { + "item_id": "3111", + "loc_data": "{1,2893,3561,0,2}-" + }, + { + "item_id": "3112", + "loc_data": "{1,2893,3563,0,2}-" + }, + { + "item_id": "3113", + "loc_data": "{1,2893,3562,0,2}-" + }, + { + "item_id": "3138", + "loc_data": "{1,3461,9480,2,30}-{1,3460,9484,2,30}-{1,3465,9477,2,30}-{1,3467,9493,0,30}-{1,3486,9517,0,30}-{1,3474,9509,0,30}-{1,3470,9502,0,30}-{1,3480,9483,0,30}-" + }, + { + "item_id": "3711", + "loc_data": "{1,2660,3676,0,196}-" + }, + { + "item_id": "3803", + "loc_data": "{1,2658,3676,0,174}-" + }, + { + "item_id": "3805", + "loc_data": "{1,2659,3676,0,196}-" + }, + { + "item_id": "4199", + "loc_data": "{1,3492,3474,0,0}-" + }, + { + "item_id": "4615", + "loc_data": "{1,3479,3092,0,0}-" + }, + { + "item_id": "4619", + "loc_data": "{1,2713,4913,0,2}-" + }, + { + "item_id": "4707", + "loc_data": "{1,3571,3312,0,40}-" + }, + { + "item_id": "4838", + "loc_data": "{1,2593,3103,1,40}-" + }, + { + "item_id": "5008", + "loc_data": "{1,3230,9609,0,44}-" + }, + { + "item_id": "5523", + "loc_data": "{1,2935,3282,1,90}-" + }, + { + "item_id": "5586", + "loc_data": "{1,2473,4941,0,90}-" + }, + { + "item_id": "6291", + "loc_data": "{1,2681,3111,0,30}-{1,2673,3112,0,30}-{1,2674,3094,0,30}-{1,2671,3089,0,30}-" + }, + { + "item_id": "6662", + "loc_data": "{1,2984,3112,0,90}-" + }, + { + "item_id": "6663", + "loc_data": "{1,2984,3114,0,90}-" + }, + { + "item_id": "7510", + "loc_data": "{1,2867,9875,0,44}-" + }, + { + "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}-" } -] +] \ No newline at end of file diff --git a/Server/data/configs/interface_configs.json b/Server/data/configs/interface_configs.json index 50a5a64ed..95087003a 100644 --- a/Server/data/configs/interface_configs.json +++ b/Server/data/configs/interface_configs.json @@ -95,6 +95,30 @@ "walkable": "false", "tabIndex": "-1" }, + { + "id": "68", + "interfaceType": "4", + "walkable": "false", + "tabIndex": "-1" + }, + { + "id": "69", + "interfaceType": "4", + "walkable": "false", + "tabIndex": "-1" + }, + { + "id": "70", + "interfaceType": "4", + "walkable": "false", + "tabIndex": "-1" + }, + { + "id": "71", + "interfaceType": "4", + "walkable": "false", + "tabIndex": "-1" + }, { "id": "74", "interfaceType": "4", @@ -365,6 +389,30 @@ "walkable": "false", "tabIndex": "-1" }, + { + "id": "245", + "interfaceType": "4", + "walkable": "false", + "tabIndex": "-1" + }, + { + "id": "246", + "interfaceType": "4", + "walkable": "false", + "tabIndex": "-1" + }, + { + "id": "247", + "interfaceType": "4", + "walkable": "false", + "tabIndex": "-1" + }, + { + "id": "248", + "interfaceType": "4", + "walkable": "false", + "tabIndex": "-1" + }, { "id": "256", "interfaceType": "8", diff --git a/Server/data/configs/item_configs.json b/Server/data/configs/item_configs.json index 8c2addbd0..bf79f32e1 100644 --- a/Server/data/configs/item_configs.json +++ b/Server/data/configs/item_configs.json @@ -4243,76 +4243,84 @@ "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", + "weight": "0.15", "id": "456" }, { "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", + "examine": "There is 1 scorpion inside.", "durability": null, "name": "Scorpion cage", "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", + "weight": "0.175", "id": "457" }, { "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", + "examine": "There are 2 scorpions inside.", "durability": null, "name": "Scorpion cage", "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", + "weight": "0.2", "id": "458" }, { "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", + "examine": "There is 1 scorpion inside.", "durability": null, "name": "Scorpion cage", "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", + "weight": "0.175", "id": "459" }, { "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", + "examine": "There are 2 scorpions inside.", "durability": null, "name": "Scorpion cage", "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", + "weight": "0.2", "id": "460" }, { "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", + "examine": "There is 1 scorpion inside.", "durability": null, "name": "Scorpion cage", "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", + "weight": "0.175", "id": "461" }, { "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", + "examine": "There are 2 scorpions inside.", "durability": null, "name": "Scorpion cage", "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", + "weight": "0.2", "id": "462" }, { "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", + "examine": "There are 3 scorpions inside.", "durability": null, "name": "Scorpion cage", "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", + "weight": "0.225", "id": "463" }, { @@ -5567,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", @@ -5711,7 +5719,7 @@ }, { "shop_price": "35", - "examine": "Allows you to rest in the luxurious Paramayer[sic] Inn.", + "examine": "Allows you to rest in the luxurious Paramayer Inn.", "durability": null, "name": "Paramaya ticket", "archery_ticket_price": "0", @@ -5719,7 +5727,7 @@ }, { "shop_price": "35", - "examine": "Allows you to rest in the luxurious Paramayer[sic] Inn.", + "examine": "Allows you to rest in the luxurious Paramayer Inn.", "durability": null, "name": "Paramaya ticket", "archery_ticket_price": "0", @@ -6236,6 +6244,7 @@ "attack_speed": "5", "weapon_interface": "6", "turn180_anim": "820", + "equip_audio": "2248", "render_anim": "292", "defence_anim": "397", "equipment_slot": "3", @@ -6842,24 +6851,26 @@ "id": "745" }, { - "attack_anims": "400,400,400,400", "examine": "A black obsidian dagger, it has a strange aura about it.", - "attack_audios": "2517,2517,2500,2517", "durability": null, + "weapon_interface": "5", + "equip_audio": "2248", + "render_anim": "2584", + "defence_anim": "378", + "equipment_slot": "3", + "attack_anims": "400,400,400,400", + "attack_audios": "2517,2517,2500,2517", "name": "Dark dagger", "archery_ticket_price": "0", "id": "746", - "weapon_interface": "5", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,0,0,0,0", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3" + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,0,0,0,0" }, { "examine": "A black obsidian dagger, it has a strange aura about it - it seems to be glowing.", "durability": null, "attack_speed": "6", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -6990,6 +7001,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "defence_anim": "424", "equipment_slot": "3", @@ -7040,6 +7052,7 @@ "attack_speed": "4", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -7265,10 +7278,12 @@ "durability": null, "attack_speed": "5", "weapon_interface": "18", + "equip_audio": "2232", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", "grand_exchange_price": "3", + "attack_audios": "2706,2706,2706", "name": "Bronze thrownaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -7282,10 +7297,12 @@ "durability": null, "attack_speed": "5", "weapon_interface": "18", + "equip_audio": "2232", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", "grand_exchange_price": "14", + "attack_audios": "2706,2706,2706", "name": "Iron thrownaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -7300,10 +7317,12 @@ "durability": null, "attack_speed": "5", "weapon_interface": "18", + "equip_audio": "2232", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", "grand_exchange_price": "60", + "attack_audios": "2706,2706,2706", "name": "Steel thrownaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -7318,10 +7337,12 @@ "durability": null, "attack_speed": "5", "weapon_interface": "18", + "equip_audio": "2232", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", "grand_exchange_price": "135", + "attack_audios": "2706,2706,2706", "name": "Mithril thrownaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -7336,10 +7357,12 @@ "durability": null, "attack_speed": "5", "weapon_interface": "18", + "equip_audio": "2232", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", "grand_exchange_price": "138", + "attack_audios": "2706,2706,2706", "name": "Adamant thrownaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -7355,10 +7378,12 @@ "durability": null, "attack_speed": "5", "weapon_interface": "18", + "equip_audio": "2232", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", "grand_exchange_price": "243", + "attack_audios": "2706,2706,2706", "name": "Rune thrownaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -7371,6 +7396,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7388,6 +7414,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7406,6 +7433,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7424,6 +7452,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7442,6 +7471,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7460,6 +7490,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7477,6 +7508,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7494,6 +7526,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7512,6 +7545,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7530,6 +7564,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7548,6 +7583,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7566,6 +7602,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -7583,6 +7620,7 @@ "name": "Poisoned dart(p)", "archery_ticket_price": "0", "id": "818", + "equip_audio": "2244", "equipment_slot": "3" }, { @@ -7658,6 +7696,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7675,6 +7714,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7693,6 +7733,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7711,6 +7752,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7729,6 +7771,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7747,6 +7790,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7764,6 +7808,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7781,6 +7826,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7799,6 +7845,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7817,6 +7864,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7835,6 +7883,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7853,6 +7902,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -7886,6 +7936,7 @@ "durability": null, "weight": "8", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Crossbow" @@ -7923,6 +7974,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "1", "attack_audios": "2700,0,0,0", "name": "Longbow" @@ -7960,6 +8012,7 @@ "durability": null, "weight": "1.33", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "1", "attack_audios": "2700,0,0,0", "name": "Shortbow" @@ -7983,6 +8036,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8014,6 +8068,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8045,6 +8100,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8076,6 +8132,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8107,6 +8164,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8139,6 +8197,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8171,6 +8230,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8202,6 +8262,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8235,6 +8296,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8268,6 +8330,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8297,6 +8360,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8315,6 +8379,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8334,6 +8399,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8352,6 +8418,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8370,6 +8437,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8388,6 +8456,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8407,6 +8476,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8425,6 +8495,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8443,6 +8514,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8462,6 +8534,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8480,6 +8553,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8499,6 +8573,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8517,6 +8592,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -8535,6 +8611,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -9922,6 +9999,7 @@ "examine": "Made from 100% real dragonhide.", "durability": null, "weight": "0.28", + "equip_audio": "2241", "equipment_slot": "9", "lendable": "true", "grand_exchange_price": "1407", @@ -9947,6 +10025,7 @@ "durability": null, "weight": "9", "absorb": "1,0,1", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "55", "name": "Iron platelegs", @@ -9972,6 +10051,7 @@ "durability": null, "weight": "9", "absorb": "1,0,1", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "426", "name": "Steel platelegs", @@ -9997,6 +10077,7 @@ "durability": null, "weight": "7.7", "absorb": "1,0,2", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "1391", "name": "Mithril platelegs", @@ -10022,6 +10103,7 @@ "durability": null, "weight": "10", "absorb": "1,0,3", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "3649", "name": "Adamant platelegs", @@ -10043,15 +10125,16 @@ "shop_price": "80", "ge_buy_limit": "100", "examine": "These look pretty heavy.", - "grand_exchange_price": "34", "durability": null, + "weight": "9", + "equip_audio": "2242", + "equipment_slot": "7", + "grand_exchange_price": "34", "name": "Bronze platelegs", "tradeable": "true", - "weight": "9", "archery_ticket_price": "0", "id": "1075", - "bonuses": "0,0,0,-21,-7,8,7,6,-4,7,0,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-21,-7,8,7,6,-4,7,0,0,0,0,0" }, { "ge_buy_limit": "100", @@ -10070,6 +10153,7 @@ "durability": null, "weight": "9", "absorb": "1,0,2", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "4995", "name": "Black platelegs", @@ -10095,6 +10179,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "38500", @@ -10340,6 +10425,7 @@ "durability": null, "weight": "5.4", "absorb": "0,4,2", + "equip_audio": "2241", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "2228", @@ -10535,6 +10621,7 @@ "examine": "Provides excellent protection.", "durability": null, "weight": "9.95", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "434", @@ -10559,6 +10646,7 @@ "examine": "Provides excellent protection.", "durability": null, "weight": "9.5", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "36", @@ -10584,6 +10672,7 @@ "examine": "Provides excellent protection.", "durability": null, "weight": "9.9", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "1077", @@ -10610,6 +10699,7 @@ "durability": null, "weight": "8.6", "absorb": "1,0,3", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "2965", @@ -10636,6 +10726,7 @@ "durability": null, "weight": "11", "absorb": "2,0,4", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "9790", @@ -10662,6 +10753,7 @@ "durability": null, "weight": "9.9", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "4915", @@ -10688,6 +10780,7 @@ "durability": null, "weight": "9.9", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -10788,6 +10881,7 @@ "durability": null, "weight": "6.8", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "lendable": "true", "grand_exchange_price": "4544", @@ -10813,6 +10907,7 @@ "examine": "A medium sized helmet.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "22", "name": "Iron med helm", @@ -10837,6 +10932,7 @@ "examine": "A medium sized helmet.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "39", "name": "Bronze med helm", @@ -10862,6 +10958,7 @@ "examine": "A medium sized helmet.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "52", "name": "Steel med helm", @@ -10887,6 +10984,7 @@ "examine": "A medium sized helmet.", "durability": null, "weight": "1.3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "272", "name": "Mithril med helm", @@ -10913,6 +11011,7 @@ "durability": null, "weight": "1.8", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "1024", "name": "Adamant med helm", @@ -10939,6 +11038,7 @@ "durability": null, "weight": "1.8", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "11400", @@ -10965,6 +11065,7 @@ "durability": null, "weight": "1.3", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "60500", @@ -10991,6 +11092,7 @@ "examine": "A medium sized helmet.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "242", "name": "Black med helm", @@ -11015,6 +11117,7 @@ "examine": "A full face helmet.", "durability": null, "weight": "2.7", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "56", @@ -11040,6 +11143,7 @@ "examine": "A full face helmet.", "durability": null, "weight": "2.7", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "67", @@ -11066,6 +11170,7 @@ "examine": "A full face helmet.", "durability": null, "weight": "2.7", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "195", @@ -11092,6 +11197,7 @@ "examine": "A full face helmet.", "durability": null, "weight": "2.2", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "675", @@ -11119,6 +11225,7 @@ "durability": null, "weight": "2.7", "absorb": "1,0,2", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "1911", @@ -11145,6 +11252,7 @@ "durability": null, "weight": "2.7", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -11172,6 +11280,7 @@ "examine": "A full face helmet.", "durability": null, "weight": "2.7", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "731", @@ -11222,6 +11331,7 @@ "examine": "Light weight head protection.", "durability": null, "weight": "0.9", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "56", "name": "Coif", @@ -11250,6 +11360,7 @@ "archery_ticket_price": "0", "id": "1171", "bonuses": "0,0,0,0,0,4,5,3,1,4,0,0,0,0,0", + "equip_audio": "2250", "equipment_slot": "5" }, { @@ -11265,15 +11376,16 @@ "shop_price": "48", "ge_buy_limit": "100", "examine": "A medium square shield.", - "grand_exchange_price": "12", "durability": null, + "weight": "3", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "12", "name": "Bronze sq shield", "tradeable": "true", - "weight": "3", "archery_ticket_price": "0", "id": "1173", - "bonuses": "0,0,0,-6,-2,5,6,4,0,5,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-6,-2,5,6,4,0,5,0,0,0,0,0" }, { "ge_buy_limit": "100", @@ -11288,15 +11400,16 @@ "shop_price": "168", "ge_buy_limit": "100", "examine": "A medium square shield.", - "grand_exchange_price": "39", "durability": null, + "weight": "3.6", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "39", "name": "Iron sq shield", "tradeable": "true", - "weight": "3.6", "archery_ticket_price": "0", "id": "1175", - "bonuses": "0,0,0,-6,-2,8,9,7,0,8,1,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-6,-2,8,9,7,0,8,1,0,0,0,0" }, { "ge_buy_limit": "100", @@ -11314,6 +11427,7 @@ "examine": "A medium square shield.", "durability": null, "weight": "3", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "185", "name": "Steel sq shield", @@ -11339,6 +11453,7 @@ "durability": null, "weight": "4", "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "511", "name": "Black sq shield", @@ -11364,6 +11479,7 @@ "durability": null, "weight": "3.1", "absorb": "2,0,4", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "744", "name": "Mithril sq shield", @@ -11388,6 +11504,7 @@ "durability": null, "weight": "4", "absorb": "3,0,6", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "2136", "name": "Adamant sq shield", @@ -11412,6 +11529,7 @@ "durability": null, "weight": "3.64", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "22800", @@ -11437,6 +11555,7 @@ "durability": null, "weight": "3", "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "431900", @@ -11459,15 +11578,16 @@ "shop_price": "86", "ge_buy_limit": "100", "examine": "A large metal shield.", - "grand_exchange_price": "18", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "18", "name": "Bronze kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "1189", - "bonuses": "0,0,0,-8,-2,5,7,6,-1,6,1,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,5,7,6,-1,6,1,0,0,0,0" }, { "ge_buy_limit": "100", @@ -11482,15 +11602,16 @@ "shop_price": "233", "ge_buy_limit": "100", "examine": "A large metal shield.", - "grand_exchange_price": "57", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "57", "name": "Iron kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "1191", - "bonuses": "0,0,0,-8,-2,8,10,9,-1,9,2,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,8,10,9,-1,9,2,0,0,0,0" }, { "ge_buy_limit": "100", @@ -11508,6 +11629,7 @@ "examine": "A large metal shield.", "durability": null, "weight": "5.4", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "354", "name": "Steel kiteshield", @@ -11533,6 +11655,7 @@ "durability": null, "weight": "5.4", "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "3311", "name": "Black kiteshield", @@ -11557,6 +11680,7 @@ "durability": null, "weight": "4.5", "absorb": "2,0,4", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "1128", "name": "Mithril kiteshield", @@ -11581,6 +11705,7 @@ "durability": null, "weight": "5.8", "absorb": "3,0,6", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "3066", "name": "Adamant kiteshield", @@ -11605,6 +11730,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "35600", @@ -11631,6 +11757,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11660,6 +11787,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11679,7 +11807,8 @@ "name": "Bronze dagger", "tradeable": "true", "archery_ticket_price": "0", - "id": "1206" + "id": "1206", + "equip_audio": "" }, { "requirements": "{0,5}", @@ -11690,6 +11819,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11720,6 +11850,7 @@ "weight": "0.3", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11750,6 +11881,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11780,6 +11912,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11812,6 +11945,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -11843,6 +11977,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11872,6 +12007,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11901,6 +12037,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11931,6 +12068,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11961,6 +12099,7 @@ "weight": "0.3", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11991,6 +12130,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -12021,6 +12161,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -12053,6 +12194,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -12084,6 +12226,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -12142,6 +12285,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Bronze spear" }, @@ -12159,26 +12303,27 @@ "turn90cw_anim": "1207", "examine": "An iron tipped spear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", "defence_anim": "2079", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "428,2081,429,428", "grand_exchange_price": "209", "stand_anim": "813", "tradeable": "true", - "name": "Iron spear", "run_anim": "1210", "archery_ticket_price": "0", "id": "1239", "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0" + "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "name": "Iron spear" }, { "ge_buy_limit": "100", @@ -12213,6 +12358,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Steel spear" }, @@ -12249,6 +12395,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Mithril spear" }, @@ -12285,6 +12432,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Adamant spear" }, @@ -12321,6 +12469,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Rune spear" @@ -12359,6 +12508,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon spear" @@ -12378,26 +12528,27 @@ "turn90cw_anim": "1207", "examine": "A bronze tipped spear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", "defence_anim": "2079", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "428,2081,429,428", "grand_exchange_price": "389", "stand_anim": "813", "tradeable": "true", - "name": "Bronze spear(p)", "run_anim": "1210", "archery_ticket_price": "0", "id": "1251", "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0" + "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "name": "Bronze spear(p)" }, { "ge_buy_limit": "100", @@ -12413,26 +12564,27 @@ "turn90cw_anim": "1207", "examine": "An iron tipped spear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", "defence_anim": "2079", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "428,2081,429,428", "grand_exchange_price": "212", "stand_anim": "813", "tradeable": "true", - "name": "Iron spear(p)", "run_anim": "1210", "archery_ticket_price": "0", "id": "1253", "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0" + "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "name": "Iron spear(p)" }, { "ge_buy_limit": "100", @@ -12444,30 +12596,31 @@ "id": "1254" }, { - "requirements": "{0,5}", "ge_buy_limit": "100", "turn90cw_anim": "1207", "examine": "A steel tipped spear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "428,2081,429,428", "grand_exchange_price": "136", "stand_anim": "813", "tradeable": "true", - "name": "Steel spear(p)", "run_anim": "1210", "archery_ticket_price": "0", "id": "1255", "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0" + "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0", + "requirements": "{0,5}", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "name": "Steel spear(p)" }, { "ge_buy_limit": "100", @@ -12502,6 +12655,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Mithril spear(p)" }, @@ -12538,6 +12692,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Adamant spear(p)" }, @@ -12574,6 +12729,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Rune spear(p)" @@ -12612,6 +12768,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon spear(p)" @@ -12633,6 +12790,7 @@ "weight": "2.25", "attack_speed": "5", "weapon_interface": "4", + "equip_audio": "2232", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", @@ -12661,6 +12819,7 @@ "weight": "2.25", "attack_speed": "5", "weapon_interface": "4", + "equip_audio": "2232", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", @@ -12690,6 +12849,7 @@ "weight": "2.25", "attack_speed": "5", "weapon_interface": "4", + "equip_audio": "2232", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", @@ -12719,6 +12879,7 @@ "weight": "2.7", "attack_speed": "5", "weapon_interface": "4", + "equip_audio": "2232", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", @@ -12748,6 +12909,7 @@ "weight": "1.8", "attack_speed": "5", "weapon_interface": "4", + "equip_audio": "2232", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", @@ -12777,6 +12939,7 @@ "weight": "2.2", "attack_speed": "5", "weapon_interface": "4", + "equip_audio": "2232", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", @@ -12821,6 +12984,7 @@ "durability": null, "weight": "1", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "attack_audios": "2500,2500,2517,2500", "name": "Bronze sword" @@ -12857,6 +13021,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "attack_audios": "2500,2500,2517,2500", "name": "Iron sword" @@ -12894,6 +13059,7 @@ "durability": null, "weight": "1", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "attack_audios": "2500,2500,2517,2500", "name": "Steel sword" @@ -12931,6 +13097,7 @@ "durability": null, "weight": "1", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "attack_audios": "2500,2500,2517,2500", "name": "Black sword" @@ -12968,6 +13135,7 @@ "durability": null, "weight": "1.5", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "attack_audios": "2500,2500,2517,2500", "name": "Mithril sword" @@ -13005,6 +13173,7 @@ "durability": null, "weight": "1", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "attack_audios": "2500,2500,2517,2500", "name": "Adamant sword" @@ -13042,6 +13211,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "lendable": "true", "attack_audios": "2500,2500,2517,2500", @@ -13079,6 +13249,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,2500,2517,2500", "name": "Bronze longsword" @@ -13115,6 +13286,7 @@ "durability": null, "weight": "2", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,2500,2517,2500", "name": "Iron longsword" @@ -13152,6 +13324,7 @@ "durability": null, "weight": "2", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,2500,2517,2500", "name": "Steel longsword" @@ -13189,6 +13362,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,2500,2517,2500", "name": "Black longsword" @@ -13226,6 +13400,7 @@ "durability": null, "weight": "1.5", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,2500,2517,2500", "name": "Mithril longsword" @@ -13263,6 +13438,7 @@ "durability": null, "weight": "2", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,2500,2517,2500", "name": "Adamant longsword" @@ -13300,6 +13476,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "lendable": "true", "attack_audios": "2500,2500,2517,2500", @@ -13339,6 +13516,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1426", "lendable": "true", "attack_audios": "2500,2500,2517,2500", @@ -13377,6 +13555,7 @@ "durability": null, "weight": "3.6", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "attack_audios": "2503,0,2504,0", "name": "Bronze 2h sword" @@ -13414,6 +13593,7 @@ "durability": null, "weight": "3.6", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "attack_audios": "2503,0,2504,0", "name": "Iron 2h sword" @@ -13452,6 +13632,7 @@ "durability": null, "weight": "3", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "attack_audios": "2503,0,2504,0", "name": "Steel 2h sword" @@ -13490,6 +13671,7 @@ "durability": null, "weight": "3.6", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "attack_audios": "2503,0,2504,0", "name": "Black 2h sword" @@ -13528,6 +13710,7 @@ "durability": null, "weight": "3.1", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "attack_audios": "2503,0,2504,0", "name": "Mithril 2h sword" @@ -13566,6 +13749,7 @@ "durability": null, "weight": "4", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "attack_audios": "2503,0,2504,0", "name": "Adamant 2h sword" @@ -13604,6 +13788,7 @@ "durability": null, "weight": "3.6", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "2503,0,2504,0", @@ -13641,6 +13826,7 @@ "durability": null, "weight": "1", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,0,2517,0", "name": "Bronze scimitar" @@ -13677,6 +13863,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,0,2517,0", "name": "Iron scimitar" @@ -13714,6 +13901,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,0,2517,0", "name": "Steel scimitar" @@ -13750,6 +13938,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,0,2517,0", "name": "Black scimitar" @@ -13787,6 +13976,7 @@ "durability": null, "weight": "1.5", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,0,2517,0", "name": "Mithril scimitar" @@ -13824,6 +14014,7 @@ "durability": null, "weight": "2", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,0,2517,0", "name": "Adamant scimitar" @@ -13860,6 +14051,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "lendable": "true", "attack_audios": "2500,0,2517,0", @@ -13897,6 +14089,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1430", "attack_audios": "2504,0,0,0", "name": "Iron warhammer" @@ -13933,6 +14126,7 @@ "durability": null, "weight": "1", "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1430", "attack_audios": "2504,0,0,0", "name": "Bronze warhammer" @@ -13970,6 +14164,7 @@ "durability": null, "weight": "1", "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1430", "attack_audios": "2504,0,0,0", "name": "Steel warhammer" @@ -14006,6 +14201,7 @@ "shop_price": "1274", "durability": null, "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1430", "attack_audios": "2504,0,0,0", "name": "Black warhammer" @@ -14043,6 +14239,7 @@ "durability": null, "weight": "1.5", "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1430", "attack_audios": "2504,0,0,0", "name": "Mithril warhammer" @@ -14080,6 +14277,7 @@ "durability": null, "weight": "2", "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1430", "attack_audios": "2504,0,0,0", "name": "Adamant warhammer" @@ -14117,6 +14315,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1430", "lendable": "true", "attack_audios": "2504,0,0,0", @@ -14139,6 +14338,7 @@ "weight": "1.3", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", @@ -14167,6 +14367,7 @@ "weight": "1.35", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14197,6 +14398,7 @@ "weight": "1.3", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14227,6 +14429,7 @@ "weight": "1.1", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14257,6 +14460,7 @@ "weight": "2", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14284,6 +14488,7 @@ "examine": "A powerful axe.", "durability": null, "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14313,6 +14518,7 @@ "weight": "2", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14342,6 +14548,7 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14372,6 +14579,7 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14402,6 +14610,7 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14432,6 +14641,7 @@ "weight": "2", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14462,6 +14672,7 @@ "weight": "3", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14491,6 +14702,7 @@ "weight": "2", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14521,6 +14733,7 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -14600,6 +14813,7 @@ "durability": null, "weight": "2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Staff" @@ -14636,7 +14850,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Staff of air" @@ -14673,7 +14887,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Staff of water" @@ -14710,7 +14924,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Staff of earth" @@ -14747,7 +14961,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Staff of fire" @@ -14784,6 +14998,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Magic staff" @@ -14821,6 +15036,7 @@ "durability": null, "weight": "2.25", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Battlestaff" @@ -14858,7 +15074,7 @@ "durability": null, "weight": "2.25", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Fire battlestaff" @@ -14895,7 +15111,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Water battlestaff" @@ -14933,7 +15149,7 @@ "durability": null, "weight": "2.25", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Air battlestaff" @@ -14971,7 +15187,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Earth battlestaff" @@ -15008,6 +15224,7 @@ "durability": null, "weight": "2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Mystic fire staff" @@ -15022,30 +15239,31 @@ "id": "1402" }, { - "requirements": "{0,40}-{6,40}", "ge_buy_limit": "100", "turn90cw_anim": "1207", "examine": "It's a slightly magical stick.", "walk_anim": "1205", - "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", "defence_anim": "420", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "419,419,419,419", "grand_exchange_price": "24800", "stand_anim": "813", - "attack_audios": "2555,0,0,0", "tradeable": "true", - "name": "Mystic water staff", "run_anim": "1210", "archery_ticket_price": "0", "id": "1403", "stand_turn_anim": "1209", - "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0" + "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", + "requirements": "{0,40}-{6,40}", + "durability": null, + "weapon_interface": "1", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Mystic water staff" }, { "ge_buy_limit": "100", @@ -15079,6 +15297,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Mystic air staff" @@ -15116,6 +15335,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Mystic earth staff" @@ -15139,6 +15359,7 @@ "attack_speed": "5", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -15224,11 +15445,13 @@ "two_handed": "true", "weapon_interface": "22", "turn180_anim": "820", + "equip_audio": "2247", "render_anim": "1383", "defence_anim": "383", "equipment_slot": "3", - "attack_anims": "382,382,382,382", + "attack_anims": "440,440,438,382", "stand_anim": "847", + "attack_audios": "2524,2524,2522,2524", "name": "Scythe", "run_anim": "824", "archery_ticket_price": "0", @@ -15244,10 +15467,12 @@ "weight": "1.8", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", "grand_exchange_price": "7", + "attack_audios": "2508,2508,25092508", "name": "Iron mace", "tradeable": "true", "archery_ticket_price": "0", @@ -15271,10 +15496,12 @@ "weight": "1.79", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", "grand_exchange_price": "12", + "attack_audios": "2508,2508,25092508", "name": "Bronze mace", "tradeable": "true", "archery_ticket_price": "0", @@ -15299,10 +15526,12 @@ "weight": "1.79", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", "grand_exchange_price": "31", + "attack_audios": "2508,2508,25092508", "name": "Steel mace", "tradeable": "true", "archery_ticket_price": "0", @@ -15326,10 +15555,12 @@ "weight": "1.79", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", "grand_exchange_price": "954", + "attack_audios": "2508,2508,25092508", "name": "Black mace", "tradeable": "true", "archery_ticket_price": "0", @@ -15354,10 +15585,12 @@ "weight": "1.5", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", "grand_exchange_price": "157", + "attack_audios": "2508,2508,25092508", "name": "Mithril mace", "tradeable": "true", "archery_ticket_price": "0", @@ -15382,10 +15615,12 @@ "weight": "2", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", "grand_exchange_price": "673", + "attack_audios": "2508,2508,25092508", "name": "Adamant mace", "tradeable": "true", "archery_ticket_price": "0", @@ -15410,11 +15645,13 @@ "weight": "1.79", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", "lendable": "true", "grand_exchange_price": "8380", + "attack_audios": "2508,2508,25092508", "name": "Rune mace", "tradeable": "true", "archery_ticket_price": "0", @@ -15440,11 +15677,13 @@ "weight": "1.8", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "403", "equipment_slot": "3", "attack_anims": "401,401,400,401", "lendable": "true", "grand_exchange_price": "29300", + "attack_audios": "2508,2508,25092508", "name": "Dragon mace", "tradeable": "true", "archery_ticket_price": "0", @@ -16033,7 +16272,8 @@ "durability": null, "name": "Iban's shadow", "archery_ticket_price": "0", - "id": "1500" + "id": "1500", + "equip_audio": "" }, { "examine": "Smells stronger than most spirits.", @@ -16069,6 +16309,7 @@ "examine": "Maybe I should read this...", "durability": null, "name": "A magic scroll", + "tradeable": "false", "archery_ticket_price": "0", "id": "1505" }, @@ -16084,7 +16325,7 @@ "equipment_slot": "0" }, { - "examine": "Quite a small key (Plague City).", + "examine": "Quite a small key.", "durability": null, "name": "A small key", "archery_ticket_price": "0", @@ -16629,7 +16870,7 @@ "id": "1583" }, { - "examine": "Apparently my name is Hartigan", + "examine": "Apparently my name is Hartigen.", "durability": null, "name": "Id papers", "tradeable": "false", @@ -18673,7 +18914,8 @@ "name": "Woad leaf", "tradeable": "true", "archery_ticket_price": "0", - "id": "1793" + "id": "1793", + "equip_audio": "" }, { "ge_buy_limit": "100", @@ -18704,7 +18946,7 @@ "equipment_slot": "2" }, { - "examine": "Anna's shiny silver coated necklace.", + "examine": "Anna's shiny silver coated necklace coated with a thin layer of flour.", "durability": null, "name": "Silver necklace", "weight": "1", @@ -18722,7 +18964,7 @@ "id": "1798" }, { - "examine": "Bob's shiny silver coated tea cup.", + "examine": "Bob's shiny silver coated tea cup coated with a thin layer of flour.", "durability": null, "name": "Silver cup", "tradeable": "false", @@ -18740,7 +18982,7 @@ "id": "1800" }, { - "examine": "Carol's shiny silver coated bottle.", + "examine": "Carol's shiny silver coated bottle coated with a thin layer of flour.", "durability": null, "name": "Silver bottle", "tradeable": "false", @@ -18758,7 +19000,7 @@ "id": "1802" }, { - "examine": "David's shiny silver coated book.", + "examine": "David's shiny silver coated book coated with a thin layer of flour.", "durability": null, "name": "Silver book", "tradeable": "false", @@ -18776,7 +19018,7 @@ "id": "1804" }, { - "examine": "Elizabeth's shiny silver coated needle.", + "examine": "Elizabeth's shiny silver coated needle coated with a thin layer of flour.", "durability": null, "name": "Silver needle", "tradeable": "false", @@ -18794,7 +19036,7 @@ "id": "1806" }, { - "examine": "Frank's shiny silver coated pot.", + "examine": "Frank's shiny silver coated pot coated with a thin layer of flour.", "durability": null, "name": "Silver pot", "tradeable": "false", @@ -18803,7 +19045,7 @@ "id": "1807" }, { - "examine": "Some (colour) thread found at the murder scene.", + "examine": "Some red thread found at the murder scene.", "durability": null, "name": "Criminal's thread", "tradeable": "false", @@ -18812,7 +19054,7 @@ "id": "1808" }, { - "examine": "Some (colour) thread found at the murder scene.", + "examine": "Some green thread found at the murder scene.", "durability": null, "name": "Criminal's thread", "tradeable": "false", @@ -18821,7 +19063,7 @@ "id": "1809" }, { - "examine": "Some (colour) thread found at the murder scene.", + "examine": "Some blue thread found at the murder scene.", "durability": null, "name": "Criminal's thread", "tradeable": "false", @@ -18848,7 +19090,7 @@ "id": "1812" }, { - "examine": "A flimsy-looking dagger found at the crime scene./A flimsy looking dagger found at the crime scene coated with a thin layer of flour.", + "examine": "A flimsy-looking dagger found at the crime scene.", "attack_audios": "2517,2517,2500,2517", "durability": null, "name": "Criminal's dagger", @@ -18856,7 +19098,7 @@ "id": "1813" }, { - "examine": "A flimsy-looking dagger found at the crime scene./A flimsy looking dagger found at the crime scene coated with a thin layer of flour.", + "examine": "A flimsy looking dagger found at the crime scene coated with a thin layer of flour.", "attack_audios": "2517,2517,2500,2517", "durability": null, "name": "Criminal's dagger", @@ -20318,7 +20560,7 @@ }, { "ge_buy_limit": "1000", - "examine": "On ground: Cabbage... yuck!In inventory: Yuck, I don't like cabbage.", + "examine": "Yuck, I don't like cabbage.", "grand_exchange_price": "41", "durability": null, "name": "Cabbage", @@ -20337,7 +20579,7 @@ "id": "1966" }, { - "examine": "On ground: Cabbage... yuck!In inventory: Yuck, I don't like cabbage.", + "examine": "Yuck, a cabbage from Draynor Manor. I don't like cabbage.", "grand_exchange_price": "55", "durability": null, "name": "Cabbage", @@ -24322,6 +24564,7 @@ "weight": "2.2", "attack_speed": "4", "weapon_interface": "1", + "equip_audio": "2247", "defence_anim": "420", "equipment_slot": "3", "attack_anims": "419,419,419,419", @@ -24339,6 +24582,7 @@ "weight": "2", "attack_speed": "4", "weapon_interface": "1", + "equip_audio": "2247", "defence_anim": "420", "equipment_slot": "3", "attack_anims": "419,419,419,419", @@ -24360,6 +24604,7 @@ "attack_speed": "4", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -25074,6 +25319,7 @@ "examine": "Made from 100% real dragonhide.", "durability": null, "weight": "0.2", + "equip_audio": "2241", "equipment_slot": "9", "lendable": "true", "grand_exchange_price": "1705", @@ -25098,6 +25344,7 @@ "examine": "Vambraces made from 100% real dragonhide.", "durability": null, "weight": "0.25", + "equip_audio": "2241", "equipment_slot": "9", "lendable": "true", "grand_exchange_price": "2046", @@ -25122,6 +25369,7 @@ "examine": "Vambraces made from 100% real dragonhide.", "durability": null, "weight": "0.25", + "equip_audio": "2241", "equipment_slot": "9", "lendable": "true", "grand_exchange_price": "2506", @@ -25147,6 +25395,7 @@ "durability": null, "weight": "5.4", "absorb": "0,5,2", + "equip_audio": "2241", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "2504", @@ -25172,6 +25421,7 @@ "durability": null, "weight": "5.4", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "3142", @@ -25197,6 +25447,7 @@ "durability": null, "weight": "5.4", "absorb": "0,7,3", + "equip_audio": "2241", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "4744", @@ -25222,6 +25473,7 @@ "durability": null, "weight": "6.8", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "lendable": "true", "grand_exchange_price": "5420", @@ -25247,6 +25499,7 @@ "durability": null, "weight": "6.8", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "lendable": "true", "grand_exchange_price": "6680", @@ -25272,6 +25525,7 @@ "durability": null, "weight": "6.8", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "lendable": "true", "grand_exchange_price": "8098", @@ -25984,6 +26238,7 @@ "durability": null, "weight": "9.07", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "74900", @@ -26006,15 +26261,16 @@ "requirements": "{1,10}", "ge_buy_limit": "2", "examine": "Black platelegs with trim.", - "grand_exchange_price": "39900", "durability": null, + "weight": "9", + "equip_audio": "2242", + "equipment_slot": "7", + "grand_exchange_price": "39900", "name": "Black platelegs (t)", "tradeable": "true", - "weight": "9", "archery_ticket_price": "0", "id": "2585", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0" }, { "ge_buy_limit": "2", @@ -26032,6 +26288,7 @@ "examine": "Black full helmet with trim.", "durability": null, "weight": "2", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "82200", @@ -26058,6 +26315,7 @@ "durability": null, "weight": "5", "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "73000", "name": "Black kiteshield (t)", @@ -26082,6 +26340,7 @@ "durability": null, "weight": "9.07", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "589000", @@ -26104,15 +26363,16 @@ "requirements": "{1,10}", "ge_buy_limit": "2", "examine": "Black platelegs with gold trim.", - "grand_exchange_price": "216700", "durability": null, + "weight": "9", + "equip_audio": "2242", + "equipment_slot": "7", + "grand_exchange_price": "216700", "name": "Black platelegs (g)", "tradeable": "true", - "weight": "9", "archery_ticket_price": "0", "id": "2593", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0" }, { "ge_buy_limit": "2", @@ -26130,6 +26390,7 @@ "examine": "Black full helmet with gold trim.", "durability": null, "weight": "2.7", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "364800", @@ -26155,6 +26416,7 @@ "durability": null, "weight": "5", "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "355600", "name": "Black kiteshield (g)", @@ -26179,6 +26441,7 @@ "durability": null, "weight": "9.07", "absorb": "2,0,4", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "104000", @@ -26205,6 +26468,7 @@ "durability": null, "weight": "10", "absorb": "1,0,3", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "23200", "name": "Adam platelegs (t)", @@ -26229,6 +26493,7 @@ "durability": null, "weight": "5.8", "absorb": "3,0,6", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "49100", "name": "Adam kiteshield (t)", @@ -26254,6 +26519,7 @@ "durability": null, "weight": "2.7", "absorb": "1,0,2", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "85000", @@ -26279,6 +26545,7 @@ "durability": null, "weight": "9.07", "absorb": "2,0,4", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "359500", @@ -26305,6 +26572,7 @@ "durability": null, "weight": "10", "absorb": "1,0,3", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "94300", "name": "Adam platelegs (g)", @@ -26329,6 +26597,7 @@ "durability": null, "weight": "6", "absorb": "3,0,6", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "170100", "name": "Adam kiteshield (g)", @@ -26354,6 +26623,7 @@ "durability": null, "weight": "2.7", "absorb": "1,0,2", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "222400", @@ -26379,6 +26649,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26406,6 +26677,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "243400", @@ -26433,6 +26705,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -26459,6 +26732,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "392700", @@ -26484,6 +26758,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26510,6 +26785,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "95400", @@ -26536,6 +26812,7 @@ "durability": null, "weight": "2.7", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -26562,7 +26839,9 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", + "attack_anims": "", "lendable": "true", "grand_exchange_price": "160300", "name": "Rune kiteshield (t)", @@ -26824,6 +27103,7 @@ "durability": null, "weight": "9", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26850,6 +27130,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "281500", @@ -26876,6 +27157,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -26902,6 +27184,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "499500", @@ -26927,6 +27210,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26953,6 +27237,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "444000", @@ -26979,6 +27264,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -27005,6 +27291,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "795900", @@ -27030,6 +27317,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -27056,6 +27344,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "169900", @@ -27082,6 +27371,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -27108,6 +27398,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "247300", @@ -30333,7 +30624,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Lava battlestaff" @@ -30362,7 +30653,7 @@ "durability": null, "weight": "2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Mystic lava staff" @@ -30440,6 +30731,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -30458,6 +30750,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -30484,6 +30777,7 @@ "equipment_slot": "3", "attack_anims": "390,390,390,390", "grand_exchange_price": "702", + "attack_audios": "2548,2548,2548,2548", "name": "Bronze claws", "tradeable": "true", "archery_ticket_price": "0", @@ -30505,6 +30799,7 @@ "equipment_slot": "3", "attack_anims": "390,390,390,390", "grand_exchange_price": "13", + "attack_audios": "2548,2548,2548,2548", "name": "Iron claws", "tradeable": "true", "archery_ticket_price": "0", @@ -30527,6 +30822,7 @@ "equipment_slot": "3", "attack_anims": "390,390,390,390", "grand_exchange_price": "20", + "attack_audios": "2548,2548,2548,2548", "name": "Steel claws", "tradeable": "true", "archery_ticket_price": "0", @@ -30548,6 +30844,7 @@ "equipment_slot": "3", "attack_anims": "390,390,390,390", "grand_exchange_price": "940", + "attack_audios": "2548,2548,2548,2548", "name": "Black claws", "tradeable": "true", "archery_ticket_price": "0", @@ -30569,6 +30866,7 @@ "equipment_slot": "3", "attack_anims": "390,390,390,390", "grand_exchange_price": "114", + "attack_audios": "2548,2548,2548,2548", "name": "Mithril claws", "tradeable": "true", "archery_ticket_price": "0", @@ -30589,6 +30887,7 @@ "equipment_slot": "3", "attack_anims": "390,390,390,390", "grand_exchange_price": "649", + "attack_audios": "2548,2548,2548,2548", "name": "Adamant claws", "tradeable": "true", "archery_ticket_price": "0", @@ -30612,6 +30911,7 @@ "attack_anims": "390,390,390,390", "lendable": "true", "grand_exchange_price": "6939", + "attack_audios": "2548,2548,2548,2548", "name": "Rune claws", "tradeable": "true", "archery_ticket_price": "0", @@ -30646,6 +30946,7 @@ "durability": null, "destroy": "true", "weight": "0.3", + "equip_audio": "2237", "equipment_slot": "10", "grand_exchange_price": "51400", "name": "Climbing boots", @@ -30671,6 +30972,7 @@ "archery_ticket_price": "0", "id": "3107", "bonuses": "0,0,0,0,0,0,2,2,0,0,0,2,0,0,0", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -31200,6 +31502,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31224,6 +31527,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31249,6 +31553,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31274,6 +31579,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31299,6 +31605,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31324,6 +31631,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31338,30 +31646,31 @@ "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0" }, { - "requirements": "{0,60}", "turn90cw_anim": "1207", "examine": "A Karambwan poisoned dragon tipped spear.", "walk_anim": "1205", "has_special": "true", - "durability": null, - "weight": "2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", "defence_anim": "2079", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "2080,2081,2082,2080", "grand_exchange_price": "37446", "stand_anim": "813", - "name": "Dragon spear(kp)", "run_anim": "1210", "archery_ticket_price": "0", "id": "3176", "stand_turn_anim": "1209", - "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0" + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "requirements": "{0,60}", + "durability": null, + "weight": "2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "name": "Dragon spear(kp)" }, { "durability": null, @@ -31512,6 +31821,7 @@ "durability": null, "weight": "3", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Bronze halberd" }, @@ -31548,6 +31858,7 @@ "durability": null, "weight": "3.1", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Iron halberd" }, @@ -31585,6 +31896,7 @@ "durability": null, "weight": "3", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Steel halberd" }, @@ -31617,11 +31929,12 @@ "id": "3196", "stand_turn_anim": "1209", "bonuses": "19,25,0,-4,0,-1,2,3,0,0,0,20,0,0,0", - "requirements": "{0,5}-{2,10}", + "requirements": "{0,10}-{2,5}", "shop_price": "2496", "durability": null, "weight": "3", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Black halberd" }, @@ -31659,6 +31972,7 @@ "durability": null, "weight": "2.7", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Mithril halberd" }, @@ -31696,6 +32010,7 @@ "durability": null, "weight": "3.6", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Adamant halberd" }, @@ -31733,6 +32048,7 @@ "durability": null, "weight": "3", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Rune halberd" @@ -31772,6 +32088,7 @@ "durability": null, "weight": "3.1", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon halberd" @@ -32873,6 +33190,7 @@ "examine": "Wooden foot protection.", "durability": null, "weight": "0.9", + "equip_audio": "2237", "equipment_slot": "10", "lendable": "true", "grand_exchange_price": "3808", @@ -33665,10 +33983,11 @@ { "requirements": "{1,40}", "ge_buy_limit": "2", - "examine": "Rune platebody with complete gold trim & plating.", + "examine": "Rune platebody with complete gold trim & plating.", "durability": null, "weight": "10", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -33695,6 +34014,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "2200000", @@ -33737,6 +34057,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -33763,6 +34084,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "515400", @@ -36959,6 +37281,7 @@ "turn90ccw_anim": "822", "weapon_interface": "6", "turn180_anim": "820", + "equip_audio": "2248", "render_anim": "1", "castle_wars_ticket_price": "5", "equipment_slot": "3", @@ -37127,8 +37450,10 @@ "ge_buy_limit": "10", "examine": "Looks pretty heavy.", "durability": null, + "rare_item": "true", "weight": "9", "absorb": "3,0,6", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "190500", @@ -37545,6 +37870,7 @@ "archery_ticket_price": "0", "id": "4119", "bonuses": "0,0,0,-3,-1,1,2,3,0,0,0,0,0,0,0", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -37567,6 +37893,7 @@ "archery_ticket_price": "0", "id": "4121", "bonuses": "0,0,0,-3,-1,2,3,4,0,0,0,0,0,0,0", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -37582,15 +37909,16 @@ "requirements": "{1,5}", "ge_buy_limit": "100", "examine": "These will protect my feet.", - "grand_exchange_price": "258", "durability": null, + "weight": "1.3", + "equip_audio": "2237", + "equipment_slot": "10", + "grand_exchange_price": "258", "name": "Steel boots", "tradeable": "true", - "weight": "1.3", "archery_ticket_price": "0", "id": "4123", - "bonuses": "0,0,0,-3,-1,5,6,7,0,0,5,0,0,0,0", - "equipment_slot": "10" + "bonuses": "0,0,0,-3,-1,5,6,7,0,0,5,0,0,0,0" }, { "ge_buy_limit": "100", @@ -37605,15 +37933,16 @@ "requirements": "{1,10}", "ge_buy_limit": "100", "examine": "These will protect my feet.", - "grand_exchange_price": "317", "durability": null, + "weight": "1.3", + "equip_audio": "2237", + "equipment_slot": "10", + "grand_exchange_price": "317", "name": "Black boots", "tradeable": "true", - "weight": "1.3", "archery_ticket_price": "0", "id": "4125", - "bonuses": "0,0,0,-3,-1,7,8,9,0,0,7,0,0,0,0", - "equipment_slot": "10" + "bonuses": "0,0,0,-3,-1,7,8,9,0,0,7,0,0,0,0" }, { "ge_buy_limit": "100", @@ -37628,15 +37957,16 @@ "requirements": "{1,20}", "ge_buy_limit": "100", "examine": "These will protect my feet.", - "grand_exchange_price": "580", "durability": null, + "weight": "1", + "equip_audio": "2237", + "equipment_slot": "10", + "grand_exchange_price": "580", "name": "Mithril boots", "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", "id": "4127", - "bonuses": "0,0,0,-3,-1,8,9,10,0,0,8,0,0,0,0", - "equipment_slot": "10" + "bonuses": "0,0,0,-3,-1,8,9,10,0,0,8,0,0,0,0" }, { "ge_buy_limit": "100", @@ -37651,15 +37981,16 @@ "requirements": "{1,30}", "ge_buy_limit": "100", "examine": "These will protect my feet.", - "grand_exchange_price": "1336", "durability": null, + "weight": "1.3", + "equip_audio": "2237", + "equipment_slot": "10", + "grand_exchange_price": "1336", "name": "Adamant boots", "tradeable": "true", - "weight": "1.3", "archery_ticket_price": "0", "id": "4129", - "bonuses": "0,0,0,-3,-1,10,11,12,0,0,9,1,0,0,0", - "equipment_slot": "10" + "bonuses": "0,0,0,-3,-1,10,11,12,0,0,9,1,0,0,0" }, { "ge_buy_limit": "100", @@ -37676,6 +38007,7 @@ "examine": "These will protect my feet.", "durability": null, "weight": "1.3", + "equip_audio": "2237", "equipment_slot": "10", "lendable": "true", "grand_exchange_price": "7474", @@ -37958,6 +38290,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -37980,6 +38313,7 @@ "archery_ticket_price": "0", "attack_speed": "5", "id": "4159", + "equip_audio": "2247", "equipment_slot": "3" }, { @@ -38119,6 +38453,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Slayer's staff" @@ -38362,7 +38697,7 @@ }, { "destroy_message": "Another can be received from pickpocketing Dr. Fenkenstrain in the tower.", - "examine": "Unactivated: The Ring of charos.Activated: The power within this ring has been activated.", + "examine": "The Ring of charos", "durability": null, "name": "Ring of charos", "tradeable": "false", @@ -38421,12 +38756,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "905300", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "New crystal bow", "tradeable": "true", "archery_ticket_price": "0", @@ -38451,12 +38787,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow full", "archery_ticket_price": "0", "id": "4214", @@ -38471,12 +38808,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 9/10", "archery_ticket_price": "0", "id": "4215", @@ -38491,12 +38829,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 8/10", "archery_ticket_price": "0", "id": "4216", @@ -38511,12 +38850,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 7/10", "archery_ticket_price": "0", "id": "4217", @@ -38531,12 +38871,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 6/10", "archery_ticket_price": "0", "id": "4218", @@ -38551,12 +38892,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 5/10", "archery_ticket_price": "0", "id": "4219", @@ -38571,12 +38913,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 4/10", "archery_ticket_price": "0", "id": "4220", @@ -38591,12 +38934,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 3/10", "archery_ticket_price": "0", "id": "4221", @@ -38611,12 +38955,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 2/10", "archery_ticket_price": "0", "id": "4222", @@ -38631,12 +38976,13 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2238", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "426,426,426,426", "grand_exchange_price": "901019", - "attack_audios": "2700,0,0,0", + "attack_audios": "1352,1352,1352,0", "name": "Crystal bow 1/10", "archery_ticket_price": "0", "id": "4223", @@ -39424,6 +39770,7 @@ "archery_ticket_price": "0", "id": "4310", "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "equip_audio": "", "equipment_slot": "10" }, { @@ -41259,6 +41606,7 @@ "turn90ccw_anim": "822", "weapon_interface": "6", "turn180_anim": "820", + "equip_audio": "2248", "render_anim": "1", "defence_anim": "397", "castle_wars_ticket_price": "50", @@ -41330,6 +41678,7 @@ "turn90ccw_anim": "822", "weapon_interface": "6", "turn180_anim": "820", + "equip_audio": "2248", "render_anim": "1", "defence_anim": "397", "castle_wars_ticket_price": "500", @@ -41855,10 +42204,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "12", + "equip_audio": "2238", "defence_anim": "1834", "equipment_slot": "3", "attack_anims": "1833,1833,1833,1833", "fun_weapon": "true", + "attack_audios": "2257,2257,2257", "name": "Rubber chicken", "archery_ticket_price": "0", "id": "4566", @@ -41937,6 +42288,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "536", @@ -41971,6 +42323,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "2808", @@ -42004,6 +42357,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "309", @@ -42020,6 +42374,7 @@ "ge_buy_limit": "10", "examine": "This looks pretty heavy.", "durability": null, + "rare_item": "true", "weight": "9", "absorb": "3,0,6", "equipment_slot": "7", @@ -42065,6 +42420,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "lendable": "true", "attack_audios": "2500,0,2517,0", @@ -42305,7 +42661,7 @@ "id": "4619" }, { - "examine": "It looks horrible.On ground: Not good for eating.", + "examine": "It looks horrible.", "durability": null, "name": "Black mushroom", "weight": "1", @@ -42394,7 +42750,7 @@ "equipment_slot": "12" }, { - "examine": "Frank's shiny silver coated pot.", + "examine": "A silver pot made by Ruantun.", "durability": null, "name": "Silver pot", "tradeable": "false", @@ -42411,7 +42767,7 @@ "id": "4659" }, { - "examine": "Frank's shiny silver coated pot.", + "examine": "A silver pot made by Ruantun filled with your blood.", "durability": null, "name": "Silver pot", "tradeable": "false", @@ -42420,7 +42776,7 @@ "id": "4660" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", + "examine": "A blessed silver pot made by Ruantun filled with your blood.", "durability": null, "name": "Blessed pot", "weight": "2", @@ -42428,7 +42784,7 @@ "id": "4661" }, { - "examine": "Frank's shiny silver coated pot.", + "examine": "A silver pot made by Ruantun filled with blood and garlic.", "durability": null, "name": "Silver pot", "tradeable": "false", @@ -42437,7 +42793,7 @@ "id": "4662" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", + "examine": "A blessed silver pot filled with blood and garlic.", "durability": null, "name": "Blessed pot", "weight": "2", @@ -42445,7 +42801,7 @@ "id": "4663" }, { - "examine": "Frank's shiny silver coated pot.", + "examine": "A silver pot made by Ruantun filled with blood and spices.", "durability": null, "name": "Silver pot", "tradeable": "false", @@ -42454,7 +42810,7 @@ "id": "4664" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", + "examine": "A blessed silver pot filled with blood and spices.", "durability": null, "name": "Blessed pot", "weight": "2", @@ -42462,7 +42818,7 @@ "id": "4665" }, { - "examine": "Frank's shiny silver coated pot.", + "examine": "A silver pot made by Ruantun filled with blood, garlic and spices.", "durability": null, "name": "Silver pot", "tradeable": "false", @@ -42471,7 +42827,7 @@ "id": "4666" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", + "examine": "A blessed silver pot filled with blood, garlic and spices.", "durability": null, "name": "Blessed pot", "weight": "2", @@ -42546,6 +42902,7 @@ "durability": null, "weight": "2.25", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "attack_audios": "2555,0,0,0", @@ -42770,13 +43127,13 @@ "id": "4702" }, { - "shop_price": "975000", - "examine": "A magic stone to make high-level furniture.", - "grand_exchange_price": "977755", + "shop_price": "1", + "examine": "Doesn't look very special.", + "grand_exchange_price": "0", "durability": null, "name": "Magic stone", - "tradeable": "true", - "weight": "1", + "tradeable": "false", + "weight": "2.267", "archery_ticket_price": "0", "id": "4703" }, @@ -42858,6 +43215,7 @@ "durability": null, "weight": "2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Ahrim's staff" @@ -42934,6 +43292,7 @@ "rare_item": "true", "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "1300000", "name": "Dharok's helm", @@ -42977,6 +43336,7 @@ "durability": null, "weight": "13", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "134", "name": "Dharok's greataxe" }, @@ -42998,6 +43358,7 @@ "rare_item": "true", "weight": "9.9", "absorb": "5,0,10", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "511400", @@ -43025,6 +43386,7 @@ "rare_item": "true", "weight": "10", "absorb": "3,0,7", + "equip_audio": "2243", "equipment_slot": "7", "grand_exchange_price": "748800", "name": "Dharok's platelegs", @@ -43052,6 +43414,7 @@ "rare_item": "true", "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "396300", @@ -43096,6 +43459,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Guthan's warspear" }, @@ -43117,6 +43481,7 @@ "rare_item": "true", "weight": "9", "absorb": "5,0,10", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "316800", @@ -43144,6 +43509,7 @@ "rare_item": "true", "weight": "8", "absorb": "3,0,7", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "315600", "name": "Guthan's chainskirt", @@ -43170,6 +43536,7 @@ "durability": null, "rare_item": "true", "absorb": "0,5,2", + "equip_audio": "2238", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "38900", @@ -43214,6 +43581,7 @@ "durability": null, "weight": "2", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "372", "attack_audios": "2700,0,0,0", "name": "Karil's crossbow" @@ -43236,6 +43604,7 @@ "rare_item": "true", "weight": "6", "absorb": "0,10,5", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "1800000", @@ -43263,6 +43632,7 @@ "rare_item": "true", "weight": "2", "absorb": "0,7,3", + "equip_audio": "2238", "equipment_slot": "7", "grand_exchange_price": "217000", "name": "Karil's leatherskirt", @@ -43304,6 +43674,7 @@ "rare_item": "true", "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "139000", @@ -43333,6 +43704,7 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "10", + "equip_audio": "2233", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "2068,2068,2068,2068", @@ -43362,6 +43734,7 @@ "rare_item": "true", "weight": "9", "absorb": "5,0,10", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "346200", @@ -43389,6 +43762,7 @@ "rare_item": "true", "weight": "9", "absorb": "3,0,7", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "427200", "name": "Torag's platelegs", @@ -43416,6 +43790,7 @@ "rare_item": "true", "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "687200", @@ -43460,6 +43835,7 @@ "durability": null, "weight": "2", "weapon_interface": "8", + "equip_audio": "2246", "render_anim": "1426", "attack_audios": "1323,0,0,0", "name": "Verac's flail" @@ -43482,6 +43858,7 @@ "rare_item": "true", "weight": "5", "absorb": "5,0,10", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "270000", @@ -43509,6 +43886,7 @@ "rare_item": "true", "weight": "5", "absorb": "3,0,7", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "411400", "name": "Verac's plateskirt", @@ -44161,6 +44539,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -44186,6 +44565,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -44211,6 +44591,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -44236,6 +44617,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -44262,6 +44644,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "50600", @@ -44445,57 +44828,61 @@ "remove_head": "true", "requirements": "{1,70}", "examine": "Dharok the Wretched's helm.", - "grand_exchange_price": "1355582", "durability": null, - "name": "Dharok's helm 100", "weight": "1", + "absorb": "2,0,5", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "1355582", + "name": "Dharok's helm 100", "archery_ticket_price": "0", "id": "4880", - "absorb": "2,0,5", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,70}", "examine": "Dharok the Wretched's helm.", - "grand_exchange_price": "1355582", "durability": null, - "name": "Dharok's helm 75", "weight": "1", + "absorb": "2,0,5", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "1355582", + "name": "Dharok's helm 75", "archery_ticket_price": "0", "id": "4881", - "absorb": "2,0,5", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,70}", "examine": "Dharok the Wretched's helm.", - "grand_exchange_price": "1355582", "durability": null, - "name": "Dharok's helm 50", "weight": "1", + "absorb": "2,0,5", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "1355582", + "name": "Dharok's helm 50", "archery_ticket_price": "0", "id": "4882", - "absorb": "2,0,5", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,70}", "examine": "Dharok the Wretched's helm.", - "grand_exchange_price": "1355582", "durability": null, - "name": "Dharok's helm 25", "weight": "1", + "absorb": "2,0,5", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "1355582", + "name": "Dharok's helm 25", "archery_ticket_price": "0", "id": "4883", - "absorb": "2,0,5", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" }, { "remove_head": "true", @@ -44505,6 +44892,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "1300000", "name": "Dharok's helm 0", @@ -44523,108 +44911,112 @@ "id": "4885" }, { - "requirements": "{0,70}-{2,70}", "turn90cw_anim": "2064", "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", - "durability": null, - "weight": "13", "turn90ccw_anim": "2064", "attack_speed": "7", "two_handed": "true", - "weapon_interface": "2", "turn180_anim": "2064", "defence_anim": "424", - "render_anim": "134", "equipment_slot": "3", "attack_anims": "2067,2067,2066,2067", "grand_exchange_price": "454500", "stand_anim": "2065", - "attack_audios": "1320,0,0,0", - "name": "Dharok's axe 100", "run_anim": "824", "archery_ticket_price": "0", "id": "4886", "stand_turn_anim": "823", - "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0" + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "requirements": "{0,70}-{2,70}", + "durability": null, + "weight": "13", + "weapon_interface": "2", + "equip_audio": "2232", + "render_anim": "134", + "attack_audios": "1320,0,0,0", + "name": "Dharok's axe 100" }, { - "requirements": "{0,70}-{2,70}", "turn90cw_anim": "2064", "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", - "durability": null, - "weight": "13", "turn90ccw_anim": "2064", "attack_speed": "7", "two_handed": "true", - "weapon_interface": "2", "turn180_anim": "2064", "defence_anim": "424", - "render_anim": "134", "equipment_slot": "3", "attack_anims": "2067,2067,2066,2067", "grand_exchange_price": "454500", "stand_anim": "2065", - "attack_audios": "1320,0,0,0", - "name": "Dharok's axe 75", "run_anim": "824", "archery_ticket_price": "0", "id": "4887", "stand_turn_anim": "823", - "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0" + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "requirements": "{0,70}-{2,70}", + "durability": null, + "weight": "13", + "weapon_interface": "2", + "equip_audio": "2232", + "render_anim": "134", + "attack_audios": "1320,0,0,0", + "name": "Dharok's axe 75" }, { - "requirements": "{0,70}-{2,70}", "turn90cw_anim": "2064", "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", - "durability": null, - "weight": "13", "turn90ccw_anim": "2064", "attack_speed": "7", "two_handed": "true", - "weapon_interface": "2", "turn180_anim": "2064", "defence_anim": "424", - "render_anim": "134", "equipment_slot": "3", "attack_anims": "2067,2067,2066,2067", "grand_exchange_price": "454500", "stand_anim": "2065", - "attack_audios": "1320,0,0,0", - "name": "Dharok's axe 50", "run_anim": "824", "archery_ticket_price": "0", "id": "4888", "stand_turn_anim": "823", - "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0" + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "requirements": "{0,70}-{2,70}", + "durability": null, + "weight": "13", + "weapon_interface": "2", + "equip_audio": "2232", + "render_anim": "134", + "attack_audios": "1320,0,0,0", + "name": "Dharok's axe 50" }, { - "requirements": "{0,70}-{2,70}", "turn90cw_anim": "2064", "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", - "durability": null, - "weight": "13", "turn90ccw_anim": "2064", "attack_speed": "7", "two_handed": "true", - "weapon_interface": "2", "turn180_anim": "2064", "defence_anim": "424", - "render_anim": "134", "equipment_slot": "3", "attack_anims": "2067,2067,2066,2067", "grand_exchange_price": "454500", "stand_anim": "2065", - "attack_audios": "1320,0,0,0", - "name": "Dharok's axe 25", "run_anim": "824", "archery_ticket_price": "0", "id": "4889", "stand_turn_anim": "823", - "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0" + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "requirements": "{0,70}-{2,70}", + "durability": null, + "weight": "13", + "weapon_interface": "2", + "equip_audio": "2232", + "render_anim": "134", + "attack_audios": "1320,0,0,0", + "name": "Dharok's axe 25" }, { "requirements": "{0,70}-{2,70}", @@ -44639,6 +45031,7 @@ "two_handed": "true", "weapon_interface": "2", "turn180_anim": "2064", + "equip_audio": "2232", "render_anim": "134", "equipment_slot": "3", "grand_exchange_price": "442600", @@ -44663,59 +45056,63 @@ }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Dharok the Wretched's platebody armour.", - "grand_exchange_price": "481600", "durability": null, - "name": "Dharok's body 100", "weight": "9.9", + "absorb": "5,0,10", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "481600", + "name": "Dharok's body 100", "archery_ticket_price": "0", "id": "4892", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Dharok the Wretched's platebody armour.", - "grand_exchange_price": "481600", "durability": null, - "name": "Dharok's body 75", "weight": "9.9", + "absorb": "5,0,10", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "481600", + "name": "Dharok's body 75", "archery_ticket_price": "0", "id": "4893", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Dharok the Wretched's platebody armour.", - "grand_exchange_price": "481600", "durability": null, - "name": "Dharok's body 50", "weight": "9.9", + "absorb": "5,0,10", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "481600", + "name": "Dharok's body 50", "archery_ticket_price": "0", "id": "4894", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Dharok the Wretched's platebody armour.", - "grand_exchange_price": "481600", "durability": null, - "name": "Dharok's body 25", "weight": "9.9", + "absorb": "5,0,10", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "481600", + "name": "Dharok's body 25", "archery_ticket_price": "0", "id": "4895", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", @@ -44724,6 +45121,7 @@ "durability": null, "weight": "9.9", "absorb": "5,0,10", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "438200", @@ -44753,6 +45151,7 @@ "id": "4898", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "equip_audio": "2243", "equipment_slot": "7" }, { @@ -44766,6 +45165,7 @@ "id": "4899", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "equip_audio": "2243", "equipment_slot": "7" }, { @@ -44779,6 +45179,7 @@ "id": "4900", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "equip_audio": "2243", "equipment_slot": "7" }, { @@ -44792,21 +45193,23 @@ "id": "4901", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "equip_audio": "2243", "equipment_slot": "7" }, { "requirements": "{1,70}", "ge_buy_limit": "10", "examine": "Dharok the Wretched's plate leg armour.", - "grand_exchange_price": "676000", "durability": null, + "weight": "10", + "absorb": "3,0,7", + "equip_audio": "2243", + "equipment_slot": "7", + "grand_exchange_price": "676000", "name": "Dharok's legs 0", "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "4902", - "absorb": "3,0,7", - "equipment_slot": "7" + "id": "4902" }, { "ge_buy_limit": "10", @@ -44825,6 +45228,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "232455", @@ -44840,6 +45244,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "232455", @@ -44855,6 +45260,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "232455", @@ -44870,6 +45276,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "232455", @@ -44886,6 +45293,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "368200", @@ -44905,108 +45313,112 @@ "id": "4909" }, { - "requirements": "{0,70}", "turn90cw_anim": "1207", "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", "defence_anim": "2079", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "2080,2081,2082,2080", "grand_exchange_price": "371782", "stand_anim": "813", - "attack_audios": "1316,0,0,0", - "name": "Guthan's spear 100", "run_anim": "1210", "archery_ticket_price": "0", "id": "4910", "stand_turn_anim": "1209", - "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0" + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "requirements": "{0,70}", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "1316,0,0,0", + "name": "Guthan's spear 100" }, { - "requirements": "{0,70}", "turn90cw_anim": "1207", "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", "defence_anim": "2079", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "2080,2081,2082,2080", "grand_exchange_price": "371782", "stand_anim": "813", - "attack_audios": "1316,0,0,0", - "name": "Guthan's spear 75", "run_anim": "1210", "archery_ticket_price": "0", "id": "4911", "stand_turn_anim": "1209", - "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0" + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "requirements": "{0,70}", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "1316,0,0,0", + "name": "Guthan's spear 75" }, { - "requirements": "{0,70}", "turn90cw_anim": "1207", "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", "defence_anim": "2079", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "2080,2081,2082,2080", "grand_exchange_price": "371782", "stand_anim": "813", - "attack_audios": "1316,0,0,0", - "name": "Guthan's spear 50", "run_anim": "1210", "archery_ticket_price": "0", "id": "4912", "stand_turn_anim": "1209", - "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0" + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "requirements": "{0,70}", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "1316,0,0,0", + "name": "Guthan's spear 50" }, { - "requirements": "{0,70}", "turn90cw_anim": "1207", "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", "defence_anim": "2079", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "2080,2081,2082,2080", "grand_exchange_price": "371782", "stand_anim": "813", - "attack_audios": "1316,0,0,0", - "name": "Guthan's spear 25", "run_anim": "1210", "archery_ticket_price": "0", "id": "4913", "stand_turn_anim": "1209", - "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0" + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "requirements": "{0,70}", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "1316,0,0,0", + "name": "Guthan's spear 25" }, { "requirements": "{0,70}", @@ -45021,6 +45433,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "379500", @@ -45045,59 +45458,63 @@ }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Guthan the Infested's platebody armour.", - "grand_exchange_price": "241605", "durability": null, - "name": "Guthan's body 100", "weight": "9", + "absorb": "5,0,10", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "241605", + "name": "Guthan's body 100", "archery_ticket_price": "0", "id": "4916", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Guthan the Infested's platebody armour.", - "grand_exchange_price": "241605", "durability": null, - "name": "Guthan's body 75", "weight": "9", + "absorb": "5,0,10", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "241605", + "name": "Guthan's body 75", "archery_ticket_price": "0", "id": "4917", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Guthan the Infested's platebody armour.", - "grand_exchange_price": "241605", "durability": null, - "name": "Guthan's body 50", "weight": "9", + "absorb": "5,0,10", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "241605", + "name": "Guthan's body 50", "archery_ticket_price": "0", "id": "4918", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Guthan the Infested's platebody armour.", - "grand_exchange_price": "241605", "durability": null, - "name": "Guthan's body 25", "weight": "9", + "absorb": "5,0,10", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "241605", + "name": "Guthan's body 25", "archery_ticket_price": "0", "id": "4919", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", @@ -45106,6 +45523,7 @@ "durability": null, "weight": "9", "absorb": "5,0,10", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "245900", @@ -45135,6 +45553,7 @@ "id": "4922", "absorb": "3,0,7", "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -45148,6 +45567,7 @@ "id": "4923", "absorb": "3,0,7", "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -45161,6 +45581,7 @@ "id": "4924", "absorb": "3,0,7", "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -45174,21 +45595,23 @@ "id": "4925", "absorb": "3,0,7", "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { "requirements": "{1,70}", "ge_buy_limit": "10", "examine": "Guthan the Infested's chainskirt.", - "grand_exchange_price": "240900", "durability": null, + "weight": "8", + "absorb": "3,0,7", + "equip_audio": "2242", + "equipment_slot": "7", + "grand_exchange_price": "240900", "name": "Guthan's skirt 0", "tradeable": "true", - "weight": "8", "archery_ticket_price": "0", - "id": "4926", - "absorb": "3,0,7", - "equipment_slot": "7" + "id": "4926" }, { "ge_buy_limit": "10", @@ -45204,57 +45627,61 @@ "remove_head": "true", "requirements": "{1,70}-{4,70}", "examine": "Karil the Tainted's coif.", - "grand_exchange_price": "7520", "durability": null, + "absorb": "0,5,2", + "equip_audio": "2238", + "remove_beard": "true", + "equipment_slot": "0", + "grand_exchange_price": "7520", "name": "Karil's coif 100", "archery_ticket_price": "0", "id": "4928", - "absorb": "0,5,2", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" + "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,70}-{4,70}", "examine": "Karil the Tainted's coif.", - "grand_exchange_price": "7520", "durability": null, + "absorb": "0,5,2", + "equip_audio": "2238", + "remove_beard": "true", + "equipment_slot": "0", + "grand_exchange_price": "7520", "name": "Karil's coif 75", "archery_ticket_price": "0", "id": "4929", - "absorb": "0,5,2", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" + "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,70}-{4,70}", "examine": "Karil the Tainted's coif.", - "grand_exchange_price": "7520", "durability": null, + "absorb": "0,5,2", + "equip_audio": "2238", + "remove_beard": "true", + "equipment_slot": "0", + "grand_exchange_price": "7520", "name": "Karil's coif 50", "archery_ticket_price": "0", "id": "4930", - "absorb": "0,5,2", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" + "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,70}-{4,70}", "examine": "Karil the Tainted's coif.", - "grand_exchange_price": "7520", "durability": null, + "absorb": "0,5,2", + "equip_audio": "2238", + "remove_beard": "true", + "equipment_slot": "0", + "grand_exchange_price": "7520", "name": "Karil's coif 25", "archery_ticket_price": "0", "id": "4931", - "absorb": "0,5,2", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" + "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0" }, { "remove_head": "true", @@ -45263,6 +45690,7 @@ "examine": "Karil the Tainted's coif.", "durability": null, "absorb": "0,5,2", + "equip_audio": "2238", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "7557", @@ -45282,108 +45710,112 @@ "id": "4933" }, { - "requirements": "{4,70}", "turn90cw_anim": "2076", "examine": "Karil the Tainted's repeating crossbow.", "walk_anim": "2076", - "durability": null, - "weight": "2", "turn90ccw_anim": "2076", "attack_speed": "4", "two_handed": "true", - "weapon_interface": "17", "turn180_anim": "2076", "defence_anim": "424", - "render_anim": "372", "equipment_slot": "3", "attack_anims": "2075,2075,2075,2075", "grand_exchange_price": "108029", "stand_anim": "2074", - "attack_audios": "2700,0,0,0", - "name": "Karil's x-bow 100", "run_anim": "2077", "archery_ticket_price": "0", "id": "4934", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0" + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "requirements": "{4,70}", + "durability": null, + "weight": "2", + "weapon_interface": "17", + "equip_audio": "2244", + "render_anim": "372", + "attack_audios": "2700,0,0,0", + "name": "Karil's x-bow 100" }, { - "requirements": "{4,70}", "turn90cw_anim": "2076", "examine": "Karil the Tainted's repeating crossbow.", "walk_anim": "2076", - "durability": null, - "weight": "2", "turn90ccw_anim": "2076", "attack_speed": "4", "two_handed": "true", - "weapon_interface": "17", "turn180_anim": "2076", "defence_anim": "424", - "render_anim": "372", "equipment_slot": "3", "attack_anims": "2075,2075,2075,2075", "grand_exchange_price": "108029", "stand_anim": "2074", - "attack_audios": "2700,0,0,0", - "name": "Karil's x-bow 75", "run_anim": "2077", "archery_ticket_price": "0", "id": "4935", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0" + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "requirements": "{4,70}", + "durability": null, + "weight": "2", + "weapon_interface": "17", + "equip_audio": "2244", + "render_anim": "372", + "attack_audios": "2700,0,0,0", + "name": "Karil's x-bow 75" }, { - "requirements": "{4,70}", "turn90cw_anim": "2076", "examine": "Karil the Tainted's repeating crossbow.", "walk_anim": "2076", - "durability": null, - "weight": "2", "turn90ccw_anim": "2076", "attack_speed": "4", "two_handed": "true", - "weapon_interface": "17", "turn180_anim": "2076", "defence_anim": "424", - "render_anim": "372", "equipment_slot": "3", "attack_anims": "2075,2075,2075,2075", "grand_exchange_price": "108029", "stand_anim": "2074", - "attack_audios": "2700,0,0,0", - "name": "Karil's x-bow 50", "run_anim": "2077", "archery_ticket_price": "0", "id": "4936", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0" + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "requirements": "{4,70}", + "durability": null, + "weight": "2", + "weapon_interface": "17", + "equip_audio": "2244", + "render_anim": "372", + "attack_audios": "2700,0,0,0", + "name": "Karil's x-bow 50" }, { - "requirements": "{4,70}", "turn90cw_anim": "2076", "examine": "Karil the Tainted's repeating crossbow.", "walk_anim": "2076", - "durability": null, - "weight": "2", "turn90ccw_anim": "2076", "attack_speed": "4", "two_handed": "true", - "weapon_interface": "17", "turn180_anim": "2076", "defence_anim": "424", - "render_anim": "372", "equipment_slot": "3", "attack_anims": "2075,2075,2075,2075", "grand_exchange_price": "108029", "stand_anim": "2074", - "attack_audios": "2700,0,0,0", - "name": "Karil's x-bow 25", "run_anim": "2077", "archery_ticket_price": "0", "id": "4937", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0" + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "requirements": "{4,70}", + "durability": null, + "weight": "2", + "weapon_interface": "17", + "equip_audio": "2244", + "render_anim": "372", + "attack_audios": "2700,0,0,0", + "name": "Karil's x-bow 25" }, { "requirements": "{4,70}", @@ -45398,6 +45830,7 @@ "two_handed": "true", "weapon_interface": "17", "turn180_anim": "2076", + "equip_audio": "2244", "render_anim": "372", "equipment_slot": "3", "grand_exchange_price": "106300", @@ -45417,63 +45850,68 @@ "name": "Karil's x-bow 0", "tradeable": "true", "archery_ticket_price": "0", - "id": "4939" + "id": "4939", + "equip_audio": "" }, { "requirements": "{1,70}-{4,70}", - "remove_sleeves": "true", "examine": "Karil the Tainted's leather body armour.", - "grand_exchange_price": "983428", "durability": null, - "name": "Karil's top 100", "weight": "6", + "absorb": "0,10,5", + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "983428", + "name": "Karil's top 100", "archery_ticket_price": "0", "id": "4940", - "absorb": "0,10,5", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" }, { "requirements": "{1,70}-{4,70}", - "remove_sleeves": "true", "examine": "Karil the Tainted's leather body armour.", - "grand_exchange_price": "983428", "durability": null, - "name": "Karil's top 75", "weight": "6", + "absorb": "0,10,5", + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "983428", + "name": "Karil's top 75", "archery_ticket_price": "0", "id": "4941", - "absorb": "0,10,5", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" }, { "requirements": "{1,70}-{4,70}", - "remove_sleeves": "true", "examine": "Karil the Tainted's leather body armour.", - "grand_exchange_price": "983428", "durability": null, - "name": "Karil's top 50", "weight": "6", + "absorb": "0,10,5", + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "983428", + "name": "Karil's top 50", "archery_ticket_price": "0", "id": "4942", - "absorb": "0,10,5", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" }, { "requirements": "{1,70}-{4,70}", - "remove_sleeves": "true", "examine": "Karil the Tainted's leather body armour.", - "grand_exchange_price": "983428", "durability": null, - "name": "Karil's top 25", "weight": "6", + "absorb": "0,10,5", + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "983428", + "name": "Karil's top 25", "archery_ticket_price": "0", "id": "4943", - "absorb": "0,10,5", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" }, { "requirements": "{1,70}-{4,70}", @@ -45482,6 +45920,7 @@ "durability": null, "weight": "6", "absorb": "0,10,5", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "1500000", @@ -45511,6 +45950,7 @@ "id": "4946", "absorb": "0,7,3", "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", + "equip_audio": "2238", "equipment_slot": "7" }, { @@ -45524,6 +45964,7 @@ "id": "4947", "absorb": "0,7,3", "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", + "equip_audio": "2238", "equipment_slot": "7" }, { @@ -45537,6 +45978,7 @@ "id": "4948", "absorb": "0,7,3", "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", + "equip_audio": "2238", "equipment_slot": "7" }, { @@ -45550,21 +45992,23 @@ "id": "4949", "absorb": "0,7,3", "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", + "equip_audio": "2238", "equipment_slot": "7" }, { "requirements": "{1,70}-{4,70}", "ge_buy_limit": "1", "examine": "Karil the Tainted's leather skirt.", - "grand_exchange_price": "153100", "durability": null, + "weight": "2", + "absorb": "0,7,3", + "equip_audio": "2238", + "equipment_slot": "7", + "grand_exchange_price": "153100", "name": "Karil's skirt 0", "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "4950", - "absorb": "0,7,3", - "equipment_slot": "7" + "id": "4950" }, { "ge_buy_limit": "1", @@ -45583,6 +46027,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "71798", @@ -45598,6 +46043,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "71798", @@ -45613,6 +46059,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "71798", @@ -45628,6 +46075,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "71798", @@ -45644,6 +46092,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "78200", @@ -45670,6 +46119,7 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "10", + "equip_audio": "2233", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "2068,2068,2068,2068", @@ -45688,6 +46138,7 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "10", + "equip_audio": "2233", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "2068,2068,2068,2068", @@ -45706,6 +46157,7 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "10", + "equip_audio": "2233", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "2068,2068,2068,2068", @@ -45724,6 +46176,7 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "10", + "equip_audio": "2233", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "2068,2068,2068,2068", @@ -45743,6 +46196,7 @@ "attack_speed": "5", "two_handed": "true", "weapon_interface": "10", + "equip_audio": "2233", "equipment_slot": "3", "grand_exchange_price": "95600", "attack_audios": "1332,0,0,0", @@ -45763,59 +46217,63 @@ }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Torag the Corrupted's platebody armour.", - "grand_exchange_price": "297733", "durability": null, - "name": "Torag's body 100", "weight": "9", + "absorb": "5,0,10", + "equip_audio": "2240", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "297733", + "name": "Torag's body 100", "archery_ticket_price": "0", "id": "4964", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Torag the Corrupted's platebody armour.", - "grand_exchange_price": "297733", "durability": null, - "name": "Torag's body 75", "weight": "9", + "absorb": "5,0,10", + "equip_audio": "2240", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "297733", + "name": "Torag's body 75", "archery_ticket_price": "0", "id": "4965", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Torag the Corrupted's platebody armour.", - "grand_exchange_price": "297733", "durability": null, - "name": "Torag's body 50", "weight": "9", + "absorb": "5,0,10", + "equip_audio": "2240", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "297733", + "name": "Torag's body 50", "archery_ticket_price": "0", "id": "4966", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Torag the Corrupted's platebody armour.", - "grand_exchange_price": "297733", "durability": null, - "name": "Torag's body 25", "weight": "9", + "absorb": "5,0,10", + "equip_audio": "2240", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "297733", + "name": "Torag's body 25", "archery_ticket_price": "0", "id": "4967", - "absorb": "5,0,10", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" }, { "requirements": "{1,70}", @@ -45824,6 +46282,7 @@ "durability": null, "weight": "9", "absorb": "5,0,10", + "equip_audio": "2240", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "271600", @@ -45853,6 +46312,7 @@ "id": "4970", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -45866,6 +46326,7 @@ "id": "4971", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -45879,6 +46340,7 @@ "id": "4972", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -45892,21 +46354,23 @@ "id": "4973", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { "requirements": "{1,70}", "ge_buy_limit": "10", "examine": "Torag the Corrupted's plate leg armour.", - "grand_exchange_price": "371300", "durability": null, + "weight": "9", + "absorb": "3,0,7", + "equip_audio": "2242", + "equipment_slot": "7", + "grand_exchange_price": "371300", "name": "Torag's legs 0", "tradeable": "true", - "weight": "9", "archery_ticket_price": "0", - "id": "4974", - "absorb": "3,0,7", - "equipment_slot": "7" + "id": "4974" }, { "ge_buy_limit": "10", @@ -45925,6 +46389,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "459537", @@ -45940,6 +46405,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "459537", @@ -45955,6 +46421,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "459537", @@ -45970,6 +46437,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "459537", @@ -45986,6 +46454,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "648900", @@ -46005,108 +46474,112 @@ "id": "4981" }, { - "requirements": "{0,70}", "turn90cw_anim": "2060", "examine": "Verac the Defiled's flail.", "walk_anim": "1830", - "durability": null, - "weight": "2", "turn90ccw_anim": "2060", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "8", "turn180_anim": "2060", "defence_anim": "424", - "render_anim": "1426", "equipment_slot": "3", "attack_anims": "2062,2062,2062,2062", "grand_exchange_price": "95474", "stand_anim": "1832", - "attack_audios": "1323,0,0,0", - "name": "Verac's flail 100", "run_anim": "1831", "archery_ticket_price": "0", "id": "4982", "stand_turn_anim": "823", - "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0" + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "requirements": "{0,70}", + "durability": null, + "weight": "2", + "weapon_interface": "8", + "equip_audio": "2246", + "render_anim": "1426", + "attack_audios": "1323,0,0,0", + "name": "Verac's flail 100" }, { - "requirements": "{0,70}", "turn90cw_anim": "2060", "examine": "Verac the Defiled's flail.", "walk_anim": "1830", - "durability": null, - "weight": "2", "turn90ccw_anim": "2060", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "8", "turn180_anim": "2060", "defence_anim": "424", - "render_anim": "1426", "equipment_slot": "3", "attack_anims": "2062,2062,2062,2062", "grand_exchange_price": "95474", "stand_anim": "1832", - "attack_audios": "1323,0,0,0", - "name": "Verac's flail 75", "run_anim": "1831", "archery_ticket_price": "0", "id": "4983", "stand_turn_anim": "823", - "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0" + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "requirements": "{0,70}", + "durability": null, + "weight": "2", + "weapon_interface": "8", + "equip_audio": "2246", + "render_anim": "1426", + "attack_audios": "1323,0,0,0", + "name": "Verac's flail 75" }, { - "requirements": "{0,70}", "turn90cw_anim": "2060", "examine": "Verac the Defiled's flail.", "walk_anim": "1830", - "durability": null, - "weight": "2", "turn90ccw_anim": "2060", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "8", "turn180_anim": "2060", "defence_anim": "424", - "render_anim": "1426", "equipment_slot": "3", "attack_anims": "2062,2062,2062,2062", "grand_exchange_price": "95474", "stand_anim": "1832", - "attack_audios": "1323,0,0,0", - "name": "Verac's flail 50", "run_anim": "1831", "archery_ticket_price": "0", "id": "4984", "stand_turn_anim": "823", - "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0" + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "requirements": "{0,70}", + "durability": null, + "weight": "2", + "weapon_interface": "8", + "equip_audio": "2246", + "render_anim": "1426", + "attack_audios": "1323,0,0,0", + "name": "Verac's flail 50" }, { - "requirements": "{0,70}", "turn90cw_anim": "2060", "examine": "Verac the Defiled's flail.", "walk_anim": "1830", - "durability": null, - "weight": "2", "turn90ccw_anim": "2060", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "8", "turn180_anim": "2060", "defence_anim": "424", - "render_anim": "1426", "equipment_slot": "3", "attack_anims": "2062,2062,2062,2062", "grand_exchange_price": "95474", "stand_anim": "1832", - "attack_audios": "1323,0,0,0", - "name": "Verac's flail 25", "run_anim": "1831", "archery_ticket_price": "0", "id": "4985", "stand_turn_anim": "823", - "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0" + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "requirements": "{0,70}", + "durability": null, + "weight": "2", + "weapon_interface": "8", + "equip_audio": "2246", + "render_anim": "1426", + "attack_audios": "1323,0,0,0", + "name": "Verac's flail 25" }, { "requirements": "{0,70}", @@ -46121,6 +46594,7 @@ "two_handed": "true", "weapon_interface": "8", "turn180_anim": "2060", + "equip_audio": "2246", "render_anim": "1426", "equipment_slot": "3", "grand_exchange_price": "95500", @@ -46145,59 +46619,63 @@ }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Verac the Defiled's brassard.", - "grand_exchange_price": "177963", "durability": null, - "name": "Verac's top 100", "weight": "5", + "absorb": "5,0,10", + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "177963", + "name": "Verac's top 100", "archery_ticket_price": "0", "id": "4988", - "absorb": "5,0,10", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Verac the Defiled's brassard.", - "grand_exchange_price": "177963", "durability": null, - "name": "Verac's top 75", "weight": "5", + "absorb": "5,0,10", + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "177963", + "name": "Verac's top 75", "archery_ticket_price": "0", "id": "4989", - "absorb": "5,0,10", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Verac the Defiled's brassard.", - "grand_exchange_price": "177963", "durability": null, - "name": "Verac's top 50", "weight": "5", + "absorb": "5,0,10", + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "177963", + "name": "Verac's top 50", "archery_ticket_price": "0", "id": "4990", - "absorb": "5,0,10", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" }, { "requirements": "{1,70}", - "remove_sleeves": "true", "examine": "Verac the Defiled's brassard.", - "grand_exchange_price": "177963", "durability": null, - "name": "Verac's top 25", "weight": "5", + "absorb": "5,0,10", + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "177963", + "name": "Verac's top 25", "archery_ticket_price": "0", "id": "4991", - "absorb": "5,0,10", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" }, { "requirements": "{1,70}", @@ -46206,6 +46684,7 @@ "durability": null, "weight": "5", "absorb": "5,0,10", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "174000", @@ -46235,6 +46714,7 @@ "id": "4994", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -46248,6 +46728,7 @@ "id": "4995", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -46261,6 +46742,7 @@ "id": "4996", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { @@ -46274,21 +46756,23 @@ "id": "4997", "absorb": "3,0,7", "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", + "equip_audio": "2242", "equipment_slot": "7" }, { "requirements": "{1,70}", "ge_buy_limit": "10", "examine": "Verac the Defiled's plate skirt.", - "grand_exchange_price": "321600", "durability": null, + "weight": "5", + "absorb": "3,0,7", + "equip_audio": "2242", + "equipment_slot": "7", + "grand_exchange_price": "321600", "name": "Verac's skirt 0", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", - "id": "4998", - "absorb": "3,0,7", - "equipment_slot": "7" + "id": "4998" }, { "ge_buy_limit": "10", @@ -46422,6 +46906,7 @@ "two_handed": "false", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "219", @@ -46452,7 +46937,9 @@ "weight": "0.9", "attack_speed": "6", "weapon_interface": "10", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "grand_exchange_price": "384", "name": "Bone club", "tradeable": "true", @@ -46879,6 +47366,7 @@ "name": "Dwarven battleaxe", "archery_ticket_price": "0", "id": "5060", + "equip_audio": "2232", "equipment_slot": "3" }, { @@ -46887,6 +47375,7 @@ "name": "Dwarven battleaxe", "archery_ticket_price": "0", "id": "5061", + "equip_audio": "2232", "equipment_slot": "3" }, { @@ -48499,7 +48988,7 @@ "id": "5419" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There is one potato in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(1)", @@ -48516,7 +49005,7 @@ "id": "5421" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are two potatoes in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(2)", @@ -48533,7 +49022,7 @@ "id": "5423" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are three potatoes in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(3)", @@ -48550,7 +49039,7 @@ "id": "5425" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are four potatoes in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(4)", @@ -48567,7 +49056,7 @@ "id": "5427" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are five potatoes in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(5)", @@ -48584,7 +49073,7 @@ "id": "5429" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are six potatoes in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(6)", @@ -48601,7 +49090,7 @@ "id": "5431" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are seven potatoes in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(7)", @@ -48618,7 +49107,7 @@ "id": "5433" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are eight potatoes in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(8)", @@ -48635,7 +49124,7 @@ "id": "5435" }, { - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are nine potatoes in this sack.", "grand_exchange_price": "1276", "durability": null, "name": "Potatoes(9)", @@ -48653,7 +49142,7 @@ }, { "ge_buy_limit": "1000", - "examine": "There are <number of potatoes> in this sack.", + "examine": "There are ten potatoes in this sack.", "grand_exchange_price": "1052", "durability": null, "name": "Potatoes(10)", @@ -48845,7 +49334,7 @@ "id": "5459" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There is one cabbage in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(1)", @@ -48861,7 +49350,7 @@ "id": "5461" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are two cabbages in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(2)", @@ -48877,7 +49366,7 @@ "id": "5463" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are three cabbages in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(3)", @@ -48893,7 +49382,7 @@ "id": "5465" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are four cabbages in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(4)", @@ -48909,7 +49398,7 @@ "id": "5467" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are five cabbages in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(5)", @@ -48925,7 +49414,7 @@ "id": "5469" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are six cabbages in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(6)", @@ -48941,7 +49430,7 @@ "id": "5471" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are seven cabbages in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(7)", @@ -48957,7 +49446,7 @@ "id": "5473" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are eight cabbages in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(8)", @@ -48973,7 +49462,7 @@ "id": "5475" }, { - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are nine cabbages in this sack.", "grand_exchange_price": "1304", "durability": null, "name": "Cabbages(9)", @@ -48990,7 +49479,7 @@ }, { "ge_buy_limit": "1000", - "examine": "There are <number of cabbages> in this sack.", + "examine": "There are ten cabbages in this sack.", "grand_exchange_price": "917", "durability": null, "name": "Cabbages(10)", @@ -49888,10 +50377,18 @@ "id": "5584" }, { - "examine": "It's a metal spade without a handle.", + "examine": "I hope the mould was accurate enough...", + "durability": null, + "name": "Bronze Key", + "weight": "0.01", + "archery_ticket_price": "0", + "id": "5585" + }, + { + "examine": "It's a metal spade with a wooden handle.", "durability": null, "name": "Metal spade", - "weight": "2.5", + "weight": "1.814", "archery_ticket_price": "0", "id": "5586" }, @@ -49899,7 +50396,7 @@ "examine": "It's a metal spade without a handle.", "durability": null, "name": "Metal spade", - "weight": "2.5", + "weight": "1.814", "archery_ticket_price": "0", "id": "5587" }, @@ -49911,101 +50408,101 @@ "id": "5588" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "20", + "examine": "A tin layered with some stuff from a vial.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.101", "archery_ticket_price": "0", "id": "5592" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "20", + "examine": "It's full of a white lumpy mixture that seems to be hardening.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.101", "archery_ticket_price": "0", "id": "5593" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "20", + "examine": "There is an impression of a key embedded in it.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.101", "archery_ticket_price": "0", "id": "5594" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "20", + "examine": "There is an impression of a key, filled with tin ore.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.101", "archery_ticket_price": "0", "id": "5595" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "20", + "examine": "There is an impression of a key, filled with copper ore.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.101", "archery_ticket_price": "0", "id": "5596" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "20", + "examine": "There is an impression of a key, filled with tin and copper ore.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.101", "archery_ticket_price": "0", "id": "5597" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "20", + "examine": "There is a bronze key surrounded by plaster in this tin.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.101", "archery_ticket_price": "0", "id": "5598" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "20", + "examine": "There is a strange concoction filling this tin.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.101", "archery_ticket_price": "0", "id": "5599" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", + "shop_price": "10", + "examine": "I could probably pour something into this.", + "grand_exchange_price": "0", "durability": null, "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "tradeable": "false", + "weight": "0.1", "archery_ticket_price": "0", "id": "5600" }, @@ -50035,8 +50532,8 @@ "grand_exchange_price": "41", "durability": null, "name": "Shears", - "tradeable": "true", - "weight": "0.1", + "tradeable": "false", + "weight": "0.113", "archery_ticket_price": "0", "id": "5603" }, @@ -50318,6 +50815,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50335,6 +50833,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50353,6 +50852,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50371,6 +50871,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50389,6 +50890,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50407,6 +50909,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50425,6 +50928,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50442,6 +50946,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50459,6 +50964,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50477,6 +50983,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50495,6 +51002,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50513,6 +51021,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50531,6 +51040,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50549,6 +51059,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "582,582,582,582", @@ -50567,6 +51078,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50584,6 +51096,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50602,6 +51115,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50620,6 +51134,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50637,6 +51152,7 @@ "examine": "An adamant tipped javelin.", "durability": null, "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50654,6 +51170,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50688,6 +51205,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50706,6 +51224,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50724,6 +51243,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50741,6 +51261,7 @@ "examine": "An adamant tipped javelin.", "durability": null, "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50759,6 +51280,7 @@ "durability": null, "attack_speed": "6", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50776,6 +51298,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50794,6 +51317,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50813,6 +51337,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50831,6 +51356,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50850,6 +51376,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50868,6 +51395,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50886,6 +51414,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50904,6 +51433,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50922,6 +51452,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50941,6 +51472,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50959,6 +51491,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50978,6 +51511,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -50995,6 +51529,7 @@ "examine": "A finely balanced throwing knife.", "durability": null, "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -51013,6 +51548,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "defence_anim": "424", "equipment_slot": "3", "attack_anims": "806,806,806,806", @@ -51032,10 +51568,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "483", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Iron dagger(p+)", "tradeable": "true", "archery_ticket_price": "0", @@ -51059,10 +51597,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "350", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Bronze dagger(p+)", "tradeable": "true", "archery_ticket_price": "0", @@ -51087,10 +51627,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "575", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Steel dagger(p+)", "tradeable": "true", "archery_ticket_price": "0", @@ -51115,10 +51657,12 @@ "weight": "0.3", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "538", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Mithril dagger(p+)", "tradeable": "true", "archery_ticket_price": "0", @@ -51139,10 +51683,12 @@ "ge_buy_limit": "100", "durability": null, "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "800", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Adamant dagger(p+)", "tradeable": "true", "archery_ticket_price": "0", @@ -51167,8 +51713,10 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", + "attack_anims": "400,400,401,400", "lendable": "true", "grand_exchange_price": "4538", "attack_audios": "2517,2517,2500,2517", @@ -51197,6 +51745,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -51231,10 +51780,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "664", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Black dagger(p+)", "tradeable": "true", "archery_ticket_price": "0", @@ -51258,6 +51809,7 @@ "id": "5684", "weapon_interface": "5", "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3" }, @@ -51276,10 +51828,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "4128", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Iron dagger(p++)", "tradeable": "true", "archery_ticket_price": "0", @@ -51303,10 +51857,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "5088", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Br'ze dagger(p++)", "tradeable": "true", "archery_ticket_price": "0", @@ -51331,10 +51887,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "4729", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Steel dagger(p++)", "tradeable": "true", "archery_ticket_price": "0", @@ -51359,10 +51917,12 @@ "weight": "0.3", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "3476", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Mithril dagger(p++)", "tradeable": "true", "archery_ticket_price": "0", @@ -51383,10 +51943,12 @@ "ge_buy_limit": "100", "durability": null, "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "4612", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Adamant dagger(p++)", "tradeable": "true", "archery_ticket_price": "0", @@ -51411,11 +51973,13 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "lendable": "true", "grand_exchange_price": "5061", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Rune dagger(p++)", "tradeable": "true", "archery_ticket_price": "0", @@ -51441,6 +52005,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -51475,10 +52040,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "4921", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "Black dagger(p++)", "tradeable": "true", "archery_ticket_price": "0", @@ -51502,6 +52069,7 @@ "id": "5702", "weapon_interface": "5", "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3" }, @@ -51524,6 +52092,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "861", @@ -51557,6 +52126,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "676", @@ -51591,6 +52161,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "596", @@ -51625,6 +52196,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "802", @@ -51659,6 +52231,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "1394", @@ -51681,30 +52254,31 @@ "id": "5713" }, { - "requirements": "{0,40}", "ge_buy_limit": "100", "turn90cw_anim": "1207", "examine": "A rune tipped spear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", - "render_anim": "28", "equipment_slot": "3", - "lendable": "true", "grand_exchange_price": "12500", "stand_anim": "813", "tradeable": "true", - "name": "Rune spear(p+)", "run_anim": "1210", "archery_ticket_price": "0", "id": "5714", "stand_turn_anim": "1209", - "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0" + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "requirements": "{0,40}", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "lendable": "true", + "name": "Rune spear(p+)" }, { "ge_buy_limit": "100", @@ -51740,6 +52314,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon spear(p+)" @@ -51767,6 +52342,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "7557", @@ -51800,6 +52376,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "8394", @@ -51834,6 +52411,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "8336", @@ -51868,6 +52446,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "7035", @@ -51900,6 +52479,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "7530", @@ -51922,30 +52502,31 @@ "id": "5727" }, { - "requirements": "{0,40}", "ge_buy_limit": "100", "turn90cw_anim": "1207", "examine": "A rune tipped spear.", "walk_anim": "1205", - "durability": null, - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "14", "turn180_anim": "1206", - "render_anim": "28", "equipment_slot": "3", - "lendable": "true", "grand_exchange_price": "17700", "stand_anim": "813", "tradeable": "true", - "name": "Rune spear(p++)", "run_anim": "1210", "archery_ticket_price": "0", "id": "5728", "stand_turn_anim": "1209", - "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0" + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "requirements": "{0,40}", + "durability": null, + "weight": "2.2", + "weapon_interface": "14", + "equip_audio": "2247", + "render_anim": "28", + "lendable": "true", + "name": "Rune spear(p++)" }, { "ge_buy_limit": "100", @@ -51981,6 +52562,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon spear(p++)" @@ -52023,6 +52605,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "3351", @@ -52057,6 +52640,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "9065", @@ -55169,7 +55753,7 @@ "equipment_slot": "4" }, { - "examine": "Damaged: These are in need of a good tailor.Repaired: A pair of mourner trousers.", + "examine": "These are in need of a good tailor.", "durability": null, "name": "Mourner trousers", "weight": "2.25", @@ -55177,7 +55761,7 @@ "id": "6066" }, { - "examine": "Damaged: These are in need of a good tailor.Repaired: A pair of mourner trousers.", + "examine": "A pair of mourner trousers.", "durability": null, "name": "Mourner trousers", "weight": "2.25", @@ -55804,18 +56388,19 @@ "id": "6142" }, { - "lendable": "true", "ge_buy_limit": "100", "examine": "Some finely crafted Fremennik boots, made from spined dagannoth hide.", - "grand_exchange_price": "1671", "durability": null, + "weight": "3", + "equip_audio": "2237", + "equipment_slot": "10", + "lendable": "true", + "grand_exchange_price": "1671", "name": "Spined boots", "tradeable": "true", - "weight": "3", "archery_ticket_price": "0", "id": "6143", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0", - "equipment_slot": "10" + "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" }, { "ge_buy_limit": "100", @@ -55827,18 +56412,19 @@ "id": "6144" }, { - "lendable": "true", "ge_buy_limit": "100", "examine": "Some Fremennik boots, made from the shards of a rock crab's shell.", - "grand_exchange_price": "756", "durability": null, + "weight": "3.1", + "equip_audio": "2237", + "equipment_slot": "10", + "lendable": "true", + "grand_exchange_price": "756", "name": "Rock-shell boots", "tradeable": "true", - "weight": "3.1", "archery_ticket_price": "0", "id": "6145", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0", - "equipment_slot": "10" + "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" }, { "ge_buy_limit": "100", @@ -57738,7 +58324,7 @@ { "shop_price": "150", "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", + "examine": "A brown armband, as worn by the Tai Bwo Wannai locals.", "grand_exchange_price": "734", "durability": null, "name": "Villager armband", @@ -57871,7 +58457,7 @@ { "shop_price": "150", "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", + "examine": "A blue armband, as worn by the Tai Bwo Wannai locals.", "grand_exchange_price": "672", "durability": null, "name": "Villager armband", @@ -57982,7 +58568,7 @@ { "shop_price": "150", "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", + "examine": "A yellow armband, as worn by the Tai Bwo Wannai locals.", "grand_exchange_price": "823", "durability": null, "name": "Villager armband", @@ -58093,7 +58679,7 @@ { "shop_price": "150", "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", + "examine": "A pink armband, as worn by the Tai Bwo Wannai locals.", "grand_exchange_price": "1697", "durability": null, "name": "Villager armband", @@ -58834,7 +59420,7 @@ }, { "destroy_message": "Another can be received from pickpocketing Dr. Fenkenstrain in the tower.", - "examine": "Unactivated: The Ring of charos.Activated: The power within this ring has been activated.", + "examine": "The power within this ring has been activated.", "durability": null, "name": "Ring of charos(a)", "tradeable": "false", @@ -59193,7 +59779,7 @@ }, { "destroy_message": "You will not get a replacement for the present. Open it for your reward.", - "examine": "Thanks for all your help! Love, Bob & Neite.", + "examine": "Thanks for all your help! Love, Bob & Neite.", "durability": null, "name": "Present", "tradeable": "false", @@ -59373,36 +59959,38 @@ "durability": null, "weight": "2.2", "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Mud battlestaff" }, { - "requirements": "{0,40}-{6,40}", "ge_buy_limit": "10", - "shop_price": "40000", "turn90cw_anim": "1207", "examine": "It's a slightly magical stick.", "walk_anim": "1205", - "durability": null, - "weight": "2", "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", - "render_anim": "28", "equipment_slot": "3", + "attack_anims": "419,419,419,419", "grand_exchange_price": "423100", "stand_anim": "813", - "attack_audios": "2555,0,0,0", "tradeable": "true", - "name": "Mystic mud staff", "run_anim": "1210", "archery_ticket_price": "0", "id": "6563", "stand_turn_anim": "1209", - "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0" + "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", + "requirements": "{0,40}-{6,40}", + "shop_price": "40000", + "durability": null, + "weight": "2", + "weapon_interface": "1", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Mystic mud staff" }, { "examine": "A valuable ring.", @@ -59640,6 +60228,7 @@ "equipment_slot": "3", "attack_anims": "390,390,390,390", "grand_exchange_price": "1013", + "attack_audios": "2548,2548,2548,2548", "name": "White claws", "tradeable": "true", "archery_ticket_price": "0", @@ -59664,6 +60253,7 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -59694,6 +60284,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -59724,10 +60315,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "497", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "White dagger(p)", "tradeable": "true", "archery_ticket_price": "0", @@ -59752,10 +60345,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "982", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "White dagger(p+)", "tradeable": "true", "archery_ticket_price": "0", @@ -59780,10 +60375,12 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "8422", "attack_audios": "2517,2517,2500,2517", + "attack_anims": "400,400,401,400", "name": "White dagger(p++)", "tradeable": "true", "archery_ticket_price": "0", @@ -59819,11 +60416,12 @@ "id": "6599", "stand_turn_anim": "1209", "bonuses": "19,25,0,-4,0,-1,2,3,0,0,0,20,1,0,0", - "requirements": "{0,10}-{5,10}", + "requirements": "{0,10}-{2,5}", "shop_price": "1920", "durability": null, "weight": "3.1", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "White halberd" }, @@ -59845,10 +60443,12 @@ "weight": "1.8", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "390,390,381,390", "grand_exchange_price": "481", + "attack_audios": "2508,2508,25092508", "name": "White mace", "tradeable": "true", "archery_ticket_price": "0", @@ -59876,6 +60476,7 @@ "turn90ccw_anim": "1208", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "650", @@ -59922,6 +60523,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "name": "White sword" }, @@ -59958,6 +60560,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "name": "White longsword" }, @@ -59995,6 +60598,7 @@ "durability": null, "weight": "3.6", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "attack_audios": "2503,0,2504,0", "name": "White 2h sword" @@ -60032,6 +60636,7 @@ "durability": null, "weight": "1", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,0,2517,0", "name": "White scimitar" @@ -60054,6 +60659,7 @@ "weight": "1.8", "attack_speed": "6", "weapon_interface": "10", + "equip_audio": "2233", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,395,395", @@ -60107,6 +60713,7 @@ "durability": null, "weight": "9.9", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "5837", @@ -60157,6 +60764,7 @@ "examine": "A medium sized helmet.", "durability": null, "weight": "1.9", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "853", "name": "White med helm", @@ -60182,6 +60790,7 @@ "examine": "A full face helmet.", "durability": null, "weight": "2.7", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "2624", @@ -60280,6 +60889,7 @@ "durability": null, "weight": "3.6", "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "1360", "name": "White sq shield", @@ -60305,6 +60915,7 @@ "durability": null, "weight": "5.4", "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "2390", "name": "White kiteshield", @@ -61240,6 +61851,7 @@ "rare_item": "true", "durability": null, "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -64423,6 +65035,7 @@ "durability": null, "weight": "3", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "2503,0,2504,0", @@ -66056,6 +66669,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "34200", "name": "Rune shield(h1)", @@ -66128,6 +66742,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "32400", "name": "Rune shield(h2)", @@ -66200,6 +66815,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "32700", "name": "Rune shield(h3)", @@ -66272,6 +66888,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "32500", "name": "Rune shield(h4)", @@ -66344,6 +66961,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "32400", "name": "Rune shield(h5)", @@ -66462,15 +67080,16 @@ "requirements": "{1,40}-{4,40}", "ge_buy_limit": "5000", "examine": "Made from 100% real dragonhide. With colourful trim!", - "grand_exchange_price": "212400", "durability": null, + "weight": "6", + "equip_audio": "2241", + "equipment_slot": "4", + "grand_exchange_price": "212400", "name": "D'hide body(g)", "tradeable": "true", - "weight": "6", "archery_ticket_price": "0", "id": "7370", - "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0" }, { "ge_buy_limit": "5000", @@ -66485,15 +67104,16 @@ "requirements": "{1,40}-{4,40}", "ge_buy_limit": "2", "examine": "Made from 100% real dragonhide. With colourful trim!", - "grand_exchange_price": "38800", "durability": null, + "weight": "6", + "equip_audio": "2241", + "equipment_slot": "4", + "grand_exchange_price": "38800", "name": "D'hide body (t)", "tradeable": "true", - "weight": "6", "archery_ticket_price": "0", "id": "7372", - "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0" }, { "ge_buy_limit": "2", @@ -66508,15 +67128,16 @@ "requirements": "{1,40}-{4,50}", "ge_buy_limit": "2", "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "6883", "durability": null, + "weight": "6", + "equip_audio": "2241", + "equipment_slot": "4", + "grand_exchange_price": "6883", "name": "D'hide body (g)", "tradeable": "true", - "weight": "6", "archery_ticket_price": "0", "id": "7374", - "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0" }, { "ge_buy_limit": "2", @@ -66531,15 +67152,16 @@ "requirements": "{1,40}-{4,50}", "ge_buy_limit": "2", "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "5268", "durability": null, + "weight": "6", + "equip_audio": "2241", + "equipment_slot": "4", + "grand_exchange_price": "5268", "name": "D'hide body (t)", "tradeable": "true", - "weight": "6", "archery_ticket_price": "0", "id": "7376", - "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0" }, { "ge_buy_limit": "2", @@ -66554,15 +67176,16 @@ "requirements": "{4,40}", "ge_buy_limit": "2", "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "1300000", "durability": null, + "weight": "5", + "equip_audio": "2241", + "equipment_slot": "7", + "grand_exchange_price": "1300000", "name": "D'hide chaps (g)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "7378", - "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0" }, { "ge_buy_limit": "2", @@ -66577,15 +67200,16 @@ "requirements": "{4,40}", "ge_buy_limit": "2", "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "304300", "durability": null, + "weight": "5", + "equip_audio": "2241", + "equipment_slot": "7", + "grand_exchange_price": "304300", "name": "D'hide chaps (t)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "7380", - "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0" }, { "ge_buy_limit": "2", @@ -66600,15 +67224,16 @@ "requirements": "{4,50}", "ge_buy_limit": "2", "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "21300", "durability": null, + "weight": "5", + "equip_audio": "2241", + "equipment_slot": "7", + "grand_exchange_price": "21300", "name": "D'hide chaps (g)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "7382", - "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0" }, { "ge_buy_limit": "2", @@ -66623,15 +67248,16 @@ "requirements": "{4,50}", "ge_buy_limit": "2", "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "2554", "durability": null, + "weight": "5", + "equip_audio": "2241", + "equipment_slot": "7", + "grand_exchange_price": "2554", "name": "D'hide chaps (t)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "7384", - "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0" }, { "ge_buy_limit": "2", @@ -67400,6 +68026,7 @@ "durability": null, "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2244", "equipment_slot": "3", "grand_exchange_price": "4686", "attack_audios": "2704,0,0,0", @@ -67492,6 +68119,7 @@ "archery_ticket_price": "0", "id": "7453", "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67503,6 +68131,7 @@ "archery_ticket_price": "0", "id": "7454", "bonuses": "2,2,2,1,2,2,2,2,1,2,1,2,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67514,6 +68143,7 @@ "archery_ticket_price": "0", "id": "7455", "bonuses": "3,3,3,2,3,3,3,3,2,3,2,3,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67525,6 +68155,7 @@ "archery_ticket_price": "0", "id": "7456", "bonuses": "4,4,4,2,4,4,4,4,2,4,2,4,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67536,6 +68167,7 @@ "archery_ticket_price": "0", "id": "7457", "bonuses": "5,5,5,3,5,5,5,5,3,5,3,5,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67546,6 +68178,7 @@ "archery_ticket_price": "0", "id": "7458", "bonuses": "6,6,6,3,6,6,6,6,3,6,3,6,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67557,6 +68190,7 @@ "archery_ticket_price": "0", "id": "7459", "bonuses": "7,7,7,4,7,7,7,7,4,7,4,7,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67568,6 +68202,7 @@ "archery_ticket_price": "0", "id": "7460", "bonuses": "8,8,8,4,8,8,8,8,4,8,4,8,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67579,6 +68214,7 @@ "archery_ticket_price": "0", "id": "7461", "bonuses": "9,9,9,5,9,9,9,9,5,9,5,9,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -67591,6 +68227,7 @@ "archery_ticket_price": "0", "id": "7462", "bonuses": "12,12,12,6,12,12,12,12,6,12,6,12,0,0,0", + "equip_audio": "2236", "equipment_slot": "9" }, { @@ -68843,9 +69480,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -68866,9 +69504,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -68891,9 +69530,10 @@ "weight": "2", "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -68914,9 +69554,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -68937,9 +69578,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -68960,9 +69602,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -68983,9 +69626,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -69006,9 +69650,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -69029,9 +69674,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -69052,9 +69698,10 @@ "durability": null, "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", + "two_handed": "false", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -69335,6 +69982,7 @@ "id": "7675", "weapon_interface": "5", "bonuses": "4,3,-2,0,0,0,2,1,0,0,0,5,0,0,0", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3" }, @@ -69343,6 +69991,7 @@ "name": "Wooden shield", "archery_ticket_price": "0", "id": "7676", + "equip_audio": "2250", "equipment_slot": "5" }, { @@ -69408,6 +70057,7 @@ "archery_ticket_price": "0", "attack_speed": "3", "id": "7684", + "equip_audio": "2244", "equipment_slot": "3" }, { @@ -70061,7 +70711,7 @@ }, { "ge_buy_limit": "100", - "examine": "In inventory: Nice bit of crafting! When released: Nice bit of crafting that...", + "examine": "Nice bit of crafting!", "grand_exchange_price": "3236", "durability": null, "name": "Toy doll", @@ -70079,7 +70729,7 @@ "id": "7764" }, { - "examine": "In inventory: Nice bit of crafting! When released: Nice bit of crafting that...", + "examine": "Nice bit of crafting!", "grand_exchange_price": "3289", "durability": null, "name": "Toy doll (wound)", @@ -70466,6 +71116,7 @@ "id": "7807", "weapon_interface": "2", "bonuses": "20,20,20,0,0,0,0,0,0,-1,0,13,0,0,0", + "equip_audio": "2232", "defence_anim": "397", "equipment_slot": "3" }, @@ -70490,6 +71141,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "2079", "equipment_slot": "3", @@ -71406,9 +72058,12 @@ { "examine": "A ring given to you by the Easter Bunny.", "durability": null, + "attack_audios": "", "name": "Easter ring", "archery_ticket_price": "0", - "id": "7927" + "id": "7927", + "equip_audio": "", + "equipment_slot": "12" }, { "examine": "Happy Easter.", @@ -71766,6 +72421,7 @@ "id": "7973" }, { + "examine": "I should get it stuffed!", "durability": null, "name": "Crawling hand", "archery_ticket_price": "0", @@ -71830,6 +72486,7 @@ "id": "7981" }, { + "examine": "I could mount this on my wall!", "durability": null, "name": "Crawling hand", "archery_ticket_price": "0", @@ -71837,7 +72494,7 @@ "id": "7982" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Cockatrice head", "tradeable": "false", @@ -71847,7 +72504,7 @@ "id": "7983" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Basilisk head", "tradeable": "false", @@ -71857,7 +72514,7 @@ "id": "7984" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Kurask head", "tradeable": "false", @@ -71867,7 +72524,7 @@ "id": "7985" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Abyssal head", "tradeable": "false", @@ -71877,7 +72534,7 @@ "id": "7986" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount these on my wall!", "durability": null, "name": "Kbd heads", "tradeable": "false", @@ -71887,7 +72544,7 @@ "id": "7987" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Kq head", "archery_ticket_price": "0", @@ -71902,7 +72559,7 @@ "id": "7989" }, { - "examine": "Whopper! I should get this stuffed!", + "examine": "I should mount this on my wall!", "durability": null, "name": "Big bass", "weight": "6", @@ -71917,14 +72574,14 @@ "id": "7991" }, { - "examine": "Whopper! I should get this stuffed!", + "examine": "I should mount this on my wall!", "durability": null, "name": "Big swordfish", "archery_ticket_price": "0", "id": "7992" }, { - "examine": "Its a monster! I should get this stuffed!", + "examine": "It's a monster! I should get this stuffed!", "durability": null, "name": "Big shark", "weight": "0.7", @@ -71932,7 +72589,7 @@ "id": "7993" }, { - "examine": "Its a monster! I should get this stuffed!", + "examine": "I should mount this on my wall!", "durability": null, "name": "Big shark", "weight": "0.7", @@ -72661,6 +73318,7 @@ "id": "8248" }, { + "examine": "A trophy of a mighty slayer!", "durability": null, "name": "Crawling hand", "archery_ticket_price": "0", @@ -72668,7 +73326,7 @@ "id": "8260" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty slayer!", "durability": null, "name": "Cockatrice head", "tradeable": "false", @@ -72678,7 +73336,7 @@ "id": "8261" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty slayer!", "durability": null, "name": "Basilisk head", "tradeable": "false", @@ -72688,7 +73346,7 @@ "id": "8262" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty slayer!", "durability": null, "name": "Kurask head", "tradeable": "false", @@ -72698,7 +73356,7 @@ "id": "8263" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty slayer!!", "durability": null, "name": "Abyssal head", "tradeable": "false", @@ -72708,7 +73366,7 @@ "id": "8264" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty dragon-slayer!", "durability": null, "name": "Kbd heads", "tradeable": "false", @@ -72718,7 +73376,7 @@ "id": "8265" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty kalphite slayer!", "durability": null, "name": "Kq head", "archery_ticket_price": "0", @@ -75276,6 +75934,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75291,6 +75950,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75306,6 +75966,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75321,6 +75982,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75336,6 +75998,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75351,6 +76014,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75366,6 +76030,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75381,6 +76046,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75396,6 +76062,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75411,6 +76078,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75426,6 +76094,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75441,6 +76110,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75456,6 +76126,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75471,6 +76142,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75486,6 +76158,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75501,6 +76174,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "32583", @@ -75514,225 +76188,241 @@ "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8746", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8748", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8750", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8752", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8754", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8756", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8758", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8760", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8762", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8764", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8766", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8768", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8770", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8772", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8774", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "requirements": "{1,5}", "shop_price": "850", "examine": "A large metal shield.", - "grand_exchange_price": "346", "durability": null, + "weight": "5.4", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "346", "name": "Steel kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8776", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" }, { "ge_buy_limit": "10000", @@ -75984,9 +76674,11 @@ "weight": "1", "attack_speed": "5", "weapon_interface": "1", + "equip_audio": "2246", "render_anim": "2553", "equipment_slot": "3", "attack_anims": "401,401,401,401", + "attack_audios": "2508,2508,25092508", "name": "Void knight mace", "tradeable": "false", "archery_ticket_price": "0", @@ -76014,6 +76706,7 @@ "archery_ticket_price": "0", "id": "8844", "bonuses": "3,2,1,-3,-2,3,2,1,-3,-2,1,0,0,0,0", + "equip_audio": "2238", "defence_anim": "4177", "equipment_slot": "5" }, @@ -76025,6 +76718,7 @@ "archery_ticket_price": "0", "id": "8845", "bonuses": "5,4,3,-3,-2,5,4,3,-3,-2,2,0,0,0,0", + "equip_audio": "2238", "defence_anim": "4177", "equipment_slot": "5" }, @@ -76037,6 +76731,7 @@ "archery_ticket_price": "0", "id": "8846", "bonuses": "7,6,5,-3,-2,7,6,5,-3,-2,3,1,0,0,0", + "equip_audio": "2238", "defence_anim": "4177", "equipment_slot": "5" }, @@ -76049,6 +76744,7 @@ "archery_ticket_price": "0", "id": "8847", "bonuses": "9,8,7,-3,-2,9,8,7,-3,-2,4,2,0,0,0", + "equip_audio": "2238", "defence_anim": "4177", "equipment_slot": "5" }, @@ -76061,6 +76757,7 @@ "archery_ticket_price": "0", "id": "8848", "bonuses": "10,9,8,-3,-2,10,9,8,-3,-2,5,3,0,0,0", + "equip_audio": "2238", "defence_anim": "4177", "equipment_slot": "5" }, @@ -76073,6 +76770,7 @@ "archery_ticket_price": "0", "id": "8849", "bonuses": "13,12,11,-3,-2,13,12,11,-3,-2,6,4,0,0,0", + "equip_audio": "2238", "defence_anim": "4177", "equipment_slot": "5" }, @@ -76085,6 +76783,7 @@ "archery_ticket_price": "0", "id": "8850", "bonuses": "20,19,18,-3,-2,20,19,18,-3,-2,8,5,0,0,0", + "equip_audio": "2238", "defence_anim": "4177", "equipment_slot": "5" }, @@ -76221,6 +76920,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -76252,6 +76952,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -76283,6 +76984,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -76314,6 +77016,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -76415,15 +77118,16 @@ "requirements": "{1,20}-{2,10}", "ge_buy_limit": "10", "examine": "A magic cave horror mask.", - "grand_exchange_price": "989400", "durability": null, + "rare_item": "true", + "weight": "10", + "equipment_slot": "0", + "grand_exchange_price": "989400", "name": "Black mask (10)", "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", "id": "8901", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0" }, { "ge_buy_limit": "10", @@ -77377,6 +78081,7 @@ "archery_ticket_price": "0", "id": "9005", "bonuses": "0,0,0,-3,-1,1,2,3,0,0,0,0,0,0,0", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -77457,6 +78162,7 @@ "attack_speed": "5", "weapon_interface": "3", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "You can obtain another Sceptre by collecting all four pieces from the Stronghold of Security.", @@ -78090,30 +78796,31 @@ "equipment_slot": "2" }, { - "requirements": "{6,65}", - "shop_price": "30000", "turn90cw_anim": "1207", "examine": "A Moonclan staff.", "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", - "render_anim": "28", "equipment_slot": "3", "destroy_message": "The Oneiromancer might be able to help you get another.", "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Lunar staff", "tradeable": "false", "run_anim": "1210", "archery_ticket_price": "0", "id": "9084", "stand_turn_anim": "1209", - "bonuses": "3,2,16,13,0,2,3,2,13,1,0,15,3,0,0" + "bonuses": "3,2,16,13,0,2,3,2,13,1,0,15,3,0,0", + "requirements": "{6,65}", + "shop_price": "30000", + "durability": null, + "destroy": "true", + "weight": "2.2", + "weapon_interface": "1", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Lunar staff" }, { "destroy_message": "You'll have to get another from the Oneiromancer.", @@ -78179,6 +78886,7 @@ "turn90ccw_anim": "1208", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "You can reclaim this item from the place you found it.", @@ -78203,6 +78911,7 @@ "turn90ccw_anim": "1208", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "You can reclaim this item from the place you found it.", @@ -78227,6 +78936,7 @@ "turn90ccw_anim": "1208", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "You can reclaim this item from the place you found it.", @@ -78511,6 +79221,7 @@ "durability": null, "weight": "4", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Bronze crossbow" @@ -78535,6 +79246,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "equipment_slot": "3", "attack_anims": "4230,4230,4230,4230", @@ -78571,6 +79283,7 @@ "durability": null, "weight": "4", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Iron crossbow" @@ -78607,6 +79320,7 @@ "durability": null, "weight": "5", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Steel crossbow" @@ -78643,6 +79357,7 @@ "durability": null, "weight": "6", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Mith crossbow" @@ -78680,6 +79395,7 @@ "durability": null, "weight": "6", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Adamant crossbow" @@ -78717,6 +79433,7 @@ "durability": null, "weight": "6", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "lendable": "true", "attack_audios": "2700,0,0,0", @@ -81369,6 +82086,7 @@ "durability": null, "weight": "2", "absorb": "1,0,2", + "equip_audio": "2238", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "4619", @@ -81679,6 +82397,7 @@ "weight": "1.8", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -83632,6 +84351,7 @@ "destroy": "true", "archery_ticket_price": "0", "id": "9920", + "equip_audio": "3227", "remove_beard": "true", "equipment_slot": "0" }, @@ -84282,7 +85002,9 @@ "weapon_interface": "10", "turn180_anim": "1206", "render_anim": "28", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "grand_exchange_price": "1", "stand_anim": "813", "name": "Butterfly net", @@ -85081,6 +85803,7 @@ "archery_ticket_price": "0", "id": "10077", "bonuses": "0,0,0,0,4,2,2,1,0,0,1,2,0,0,0", + "equip_audio": "2241", "equipment_slot": "9" }, { @@ -85096,15 +85819,16 @@ "requirements": "{4,40}", "ge_buy_limit": "100", "examine": "Made from 100% real dragonhide. Now with added spikiness.", - "grand_exchange_price": "1353", "durability": null, + "weight": "1", + "equip_audio": "2241", + "equipment_slot": "9", + "grand_exchange_price": "1353", "name": "Green spiky vambs", "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", "id": "10079", - "bonuses": "0,0,0,-10,8,3,2,4,2,0,3,2,0,0,0", - "equipment_slot": "9" + "bonuses": "0,0,0,-10,8,3,2,4,2,0,3,2,0,0,0" }, { "ge_buy_limit": "100", @@ -85126,6 +85850,7 @@ "archery_ticket_price": "0", "id": "10081", "bonuses": "0,0,0,-10,9,4,3,5,4,0,4,2,0,0,0", + "equip_audio": "2241", "equipment_slot": "9" }, { @@ -85141,15 +85866,16 @@ "requirements": "{4,60}", "ge_buy_limit": "100", "examine": "Vambraces made from 100% real dragonhide. Now with added spikes.", - "grand_exchange_price": "1978", "durability": null, + "weight": "1", + "equip_audio": "2241", + "equipment_slot": "9", + "grand_exchange_price": "1978", "name": "Red spiky vambs", "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", "id": "10083", - "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,2,0,0,0", - "equipment_slot": "9" + "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,2,0,0,0" }, { "ge_buy_limit": "100", @@ -85164,15 +85890,16 @@ "requirements": "{4,70}", "ge_buy_limit": "100", "examine": "Vambraces made from 100% real dragonhide. Now with added spikiness.", - "grand_exchange_price": "2727", "durability": null, + "weight": "1", + "equip_audio": "2241", + "equipment_slot": "9", + "grand_exchange_price": "2727", "name": "Black spiky vambs", "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", "id": "10085", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,2,0,0,0", - "equipment_slot": "9" + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,2,0,0,0" }, { "ge_buy_limit": "100", @@ -85902,30 +86629,31 @@ "id": "10151" }, { - "requirements": "{4,50}", "ge_buy_limit": "100", - "shop_price": "1300", "turn90cw_anim": "821", "examine": "A weapon made of bone and wood.", "walk_anim": "4226", - "durability": null, - "weight": "5", "turn90ccw_anim": "822", "attack_speed": "4", - "weapon_interface": "17", "turn180_anim": "4227", - "render_anim": "175", "equipment_slot": "3", "grand_exchange_price": "2496", "stand_anim": "4591", - "attack_audios": "2700,0,0,0", "tradeable": "true", - "name": "Hunters' crossbow", "run_anim": "4228", "archery_ticket_price": "0", "id": "10156", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,55,0,0,0,0,0,0,0,0,0,0" + "bonuses": "0,0,0,0,55,0,0,0,0,0,0,0,0,0,0", + "requirements": "{4,50}", + "shop_price": "1300", + "durability": null, + "weight": "5", + "weapon_interface": "17", + "equip_audio": "2244", + "render_anim": "175", + "attack_audios": "2700,0,0,0", + "name": "Hunters' crossbow" }, { "ge_buy_limit": "100", @@ -87132,6 +87860,7 @@ "examine": "A rune helmet with a heraldic design.", "durability": null, "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "23600", "name": "Rune helm (h1)", @@ -87157,6 +87886,7 @@ "durability": null, "weight": "3", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "20800", "name": "Rune helm (h2)", @@ -87182,6 +87912,7 @@ "durability": null, "weight": "4", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "20800", "name": "Rune helm (h3)", @@ -87207,6 +87938,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "21100", "name": "Rune helm (h4)", @@ -87232,6 +87964,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "24900", "name": "Rune helm (h5)", @@ -87257,6 +87990,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "31500", "name": "Adamant helm (h1)", @@ -87282,6 +88016,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "10100", "name": "Adamant helm (h2)", @@ -87307,6 +88042,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "8797", "name": "Adamant helm (h3)", @@ -87332,6 +88068,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "10900", "name": "Adamant helm (h4)", @@ -87357,6 +88094,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "17100", "name": "Adamant helm (h5)", @@ -87381,6 +88119,7 @@ "examine": "A black helmet with a heraldic design.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "20400", "name": "Black helm (h1)", @@ -87405,6 +88144,7 @@ "examine": "A black helmet with a heraldic design.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "7033", "name": "Black helm (h2)", @@ -87429,6 +88169,7 @@ "examine": "A black helmet with a heraldic design.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "7574", @@ -87454,6 +88195,7 @@ "examine": "A black helmet with a heraldic design.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "10100", "name": "Black helm (h4)", @@ -87478,6 +88220,7 @@ "examine": "A black helmet with a heraldic design.", "durability": null, "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "10400", "name": "Black helm (h5)", @@ -87698,6 +88441,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "6100000", @@ -87722,6 +88466,7 @@ "examine": "Fabulously ancient range protection crafted from white dragonhide.", "durability": null, "weight": "1", + "equip_audio": "2241", "equipment_slot": "9", "lendable": "true", "grand_exchange_price": "7100000", @@ -87846,6 +88591,7 @@ "durability": null, "weight": "2", "absorb": "3,0,6", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "92800000", @@ -87871,6 +88617,7 @@ "durability": null, "weight": "9", "absorb": "4,0,9", + "equip_audio": "2234", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -87898,6 +88645,7 @@ "durability": null, "weight": "1", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "41600000", @@ -87923,6 +88671,7 @@ "durability": null, "weight": "2", "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "65100000", @@ -88088,15 +88837,16 @@ "requirements": "{1,40}-{4,70}", "ge_buy_limit": "2", "examine": "Zamorak blessed dragonhide vambraces.", - "grand_exchange_price": "116200", "durability": null, + "weight": "1", + "equip_audio": "2241", + "equipment_slot": "9", + "grand_exchange_price": "116200", "name": "Zamorak bracers", "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", "id": "10368", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0", - "equipment_slot": "9" + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" }, { "ge_buy_limit": "2", @@ -88114,6 +88864,7 @@ "durability": null, "weight": "6", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "155200", @@ -88139,6 +88890,7 @@ "durability": null, "weight": "5", "absorb": "0,4,2", + "equip_audio": "2241", "equipment_slot": "7", "grand_exchange_price": "35100", "name": "Zamorak chaps", @@ -88163,6 +88915,7 @@ "examine": "Zamorak blessed dragonhide coif.", "durability": null, "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "35900", "name": "Zamorak coif", @@ -88184,15 +88937,16 @@ "requirements": "{1,40}-{4,70}", "ge_buy_limit": "2", "examine": "Guthix blessed dragonhide vambraces.", - "grand_exchange_price": "3181", "durability": null, + "weight": "1", + "equip_audio": "2241", + "equipment_slot": "9", + "grand_exchange_price": "3181", "name": "Guthix bracers", "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", "id": "10376", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0", - "equipment_slot": "9" + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" }, { "ge_buy_limit": "2", @@ -88211,6 +88965,7 @@ "durability": null, "weight": "6", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "39800", @@ -88236,6 +88991,7 @@ "durability": null, "weight": "5", "absorb": "0,4,2", + "equip_audio": "2241", "equipment_slot": "7", "grand_exchange_price": "7346", "name": "Guthix chaps", @@ -88261,6 +89017,7 @@ "durability": null, "weight": "0.85", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "27500", "name": "Guthix coif", @@ -88279,18 +89036,19 @@ "id": "10383" }, { - "requirements": "{1,70}-{4,40}", + "requirements": "{1,40}-{4,70}", "ge_buy_limit": "2", "examine": "Saradomin blessed dragonhide vambraces.", - "grand_exchange_price": "70800", "durability": null, + "weight": "1", + "equip_audio": "2241", + "equipment_slot": "9", + "grand_exchange_price": "70800", "name": "Saradomin bracers", "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", "id": "10384", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0", - "equipment_slot": "9" + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" }, { "ge_buy_limit": "2", @@ -88310,6 +89068,7 @@ "destroy": "true", "weight": "6", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "164100", @@ -88332,15 +89091,16 @@ "requirements": "{1,40}-{4,70}", "ge_buy_limit": "2", "examine": "Saradomin blessed dragonhide chaps.", - "grand_exchange_price": "36600", "durability": null, + "absorb": "0,4,2", + "equip_audio": "2241", + "equipment_slot": "7", + "grand_exchange_price": "36600", "name": "Saradomin chaps", "tradeable": "true", "archery_ticket_price": "0", "id": "10388", - "absorb": "0,4,2", - "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0" }, { "ge_buy_limit": "2", @@ -88358,6 +89118,7 @@ "examine": "Saradomin blessed dragonhide coif.", "durability": null, "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "28000", "name": "Saradomin coif", @@ -89434,9 +90195,13 @@ "weight": "1.1", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", + "defence_anim": "397", "equipment_slot": "3", + "attack_anims": "395,395,401,395", "destroy_message": "I can obtain a replacement axe from the Burthorpe Slayer Master.", + "attack_audios": "2498,2498,2497,2498", "name": "Blessed axe", "tradeable": "false", "archery_ticket_price": "0", @@ -89905,13 +90670,14 @@ "shop_price": "275", "examine": "A Penance Fighter hat.", "durability": null, - "name": "Fighter hat", "weight": "2", + "absorb": "1,0,3", + "equip_audio": "2240", + "equipment_slot": "0", + "name": "Fighter hat", "archery_ticket_price": "0", "id": "10548", - "absorb": "1,0,3", - "bonuses": "5,5,5,-7,-7,30,32,28,-3,28,7,0,0,0,0", - "equipment_slot": "0" + "bonuses": "5,5,5,-7,-7,30,32,28,-3,28,7,0,0,0,0" }, { "requirements": "{1,40}", @@ -89947,6 +90713,7 @@ "destroy": "true", "weight": "4", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "name": "Fighter torso", @@ -90630,6 +91397,7 @@ "durability": null, "weight": "9.9", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "4595", @@ -91219,16 +91987,17 @@ { "requirements": "{1,40}", "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "35300", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "35300", "name": "Rune shield(h1)", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "10667", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" }, { "requirements": "{1,10}", @@ -91262,16 +92031,17 @@ { "requirements": "{1,40}", "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "31903", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "31903", "name": "Rune shield(h2)", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "10670", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" }, { "requirements": "{1,10}", @@ -91305,16 +92075,17 @@ { "requirements": "{1,40}", "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "32082", "durability": null, + "weight": "5", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "32082", "name": "Rune shield(h3)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "10673", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" }, { "requirements": "{1,10}", @@ -91348,16 +92119,17 @@ { "requirements": "{1,40}", "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "32351", "durability": null, + "weight": "5", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "32351", "name": "Rune shield(h4)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "10676", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" }, { "requirements": "{1,10}", @@ -91391,16 +92163,17 @@ { "requirements": "{1,40}", "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "35200", "durability": null, + "weight": "5", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "grand_exchange_price": "35200", "name": "Rune shield(h5)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "10679", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" }, { "examine": "Those studs should provide a bit more protection. Nice trim too!", @@ -91434,6 +92207,7 @@ "archery_ticket_price": "0", "id": "10682", "bonuses": "0,0,0,-15,15,40,32,45,20,40,0,0,0,0,0", + "equip_audio": "2241", "equipment_slot": "4" }, { @@ -91442,6 +92216,7 @@ "archery_ticket_price": "0", "id": "10683", "bonuses": "0,0,0,-15,15,40,32,45,20,40,0,0,0,0,0", + "equip_audio": "2241", "equipment_slot": "4" }, { @@ -91450,6 +92225,7 @@ "archery_ticket_price": "0", "id": "10684", "bonuses": "0,0,0,-15,20,45,37,50,30,45,0,0,0,0,0", + "equip_audio": "2241", "equipment_slot": "4" }, { @@ -91458,6 +92234,7 @@ "archery_ticket_price": "0", "id": "10685", "bonuses": "0,0,0,-15,20,45,37,50,30,45,0,0,0,0,0", + "equip_audio": "2241", "equipment_slot": "4" }, { @@ -91518,6 +92295,7 @@ "durability": null, "weight": "9.07", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "90744", @@ -91533,6 +92311,7 @@ "durability": null, "weight": "9.07", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "230800", @@ -91609,6 +92388,7 @@ "durability": null, "weight": "9.07", "absorb": "2,0,4", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "79300", @@ -91625,6 +92405,7 @@ "durability": null, "weight": "9.07", "absorb": "2,0,4", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "207912", @@ -91638,85 +92419,91 @@ "remove_head": "true", "requirements": "{1,10}", "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "22022", "durability": null, + "weight": "1.8", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "22022", "name": "Black helm (h1)", "tradeable": "true", - "weight": "1.8", "archery_ticket_price": "0", "id": "10699", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,10}", "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "11700", "durability": null, + "weight": "1.8", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "11700", "name": "Black helm (h2)", "tradeable": "true", - "weight": "1.8", "archery_ticket_price": "0", "id": "10700", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,10}", "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "10424", "durability": null, + "weight": "1.8", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "10424", "name": "Black helm (h3)", "tradeable": "true", - "weight": "1.8", "archery_ticket_price": "0", "id": "10701", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,10}", "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "12717", "durability": null, + "weight": "1.8", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "12717", "name": "Black helm (h4)", "tradeable": "true", - "weight": "1.8", "archery_ticket_price": "0", "id": "10702", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,10}", "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "17852", "durability": null, + "weight": "1.8", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "17852", "name": "Black helm (h5)", "tradeable": "true", - "weight": "1.8", "archery_ticket_price": "0", "id": "10703", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" }, { "remove_head": "true", "requirements": "{1,40}", "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "30298", "durability": null, + "absorb": "1,0,3", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "30298", "name": "Rune helm (h1)", "tradeable": "true", "archery_ticket_price": "0", "id": "10704", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0" }, { "remove_head": "true", @@ -91725,6 +92512,7 @@ "durability": null, "weight": "3", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "26127", "name": "Rune helm (h2)", @@ -91740,6 +92528,7 @@ "durability": null, "weight": "4", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "37333", "name": "Rune helm (h3)", @@ -91755,6 +92544,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "29600", "name": "Rune helm (h4)", @@ -91770,6 +92560,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "41325", "name": "Rune helm (h5)", @@ -91786,6 +92577,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "28407", "name": "Adamant helm (h1)", @@ -91802,6 +92594,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "9839", "name": "Adamant helm (h2)", @@ -91818,6 +92611,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "8485", "name": "Adamant helm (h3)", @@ -91834,6 +92628,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "10774", "name": "Adamant helm (h4)", @@ -91850,6 +92645,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "17390", "name": "Adamant helm (h5)", @@ -91971,6 +92767,7 @@ "destroy": "true", "archery_ticket_price": "0", "id": "10723", + "equip_audio": "3227", "remove_beard": "true", "equipment_slot": "0" }, @@ -92031,7 +92828,9 @@ "durability": null, "name": "Easter ring", "archery_ticket_price": "0", - "id": "10729" + "id": "10729", + "equip_audio": "", + "equipment_slot": "" }, { "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", @@ -92060,10 +92859,12 @@ "attack_speed": "4", "weapon_interface": "12", "turn180_anim": "1830", + "equip_audio": "2238", "render_anim": "182", "equipment_slot": "3", "fun_weapon": "true", "stand_anim": "1832", + "attack_audios": "2257,2257,2257", "name": "Rubber chicken", "run_anim": "824", "archery_ticket_price": "0", @@ -92097,6 +92898,7 @@ "attack_speed": "7", "two_handed": "true", "turn180_anim": "820", + "equip_audio": "2247", "render_anim": "1383", "equipment_slot": "3", "stand_anim": "847", @@ -92333,6 +93135,7 @@ "durability": null, "weight": "9", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92365,6 +93168,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92377,10 +93181,11 @@ }, { "requirements": "{1,40}", - "examine": "Rune platebody with complete gold trim & plating.", + "examine": "Rune platebody with complete gold trim & plating.", "durability": null, "weight": "10", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92500,6 +93305,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92516,6 +93322,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92782,6 +93589,7 @@ "durability": null, "weight": "2.2", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "44800", "name": "Helm of neitiznot", @@ -92984,6 +93792,7 @@ "two_handed": "true", "weapon_interface": "7", "turn180_anim": "7045", + "equip_audio": "2248", "render_anim": "124", "equipment_slot": "3", "destroy_message": "You can get another one by visiting the cave near the Fishing guild. A friendly ghost will replace it for you. You must be wearing a Ring of visibility, or you cannot get it back.", @@ -93241,6 +94050,7 @@ "two_handed": "true", "weapon_interface": "8", "turn180_anim": "5867", + "equip_audio": "2246", "defence_anim": "5866", "render_anim": "985", "equipment_slot": "3", @@ -93601,6 +94411,7 @@ "weight": "0.9", "archery_ticket_price": "0", "id": "10933", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -94655,6 +95466,7 @@ }, { "durability": null, + "attack_audios": "2508,2508,25092508", "name": "Mace", "archery_ticket_price": "0", "attack_speed": "5", @@ -94688,10 +95500,12 @@ "weight": "1.8", "attack_speed": "5", "weapon_interface": "8", + "equip_audio": "2246", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "390,390,381,390", "grand_exchange_price": "4093", + "attack_audios": "2508,2508,25092508", "name": "Ancient mace", "tradeable": "true", "archery_ticket_price": "0", @@ -95524,6 +96338,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "equipment_slot": "3", "stand_anim": "4591", @@ -95553,6 +96368,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "equipment_slot": "3", "stand_anim": "4591", @@ -95997,6 +96813,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "equipment_slot": "3", "grand_exchange_price": "315", "attack_audios": "2547,0,0,0", @@ -96013,6 +96830,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "equipment_slot": "3", "grand_exchange_price": "445", "attack_audios": "2547,0,0,0", @@ -96040,6 +96858,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "equipment_slot": "3", "grand_exchange_price": "516", "attack_audios": "2547,0,0,0", @@ -96056,6 +96875,7 @@ "durability": null, "attack_speed": "3", "weapon_interface": "18", + "equip_audio": "2244", "equipment_slot": "3", "grand_exchange_price": "1248", "attack_audios": "2547,0,0,0", @@ -96333,29 +97153,31 @@ "id": "11258" }, { - "requirements": "{21,17}", - "shop_price": "3", "turn90cw_anim": "6610", "examine": "For catching butterflies.", "walk_anim": "6607", - "durability": null, - "destroy": "true", - "weight": "0.2", "turn90ccw_anim": "6609", "attack_speed": "4", "two_handed": "", - "weapon_interface": "14", "turn180_anim": "6608", - "render_anim": "1426", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "destroy_message": "You will have to trade with Elnock to get a new magic butterfly net.", "stand_anim": "6604", - "name": "Magic butterfly net", "tradeable": "false", "run_anim": "6603", "archery_ticket_price": "0", "id": "11259", - "stand_turn_anim": "6611" + "stand_turn_anim": "6611", + "requirements": "{21,17}", + "shop_price": "3", + "durability": null, + "destroy": "true", + "weight": "0.2", + "weapon_interface": "14", + "render_anim": "1426", + "name": "Magic butterfly net" }, { "shop_price": "1", @@ -96577,6 +97399,7 @@ "archery_ticket_price": "0", "id": "11283", "bonuses": "0,0,0,-10,-5,20,25,22,10,22,17,7,0,0,0", + "equip_audio": "2245", "equipment_slot": "5" }, { @@ -96586,6 +97409,7 @@ "rare_item": "true", "durability": null, "weight": "7.2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "7700000", "name": "Dragonfire shield", @@ -96750,6 +97574,7 @@ "durability": null, "weight": "2", "absorb": "2,0,4", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -97110,6 +97935,7 @@ "equipment_slot": "3", "grand_exchange_price": "204", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Bronze hasta", "tradeable": "true", "run_anim": "1210", @@ -97142,6 +97968,7 @@ "equipment_slot": "3", "grand_exchange_price": "25", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Iron hasta", "tradeable": "true", "run_anim": "1210", @@ -97175,6 +98002,7 @@ "equipment_slot": "3", "grand_exchange_price": "93", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Steel hasta", "tradeable": "true", "run_anim": "1210", @@ -97208,6 +98036,7 @@ "equipment_slot": "3", "grand_exchange_price": "357", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Mithril hasta", "tradeable": "true", "run_anim": "1210", @@ -97241,6 +98070,7 @@ "equipment_slot": "3", "grand_exchange_price": "1095", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Adamant hasta", "tradeable": "true", "run_anim": "1210", @@ -97274,6 +98104,7 @@ "equipment_slot": "3", "grand_exchange_price": "12300", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Rune hasta", "tradeable": "true", "run_anim": "1210", @@ -97306,6 +98137,7 @@ "equipment_slot": "3", "grand_exchange_price": "290", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Bronze hasta(p)", "tradeable": "true", "run_anim": "1210", @@ -97357,6 +98189,7 @@ "equipment_slot": "3", "grand_exchange_price": "775", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Bronze hasta(p+)", "tradeable": "true", "run_anim": "1210", @@ -97389,6 +98222,7 @@ "equipment_slot": "3", "grand_exchange_price": "8974", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Bronze hasta(p++)", "tradeable": "true", "run_anim": "1210", @@ -97421,6 +98255,7 @@ "equipment_slot": "3", "grand_exchange_price": "99", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Iron hasta(p)", "tradeable": "true", "run_anim": "1210", @@ -97472,6 +98307,7 @@ "equipment_slot": "3", "grand_exchange_price": "584", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Iron hasta(p+)", "tradeable": "true", "run_anim": "1210", @@ -97504,6 +98340,7 @@ "equipment_slot": "3", "grand_exchange_price": "8783", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Iron hasta(p++)", "tradeable": "true", "run_anim": "1210", @@ -97537,6 +98374,7 @@ "equipment_slot": "3", "grand_exchange_price": "151", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Steel hasta(p)", "tradeable": "true", "run_anim": "1210", @@ -97590,6 +98428,7 @@ "equipment_slot": "3", "grand_exchange_price": "636", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Steel hasta(p+)", "tradeable": "true", "run_anim": "1210", @@ -97623,6 +98462,7 @@ "equipment_slot": "3", "grand_exchange_price": "8835", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Steel hasta(p++)", "tradeable": "true", "run_anim": "1210", @@ -97656,6 +98496,7 @@ "equipment_slot": "3", "grand_exchange_price": "440", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Mithril hasta(p)", "tradeable": "true", "run_anim": "1210", @@ -97688,6 +98529,7 @@ "equipment_slot": "3", "grand_exchange_price": "353", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Mithril hasta(kp)", "run_anim": "1210", "archery_ticket_price": "0", @@ -97711,6 +98553,7 @@ "equipment_slot": "3", "grand_exchange_price": "925", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Mithril hasta(p+)", "tradeable": "true", "run_anim": "1210", @@ -97744,6 +98587,7 @@ "equipment_slot": "3", "grand_exchange_price": "8711", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Mithril hasta(p++)", "tradeable": "true", "run_anim": "1210", @@ -97777,6 +98621,7 @@ "equipment_slot": "3", "grand_exchange_price": "1154", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Adamant hasta(p)", "tradeable": "true", "run_anim": "1210", @@ -97802,11 +98647,13 @@ "durability": null, "weight": "2.2", "turn90ccw_anim": "1208", + "attack_speed": "5", "weapon_interface": "14", "turn180_anim": "1206", "render_anim": "28", "equipment_slot": "3", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Adamant hasta(kp)", "run_anim": "1210", "archery_ticket_price": "0", @@ -97830,6 +98677,7 @@ "equipment_slot": "3", "grand_exchange_price": "1639", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Adamant hasta(p+)", "tradeable": "true", "run_anim": "1210", @@ -97895,6 +98743,7 @@ "equipment_slot": "3", "grand_exchange_price": "12300", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Rune hasta(p)", "tradeable": "true", "run_anim": "1210", @@ -97925,6 +98774,7 @@ "render_anim": "28", "equipment_slot": "3", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Rune hasta(kp)", "run_anim": "1210", "archery_ticket_price": "0", @@ -97933,6 +98783,7 @@ "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0" }, { + "requirements": "{0,40}", "ge_buy_limit": "100", "turn90cw_anim": "1207", "examine": "A rune-tipped, one-handed hasta.", @@ -97947,6 +98798,7 @@ "equipment_slot": "3", "grand_exchange_price": "12200", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Rune hasta(p+)", "tradeable": "true", "run_anim": "1210", @@ -97980,6 +98832,7 @@ "equipment_slot": "3", "grand_exchange_price": "15500", "stand_anim": "813", + "attack_audios": "2562,2556,2555,2562", "name": "Rune hasta(p++)", "tradeable": "true", "run_anim": "1210", @@ -99317,6 +100170,7 @@ "durability": null, "weight": "10", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99357,6 +100211,7 @@ "durability": null, "weight": "10", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99396,6 +100251,7 @@ "durability": null, "weight": "10", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99435,6 +100291,7 @@ "durability": null, "weight": "10", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99623,6 +100480,7 @@ "durability": null, "weight": "3", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Zamorakian spear" @@ -99645,6 +100503,7 @@ "durability": null, "weight": "0.5", "absorb": "0,5,2", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "4100000", @@ -99671,6 +100530,7 @@ "durability": null, "weight": "4", "absorb": "0,10,5", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -99698,6 +100558,7 @@ "durability": null, "weight": "8", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "14800000", @@ -99776,6 +100637,7 @@ "rare_item": "true", "durability": null, "weight": "6", + "equip_audio": "2237", "equipment_slot": "10", "lendable": "true", "grand_exchange_price": "111700", @@ -99820,6 +100682,7 @@ "durability": null, "weight": "3", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99841,6 +100704,7 @@ "durability": null, "rare_item": "true", "weight": "1", + "equip_audio": "2237", "equipment_slot": "10", "lendable": "true", "grand_exchange_price": "95400", @@ -99940,6 +100804,7 @@ "durability": null, "weight": "2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Mystic steam staff" @@ -100322,6 +101187,7 @@ "destroy_message": "Another can be found in the Grim Reaper's house.", "examine": "Used to sharpen the blade of a scythe.", "durability": null, + "attack_audios": "2524,2524,2522,2524", "name": "Scythe sharpener", "tradeable": "false", "destroy": "true", @@ -102578,7 +103444,8 @@ "tradeable": "false", "destroy": "true", "archery_ticket_price": "0", - "id": "12003" + "id": "12003", + "equip_audio": "2245" }, { "destroy_message": "I can make another one by using camel dung on a bronze two", @@ -104401,28 +105268,28 @@ "id": "12196" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", + "examine": "A hatchling green dragon.", "durability": null, "name": "Dragon hatchling", "archery_ticket_price": "0", "id": "12197" }, { - "examine": "Baby: Little NipperAdult: Bigger Nipper.", + "examine": "Little Nipper.", "durability": null, "name": "Baby giant crab", "archery_ticket_price": "0", "id": "12198" }, { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", + "examine": "A stripy little baby raccoon.", "durability": null, "name": "Baby raccoon", "archery_ticket_price": "0", "id": "12199" }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", + "examine": "An experienced nut-thief.", "durability": null, "name": "Squirrel", "archery_ticket_price": "0", @@ -105655,28 +106522,28 @@ "id": "12468" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", + "examine": "A hatchling red dragon.", "durability": null, - "name": "Hatchling dragon", + "name": "Dragon hatchling", "archery_ticket_price": "0", "id": "12469" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", + "examine": "A hatchling blue dragon.", "durability": null, "name": "Hatchling dragon", "archery_ticket_price": "0", "id": "12471" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", + "examine": "A hatchling green dragon.", "durability": null, "name": "Hatchling dragon", "archery_ticket_price": "0", "id": "12473" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", + "examine": "A hatchling black dragon.", "durability": null, "name": "Hatchling dragon", "archery_ticket_price": "0", @@ -105723,16 +106590,17 @@ "id": "12480" }, { - "examine": "(Baby) Can't fly and can barely walk, but adorable nonetheless. (adult) Emperor of all he surveys.", + "examine": "Can't fly and can barely walk, but adorable nonetheless.", "durability": null, "name": "Baby penguin", "archery_ticket_price": "0", "id": "12481" }, { - "examine": "I can hatch this in an Incubator.", + "examine": "I can hatch this in an incubator.", "durability": null, "name": "Penguin egg", + "tradeable": "false", "archery_ticket_price": "0", "id": "12483" }, @@ -106303,6 +107171,7 @@ "weight": "3", "archery_ticket_price": "0", "id": "12565", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -107004,6 +107873,7 @@ "weight": "0.4", "archery_ticket_price": "0", "id": "12634", + "equip_audio": "2238", "equipment_slot": "1" }, { @@ -107116,6 +107986,7 @@ "weight": "0.4", "archery_ticket_price": "0", "id": "12645", + "equip_audio": "2238", "equipment_slot": "1" }, { @@ -107343,6 +108214,7 @@ "durability": null, "weight": "1.3", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "61200", @@ -107358,6 +108230,7 @@ "durability": null, "weight": "1.3", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "61200", @@ -107407,6 +108280,7 @@ "durability": null, "weight": "0.5", "absorb": "0,5,2", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "3932989", @@ -107422,6 +108296,7 @@ "durability": null, "weight": "0.5", "absorb": "0,5,2", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "3932989", @@ -107566,6 +108441,7 @@ "durability": null, "weight": "2.2", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "44831", "name": "Helm of neitiznot (e)", @@ -107581,6 +108457,7 @@ "durability": null, "weight": "2.2", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "44831", "name": "Helm of neitiznot (charged)", @@ -108119,14 +108996,14 @@ "id": "12761" }, { - "examine": "(Baby) Can't fly and can barely walk, but adorable nonetheless. (adult) Emperor of all he surveys.", + "examine": "Can't fly and can barely walk, but adorable nonetheless.", "durability": null, "name": "Baby penguin", "archery_ticket_price": "0", "id": "12763" }, { - "examine": "(Baby) Can't fly and can barely walk, but adorable nonetheless. (adult) Emperor of all he surveys.", + "examine": "Can't fly and can barely walk, but adorable nonetheless.", "durability": null, "name": "Baby penguin", "archery_ticket_price": "0", @@ -110240,6 +111117,7 @@ "durability": null, "weight": "0.9", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "88084", "name": "Green d'hide coif 100", @@ -110256,6 +111134,7 @@ "durability": null, "weight": "0.9", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "88084", "name": "Green d'hide coif 80", @@ -110272,6 +111151,7 @@ "durability": null, "weight": "0.9", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "88084", "name": "Green d'hide coif 60", @@ -110288,6 +111168,7 @@ "durability": null, "weight": "0.9", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "88084", "name": "Green d'hide coif 40", @@ -110304,6 +111185,7 @@ "durability": null, "weight": "0.9", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "88084", "name": "Green d'hide coif 20", @@ -110321,6 +111203,7 @@ "durability": null, "weight": "0.9", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "92700", "name": "Green d'hide coif 0", @@ -110347,6 +111230,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "186985", "name": "Blue d'hide coif 100", @@ -110363,6 +111247,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "186985", "name": "Blue d'hide coif 80", @@ -110379,6 +111264,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "186985", "name": "Blue d'hide coif 60", @@ -110395,6 +111281,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "186985", "name": "Blue d'hide coif 40", @@ -110411,6 +111298,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "186985", "name": "Blue d'hide coif 20", @@ -110428,6 +111316,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "131900", "name": "Blue d'hide coif 0", @@ -110454,6 +111343,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "230059", "name": "Red d'hide coif 100", @@ -110470,6 +111360,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "230059", "name": "Red d'hide coif 80", @@ -110486,6 +111377,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "230059", "name": "Red d'hide coif 60", @@ -110502,6 +111394,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "230059", "name": "Red d'hide coif 40", @@ -110518,6 +111411,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "230059", "name": "Red d'hide coif 20", @@ -110535,6 +111429,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "156600", "name": "Red d'hide coif 0", @@ -110561,6 +111456,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "379555", "name": "Black d'hide coif 100", @@ -110577,6 +111473,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "379555", "name": "Black d'hide coif 80", @@ -110593,6 +111490,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "379555", "name": "Black d'hide coif 60", @@ -110609,6 +111507,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "379555", "name": "Black d'hide coif 40", @@ -110625,6 +111524,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "379555", "name": "Black d'hide coif 20", @@ -110642,6 +111542,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "229000", "name": "Black d'hide coif 0", @@ -111915,6 +112816,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "equipment_slot": "3", "grand_exchange_price": "261", @@ -113808,6 +114710,7 @@ "durability": null, "destroy": "false", "weapon_interface": "5", + "equip_audio": "2248", "attack_audios": "2517,2517,2500,2517", "name": "Leaf-bladed sword" }, @@ -115633,6 +116536,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "destroy_message": "Drop", @@ -115809,6 +116713,7 @@ "attack_speed": "4", "weapon_interface": "11", "absorb": "0,0,0", + "equip_audio": "2249", "render_anim": "1578", "lendable": "true", "destroy_message": "Drop", @@ -115895,6 +116800,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115913,6 +116819,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115931,6 +116838,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115949,6 +116857,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115977,45 +116886,48 @@ "bonuses": "85,65,65,0,0,13,13,12,0,13,0,75,0,2,0" }, { + "examine": "A helmet of great craftmanship.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,5,2", + "equip_audio": "2240", "lendable": "true", "destroy_message": "Drop", - "examine": "A helmet of great craftmanship.", "grand_exchange_price": "3732060", - "durability": null, "name": "Armadyl helmet", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13455", - "absorb": "0,5,2", "bonuses": "-5,-5,-5,-5,10,6,6,10,10,8,12,0,0,1,0" }, { + "examine": "A chestplate of great craftsmanship", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,10,5", + "equip_audio": "2239", "lendable": "true", "destroy_message": "Drop", - "examine": "A chestplate of great craftsmanship", "grand_exchange_price": "9628666", - "durability": null, "name": "Armadyl chestplate", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13456", - "absorb": "0,10,5", "bonuses": "-7,-7,-7,-15,33,56,48,61,70,57,52,0,0,1,0" }, { + "examine": "A chainskirt of great craftsmanship.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,7,3", + "equip_audio": "2242", "lendable": "true", "destroy_message": "Drop", - "examine": "A chainskirt of great craftsmanship.", "grand_exchange_price": "9754868", - "durability": null, "name": "Armadyl plateskirt", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13457", - "absorb": "0,7,3", "bonuses": "-6,-6,-6,-10,20,32,26,34,40,33,25,0,0,1,0" }, { @@ -116047,17 +116959,18 @@ "bonuses": "0,0,0,-21,-7,71,63,66,-4,93,25,2,0,1,0" }, { + "examine": "Some sturdy boots.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2237", "lendable": "true", "destroy_message": "Drop", - "examine": "Some sturdy boots.", "grand_exchange_price": "1259091", - "durability": null, "name": "Bandos boots", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13460", - "absorb": "0,0,0", "bonuses": "0,0,0,-5,-3,17,18,19,0,0,15,0,0,1,0" }, { @@ -116069,6 +116982,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -116079,17 +116993,18 @@ "bonuses": "0,82,60,0,0,0,0,0,0,0,0,82,0,2,0" }, { + "examine": "These will protect my feet.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2237", "lendable": "true", "destroy_message": "Drop", - "examine": "These will protect my feet.", "grand_exchange_price": "17336", - "durability": null, "name": "Dragon boots", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13462", - "absorb": "0,0,0", "bonuses": "0,0,0,-3,-1,16,17,18,0,0,15,4,0,0,0" }, { @@ -116125,6 +117040,7 @@ "attack_speed": "4", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "defence_anim": "397", "attack_anims": "396,396,395,396", "lendable": "true", @@ -116144,6 +117060,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2248", "defence_anim": "397", "attack_anims": "396,396,395,396", "destroy_message": "Drop", @@ -116161,6 +117078,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2248", "defence_anim": "397", "attack_anims": "396,396,395,396", "destroy_message": "Drop", @@ -116178,6 +117096,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2248", "defence_anim": "397", "attack_anims": "396,396,395,396", "destroy_message": "Drop", @@ -116194,6 +117113,7 @@ "attack_speed": "5", "weapon_interface": "2", "absorb": "0,0,0", + "equip_audio": "2229", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "7263", @@ -116210,6 +117130,7 @@ "attack_speed": "5", "weapon_interface": "2", "absorb": "0,0,0", + "equip_audio": "2229", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "3794577", @@ -116262,6 +117183,7 @@ "attack_speed": "6", "weapon_interface": "10", "absorb": "0,0,0", + "equip_audio": "2233", "render_anim": "1430", "lendable": "true", "destroy_message": "Drop", @@ -116328,6 +117250,7 @@ "attack_speed": "4", "weapon_interface": "6", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1928", "attack_anims": "390,390,381,390", "lendable": "true", @@ -116347,6 +117270,7 @@ "two_handed": "true", "weapon_interface": "15", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "destroy_message": "Drop", @@ -116366,11 +117290,13 @@ "attack_speed": "5", "weapon_interface": "8", "absorb": "0,0,0", + "equip_audio": "2246", "defence_anim": "397", "attack_anims": "390,390,381,390", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "28817", + "attack_audios": "2508,2508,25092508", "name": "Dragon mace", "archery_ticket_price": "0", "id": "13479", @@ -116383,6 +117309,7 @@ "attack_speed": "5", "weapon_interface": "4", "absorb": "0,0,0", + "equip_audio": "2232", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "18546", @@ -116420,59 +117347,63 @@ "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,6,3", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "4373", - "durability": null, "name": "Green d'hide body", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13483", - "absorb": "0,6,3", "bonuses": "0,0,0,-20,15,40,32,45,20,40,40,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,6,3", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "5266", - "durability": null, "name": "Blue d'hide body", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13484", - "absorb": "0,6,3", "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,6,3", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "6276", - "durability": null, "name": "Red d'hide body", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13485", - "absorb": "0,6,3", "bonuses": "0,0,0,-15,25,50,42,55,40,50,50,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,6,3", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "7619", - "durability": null, "name": "Black d'hide body", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13486", - "absorb": "0,6,3", "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0" }, { @@ -116532,73 +117463,78 @@ "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" }, { + "examine": "100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,4,2", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "100% real dragonhide.", "grand_exchange_price": "2037", - "durability": null, "name": "Green d'hide chaps", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13491", - "absorb": "0,4,2", "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,5,2", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "2352", - "durability": null, "name": "Blue d'hide chaps", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13492", - "absorb": "0,5,2", "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,6,3", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "2761", - "durability": null, "name": "Red d'hide chaps", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13493", - "absorb": "0,6,3", "bonuses": "0,0,0,-10,14,28,22,30,20,28,25,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,7,3", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "3377", - "durability": null, "name": "Black d'hide chaps", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13494", - "absorb": "0,7,3", "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0" }, { + "examine": "Makes the wearer pretty intimidating.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "2,0,4", + "equip_audio": "2240", "lendable": "true", "destroy_message": "Drop", - "examine": "Makes the wearer pretty intimidating.", "grand_exchange_price": "59676", - "durability": null, "name": "Dragon med helm", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13495", - "absorb": "2,0,4", "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0" }, { @@ -116616,59 +117552,63 @@ "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "1208", - "durability": null, "name": "Green d'hide vamb", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13497", - "absorb": "0,0,0", "bonuses": "0,0,0,-10,8,3,2,4,2,0,3,0,0,0,0" }, { + "examine": "Made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Made from 100% real dragonhide.", "grand_exchange_price": "1488", - "durability": null, "name": "Blue d'hide vamb", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13498", - "absorb": "0,0,0", "bonuses": "0,0,0,-10,9,4,3,5,4,0,4,0,0,0,0" }, { + "examine": "Vambraces made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Vambraces made from 100% real dragonhide.", "grand_exchange_price": "1714", - "durability": null, "name": "Red d'hide vamb", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13499", - "absorb": "0,0,0", "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,0,0,0,0" }, { + "examine": "Vambraces made from 100% real dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Vambraces made from 100% real dragonhide.", "grand_exchange_price": "2208", - "durability": null, "name": "Black d'hide vamb", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13500", - "absorb": "0,0,0", "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" }, { @@ -116728,45 +117668,48 @@ "bonuses": "0,0,0,2,-1,3,2,4,2,0,3,0,0,0,0" }, { + "examine": "Wooden foot protection.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2237", "lendable": "true", "destroy_message": "Drop", - "examine": "Wooden foot protection.", "grand_exchange_price": "6845", - "durability": null, "name": "Splitbark boots", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13505", - "absorb": "0,0,0", "bonuses": "0,0,0,2,-1,3,2,4,2,0,9,0,0,0,0" }, { + "examine": "An ancient and powerful looking Dragon Square shield.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "5,0,11", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "An ancient and powerful looking Dragon Square shield.", "grand_exchange_price": "328356", - "durability": null, "name": "Dragon sq shield", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13506", - "absorb": "5,0,11", "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0" }, { + "examine": "A large metal shield.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "3,0,7", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "A large metal shield.", "grand_exchange_price": "31885", - "durability": null, "name": "Rune kiteshield", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13507", - "absorb": "3,0,7", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { @@ -116987,6 +117930,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "193", @@ -117003,6 +117947,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "111", @@ -117019,6 +117964,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "486", @@ -117035,6 +117981,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "253", @@ -117052,6 +117999,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "1211", @@ -117069,6 +118017,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "655", @@ -117101,6 +118050,7 @@ "attack_speed": "6", "weapon_interface": "17", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "175", "lendable": "true", "destroy_message": "Drop", @@ -117345,17 +118295,18 @@ "bonuses": "0,0,0,-2,9,4,7,10,5,8,12,0,0,0,0" }, { + "examine": "Fabulously ancient range protection crafted from white dragonhide.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2241", "lendable": "true", "destroy_message": "Drop", - "examine": "Fabulously ancient range protection crafted from white dragonhide.", "grand_exchange_price": "6826420", - "durability": null, "name": "3rd age vambraces", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13547", - "absorb": "0,0,0", "bonuses": "0,0,0,-11,11,6,5,7,9,0,5,0,0,0,0" }, { @@ -117457,17 +118408,18 @@ "bonuses": "0,0,0,-5,-2,47,49,43,-3,48,12,0,0,0,0" }, { + "examine": "Ancient armour beaten from magical silver.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "5,0,11", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "Ancient armour beaten from magical silver.", "grand_exchange_price": "91262203", - "durability": null, "name": "3rd age kiteshield", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13555", - "absorb": "5,0,11", "bonuses": "0,0,0,-10,-4,63,65,61,-3,63,60,0,0,0,0" }, { @@ -118371,6 +119323,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118387,6 +119340,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118403,6 +119357,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118419,6 +119374,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118435,6 +119391,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118451,6 +119408,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118467,6 +119425,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118483,6 +119442,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118499,6 +119459,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "No", @@ -118515,6 +119476,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118531,6 +119493,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118547,6 +119510,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118563,6 +119527,7 @@ "attack_speed": "4", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "You will have to speak to Wizard Elriss to get another omni-talisman.", @@ -118797,6 +119762,7 @@ "equipment_slot": "0" }, { + "requirements": "{11,62}", "shop_price": "100", "examine": "It burns, burns, burns...", "durability": null, @@ -118812,20 +119778,22 @@ "point_price": "50" }, { - "destroy_message": "To get another pair of Flame Gloves, you need to keep ten beacons alight simultaneously and then talk to King Roald.", + "requirements": "{11,79}", "shop_price": "200", "examine": "The hottest gloves in town.", "durability": null, - "name": "Flame gloves", "destroy": "true", - "archery_ticket_price": "0", "attack_speed": "4", - "id": "13660", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" + "equipment_slot": "9", + "destroy_message": "To get another pair of Flame Gloves, you need to keep ten beacons alight simultaneously and then talk to King Roald.", + "name": "Flame gloves", + "archery_ticket_price": "0", + "id": "13660", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { + "requirements": "{11,92}", "shop_price": "300", "examine": "Danger: risk of fire.", "durability": null, @@ -119592,6 +120560,7 @@ "attack_speed": "4", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "3", "destroy_message": "You can get another one from the equipment box for this performance.", "name": "Performance knife", @@ -119714,6 +120683,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "39982", @@ -119745,6 +120715,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "936814", @@ -119775,6 +120746,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "15076199", @@ -119805,6 +120777,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "40003587", @@ -119835,6 +120808,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "28420591", @@ -119865,6 +120839,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "3616112", @@ -120359,6 +121334,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "destroy_message": "Drop", @@ -120376,6 +121352,7 @@ "two_handed": "true", "weapon_interface": "15", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "destroy_message": "Drop", @@ -120395,6 +121372,7 @@ "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "7917", + "attack_audios": "2508,2508,25092508", "name": "Rune mace", "archery_ticket_price": "0", "id": "13780", @@ -120415,17 +121393,18 @@ "bonuses": "0,0,0,-15,0,63,72,78,-3,65,40,0,0,0,0" }, { + "examine": "These will protect my feet.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "0,0,0", + "equip_audio": "2237", "lendable": "true", "destroy_message": "Drop", - "examine": "These will protect my feet.", "grand_exchange_price": "7036", - "durability": null, "name": "Rune boots", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13782", - "absorb": "0,0,0", "bonuses": "0,0,0,-3,-1,12,13,14,0,0,10,2,0,0,0" }, { @@ -120485,17 +121464,18 @@ "bonuses": "0,0,0,-31,-18,43,45,41,-4,68,20,0,0,0,0" }, { + "examine": "A medium square shield.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "3,0,7", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "A medium square shield.", "grand_exchange_price": "22225", - "durability": null, "name": "Rune sq shield", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13787", - "absorb": "3,0,7", "bonuses": "0,0,0,-6,-2,38,40,36,0,38,35,0,0,0,0" }, { @@ -120724,17 +121704,18 @@ "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" }, { + "examine": "Rune kiteshield with gold trim", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "3,0,7", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "Rune kiteshield with gold trim", "grand_exchange_price": "118205", - "durability": null, "name": "Rune kiteshield (g)", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13804", - "absorb": "3,0,7", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { @@ -120794,17 +121775,18 @@ "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" }, { + "examine": "A large, metal shield with a nice trim.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "3,0,7", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "A large, metal shield with a nice trim.", "grand_exchange_price": "46469", - "durability": null, "name": "Rune kiteshield (t)", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13809", - "absorb": "3,0,7", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { @@ -121007,17 +121989,18 @@ "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" }, { + "examine": "A Rune kiteshield in the colours of Zamorak.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "3,0,7", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "A Rune kiteshield in the colours of Zamorak.", "grand_exchange_price": "225574", - "durability": null, "name": "Zamorak kiteshield", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13824", - "absorb": "3,0,7", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { @@ -121077,17 +122060,18 @@ "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" }, { + "examine": "Rune kiteshield in the colours of Saradomin.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "3,0,7", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "Rune kiteshield in the colours of Saradomin.", "grand_exchange_price": "353916", - "durability": null, "name": "Saradomin kiteshield", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13829", - "absorb": "3,0,7", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { @@ -121147,23 +122131,24 @@ "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" }, { + "examine": "Rune kiteshield in the colours of Guthix.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "3,0,7", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "Rune kiteshield in the colours of Guthix.", "grand_exchange_price": "185266", - "durability": null, "name": "Guthix kiteshield", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13834", - "absorb": "3,0,7", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { "lendable": "true", "destroy_message": "Drop", - "examine": "Rune platebody with complete gold trim & plating.", + "examine": "Rune platebody with complete gold trim & plating.", "grand_exchange_price": "2034838", "durability": null, "name": "Gilded platebody", @@ -121217,17 +122202,18 @@ "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" }, { + "examine": "Rune kiteshield with gold plate.", + "durability": null, + "destroy": "true", + "attack_speed": "4", + "absorb": "3,0,7", + "equip_audio": "2245", "lendable": "true", "destroy_message": "Drop", - "examine": "Rune kiteshield with gold plate.", "grand_exchange_price": "506093", - "durability": null, "name": "Gilded kiteshield", - "destroy": "true", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13839", - "absorb": "3,0,7", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,45,0,0,0,0" }, { @@ -121583,6 +122569,7 @@ "durability": null, "destroy": "false", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Zuriel's staff" @@ -121626,6 +122613,7 @@ "durability": null, "destroy": "true", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Zuriel's staff (deg)" @@ -121735,6 +122723,7 @@ "destroy": "false", "attack_speed": "4", "absorb": "0,6,3", + "equip_audio": "2238", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "236411", @@ -121767,6 +122756,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,6,3", + "equip_audio": "2238", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "236411", @@ -121785,6 +122775,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -121805,6 +122796,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -121825,6 +122817,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -121845,6 +122838,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -121882,6 +122876,7 @@ "destroy": "false", "attack_speed": "4", "absorb": "6,0,12", + "equip_audio": "2239", "equipment_slot": "4", "destroy_message": "Drop", "remove_sleeves": "true", @@ -121914,6 +122909,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "6,0,12", + "equip_audio": "2239", "equipment_slot": "4", "destroy_message": "Drop", "remove_sleeves": "true", @@ -121980,6 +122976,7 @@ "destroy": "false", "attack_speed": "4", "absorb": "4,0,8", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "1735029", @@ -122011,6 +123008,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "4,0,8", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "1735029", @@ -122075,6 +123073,7 @@ "destroy": "false", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "1301986", @@ -122085,17 +123084,18 @@ "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0" }, { - "destroy_message": "Drop", "requirements": "{1,78}", "shop_price": "250000", "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "1301986", "durability": null, + "destroy": "false", + "attack_speed": "4", + "equip_audio": "", + "destroy_message": "Drop", + "grand_exchange_price": "1301986", "name": "Statius's full helm", "tradeable": "true", - "destroy": "false", "archery_ticket_price": "0", - "attack_speed": "4", "id": "13897" }, { @@ -122107,6 +123107,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "1301986", @@ -122141,6 +123142,7 @@ "durability": null, "destroy": "false", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1426", "attack_audios": "2500,2500,2517,2500", "name": "Vesta's longsword" @@ -122184,6 +123186,7 @@ "durability": null, "destroy": "true", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1426", "attack_audios": "2500,2500,2517,2500", "name": "Vesta's longsword (deg)" @@ -122214,6 +123217,7 @@ "durability": null, "destroy": "false", "weapon_interface": "10", + "equip_audio": "2233", "attack_audios": "2504,0,0,0", "name": "Statius's warhammer" }, @@ -122254,6 +123258,7 @@ "durability": null, "destroy": "true", "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1426", "attack_audios": "2504,0,0,0", "name": "Statius' warhammer (deg)" @@ -122286,6 +123291,7 @@ "destroy": "false", "weight": "4.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Vesta's spear" }, @@ -122317,6 +123323,7 @@ "turn90ccw_anim": "1208", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -122329,13 +123336,14 @@ "bonuses": "133,113,120,0,0,18,21,21,0,0,0,122,0,0,0" }, { - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, "destroy": "true", "attack_speed": "4", "absorb": "6,0,12", + "equip_audio": "2239", "equipment_slot": "4", "destroy_message": "Drop", "remove_sleeves": "true", @@ -122347,7 +123355,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "1897306", @@ -122360,13 +123368,14 @@ "id": "13909" }, { - "requirements": "{1,78}", + "requirements": "{1,20}}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, "destroy": "true", "attack_speed": "4", "absorb": "6,0,12", + "equip_audio": "2239", "equipment_slot": "4", "destroy_message": "Drop", "remove_sleeves": "true", @@ -122377,7 +123386,7 @@ "bonuses": "5,5,7,-10,-30,154,145,121,-6,157,60,5,0,0,0" }, { - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122395,7 +123404,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "2546175", @@ -122408,7 +123417,7 @@ "id": "13912" }, { - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122425,13 +123434,14 @@ "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0" }, { - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, "destroy": "true", "attack_speed": "4", "absorb": "4,0,8", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "1734527", @@ -122442,7 +123452,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "1734527", @@ -122455,13 +123465,14 @@ "id": "13915" }, { - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, "destroy": "true", "attack_speed": "4", "absorb": "4,0,8", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "1734527", @@ -122471,7 +123482,7 @@ "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0" }, { - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122488,7 +123499,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "1942627", @@ -122501,7 +123512,7 @@ "id": "13918" }, { - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122518,7 +123529,7 @@ }, { "remove_head": "true", - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "50000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122535,7 +123546,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "50000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "1062835", @@ -122549,13 +123560,14 @@ }, { "remove_head": "true", - "requirements": "{1,78}", + "requirements": "{1,20}", "shop_price": "30000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "1062835", @@ -122565,7 +123577,7 @@ "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0" }, { - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "125000", "turn90cw_anim": "1207", "examine": "This item degrades while worn, and will turn to dust.", @@ -122577,6 +123589,7 @@ "weapon_interface": "5", "turn180_anim": "1206", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1426", "equipment_slot": "3", "attack_anims": "381,390,390,390", @@ -122590,7 +123603,7 @@ "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0" }, { - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "125000", "turn90cw_anim": "1207", "examine": "This item degrades while worn, and will turn to dust.", @@ -122612,7 +123625,7 @@ "stand_turn_anim": "823" }, { - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "75000", "turn90cw_anim": "1207", "examine": "This item degrades while worn, and will turn to dust.", @@ -122624,6 +123637,7 @@ "weapon_interface": "5", "turn180_anim": "1206", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1426", "equipment_slot": "3", "destroy_message": "Drop", @@ -122636,7 +123650,7 @@ "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0" }, { - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122644,6 +123658,7 @@ "attack_speed": "6", "weapon_interface": "8", "absorb": "0,0,0", + "equip_audio": "2233", "defence_anim": "397", "equipment_slot": "3", "destroy_message": "Drop", @@ -122656,7 +123671,7 @@ }, { "destroy_message": "Drop", - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "3033516", @@ -122669,7 +123684,7 @@ "id": "13927" }, { - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122677,6 +123692,7 @@ "attack_speed": "6", "weapon_interface": "8", "absorb": "0,0,0", + "equip_audio": "2233", "equipment_slot": "3", "destroy_message": "Drop", "grand_exchange_price": "3033516", @@ -122686,7 +123702,7 @@ "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,0,0,0,0" }, { - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122695,6 +123711,7 @@ "two_handed": "true", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "attack_anims": "2080,2081,2082,2080", @@ -122706,7 +123723,7 @@ "bonuses": "133,113,120,0,0,18,21,21,0,0,0,0,0,0,0" }, { - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122721,7 +123738,7 @@ "id": "13930" }, { - "requirements": "{0,78}", + "requirements": "{0,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122730,6 +123747,7 @@ "two_handed": "true", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -122740,7 +123758,7 @@ "bonuses": "133,113,120,0,0,18,21,21,0,0,0,0,0,0,0" }, { - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122758,7 +123776,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "253230", @@ -122771,7 +123789,7 @@ "id": "13933" }, { - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122788,7 +123806,7 @@ "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0" }, { - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122805,7 +123823,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "121290", @@ -122818,7 +123836,7 @@ "id": "13936" }, { - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122835,7 +123853,7 @@ }, { "remove_head": "true", - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "50000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122852,7 +123870,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "50000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "43778", @@ -122866,7 +123884,7 @@ }, { "remove_head": "true", - "requirements": "{1,78}-{6,78}", + "requirements": "{1,20}-{6,20}", "shop_price": "30000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122882,7 +123900,7 @@ "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0" }, { - "requirements": "{6,78}", + "requirements": "{6,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122890,6 +123908,7 @@ "attack_speed": "6", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "attack_anims": "2555,0,0,0", @@ -122903,7 +123922,7 @@ }, { "destroy_message": "Drop", - "requirements": "{6,78}", + "requirements": "{6,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "219418", @@ -122916,7 +123935,7 @@ "id": "13942" }, { - "requirements": "{6,78}", + "requirements": "{6,20}", "shop_price": "70000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122924,6 +123943,7 @@ "attack_speed": "6", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -122934,7 +123954,7 @@ "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10" }, { - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122952,7 +123972,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "387212", @@ -122965,7 +123985,7 @@ "id": "13945" }, { - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122982,7 +124002,7 @@ "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0" }, { - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -122999,7 +124019,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "125000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "84592", @@ -123012,7 +124032,7 @@ "id": "13948" }, { - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -123029,13 +124049,14 @@ }, { "remove_head": "true", - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "50000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, "destroy": "true", "attack_speed": "4", "absorb": "0,6,3", + "equip_audio": "2238", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "63502", @@ -123046,7 +124067,7 @@ }, { "destroy_message": "Drop", - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "50000", "examine": "This item degrades while worn, and will turn to dust.", "grand_exchange_price": "63502", @@ -123060,13 +124081,14 @@ }, { "remove_head": "true", - "requirements": "{1,78}-{4,78}", + "requirements": "{1,20}-{4,20}", "shop_price": "30000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, "destroy": "true", "attack_speed": "4", "absorb": "0,6,3", + "equip_audio": "2238", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "63502", @@ -123076,7 +124098,7 @@ "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0" }, { - "requirements": "{4,78}", + "requirements": "{4,20}", "shop_price": "1000", "examine": "A vicious javelin.", "durability": null, @@ -123084,6 +124106,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -123094,7 +124117,7 @@ "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" }, { - "requirements": "{4,78}", + "requirements": "{4,20}", "shop_price": "10000", "examine": "A vicious javelin.", "durability": null, @@ -123102,6 +124125,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -123112,7 +124136,7 @@ "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" }, { - "requirements": "{4,78}", + "requirements": "{4,20}", "shop_price": "10000", "examine": "A vicious javelin.", "durability": null, @@ -123120,6 +124144,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -123130,7 +124155,7 @@ "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" }, { - "requirements": "{4,78}", + "requirements": "{4,20}", "shop_price": "10000", "examine": "A vicious javelin.", "durability": null, @@ -123138,6 +124163,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -123148,7 +124174,7 @@ "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" }, { - "requirements": "{4,78}", + "requirements": "{4,20}", "shop_price": "1000", "examine": "A vicious throwing axe.", "durability": null, @@ -123165,7 +124191,7 @@ "bonuses": "0,0,0,0,93,0,0,0,0,0,0,0,0,0,117" }, { - "requirements": "{0,78}-{4,78}", + "requirements": "{0,20}-{4,20}", "shop_price": "120000", "examine": "This item degrades while worn, and will turn to dust.", "durability": null, @@ -123218,6 +124244,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "121835", @@ -123247,6 +124274,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "121835", @@ -123262,6 +124290,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "79703", @@ -123290,6 +124319,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "79703", @@ -123348,6 +124378,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "87957", @@ -123376,6 +124407,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "87957", @@ -123394,6 +124426,7 @@ "attack_speed": "6", "weapon_interface": "2", "absorb": "0,0,0", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -123436,6 +124469,7 @@ "attack_speed": "6", "weapon_interface": "2", "absorb": "0,0,0", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", @@ -123456,6 +124490,7 @@ "attack_speed": "4", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "396,396,395,396", @@ -123473,6 +124508,7 @@ "durability": null, "destroy": "true", "attack_speed": "4", + "equip_audio": "2248", "defence_anim": "397", "attack_anims": "396,396,395,396", "destroy_message": "Drop", @@ -123491,6 +124527,7 @@ "attack_speed": "4", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "396,396,395,396", @@ -123512,6 +124549,7 @@ "attack_speed": "4", "weapon_interface": "6", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1928", "defence_anim": "397", "equipment_slot": "3", @@ -123535,7 +124573,8 @@ "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13980" + "id": "13980", + "equip_audio": "" }, { "shop_price": "60000", @@ -123545,10 +124584,13 @@ "attack_speed": "4", "weapon_interface": "6", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1928", "equipment_slot": "3", + "attack_anims": "390,390,381,390", "destroy_message": "Drop", "grand_exchange_price": "338644", + "attack_audios": "2500,0,2517,0", "name": "C. dragon scimitar (deg)", "archery_ticket_price": "0", "id": "13981", @@ -123565,6 +124607,7 @@ "attack_speed": "5", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1582", "defence_anim": "397", "equipment_slot": "3", @@ -123598,6 +124641,7 @@ "attack_speed": "5", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1582", "equipment_slot": "3", "destroy_message": "Drop", @@ -123617,11 +124661,13 @@ "attack_speed": "5", "weapon_interface": "8", "absorb": "0,0,0", + "equip_audio": "2246", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "390,390,381,390", "destroy_message": "Drop", "grand_exchange_price": "50096", + "attack_audios": "2508,2508,25092508", "name": "Corrupt dragon mace", "archery_ticket_price": "0", "id": "13985", @@ -123639,6 +124685,7 @@ "attack_anims": "390,390,381,390", "destroy_message": "Drop", "grand_exchange_price": "50096", + "attack_audios": "2508,2508,25092508", "name": "Corrupt dragon mace", "tradeable": "true", "archery_ticket_price": "0", @@ -123654,11 +124701,13 @@ "attack_speed": "5", "weapon_interface": "8", "absorb": "0,0,0", + "equip_audio": "2246", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "390,390,381,390", "destroy_message": "Drop", "grand_exchange_price": "50096", + "attack_audios": "2508,2508,25092508", "name": "Corrupt dragon mace (deg)", "archery_ticket_price": "0", "id": "13987", @@ -123673,6 +124722,7 @@ "two_handed": "true", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "2079", "equipment_slot": "3", @@ -123709,6 +124759,7 @@ "two_handed": "true", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "2079", "equipment_slot": "3", @@ -124215,6 +125266,7 @@ "attack_speed": "4", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1629", "equipment_slot": "3", "destroy_message": "Drop", @@ -124289,7 +125341,9 @@ "weapon_interface": "5", "absorb": "0,0,0", "render_anim": "158", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "destroy_message": "Drop", "name": "Volatile clay butterfly net", "archery_ticket_price": "0", @@ -124297,18 +125351,19 @@ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { - "destroy_message": "Drop", "shop_price": "21333", "examine": "Your volatile tool wants to make some bows or arrows.", "durability": null, - "name": "Volatile clay fletching knife", "destroy": "true", - "archery_ticket_price": "0", "attack_speed": "4", - "id": "14103", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" + "equip_audio": "", + "equipment_slot": "3", + "destroy_message": "Drop", + "name": "Volatile clay fletching knife", + "archery_ticket_price": "0", + "id": "14103", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { "destroy_message": "Drop", @@ -124403,7 +125458,9 @@ "weapon_interface": "5", "absorb": "0,0,0", "render_anim": "158", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "destroy_message": "Drop", "name": "Sacred clay butterfly net", "archery_ticket_price": "0", @@ -124545,6 +125602,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,3,1", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Sacred clay coif", @@ -124997,7 +126055,9 @@ "weapon_interface": "14", "absorb": "0,0,0", "render_anim": "158", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "destroy_message": "Drop", "name": "Butterfly net (class 1)", "tradeable": "true", @@ -125026,7 +126086,9 @@ "weapon_interface": "14", "absorb": "0,0,0", "render_anim": "158", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "destroy_message": "Drop", "name": "Butterfly net (class 2)", "tradeable": "true", @@ -125055,7 +126117,9 @@ "weapon_interface": "14", "absorb": "0,0,0", "render_anim": "158", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "destroy_message": "Drop", "name": "Butterfly net (class 3)", "tradeable": "true", @@ -125084,7 +126148,9 @@ "weapon_interface": "14", "absorb": "0,0,0", "render_anim": "158", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "destroy_message": "Drop", "name": "Butterfly net (class 4)", "tradeable": "true", @@ -125113,7 +126179,9 @@ "weapon_interface": "14", "absorb": "0,0,0", "render_anim": "158", + "defence_anim": "403", "equipment_slot": "3", + "attack_anims": "401,401,400,401", "destroy_message": "Drop", "name": "Butterfly net (class 5)", "tradeable": "true", @@ -127181,6 +128249,7 @@ "attack_speed": "6", "weapon_interface": "10", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "3", "destroy_message": "Drop", "name": "Warhammer (class 1)", @@ -128586,6 +129655,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Coif (class 1)", @@ -128604,7 +129674,8 @@ "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14412" + "id": "14412", + "equip_audio": "" }, { "remove_head": "true", @@ -128614,6 +129685,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Coif (class 2)", @@ -128642,6 +129714,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Coif (class 3)", @@ -128670,6 +129743,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Coif (class 4)", @@ -129147,6 +130221,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "4,0,9", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "destroy_message": "Drop", @@ -129178,6 +130253,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "4,0,9", + "equip_audio": "", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "4453643", @@ -129187,30 +130263,31 @@ "bonuses": "0,0,0,-30,0,109,107,97,-6,106,50,0,0,0,0" }, { - "requirements": "{0,60}", - "shop_price": "67500", "examine": "A set of fighting claws.", "has_special": "true", "rare_item": "true", - "durability": null, - "destroy": "true", "attack_speed": "4", "two_handed": "true", - "weapon_interface": "9", "absorb": "0,0,0", - "equip_audio": "1003", "defence_anim": "397", - "render_anim": "2583", "equipment_slot": "3", "attack_anims": "393,393,1067,393", "destroy_message": "Drop", - "lendable": "true", "grand_exchange_price": "1472441", "tradeable": "true", - "name": "Dragon claws", "archery_ticket_price": "0", "id": "14484", - "bonuses": "41,57,-4,0,0,13,26,7,0,0,0,56,0,0,0" + "bonuses": "41,57,-4,0,0,13,26,7,0,0,0,56,0,0,0", + "requirements": "{0,60}", + "shop_price": "67500", + "durability": null, + "destroy": "true", + "weapon_interface": "9", + "equip_audio": "1003", + "render_anim": "2583", + "lendable": "true", + "attack_audios": "2548,2548,2548,2548", + "name": "Dragon claws" }, { "destroy_message": "Drop", @@ -129240,6 +130317,7 @@ "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "1472441", + "attack_audios": "2548,2548,2548,2548", "name": "Dragon claws", "archery_ticket_price": "0", "id": "14486", @@ -129289,6 +130367,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "destroy_message": "Drop", @@ -129320,6 +130399,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "destroy_message": "Drop", @@ -129404,7 +130484,7 @@ "tradeable": "true", "archery_ticket_price": "0", "id": "14497", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,2,0" + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,2,0,0" }, { "shop_price": "120000", @@ -129460,7 +130540,7 @@ "tradeable": "true", "archery_ticket_price": "0", "id": "14501", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,2,0" + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,2,0,0" }, { "shop_price": "80000", @@ -129967,6 +131047,7 @@ "id": "14571", "absorb": "0,0,0", "bonuses": "0,0,0,-3,-1,2,3,4,0,0,0,0,0,0,0", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -129980,6 +131061,7 @@ "id": "14572", "absorb": "0,0,0", "bonuses": "0,0,0,-3,-1,8,9,10,0,0,8,0,0,0,0", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -129992,6 +131074,7 @@ "id": "14573", "absorb": "0,0,0", "bonuses": "0,0,0,-3,-1,10,11,12,0,0,9,1,0,0,0", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -130031,46 +131114,49 @@ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { - "destroy_message": "You will be able to get a replacement shield from Redbeard the Pirate in Port Sarim.", "shop_price": "100", "examine": "A buckler shield from Falador.", "durability": null, - "name": "Falador shield 1", "destroy": "true", - "archery_ticket_price": "0", "attack_speed": "4", - "id": "14577", "absorb": "0,0,0", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,3,0,0", - "equipment_slot": "5" + "equip_audio": "2245", + "equipment_slot": "5", + "destroy_message": "You will be able to get a replacement shield from Redbeard the Pirate in Port Sarim.", + "name": "Falador shield 1", + "archery_ticket_price": "0", + "id": "14577", + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,3,0,0" }, { - "destroy_message": "You will be able to get a replacement shield from the chemist in Rimmington.", "shop_price": "200", "examine": "A kiteshield from Falador.", "durability": null, - "name": "Falador shield 2", "destroy": "true", - "archery_ticket_price": "0", "attack_speed": "4", - "id": "14578", "absorb": "0,0,0", - "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,5,0,0", - "equipment_slot": "5" + "equip_audio": "2245", + "equipment_slot": "5", + "destroy_message": "You will be able to get a replacement shield from the chemist in Rimmington.", + "name": "Falador shield 2", + "archery_ticket_price": "0", + "id": "14578", + "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,5,0,0" }, { - "destroy_message": "You will be able to get a replacement shield from Sir Vyvin's squire in the White Knights' Castle.", "shop_price": "300", "examine": "A tower shield from Falador.", "durability": null, - "name": "Falador shield 3", "destroy": "true", - "archery_ticket_price": "0", "attack_speed": "4", - "id": "14579", "absorb": "0,0,0", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,7,0,0", - "equipment_slot": "5" + "equip_audio": "2245", + "equipment_slot": "5", + "destroy_message": "You will be able to get a replacement shield from Sir Vyvin's squire in the White Knights' Castle.", + "name": "Falador shield 3", + "archery_ticket_price": "0", + "id": "14579", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,7,0,0" }, { "shop_price": "1000", @@ -130896,82 +131982,85 @@ "id": "1480" }, { - "shop_price": "200", "turn90cw_anim": "1207", "examine": "A staff with a spooky raven head attached.", "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "2.2", "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", "defence_anim": "420", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "419,419,419,419", "stand_anim": "813", - "attack_audios": "2555,0,0,0", "tradeable": "false", - "name": "Staff of the raven", "run_anim": "1210", "archery_ticket_price": "0", "id": "14654", "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "shop_price": "200", - "turn90cw_anim": "1207", - "examine": "A staff with a spooky raven head attached.", - "walk_anim": "1205", "durability": null, "destroy": "true", "weight": "2.2", + "weapon_interface": "1", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Staff of the raven" + }, + { + "turn90cw_anim": "1207", + "examine": "A staff with a spooky raven head attached.", + "walk_anim": "1205", "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", "defence_anim": "420", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "419,419,419,419", "stand_anim": "813", - "attack_audios": "2555,0,0,0", "tradeable": "false", - "name": "Staff of the raven", "run_anim": "1210", "archery_ticket_price": "0", "id": "14655", "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "shop_price": "200", - "turn90cw_anim": "1207", - "examine": "A staff with a spooky raven head attached.", - "walk_anim": "1205", "durability": null, "destroy": "true", "weight": "2.2", + "weapon_interface": "1", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Staff of the raven" + }, + { + "turn90cw_anim": "1207", + "examine": "A staff with a spooky raven head attached.", + "walk_anim": "1205", "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", "defence_anim": "420", - "render_anim": "28", "equipment_slot": "3", "attack_anims": "419,419,419,419", "stand_anim": "813", - "attack_audios": "2555,0,0,0", "tradeable": "false", - "name": "Staff of the raven", "run_anim": "1210", "archery_ticket_price": "0", "id": "14656", "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "shop_price": "200", + "durability": null, + "destroy": "true", + "weight": "2.2", + "weapon_interface": "1", + "equip_audio": "2247", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Staff of the raven" }, { "examine": "Item container for C. Ele Minor Drop Table. You should not be able to obtain this item.", @@ -131018,5 +132107,32 @@ "examine": "You should not have this.", "name": "ASDT Slot", "id": "14430" + }, + { + "destroy_message": "This item can not be reclaimed.", + "shop_price": "1", + "examine": "A rabbit-like adornment, back in black!", + "name": "Bunny ears", + "tradeable": "false", + "destroy": "true", + "weight": "0.2", + "archery_ticket_price": "0", + "id": "14658", + "equipment_slot": "0" + }, + { + "examine": "Emperor of all he surveys.", + "name": "Penguin", + "id": "12482" + }, + { + "examine": "Emperor of all he surveys.", + "name": "Penguin", + "id": "12762" + }, + { + "examine": "Emperor of all he surveys.", + "name": "Penguin", + "id": "12764" } ] \ No newline at end of file diff --git a/Server/data/configs/music_configs.json b/Server/data/configs/music_configs.json deleted file mode 100644 index ba4a924e6..000000000 --- a/Server/data/configs/music_configs.json +++ /dev/null @@ -1,1640 +0,0 @@ -[ - { - "id": "1", - "borders": "{2112,4544,2175,4607}" - }, - { - "id": "2", - "borders": "{3200,3264,3263,3327}" - }, - { - "id": "3", - "borders": "{3072,3200,3135,3263}" - }, - { - "id": "4", - "borders": "{2752,3712,2815,3775}" - }, - { - "id": "5", - "borders": "{2496,3264,2559,3327}-{2752,4672,2815,4735}" - }, - { - "id": "6", - "borders": "{2752,3200,2815,3263}" - }, - { - "id": "7", - "borders": "{2688,3456,2751,3519}" - }, - { - "id": "8", - "borders": "{3072,3584,3135,3647}" - }, - { - "id": "9", - "borders": "{2816,3520,2879,3583}" - }, - { - "id": "10", - "borders": "{3136,3584,3199,3647}-{2368,3264,2431,3327}" - }, - { - "id": "11", - "borders": "{3392,9600,3455,9663}" - }, - { - "id": "12", - "borders": "{2944,3200,3007,3263}" - }, - { - "id": "13", - "borders": "{3072,3904,3135,3967}-{2496,4672,2559,4735}" - }, - { - "id": "14", - "borders": "{3264,3648,3327,3711}" - }, - { - "id": "15", - "borders": "{3008,3328,3071,3391}" - }, - { - "id": "17", - "borders": "{2688,9664,2751,9727}" - }, - { - "id": "18", - "borders": "{2880,3392,2943,3455}" - }, - { - "id": "19", - "borders": "{2816,9792,2879,9855}" - }, - { - "id": "20", - "borders": "{3328,3392,3391,3455}-{2624,3520,2687,3583}" - }, - { - "id": "21", - "borders": "{2688,3520,2751,3583}" - }, - { - "id": "22", - "borders": "{2432,3456,2495,3519}" - }, - { - "id": "24", - "borders": "{2496,3200,2559,3263}" - }, - { - "id": "25", - "borders": "{2816,9600,2879,9663}" - }, - { - "id": "26", - "borders": "{3008,10240,3071,10303}" - }, - { - "id": "27", - "borders": "{2560,3136,2623,3199}-{2560,9536,2623,9599}" - }, - { - "id": "28", - "borders": "{2240,4672,2303,4735}" - }, - { - "id": "29", - "borders": "{2560,9408,2623,9471}" - }, - { - "id": "30", - "borders": "{2368,4672,2431,4735}-{2880,9280,2943,9343}" - }, - { - "id": "32", - "borders": "{2496,3456,2559,3519}" - }, - { - "id": "33", - "borders": "{2432,3392,2495,3455}" - }, - { - "id": "34", - "borders": "{2944,3520,3007,3583}" - }, - { - "id": "35", - "borders": "{3008,3200,3071,3263}" - }, - { - "id": "36", - "borders": "{3264,3200,3327,3263}-{3392,3136,3455,3199}" - }, - { - "id": "37", - "borders": "{2944,3776,3007,3839}" - }, - { - "id": "38", - "borders": "{1856,4800,1919,4863}" - }, - { - "id": "41", - "borders": "{2432,3200,2495,3263}-{2432,9600,2495,9663}" - }, - { - "id": "42", - "borders": "{3008,3776,3071,3839}" - }, - { - "id": "43", - "borders": "{2944,3712,3007,3775}" - }, - { - "id": "45", - "borders": "{3328,9728,3391,9855}" - }, - { - "id": "46", - "borders": "{3200,9600,3263,9663}" - }, - { - "id": "47", - "borders": "{3328,3200,3391,3263}" - }, - { - "id": "48", - "borders": "{3392,3456,3455,3519}" - }, - { - "id": "49", - "borders": "{3008,3264,3071,3327}" - }, - { - "id": "50", - "borders": "{3264,3136,3327,3199}-{3328,3136,3391,3199}" - }, - { - "id": "51", - "borders": "{1920,4800,1983,4863}" - }, - { - "id": "52", - "borders": "{2944,3904,3007,3967}-{2944,10240,3007,10303}-{2816,4800,2879,4863}" - }, - { - "id": "53", - "borders": "{2880,9664,2943,9727}" - }, - { - "id": "54", - "borders": "{2944,3392,3007,3455}" - }, - { - "id": "55", - "borders": "{2752,3136,2815,3199}" - }, - { - "id": "56", - "borders": "{3136,3520,3199,3583}" - }, - { - "id": "57", - "borders": "{2880,4800,2943,4863}" - }, - { - "id": "58", - "borders": "{2752,3072,2815,3135}" - }, - { - "id": "59", - "borders": "{2368,9664,2431,9727}" - }, - { - "id": "60", - "borders": "{2624,3392,2687,3455}" - }, - { - "id": "61", - "borders": "{3456,3456,3519,3519}" - }, - { - "id": "62", - "borders": "{3052,3055,3155,3135}" - }, - { - "id": "63", - "borders": "{2624,9728,2687,9914}-{2688,9792,2751,9855}" - }, - { - "id": "64", - "borders": "{3136,3136,3199,3199}" - }, - { - "id": "65", - "borders": "{2752,4800,2815,4863}" - }, - { - "id": "66", - "borders": "{3008,3648,3071,3711}-{2496,3520,2559,3583}" - }, - { - "id": "67", - "borders": "{3136,3712,3199,3775}-{1536,5760,1599,5823}" - }, - { - "id": "68", - "borders": "{3008,10304,3071,10367}-{2560,9472,2623,9535}" - }, - { - "id": "69", - "borders": "{3264,3072,3327,3135}" - }, - { - "id": "70", - "borders": "{2624,3200,2687,3263}" - }, - { - "id": "71", - "borders": "{2624,2944,2687,3007}-{2624,9344,2687,9407}" - }, - { - "id": "72", - "borders": "{2944,3328,3007,3391}" - }, - { - "id": "73", - "borders": "" - }, - { - "id": "74", - "borders": "{2752,3392,2815,3455}" - }, - { - "id": "75", - "borders": "{3328,3328,3391,3391}" - }, - { - "id": "76", - "borders": "{3200,3200,3263,3263}" - }, - { - "id": "77", - "borders": "{2880,3456,2943,3519}" - }, - { - "id": "78", - "borders": "{2880,2880,2943,2943}" - }, - { - "id": "79", - "borders": "{3136,3008,3199,3071}" - }, - { - "id": "80", - "borders": "{2432,2944,2495,3007}" - }, - { - "id": "81", - "borders": "{2624,3328,2687,3391}" - }, - { - "id": "82", - "borders": "{2496,3392,2559,3455}-{2496,9792,2559,9855}" - }, - { - "id": "83", - "borders": "{2496,3072,2559,3135}" - }, - { - "id": "84", - "borders": "{3392,3392,3455,3455}-{2304,4992,2367,5055}" - }, - { - "id": "85", - "borders": "{3072,3136,3135,3199}-{3072,9472,3135,9535}" - }, - { - "id": "87", - "borders": "{2816,3456,2879,3519}" - }, - { - "id": "88", - "borders": "{2688,9856,2751,9919}" - }, - { - "id": "89", - "borders": "{2880,3008,2943,3071}" - }, - { - "id": "90", - "borders": "{2816,2944,2879,3007}" - }, - { - "id": "91", - "borders": "{2688,3264,2751,3327}-{2112,3072,2175,3135}" - }, - { - "id": "92", - "borders": "{2880,3136,2943,3199}" - }, - { - "id": "93", - "borders": "{3264,3456,3327,3519}" - }, - { - "id": "94", - "borders": "{2880,2944,2943,3007}" - }, - { - "id": "95", - "borders": "{2304,9792,2367,9855}" - }, - { - "id": "96", - "borders": "{3008,3520,3071,3583}" - }, - { - "id": "97", - "borders": "{2240,4736,2303,4799}" - }, - { - "id": "98", - "borders": "{3072,3456,3135,3519}-{3072,9856,3135,9919}" - }, - { - "id": "99", - "borders": "{2624,3264,2687,3327}" - }, - { - "id": "100", - "borders": "{2816,9856,2879,9919}-{2816,9920,2879,9983}" - }, - { - "id": "101", - "borders": "{2304,3392,2367,3455}" - }, - { - "id": "102", - "borders": "{3008,3456,3071,3519}-{2496,9600,2559,9663}" - }, - { - "id": "103", - "borders": "{2880,9344,2943,9407}" - }, - { - "id": "104", - "borders": "{2752,3456,2815,3519}" - }, - { - "id": "105", - "borders": "{3008,3136,3071,3199}" - }, - { - "id": "106", - "borders": "{3136,3904,3199,3967}-{3200,3328,3263,3391}-{3200,9728,3263,9791}" - }, - { - "id": "107", - "borders": "{2880,3264,2943,3327}-{2624,4672,2687,4735}" - }, - { - "id": "108", - "borders": "{2944,9536,3007,9599}-{3200,9536,3263,9599}" - }, - { - "id": "109", - "borders": "{2560,3456,2623,3519}-{2496,9856,2559,9919}" - }, - { - "id": "110", - "borders": "{2368,3136,2431,3199}" - }, - { - "id": "111", - "borders": "{3264,3328,3327,3391}" - }, - { - "id": "112", - "borders": "{2304,3456,2367,3519}" - }, - { - "id": "113", - "borders": "{3072,3520,3135,3583}" - }, - { - "id": "114", - "borders": "{2752,2944,2815,3007}-{2752,9344,2815,9407}" - }, - { - "id": "115", - "borders": "{2688,3200,2751,3263}" - }, - { - "id": "116", - "borders": "{3136,3328,3199,3391}" - }, - { - "id": "117", - "borders": "{2752,3008,2815,3071}" - }, - { - "id": "118", - "borders": "{2370,2397,2431,4479}" - }, - { - "id": "119", - "borders": "{2816,3392,2879,3455}" - }, - { - "id": "120", - "borders": "{3200,3712,3263,3775}" - }, - { - "id": "121", - "borders": "{3264,3520,3327,3583}" - }, - { - "id": "122", - "borders": "{3328,3264,3391,3327}" - }, - { - "id": "123", - "borders": "{3264,3264,3327,3327}" - }, - { - "id": "124", - "borders": "{3200,3072,3263,3135}" - }, - { - "id": "125", - "borders": "{3200,3392,3263,3455}" - }, - { - "id": "127", - "borders": "{3200,3904,3263,3967}-{2944,3264,3007,3327}" - }, - { - "id": "128", - "borders": "{2560,2944,2623,3007}" - }, - { - "id": "129", - "borders": "{2752,2880,2815,2943}-{2880,9472,2943,9535}" - }, - { - "id": "130", - "borders": "{2304,3328,2367,3391}" - }, - { - "id": "131", - "borders": "{2368,4800,2431,4863}" - }, - { - "id": "132", - "borders": "{2240,3200,2303,3263}" - }, - { - "id": "133", - "borders": "{2560,3328,2623,3391}-{2496,9728,2559,9791}" - }, - { - "id": "134", - "borders": "{2176,4800,2239,4863}" - }, - { - "id": "138", - "borders": "{2880,3200,2943,3263}-{2880,9600,2943,9663}" - }, - { - "id": "140", - "borders": "{2624,3456,2687,3519}" - }, - { - "id": "141", - "borders": "{3072,3392,3135,3455}-{3072,9792,3135,9855}" - }, - { - "id": "142", - "borders": "{2240,4800,2303,4863}" - }, - { - "id": "143", - "borders": "{2624,4800,2687,4863}" - }, - { - "id": "144", - "borders": "{3136,9856,3199,9919}-{3072,9536,3135,9599}" - }, - { - "id": "145", - "borders": "{3200,3136,3263,3199}" - }, - { - "id": "146", - "borders": "{3459,4808,5919,4863}" - }, - { - "id": "148", - "borders": "{2496,3136,2559,3199}-{2560,4416,2623,4479}-{2496,9536,2559,9599}" - }, - { - "id": "149", - "borders": "{2880,3520,2943,3583}" - }, - { - "id": "151", - "borders": "{3072,3264,3135,3327}" - }, - { - "id": "152", - "borders": "{2560,3200,2623,3263}" - }, - { - "id": "153", - "borders": "{2880,9984,2943,10047}" - }, - { - "id": "154", - "borders": "{3392,3264,3455,3327}" - }, - { - "id": "155", - "borders": "{2432,3328,2495,3391}" - }, - { - "id": "156", - "borders": "{3392,9856,3455,9919}" - }, - { - "id": "157", - "borders": "{3264,3392,3327,3455}" - }, - { - "id": "158", - "borders": "{2560,4800,2623,4863}" - }, - { - "id": "159", - "borders": "{3008,3712,3071,3775}-{2496,3008,2559,3071}-{2496,9408,2559,9471}" - }, - { - "id": "160", - "borders": "{3008,3584,3071,3647}" - }, - { - "id": "161", - "borders": "{2624,3072,2687,3135}" - }, - { - "id": "162", - "borders": "{2816,3072,2879,3135}-{2816,9472,2879,9535}" - }, - { - "id": "163", - "borders": "{3136,3264,3199,3327}" - }, - { - "id": "164", - "borders": "{2688,3136,2751,3199}" - }, - { - "id": "165", - "borders": "{2816,3008,2879,3071}" - }, - { - "id": "166", - "borders": "{2944,3008,3007,3071}" - }, - { - "id": "167", - "borders": "{2624,3136,2687,3199}" - }, - { - "id": "168", - "borders": "{3264,9408,3327,9471}" - }, - { - "id": "169", - "borders": "{3200,3520,3263,3583}-{2624,9664,2687,9727}" - }, - { - "id": "170", - "borders": "{2816,3200,2879,3263}" - }, - { - "id": "172", - "borders": "{2816,3136,2879,3199}-{2816,2880,2879,2943}" - }, - { - "id": "173", - "borders": "{2880,9728,2943,9791}" - }, - { - "id": "174", - "borders": "{3264,2944,3327,3007}-{3328,3008,3391,3071}" - }, - { - "id": "175", - "borders": "{3136,3392,3199,3455}" - }, - { - "id": "176", - "borders": "{3072,3648,3135,3711}" - }, - { - "id": "177", - "borders": "{3200,3456,3263,3519}" - }, - { - "id": "178", - "borders": "{2880,9792,2943,9855}" - }, - { - "id": "179", - "borders": "{3328,3584,3391,3647}-{2816,9728,2879,9791}" - }, - { - "id": "180", - "borders": "{2944,3136,3007,3199}" - }, - { - "id": "181", - "borders": "{2432,2397,2495,4479}" - }, - { - "id": "182", - "borders": "{3264,3776,3327,3839}-{3264,3712,3327,3775}" - }, - { - "id": "183", - "borders": "{2944,3648,3007,3711}" - }, - { - "id": "184", - "borders": "{2688,3392,2751,3455}" - }, - { - "id": "185", - "borders": "{2560,3072,2623,3135}" - }, - { - "id": "186", - "borders": "{2880,3328,2943,3391}" - }, - { - "id": "187", - "borders": "{2816,3584,2879,3647}" - }, - { - "id": "188", - "borders": "{2560,3008,2623,3071}" - }, - { - "id": "190", - "borders": "{2496,4800,2559,4863}" - }, - { - "id": "191", - "borders": "{2560,3264,2623,3327}" - }, - { - "id": "192", - "borders": "{2688,3328,2751,3391}-{2688,9728,2751,9791}" - }, - { - "id": "193", - "borders": "{2560,3392,2623,3455}" - }, - { - "id": "198", - "borders": "{1792,4352,1855,4415}-{1920,4352,1983,4415}" - }, - { - "id": "220", - "borders": "{2304,3842,2428,3901}" - }, - { - "id": "241", - "borders": "{3456,3328,3519,3391}-{2176,4992,2239,5055}" - }, - { - "id": "242", - "borders": "{2880,4544,2943,4607}" - }, - { - "id": "243", - "borders": "{2112,4800,2175,4863}" - }, - { - "id": "244", - "borders": "{3456,3392,3519,3455}-{1984,4992,2047,5055}" - }, - { - "id": "245", - "borders": "{3392,3328,3455,3391}-{2240,4992,2303,5055}" - }, - { - "id": "246", - "borders": "{3392,9728,3455,9791}" - }, - { - "id": "247", - "borders": "{1984,4736,2047,4799}" - }, - { - "id": "248", - "borders": "{2752,9536,2815,9599}" - }, - { - "id": "251", - "borders": "{2240,3136,2303,3199}" - }, - { - "id": "252", - "borders": "{2304,3200,2367,3263}" - }, - { - "id": "253", - "borders": "{2240,3072,2303,3135}" - }, - { - "id": "254", - "borders": "{2112,3136,2175,3199}" - }, - { - "id": "255", - "borders": "{2112,3200,2175,3263}" - }, - { - "id": "256", - "borders": "{2304,3264,2367,3327}" - }, - { - "id": "257", - "borders": "{2816,3648,2879,3711}" - }, - { - "id": "258", - "borders": "{2880,3584,2943,3647}" - }, - { - "id": "259", - "borders": "{2240,3264,2303,3327}" - }, - { - "id": "260", - "borders": "{3456,9472,3519,9535}" - }, - { - "id": "261", - "borders": "{2752,10112,2815,10175}-{2816,10048,2879,10111}" - }, - { - "id": "262", - "borders": "{2432,4800,2495,4863}" - }, - { - "id": "263", - "borders": "{3136,2944,3199,3007}" - }, - { - "id": "264", - "borders": "{2880,10048,2943,10111}" - }, - { - "id": "265", - "borders": "{2752,9280,2815,9343}" - }, - { - "id": "266", - "borders": "{3200,9344,3263,9407}" - }, - { - "id": "267", - "borders": "{3200,2944,3263,3007}-{3328,2880,3391,2943}" - }, - { - "id": "268", - "borders": "{2112,3264,2175,3327}" - }, - { - "id": "269", - "borders": "{2176,4928,2239,4991}" - }, - { - "id": "270", - "borders": "{2176,3072,2239,3135}" - }, - { - "id": "271", - "borders": "{2304,9600,2367,9663}" - }, - { - "id": "272", - "borders": "{3145,5189,3187,5240}" - }, - { - "id": "273", - "borders": "" - }, - { - "id": "277", - "borders": "{2752,4416,2815,4479}" - }, - { - "id": "278", - "borders": "{2688,4544,2751,4607}-{2688,4480,2751,4543}" - }, - { - "id": "282", - "borders": "{2688,4416,2751,4479}-{2752,4480,2815,4543}" - }, - { - "id": "284", - "borders": "{2496,3840,2559,3903}" - }, - { - "id": "285", - "borders": "{2560,3840,2623,3903}" - }, - { - "id": "286", - "borders": "{3392,3200,3455,3263}-{3456,3264,3519,3327}-{2112,4992,2175,5055}" - }, - { - "id": "287", - "borders": "{3456,9664,3519,9727}" - }, - { - "id": "288", - "borders": "{3520,3456,3583,3519}" - }, - { - "id": "289", - "borders": "{2563,3647,2687,3776}" - }, - { - "id": "290", - "borders": "{2560,3584,2689,3647}" - }, - { - "id": "291", - "borders": "{2688,3648,2751,3776}" - }, - { - "id": "292", - "borders": "{2816,3712,2879,3775}-{2880,3712,2943,3775}" - }, - { - "id": "293", - "borders": "{2688,3584,2751,3647}" - }, - { - "id": "294", - "borders": "{2816,3776,2879,3839}" - }, - { - "id": "295", - "borders": "{2624,10048,2687,10111}" - }, - { - "id": "296", - "borders": "{2560,4480,2623,4543}" - }, - { - "id": "303", - "borders": "{2752,2752,2815,2815}" - }, - { - "id": "304", - "borders": "{2880,2688,2943,2751}-{3008,5440,3071,5503}" - }, - { - "id": "305", - "borders": "{2688,2752,2751,2815}" - }, - { - "id": "306", - "borders": "{2688,2688,2751,2751}" - }, - { - "id": "307", - "borders": "{2752,9152,2815,9215}" - }, - { - "id": "311", - "borders": "{2688,9152,2751,9215}" - }, - { - "id": "312", - "borders": "{2688,9088,2751,9151}" - }, - { - "id": "314", - "borders": "{2368,3072,2431,3135}" - }, - { - "id": "316", - "borders": "{2624,9984,2687,10047}" - }, - { - "id": "317", - "borders": "{2432,3072,2495,3135}" - }, - { - "id": "318", - "borders": "{2368,9472,2431,9535}" - }, - { - "id": "319", - "borders": "{3456,9792,3519,9855}" - }, - { - "id": "320", - "borders": "{2496,3584,2559,3647}" - }, - { - "id": "322", - "borders": "{2496,9984,2559,10047}" - }, - { - "id": "323", - "borders": "{2368,9536,2431,9599}" - }, - { - "id": "324", - "borders": "{2752,3328,2815,3391}-{1920,4992,1983,5055}" - }, - { - "id": "325", - "borders": "{3008,9728,3071,9791}-{2944,9792,3007,9855}" - }, - { - "id": "326", - "borders": "{3328,3648,3391,3711}" - }, - { - "id": "327", - "borders": "{3136,3200,3199,3263}" - }, - { - "id": "328", - "borders": "{2496,3328,2559,3391}" - }, - { - "id": "329", - "borders": "{3264,3904,3327,3967}" - }, - { - "id": "330", - "borders": "{3136,9792,3199,9855}" - }, - { - "id": "331", - "borders": "{3264,3840,3327,3903}" - }, - { - "id": "332", - "borders": "{3200,3648,3263,3711}-{3136,3840,3199,3903}" - }, - { - "id": "333", - "borders": "{3072,3328,3135,3391}" - }, - { - "id": "334", - "borders": "{3008,3904,3071,3967}" - }, - { - "id": "335", - "borders": "{2752,3840,2815,3903}" - }, - { - "id": "336", - "borders": "{2816,9536,2879,9599}" - }, - { - "id": "337", - "borders": "{3200,3584,3263,3647}" - }, - { - "id": "338", - "borders": "{3072,9728,3135,9791}" - }, - { - "id": "339", - "borders": "{3392,3520,3455,3583}" - }, - { - "id": "340", - "borders": "{2688,9984,2751,10047}" - }, - { - "id": "341", - "borders": "{2752,9984,2815,10047}" - }, - { - "id": "342", - "borders": "{3456,9920,3519,9983}" - }, - { - "id": "343", - "borders": "{3136,9600,3199,9663}" - }, - { - "id": "344", - "borders": "{3456,3520,3519,3583}" - }, - { - "id": "345", - "borders": "{3520,9856,3583,9919}" - }, - { - "id": "346", - "borders": "{2560,9792,2623,9855}" - }, - { - "id": "347", - "borders": "{2752,3264,2815,3327}" - }, - { - "id": "348", - "borders": "{2432,9856,2495,9919}" - }, - { - "id": "351", - "borders": "{3328,2944,3391,3007}" - }, - { - "id": "352", - "borders": "{3136,2880,3199,2943}" - }, - { - "id": "353", - "borders": "{3584,3520,3647,3583}" - }, - { - "id": "354", - "borders": "{3648,9856,3711,9919}" - }, - { - "id": "355", - "borders": "{3648,3456,3711,3519}" - }, - { - "id": "356", - "borders": "{2752,3648,2815,3711}" - }, - { - "id": "357", - "borders": "{2752,10048,2815,10111}" - }, - { - "id": "358", - "borders": "{3776,3520,3839,3583}" - }, - { - "id": "359", - "borders": "{3200,9280,3263,9343}" - }, - { - "id": "361", - "borders": "{3072,9600,3135,9663}" - }, - { - "id": "362", - "borders": "{2688,9472,2751,9535}-{2688,9408,2751,9471}" - }, - { - "id": "363", - "name": "7th Realm", - "indexId": "285", - "borders": "{2624,9536,2687,9599}-{2624,9472,2687,9535}" - }, - { - "id": "364", - "borders": "{2688,9536,2751,9599}" - }, - { - "id": "369", - "borders": "{2816,10112,2879,10175}" - }, - { - "id": "370", - "borders": "{2496,4928,2559,4991}" - }, - { - "id": "372", - "borders": "{2304,3136,2367,3199}" - }, - { - "id": "373", - "borders": "{2304,4928,2367,4991}" - }, - { - "id": "375", - "borders": "{1920,4608,1983,4671}" - }, - { - "id": "376", - "borders": "{1856,4608,1919,4671}" - }, - { - "id": "377", - "borders": "{3392,3072,3455,3135}-{3456,3072,3519,3135}" - }, - { - "id": "380", - "borders": "{3520,3264,3583,3327}" - }, - { - "id": "381", - "borders": "{3520,9664,3583,9727}" - }, - { - "id": "383", - "borders": "{3200,2752,3263,2815}-{3264,2752,3327,2815}" - }, - { - "id": "386", - "borders": "{2820,5312,2849,5369}-{2849,5349,2880,5374}" - }, - { - "id": "387", - "borders": "{3264,2816,3327,2879}" - }, - { - "id": "388", - "borders": "{3264,9152,3327,9215}" - }, - { - "id": "389", - "borders": "{3136,9536,3199,9599}" - }, - { - "id": "390", - "borders": "{2304,3008,2367,3071}" - }, - { - "id": "391", - "borders": "{2879,5340,2944,5366}-{2909,5316,2944,5340}" - }, - { - "id": "392", - "borders": "{2432,3008,2495,3071}" - }, - { - "id": "393", - "borders": "{2624,5056,2687,5119}" - }, - { - "id": "394", - "borders": "{2432,9408,2495,9471}" - }, - { - "id": "395", - "borders": "{2880,10112,2943,10175}" - }, - { - "id": "396", - "borders": "{2816,10176,2879,10239}" - }, - { - "id": "397", - "borders": "{3200,9472,3263,9535}" - }, - { - "id": "399", - "borders": "{2816,5248,2943,5375,[2823,5250,2844,5310]~,[2844,5250,2878,5280]~,[2879,5340,2944,5366]~,[2909,5316,2944,5340]~,[2820,5312,2849,5369]~,[2849,5349,2880,5374]~,[2885,5253,2934,5278]~,[2913,5278,2937,5306]}" - }, - { - "id": "401", - "borders": "" - }, - { - "id": "402", - "borders": "{2944,4928,3071,4991}" - }, - { - "id": "403", - "borders": "{3008,5056,3071,5119}" - }, - { - "id": "404", - "borders": "{2823,5250,2844,5310}-{2844,5250,2878,5276}" - }, - { - "id": "407", - "borders": "{3264,9600,3327,9663}" - }, - { - "id": "408", - "borders": "{2885,5253,2934,5278}-{2913,5278,2937,5306}" - }, - { - "id": "409", - "borders": "{2432,4736,2495,4799}" - }, - { - "id": "411", - "borders": "{3403,4761,3442,4792}" - }, - { - "id": "412", - "borders": "{3008,4800,3071,4863}--{3011,4866,3066,4925}" - }, - { - "id": "413", - "borders": "{1920,4736,1983,4799}" - }, - { - "id": "418", - "borders": "{3136,4800,3199,4863}" - }, - { - "id": "419", - "borders": "{2560,4736,2623,4799}" - }, - { - "id": "425", - "borders": "{2432,4928,2495,4991}" - }, - { - "id": "428", - "borders": "{3264,4800,3327,4863}" - }, - { - "id": "432", - "borders": "{1600,4800,1663,4863}-{1664,4800,1727,4863}" - }, - { - "id": "434", - "borders": "{1920,4928,1983,4991}" - }, - { - "id": "435", - "borders": "{2944,3584,3007,3647}-{3072,3712,3135,3775}" - }, - { - "id": "442", - "borders": "" - }, - { - "id": "443", - "borders": "{2944,5632,3135,5823}" - }, - { - "id": "444", - "borders": "{2688,5632,2879,5823}" - }, - { - "id": "445", - "borders": "{3200,5632,3391,5823}" - }, - { - "id": "447", - "borders": "{3392,2880,3455,2943}" - }, - { - "id": "448", - "borders": "{2432,10112,2495,10175}" - }, - { - "id": "454", - "borders": "" - }, - { - "id": "456", - "borders": "{2202,5339,2212,5351}-{2328,10006,2338,10017}-{3436,9743,3446,9752}" - }, - { - "id": "461", - "borders": "{2496,3712,2559,3775}" - }, - { - "id": "462", - "borders": "{3328,9536,3391,9599}" - }, - { - "id": "463", - "borders": "{2368,5120,2431,5183}" - }, - { - "id": "464", - "borders": "{3328,9280,3391,9343}" - }, - { - "id": "465", - "borders": "{3392,2944,3455,3007}" - }, - { - "id": "469", - "borders": "{2432,5120,2495,5183}" - }, - { - "id": "471", - "borders": "{3648,9920,3711,9983}" - }, - { - "id": "473", - "borders": "{2368,5056,2431,5119}" - }, - { - "id": "475", - "borders": "{3008,3840,3071,3903}" - }, - { - "id": "476", - "borders": "{3136,3648,3199,3711}" - }, - { - "id": "478", - "borders": "{2496,9664,2559,9727}" - }, - { - "id": "479", - "borders": "{2880,3072,2943,3135}" - }, - { - "id": "481", - "borders": "{2624,9600,2687,9663}" - }, - { - "id": "482", - "borders": "{2880,5056,2943,5119}" - }, - { - "id": "489", - "borders": "{1920,4672,1983,4735}" - }, - { - "id": "490", - "borders": "{2944,9600,3007,9663}" - }, - { - "id": "491", - "borders": "{2816,5056,2879,5119}" - }, - { - "id": "496", - "borders": "{3136,3456,3199,3519}" - }, - { - "id": "497", - "borders": "{2112,5056,2175,5119}" - }, - { - "id": "498", - "borders": "{2112,5120,2175,5183}" - }, - { - "id": "500", - "borders": "" - }, - { - "id": "501", - "borders": "{3456,3136,3519,3199}" - }, - { - "id": "505", - "borders": "{3328,2816,3391,2879}-{3008,4672,3071,4735}" - }, - { - "id": "511", - "borders": "{2048,4416,2111,4479}" - }, - { - "id": "515", - "borders": "{2944,3072,3007,3135}" - }, - { - "id": "517", - "borders": "{2496,10112,2559,10175}" - }, - { - "id": "526", - "borders": "" - }, - { - "id": "524", - "borders": "{1856,4352,1919,4415}-{2880,4416,2943,4479}" - }, - { - "id": "528", - "borders": "{3136,9728,3199,9791}" - }, - { - "id": "529", - "borders": "{3008,9536,3071,9599}" - }, - { - "id": "530", - "borders": "{3648,2944,3711,3007}" - }, - { - "id": "532", - "borders": "{1984,4416,2047,4479}" - }, - { - "id": "537", - "borders": "{1856,5185,1917,5246}" - }, - { - "id": "542", - "borders": "{3072,9280,3135,9343}" - }, - { - "id": "544", - "borders": "{2304,3648,2367,3711}-{2304,3584,2367,3647}" - }, - { - "id": "555", - "borders": "" - }, - { - "id": "558", - "borders": "{1985,5185,2047,5247}" - }, - { - "id": "559", - "borders": "{2116,5247,2172,5308}" - }, - { - "id": "560", - "borders": "{2305,5186,2366,5247}" - }, - { - "id": "569", - "borders": "{3200,9984,3263,10047}" - }, - { - "id": "571", - "borders": "" - }, - { - "id": "573", - "borders": "{1728,5120,1791,5247}" - }, - { - "id": "575", - "borders": "{2432,4352,2495,4415}" - }, - { - "id": "576", - "borders": "{2944,9472,3007,9535}" - }, - { - "id": "583", - "borders": "{1856,5312,1919,5375}" - }, - { - "id": "586", - "borders": "{3328,3904,3391,3967}" - }, - { - "id": "587", - "borders": "{2624,2624,2687,2687}" - }, - { - "id": "588", - "borders": "{2624,2560,2687,2623}" - }, - { - "id": "594", - "borders": "{3264,9792,3297,9824}" - }, - { - "id": "603", - "borders": "{2432,5248,2495,5311}" - }, - { - "id": "604", - "borders": "" - }, - { - "id": "612", - "borders": "{1856,5120,1919,5183}" - }, - { - "id": "616", - "borders": "" - }, - { - "id": "634", - "borders": "" - }, - { - "id": "964", - "borders": "{2944,9856,3007,9919}" - }, - { - "id": "222", - "borders": "{2403,10186,2409,10192}" - }, - { - "id": "223", - "borders": "{2404,3797,2421,3820}" - }, - { - "id": "225", - "borders": "{2304,3785,2635,3818}" - }, - { - "id": "584", - "borders": "{2580,5763,2620,5700}" - }, - { - "id": "589", - "borders": "{2500,5766,2620,5810}" - }, - { - "id": "487", - "borders": "{1600,4672,1663,4735}" - }, - { - "id": "578", - "borders": "" - }, - { - "id": "378", - "borders": "{2688,4864,2752,4928}" - }, - { - "id": "379", - "borders": "{3520,4928,3584,4992}" - }, - { - "id": "512", - "borders": "{2436,4876,2485,4918}" - }, - { - "id": "564", - "borders": "{2879,4371,3001,4397}" - }, - { - "id": "488", - "borders": "{3263,5441,3327,5564}--{141,22,171,43}" - }, - { - "id": "467", - "borders": "{3199,5441,3262,5564}--{3193,5482,3198,5497}" - }, - { - "id": "459", - "borders": "{3137,5442,3192,5564}--{3193,5442,3198,5472}--{3193,5507,3198,5564}" - }, - { - "id": "565", - "borders": "{3266,9825,3297,9854}" - }, - { - "id": "602", - "borders": "{3298,9826,3325,9853}" - }, - { - "id": "600", - "borders": "{3298,9793,3325,9825}" - }, - { - "id": "232", - "borders": "{2903,5463,2920,5480}" - }, - { - "id": "230", - "borders": "{2904,5481,2927,5497}" - }, - { - "id": "229", - "borders": "{2921,5456,2937,5479}" - }, - { - "id": "231", - "borders": "{2886,5464,2901,5487}" - }, - { - "id": "228", - "borders": "{2896,5446,2919,5462}" - }, - { - "id": "632", - "borders": "{3716,9349,3837,9469}" - }, - { - "id": "349", - "borders": "{2562,4290,2621,4349}" - }, - { - "id": "625", - "borders": "{2056,3844,2110,3960}" - }, - { - "id": "627", - "borders": "{2111,3844,2171,3960}" - }, - { - "id": "274", - "borders": "{2688,10117,2751,10175}" - } -] diff --git a/Server/data/configs/music_regions.json b/Server/data/configs/music_regions.json new file mode 100644 index 000000000..51d9a41ae --- /dev/null +++ b/Server/data/configs/music_regions.json @@ -0,0 +1,2570 @@ +[ + { + "region": "6234", + "id": "67" + }, + { + "region": "6473", + "id": "487" + }, + { + "region": "6475", + "id": "432" + }, + { + "region": "6486", + "id": "588" + }, + { + "region": "6487", + "id": "503" + }, + { + "region": "6488", + "id": "507" + }, + { + "region": "6489", + "id": "507" + }, + { + "region": "6722", + "id": "74" + }, + { + "region": "6726", + "id": "205" + }, + { + "region": "6741", + "id": "561" + }, + { + "region": "6744", + "id": "507" + }, + { + "region": "6745", + "id": "507" + }, + { + "region": "6989", + "id": "309" + }, + { + "region": "6992", + "id": "573" + }, + { + "region": "6993", + "id": "573" + }, + { + "region": "6994", + "id": "367" + }, + { + "region": "7236", + "id": "198" + }, + { + "region": "7490", + "id": "374" + }, + { + "region": "7492", + "id": "524" + }, + { + "region": "7494", + "id": "470" + }, + { + "region": "7496", + "id": "376" + }, + { + "region": "7499", + "id": "38" + }, + { + "region": "7500", + "id": "203" + }, + { + "region": "7502", + "id": "371" + }, + { + "region": "7504", + "id": "612" + }, + { + "region": "7505", + "id": "537" + }, + { + "region": "7507", + "id": "583" + }, + { + "region": "7508", + "id": "211" + }, + { + "region": "7509", + "id": "604" + }, + { + "region": "7748", + "id": "198" + }, + { + "region": "7749", + "id": "591" + }, + { + "region": "7752", + "id": "375" + }, + { + "region": "7753", + "id": "489" + }, + { + "region": "7754", + "id": "413" + }, + { + "region": "7755", + "id": "51" + }, + { + "region": "7756", + "id": "620" + }, + { + "region": "7757", + "id": "434" + }, + { + "region": "7758", + "id": "324" + }, + { + "region": "7760", + "id": "202" + }, + { + "region": "7769", + "id": "578" + }, + { + "region": "8001", + "id": "426" + }, + { + "region": "8002", + "id": "426" + }, + { + "region": "8004", + "id": "579" + }, + { + "region": "8005", + "id": "532" + }, + { + "region": "8009", + "id": "271" + }, + { + "region": "8010", + "id": "247" + }, + { + "region": "8013", + "id": "620" + }, + { + "region": "8014", + "id": "244" + }, + { + "region": "8015", + "id": "640" + }, + { + "region": "8017", + "id": "558" + }, + { + "region": "8022", + "id": "421" + }, + { + "region": "8025", + "id": "595" + }, + { + "region": "8252", + "id": "625" + }, + { + "region": "8253", + "id": "625" + }, + { + "region": "8261", + "id": "511" + }, + { + "region": "8267", + "id": "126" + }, + { + "region": "8276", + "id": "303" + }, + { + "region": "8280", + "id": "527" + }, + { + "region": "8282", + "id": "592" + }, + { + "region": "8496", + "id": "91" + }, + { + "region": "8497", + "id": "254" + }, + { + "region": "8498", + "id": "255" + }, + { + "region": "8499", + "id": "268" + }, + { + "region": "8508", + "id": "627" + }, + { + "region": "8509", + "id": "627" + }, + { + "region": "8519", + "id": "1" + }, + { + "region": "8523", + "id": "243" + }, + { + "region": "8526", + "id": "286" + }, + { + "region": "8527", + "id": "497" + }, + { + "region": "8528", + "id": "498" + }, + { + "region": "8529", + "id": "559" + }, + { + "region": "8530", + "id": "559" + }, + { + "region": "8534", + "id": "23" + }, + { + "region": "8752", + "id": "270" + }, + { + "region": "8763", + "id": "630" + }, + { + "region": "8770", + "id": "433" + }, + { + "region": "8779", + "id": "134" + }, + { + "region": "8781", + "id": "269" + }, + { + "region": "8782", + "id": "241" + }, + { + "region": "8787", + "id": "456" + }, + { + "region": "9008", + "id": "253" + }, + { + "region": "9009", + "id": "251" + }, + { + "region": "9010", + "id": "132" + }, + { + "region": "9011", + "id": "259" + }, + { + "region": "9014", + "id": "366" + }, + { + "region": "9015", + "id": "460" + }, + { + "region": "9033", + "id": "28" + }, + { + "region": "9034", + "id": "97" + }, + { + "region": "9035", + "id": "142" + }, + { + "region": "9038", + "id": "245" + }, + { + "region": "9046", + "id": "150" + }, + { + "region": "9263", + "id": "390" + }, + { + "region": "9265", + "id": "372" + }, + { + "region": "9266", + "id": "252" + }, + { + "region": "9267", + "id": "256" + }, + { + "region": "9268", + "id": "130" + }, + { + "region": "9269", + "id": "101" + }, + { + "region": "9270", + "id": "112" + }, + { + "region": "9272", + "id": "544" + }, + { + "region": "9273", + "id": "544" + }, + { + "region": "9275", + "id": "225" + }, + { + "region": "9276", + "id": "220" + }, + { + "region": "9285", + "id": "73" + }, + { + "region": "9293", + "id": "373" + }, + { + "region": "9294", + "id": "84" + }, + { + "region": "9295", + "id": "201" + }, + { + "region": "9297", + "id": "560" + }, + { + "region": "9362", + "id": "153" + }, + { + "region": "9365", + "id": "271" + }, + { + "region": "9366", + "id": "271" + }, + { + "region": "9369", + "id": "95" + }, + { + "region": "9372", + "id": "456" + }, + { + "region": "9520", + "id": "314" + }, + { + "region": "9521", + "id": "110" + }, + { + "region": "9522", + "id": "41" + }, + { + "region": "9523", + "id": "10" + }, + { + "region": "9526", + "id": "112" + }, + { + "region": "9531", + "id": "223" + }, + { + "region": "9532", + "id": "220" + }, + { + "region": "9540", + "id": "118" + }, + { + "region": "9541", + "id": "118" + }, + { + "region": "9544", + "id": "197" + }, + { + "region": "9545", + "id": "30" + }, + { + "region": "9547", + "id": "131" + }, + { + "region": "9550", + "id": "84" + }, + { + "region": "9551", + "id": "473" + }, + { + "region": "9552", + "id": "463" + }, + { + "region": "9553", + "id": "531" + }, + { + "region": "9558", + "id": "276" + }, + { + "region": "9620", + "id": "318" + }, + { + "region": "9621", + "id": "323" + }, + { + "region": "9623", + "id": "59" + }, + { + "region": "9625", + "id": "194" + }, + { + "region": "9626", + "id": "296" + }, + { + "region": "9631", + "id": "222" + }, + { + "region": "9632", + "id": "224" + }, + { + "region": "9774", + "id": "80" + }, + { + "region": "9775", + "id": "392" + }, + { + "region": "9776", + "id": "317" + }, + { + "region": "9778", + "id": "41" + }, + { + "region": "9780", + "id": "155" + }, + { + "region": "9781", + "id": "33" + }, + { + "region": "9782", + "id": "22" + }, + { + "region": "9787", + "id": "225" + }, + { + "region": "9794", + "id": "439" + }, + { + "region": "9796", + "id": "575" + }, + { + "region": "9797", + "id": "181" + }, + { + "region": "9802", + "id": "409" + }, + { + "region": "9803", + "id": "262" + }, + { + "region": "9804", + "id": "512" + }, + { + "region": "9805", + "id": "425" + }, + { + "region": "9808", + "id": "469" + }, + { + "region": "9810", + "id": "603" + }, + { + "region": "9812", + "id": "279" + }, + { + "region": "9814", + "id": "275" + }, + { + "region": "9874", + "id": "394" + }, + { + "region": "9875", + "id": "394" + }, + { + "region": "9878", + "id": "41" + }, + { + "region": "9879", + "id": "59" + }, + { + "region": "9882", + "id": "348" + }, + { + "region": "9886", + "id": "448" + }, + { + "region": "10031", + "id": "159" + }, + { + "region": "10032", + "id": "83" + }, + { + "region": "10033", + "id": "148" + }, + { + "region": "10034", + "id": "24" + }, + { + "region": "10035", + "id": "5" + }, + { + "region": "10036", + "id": "328" + }, + { + "region": "10037", + "id": "82" + }, + { + "region": "10038", + "id": "32" + }, + { + "region": "10039", + "id": "66" + }, + { + "region": "10040", + "id": "320" + }, + { + "region": "10042", + "id": "461" + }, + { + "region": "10043", + "id": "225" + }, + { + "region": "10044", + "id": "284" + }, + { + "region": "10055", + "id": "129" + }, + { + "region": "10056", + "id": "322" + }, + { + "region": "10057", + "id": "13" + }, + { + "region": "10058", + "id": "411" + }, + { + "region": "10059", + "id": "190" + }, + { + "region": "10061", + "id": "370" + }, + { + "region": "10074", + "id": "589" + }, + { + "region": "10075", + "id": "589" + }, + { + "region": "10129", + "id": "453" + }, + { + "region": "10131", + "id": "159" + }, + { + "region": "10133", + "id": "148" + }, + { + "region": "10134", + "id": "102" + }, + { + "region": "10135", + "id": "478" + }, + { + "region": "10136", + "id": "133" + }, + { + "region": "10137", + "id": "82" + }, + { + "region": "10138", + "id": "109" + }, + { + "region": "10139", + "id": "66" + }, + { + "region": "10140", + "id": "322" + }, + { + "region": "10142", + "id": "517" + }, + { + "region": "10144", + "id": "506" + }, + { + "region": "10284", + "id": "474" + }, + { + "region": "10286", + "id": "128" + }, + { + "region": "10287", + "id": "188" + }, + { + "region": "10288", + "id": "185" + }, + { + "region": "10289", + "id": "27" + }, + { + "region": "10290", + "id": "152" + }, + { + "region": "10291", + "id": "191" + }, + { + "region": "10292", + "id": "133" + }, + { + "region": "10293", + "id": "193" + }, + { + "region": "10294", + "id": "109" + }, + { + "region": "10296", + "id": "290" + }, + { + "region": "10297", + "id": "289" + }, + { + "region": "10298", + "id": "289" + }, + { + "region": "10299", + "id": "225" + }, + { + "region": "10300", + "id": "285" + }, + { + "region": "10307", + "id": "349" + }, + { + "region": "10309", + "id": "148" + }, + { + "region": "10310", + "id": "296" + }, + { + "region": "10311", + "id": "308" + }, + { + "region": "10314", + "id": "419" + }, + { + "region": "10315", + "id": "158" + }, + { + "region": "10321", + "id": "638" + }, + { + "region": "10322", + "id": "66" + }, + { + "region": "10330", + "id": "589" + }, + { + "region": "10387", + "id": "29" + }, + { + "region": "10388", + "id": "68" + }, + { + "region": "10389", + "id": "27" + }, + { + "region": "10390", + "id": "102" + }, + { + "region": "10393", + "id": "346" + }, + { + "region": "10394", + "id": "109" + }, + { + "region": "10536", + "id": "588" + }, + { + "region": "10537", + "id": "587" + }, + { + "region": "10542", + "id": "71" + }, + { + "region": "10544", + "id": "161" + }, + { + "region": "10545", + "id": "167" + }, + { + "region": "10546", + "id": "70" + }, + { + "region": "10547", + "id": "99" + }, + { + "region": "10548", + "id": "81" + }, + { + "region": "10549", + "id": "60" + }, + { + "region": "10550", + "id": "140" + }, + { + "region": "10551", + "id": "20" + }, + { + "region": "10552", + "id": "290" + }, + { + "region": "10553", + "id": "289" + }, + { + "region": "10554", + "id": "289" + }, + { + "region": "10555", + "id": "225" + }, + { + "region": "10558", + "id": "217" + }, + { + "region": "10569", + "id": "107" + }, + { + "region": "10571", + "id": "143" + }, + { + "region": "10575", + "id": "393" + }, + { + "region": "10577", + "id": "568" + }, + { + "region": "10642", + "id": "71" + }, + { + "region": "10644", + "id": "363" + }, + { + "region": "10645", + "id": "363" + }, + { + "region": "10646", + "id": "481" + }, + { + "region": "10647", + "id": "169" + }, + { + "region": "10648", + "id": "63" + }, + { + "region": "10649", + "id": "63" + }, + { + "region": "10650", + "id": "63" + }, + { + "region": "10652", + "id": "316" + }, + { + "region": "10653", + "id": "295" + }, + { + "region": "10794", + "id": "306" + }, + { + "region": "10795", + "id": "305" + }, + { + "region": "10801", + "id": "164" + }, + { + "region": "10802", + "id": "115" + }, + { + "region": "10803", + "id": "91" + }, + { + "region": "10804", + "id": "192" + }, + { + "region": "10805", + "id": "184" + }, + { + "region": "10806", + "id": "7" + }, + { + "region": "10807", + "id": "21" + }, + { + "region": "10808", + "id": "293" + }, + { + "region": "10809", + "id": "291" + }, + { + "region": "10810", + "id": "291" + }, + { + "region": "10811", + "id": "291" + }, + { + "region": "10820", + "id": "563" + }, + { + "region": "10821", + "id": "282" + }, + { + "region": "10822", + "id": "278" + }, + { + "region": "10823", + "id": "278" + }, + { + "region": "10827", + "id": "146" + }, + { + "region": "10828", + "id": "378" + }, + { + "region": "10829", + "id": "378" + }, + { + "region": "10831", + "id": "393" + }, + { + "region": "10833", + "id": "249" + }, + { + "region": "10834", + "id": "240" + }, + { + "region": "10838", + "id": "442" + }, + { + "region": "10839", + "id": "442" + }, + { + "region": "10840", + "id": "444" + }, + { + "region": "10841", + "id": "444" + }, + { + "region": "10842", + "id": "444" + }, + { + "region": "10894", + "id": "312" + }, + { + "region": "10895", + "id": "311" + }, + { + "region": "10899", + "id": "362" + }, + { + "region": "10900", + "id": "362" + }, + { + "region": "10901", + "id": "364" + }, + { + "region": "10903", + "id": "17" + }, + { + "region": "10904", + "id": "192" + }, + { + "region": "10905", + "id": "63" + }, + { + "region": "10906", + "id": "88" + }, + { + "region": "10907", + "id": "340" + }, + { + "region": "10908", + "id": "340" + }, + { + "region": "10910", + "id": "274" + }, + { + "region": "10911", + "id": "548" + }, + { + "region": "11051", + "id": "303" + }, + { + "region": "11053", + "id": "129" + }, + { + "region": "11054", + "id": "114" + }, + { + "region": "11055", + "id": "117" + }, + { + "region": "11056", + "id": "58" + }, + { + "region": "11057", + "id": "55" + }, + { + "region": "11058", + "id": "6" + }, + { + "region": "11059", + "id": "347" + }, + { + "region": "11060", + "id": "324" + }, + { + "region": "11061", + "id": "74" + }, + { + "region": "11062", + "id": "104" + }, + { + "region": "11065", + "id": "356" + }, + { + "region": "11066", + "id": "4" + }, + { + "region": "11068", + "id": "335" + }, + { + "region": "11077", + "id": "277" + }, + { + "region": "11078", + "id": "282" + }, + { + "region": "11081", + "id": "5" + }, + { + "region": "11083", + "id": "65" + }, + { + "region": "11084", + "id": "378" + }, + { + "region": "11085", + "id": "378" + }, + { + "region": "11096", + "id": "444" + }, + { + "region": "11097", + "id": "444" + }, + { + "region": "11098", + "id": "444" + }, + { + "region": "11151", + "id": "307" + }, + { + "region": "11153", + "id": "265" + }, + { + "region": "11154", + "id": "114" + }, + { + "region": "11157", + "id": "248" + }, + { + "region": "11161", + "id": "19" + }, + { + "region": "11164", + "id": "341" + }, + { + "region": "11165", + "id": "357" + }, + { + "region": "11166", + "id": "261" + }, + { + "region": "11309", + "id": "172" + }, + { + "region": "11310", + "id": "90" + }, + { + "region": "11311", + "id": "165" + }, + { + "region": "11312", + "id": "162" + }, + { + "region": "11313", + "id": "172" + }, + { + "region": "11314", + "id": "170" + }, + { + "region": "11317", + "id": "119" + }, + { + "region": "11318", + "id": "87" + }, + { + "region": "11319", + "id": "9" + }, + { + "region": "11320", + "id": "187" + }, + { + "region": "11321", + "id": "257" + }, + { + "region": "11322", + "id": "292" + }, + { + "region": "11323", + "id": "294" + }, + { + "region": "11324", + "id": "569" + }, + { + "region": "11331", + "id": "553" + }, + { + "region": "11332", + "id": "564" + }, + { + "region": "11335", + "id": "504" + }, + { + "region": "11339", + "id": "52" + }, + { + "region": "11343", + "id": "491" + }, + { + "region": "11352", + "id": "444" + }, + { + "region": "11353", + "id": "444" + }, + { + "region": "11354", + "id": "444" + }, + { + "region": "11356", + "id": "442" + }, + { + "region": "11408", + "id": "365" + }, + { + "region": "11410", + "id": "90" + }, + { + "region": "11412", + "id": "162" + }, + { + "region": "11413", + "id": "336" + }, + { + "region": "11414", + "id": "25" + }, + { + "region": "11416", + "id": "179" + }, + { + "region": "11417", + "id": "19" + }, + { + "region": "11418", + "id": "100" + }, + { + "region": "11419", + "id": "100" + }, + { + "region": "11421", + "id": "261" + }, + { + "region": "11422", + "id": "369" + }, + { + "region": "11423", + "id": "396" + }, + { + "region": "11562", + "id": "304" + }, + { + "region": "11565", + "id": "78" + }, + { + "region": "11566", + "id": "94" + }, + { + "region": "11567", + "id": "89" + }, + { + "region": "11568", + "id": "479" + }, + { + "region": "11569", + "id": "92" + }, + { + "region": "11570", + "id": "138" + }, + { + "region": "11571", + "id": "107" + }, + { + "region": "11572", + "id": "186" + }, + { + "region": "11573", + "id": "18" + }, + { + "region": "11574", + "id": "77" + }, + { + "region": "11575", + "id": "149" + }, + { + "region": "11576", + "id": "258" + }, + { + "region": "11578", + "id": "292" + }, + { + "region": "11587", + "id": "564" + }, + { + "region": "11588", + "id": "564" + }, + { + "region": "11589", + "id": "524" + }, + { + "region": "11591", + "id": "242" + }, + { + "region": "11595", + "id": "57" + }, + { + "region": "11599", + "id": "482" + }, + { + "region": "11605", + "id": "232" + }, + { + "region": "11606", + "id": "442" + }, + { + "region": "11608", + "id": "442" + }, + { + "region": "11612", + "id": "442" + }, + { + "region": "11665", + "id": "30" + }, + { + "region": "11666", + "id": "103" + }, + { + "region": "11668", + "id": "129" + }, + { + "region": "11670", + "id": "138" + }, + { + "region": "11671", + "id": "53" + }, + { + "region": "11672", + "id": "173" + }, + { + "region": "11673", + "id": "178" + }, + { + "region": "11676", + "id": "153" + }, + { + "region": "11677", + "id": "264" + }, + { + "region": "11678", + "id": "395" + }, + { + "region": "11821", + "id": "162" + }, + { + "region": "11823", + "id": "166" + }, + { + "region": "11824", + "id": "515" + }, + { + "region": "11825", + "id": "180" + }, + { + "region": "11826", + "id": "12" + }, + { + "region": "11827", + "id": "127" + }, + { + "region": "11828", + "id": "72" + }, + { + "region": "11829", + "id": "54" + }, + { + "region": "11831", + "id": "34" + }, + { + "region": "11832", + "id": "435" + }, + { + "region": "11833", + "id": "183" + }, + { + "region": "11834", + "id": "43" + }, + { + "region": "11835", + "id": "37" + }, + { + "region": "11837", + "id": "52" + }, + { + "region": "11853", + "id": "402" + }, + { + "region": "11864", + "id": "443" + }, + { + "region": "11865", + "id": "443" + }, + { + "region": "11866", + "id": "443" + }, + { + "region": "11924", + "id": "576" + }, + { + "region": "11925", + "id": "108" + }, + { + "region": "11926", + "id": "490" + }, + { + "region": "11929", + "id": "325" + }, + { + "region": "11930", + "id": "582" + }, + { + "region": "11936", + "id": "52" + }, + { + "region": "11937", + "id": "52" + }, + { + "region": "12078", + "id": "549" + }, + { + "region": "12079", + "id": "62" + }, + { + "region": "12080", + "id": "62" + }, + { + "region": "12081", + "id": "105" + }, + { + "region": "12082", + "id": "35" + }, + { + "region": "12083", + "id": "49" + }, + { + "region": "12084", + "id": "15" + }, + { + "region": "12085", + "id": "310" + }, + { + "region": "12086", + "id": "102" + }, + { + "region": "12087", + "id": "96" + }, + { + "region": "12088", + "id": "160" + }, + { + "region": "12089", + "id": "66" + }, + { + "region": "12090", + "id": "159" + }, + { + "region": "12091", + "id": "42" + }, + { + "region": "12092", + "id": "475" + }, + { + "region": "12093", + "id": "334" + }, + { + "region": "12100", + "id": "236" + }, + { + "region": "12102", + "id": "633" + }, + { + "region": "12105", + "id": "505" + }, + { + "region": "12107", + "id": "412" + }, + { + "region": "12108", + "id": "412" + }, + { + "region": "12109", + "id": "402" + }, + { + "region": "12111", + "id": "403" + }, + { + "region": "12115", + "id": "86" + }, + { + "region": "12117", + "id": "304" + }, + { + "region": "12120", + "id": "443" + }, + { + "region": "12121", + "id": "443" + }, + { + "region": "12122", + "id": "443" + }, + { + "region": "12181", + "id": "529" + }, + { + "region": "12184", + "id": "325" + }, + { + "region": "12186", + "id": "570" + }, + { + "region": "12187", + "id": "585" + }, + { + "region": "12192", + "id": "26" + }, + { + "region": "12193", + "id": "68" + }, + { + "region": "12335", + "id": "62" + }, + { + "region": "12336", + "id": "62" + }, + { + "region": "12337", + "id": "85" + }, + { + "region": "12338", + "id": "3" + }, + { + "region": "12339", + "id": "151" + }, + { + "region": "12340", + "id": "333" + }, + { + "region": "12341", + "id": "141" + }, + { + "region": "12342", + "id": "98" + }, + { + "region": "12343", + "id": "113" + }, + { + "region": "12344", + "id": "8" + }, + { + "region": "12345", + "id": "176" + }, + { + "region": "12346", + "id": "435" + }, + { + "region": "12349", + "id": "13" + }, + { + "region": "12354", + "id": "494" + }, + { + "region": "12367", + "id": "441" + }, + { + "region": "12376", + "id": "443" + }, + { + "region": "12377", + "id": "443" + }, + { + "region": "12378", + "id": "443" + }, + { + "region": "12436", + "id": "85" + }, + { + "region": "12437", + "id": "144" + }, + { + "region": "12438", + "id": "361" + }, + { + "region": "12440", + "id": "338" + }, + { + "region": "12441", + "id": "141" + }, + { + "region": "12442", + "id": "98" + }, + { + "region": "12589", + "id": "352" + }, + { + "region": "12590", + "id": "263" + }, + { + "region": "12591", + "id": "79" + }, + { + "region": "12593", + "id": "64" + }, + { + "region": "12594", + "id": "327" + }, + { + "region": "12595", + "id": "163" + }, + { + "region": "12596", + "id": "116" + }, + { + "region": "12597", + "id": "175" + }, + { + "region": "12598", + "id": "496" + }, + { + "region": "12599", + "id": "56" + }, + { + "region": "12600", + "id": "10" + }, + { + "region": "12601", + "id": "476" + }, + { + "region": "12602", + "id": "67" + }, + { + "region": "12603", + "id": "449" + }, + { + "region": "12604", + "id": "332" + }, + { + "region": "12605", + "id": "106" + }, + { + "region": "12610", + "id": "493" + }, + { + "region": "12610", + "id": "493" + }, + { + "region": "12614", + "id": "572" + }, + { + "region": "12615", + "id": "214" + }, + { + "region": "12619", + "id": "418" + }, + { + "region": "12623", + "id": "441" + }, + { + "region": "12625", + "id": "272" + }, + { + "region": "12693", + "id": "389" + }, + { + "region": "12694", + "id": "343" + }, + { + "region": "12696", + "id": "528" + }, + { + "region": "12697", + "id": "330" + }, + { + "region": "12698", + "id": "144" + }, + { + "region": "12843", + "id": "383" + }, + { + "region": "12846", + "id": "267" + }, + { + "region": "12848", + "id": "124" + }, + { + "region": "12849", + "id": "145" + }, + { + "region": "12850", + "id": "76" + }, + { + "region": "12851", + "id": "2" + }, + { + "region": "12852", + "id": "106" + }, + { + "region": "12853", + "id": "125" + }, + { + "region": "12854", + "id": "177" + }, + { + "region": "12855", + "id": "169" + }, + { + "region": "12856", + "id": "337" + }, + { + "region": "12857", + "id": "332" + }, + { + "region": "12858", + "id": "120" + }, + { + "region": "12861", + "id": "127" + }, + { + "region": "12880", + "id": "441" + }, + { + "region": "12888", + "id": "445" + }, + { + "region": "12889", + "id": "445" + }, + { + "region": "12890", + "id": "445" + }, + { + "region": "12944", + "id": "606" + }, + { + "region": "12945", + "id": "359" + }, + { + "region": "12946", + "id": "266" + }, + { + "region": "12948", + "id": "397" + }, + { + "region": "12949", + "id": "108" + }, + { + "region": "12950", + "id": "46" + }, + { + "region": "12952", + "id": "106" + }, + { + "region": "12953", + "id": "125" + }, + { + "region": "12956", + "id": "569" + }, + { + "region": "13099", + "id": "383" + }, + { + "region": "13100", + "id": "387" + }, + { + "region": "13102", + "id": "174" + }, + { + "region": "13104", + "id": "69" + }, + { + "region": "13105", + "id": "50" + }, + { + "region": "13106", + "id": "36" + }, + { + "region": "13107", + "id": "123" + }, + { + "region": "13108", + "id": "111" + }, + { + "region": "13109", + "id": "157" + }, + { + "region": "13110", + "id": "93" + }, + { + "region": "13111", + "id": "121" + }, + { + "region": "13113", + "id": "14" + }, + { + "region": "13114", + "id": "182" + }, + { + "region": "13115", + "id": "182" + }, + { + "region": "13116", + "id": "331" + }, + { + "region": "13117", + "id": "329" + }, + { + "region": "13131", + "id": "428" + }, + { + "region": "13133", + "id": "552" + }, + { + "region": "13135", + "id": "441" + }, + { + "region": "13141", + "id": "272" + }, + { + "region": "13142", + "id": "459" + }, + { + "region": "13144", + "id": "445" + }, + { + "region": "13145", + "id": "445" + }, + { + "region": "13146", + "id": "445" + }, + { + "region": "13199", + "id": "388" + }, + { + "region": "13200", + "id": "213" + }, + { + "region": "13203", + "id": "168" + }, + { + "region": "13206", + "id": "407" + }, + { + "region": "13209", + "id": "594" + }, + { + "region": "13356", + "id": "505" + }, + { + "region": "13357", + "id": "267" + }, + { + "region": "13358", + "id": "351" + }, + { + "region": "13359", + "id": "174" + }, + { + "region": "13361", + "id": "50" + }, + { + "region": "13362", + "id": "47" + }, + { + "region": "13363", + "id": "122" + }, + { + "region": "13364", + "id": "75" + }, + { + "region": "13365", + "id": "20" + }, + { + "region": "13366", + "id": "93" + }, + { + "region": "13368", + "id": "179" + }, + { + "region": "13369", + "id": "326" + }, + { + "region": "13373", + "id": "586" + }, + { + "region": "13400", + "id": "445" + }, + { + "region": "13401", + "id": "445" + }, + { + "region": "13402", + "id": "445" + }, + { + "region": "13456", + "id": "452" + }, + { + "region": "13457", + "id": "464" + }, + { + "region": "13458", + "id": "540" + }, + { + "region": "13459", + "id": "540" + }, + { + "region": "13461", + "id": "462" + }, + { + "region": "13463", + "id": "534" + }, + { + "region": "13464", + "id": "45" + }, + { + "region": "13465", + "id": "45" + }, + { + "region": "13611", + "id": "451" + }, + { + "region": "13613", + "id": "447" + }, + { + "region": "13614", + "id": "465" + }, + { + "region": "13616", + "id": "377" + }, + { + "region": "13617", + "id": "36" + }, + { + "region": "13618", + "id": "286" + }, + { + "region": "13619", + "id": "154" + }, + { + "region": "13620", + "id": "245" + }, + { + "region": "13621", + "id": "84" + }, + { + "region": "13622", + "id": "48" + }, + { + "region": "13623", + "id": "339" + }, + { + "region": "13642", + "id": "411" + }, + { + "region": "13712", + "id": "452" + }, + { + "region": "13718", + "id": "11" + }, + { + "region": "13720", + "id": "246" + }, + { + "region": "13722", + "id": "156" + }, + { + "region": "13723", + "id": "514" + }, + { + "region": "13872", + "id": "377" + }, + { + "region": "13873", + "id": "501" + }, + { + "region": "13875", + "id": "286" + }, + { + "region": "13876", + "id": "241" + }, + { + "region": "13877", + "id": "244" + }, + { + "region": "13878", + "id": "61" + }, + { + "region": "13879", + "id": "344" + }, + { + "region": "13899", + "id": "146" + }, + { + "region": "13968", + "id": "539" + }, + { + "region": "13972", + "id": "260" + }, + { + "region": "13975", + "id": "287" + }, + { + "region": "13977", + "id": "319" + }, + { + "region": "13979", + "id": "342" + }, + { + "region": "14131", + "id": "380" + }, + { + "region": "14134", + "id": "288" + }, + { + "region": "14155", + "id": "146" + }, + { + "region": "14157", + "id": "379" + }, + { + "region": "14158", + "id": "379" + }, + { + "region": "14167", + "id": "562" + }, + { + "region": "14231", + "id": "381" + }, + { + "region": "14234", + "id": "345" + }, + { + "region": "14391", + "id": "353" + }, + { + "region": "14413", + "id": "379" + }, + { + "region": "14414", + "id": "379" + }, + { + "region": "14486", + "id": "197" + }, + { + "region": "14488", + "id": "521" + }, + { + "region": "14638", + "id": "530" + }, + { + "region": "14646", + "id": "355" + }, + { + "region": "14746", + "id": "354" + }, + { + "region": "14747", + "id": "471" + }, + { + "region": "14894", + "id": "631" + }, + { + "region": "14994", + "id": "632" + }, + { + "region": "14995", + "id": "632" + }, + { + "region": "15148", + "id": "238" + }, + { + "region": "15150", + "id": "611" + }, + { + "region": "15151", + "id": "610" + }, + { + "region": "15159", + "id": "358" + }, + { + "region": "15248", + "id": "238" + }, + { + "region": "15251", + "id": "632" + } +] \ No newline at end of file diff --git a/Server/data/configs/music_tiles.json b/Server/data/configs/music_tiles.json new file mode 100644 index 000000000..7b901f974 --- /dev/null +++ b/Server/data/configs/music_tiles.json @@ -0,0 +1,62 @@ +[ + { + "id": "98", + "borders": "{3072,3456,3135,3519,[3076,3456,3085,3458]~,[3082,3459,3085,3460]}" + }, + { + "id": "141", + "borders": "{3072,3392,3135,3455,[3076,3452,3085,3455]}" + }, + { + "id": "228", + "borders": "{2896,5446,2919,5462}" + }, + { + "id": "229", + "borders": "{2921,5456,2937,5479}" + }, + { + "id": "230", + "borders": "{2904,5481,2927,5497}" + }, + { + "id": "231", + "borders": "{2886,5464,2902,5487}" + }, + { + "id": "386", + "borders": "{2820,5312,2849,5369}-{2849,5349,2880,5374}" + }, + { + "id": "391", + "borders": "{2879,5340,2944,5366}-{2909,5316,2944,5340}" + }, + { + "id": "399", + "borders": "{2816,5248,2943,5375,[2823,5250,2844,5310]~,[2844,5250,2878,5280]~,[2879,5340,2944,5366]~,[2909,5316,2944,5340]~,[2820,5312,2849,5369]~,[2849,5349,2880,5374]~,[2885,5253,2934,5278]~,[2913,5278,2937,5306]}" + }, + { + "id": "404", + "borders": "{2823,5250,2844,5310}-{2844,5250,2878,5276}" + }, + { + "id": "408", + "borders": "{2885,5253,2934,5278}-{2913,5278,2937,5306}" + }, + { + "id": "459", + "borders": "{3137,5442,3192,5564}-{3193,5442,3198,5472}-{3193,5507,3198,5564}" + }, + { + "id": "467", + "borders": "{3199,5441,3262,5564}-{3193,5482,3198,5497}" + }, + { + "id": "488", + "borders": "{3263,5441,3327,5566}" + }, + { + "id": "492", + "borders": "{3076,3452,3085,3460,[3076,3459,3081,3460]}" + } +] \ No newline at end of file diff --git a/Server/data/configs/npc_configs.json b/Server/data/configs/npc_configs.json index 169b5ad13..d4efc299f 100644 --- a/Server/data/configs/npc_configs.json +++ b/Server/data/configs/npc_configs.json @@ -749,7 +749,6 @@ }, { "examine": "Yep. Definitely a chicken.", - "slayer_task": "7", "melee_animation": "5387", "range_animation": "5387", "combat_audio": "355,357,356", @@ -836,7 +835,6 @@ }, { "examine": "Quackers.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "respawn_delay": "60", @@ -856,7 +854,6 @@ }, { "examine": "A popular dwarven delicacy.", - "slayer_task": "67", "melee_animation": "2705", "range_animation": "2705", "combat_audio": "3102,3104,3103", @@ -879,7 +876,6 @@ }, { "examine": "A jungle version of the chicken, but more vicious.", - "slayer_task": "7", "melee_animation": "5387", "range_animation": "0", "attack_speed": "4", @@ -899,7 +895,6 @@ }, { "examine": "Hello, nice doggy...", - "slayer_task": "43", "melee_animation": "6562", "range_animation": "6562", "combat_audio": "3717,3719,3718", @@ -925,7 +920,6 @@ { "agg_radius": "64", "examine": "The biggest, meanest dragon around.", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -967,7 +961,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "11", "combat_audio": "405,407,406", "melee_animation": "25", "attack_speed": "4", @@ -991,7 +984,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -1017,7 +1009,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -1043,7 +1034,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -1101,7 +1091,6 @@ }, { "examine": "Is it a spider or is it a shadow?", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -1124,7 +1113,6 @@ }, { "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -1147,7 +1135,6 @@ }, { "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "5327", "combat_audio": "537,539,538", @@ -1169,7 +1156,6 @@ }, { "examine": "Incey wincey.", - "slayer_task": "76", "melee_animation": "6249", "range_animation": "0", "respawn_delay": "60", @@ -1181,7 +1167,7 @@ "name": "Spider", "defence_level": "1", "safespot": null, - "lifepoints": "1", + "lifepoints": "2", "strength_level": "1", "id": "61", "aggressive": "true", @@ -1190,7 +1176,6 @@ }, { "examine": "A barely visible deadly jungle spider.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "5327", "combat_audio": "537,539,538", @@ -1216,7 +1201,6 @@ }, { "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "5327", "combat_audio": "537,539,538", @@ -1240,7 +1224,6 @@ }, { "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -1332,7 +1315,6 @@ }, { "examine": "A scaly reptilian creature.", - "slayer_task": "", "melee_animation": "422", "range_animation": "422", "attack_speed": "4", @@ -1387,7 +1369,6 @@ }, { "examine": "Dead man walking.", - "slayer_task": "93", "melee_animation": "5567", "combat_audio": "931,923,922", "attack_speed": "4", @@ -1407,7 +1388,6 @@ }, { "examine": "Dead woman walking.", - "slayer_task": "93", "melee_animation": "5578", "range_animation": "5578", "combat_audio": "931,923,922", @@ -1429,7 +1409,6 @@ }, { "examine": "The walking dead.", - "slayer_task": "93", "melee_animation": "5578", "range_animation": "5578", "combat_audio": "931,923,922", @@ -1451,7 +1430,6 @@ }, { "examine": "The walking dead.", - "slayer_task": "93", "melee_animation": "5571", "combat_audio": "931,923,922", "attack_speed": "4", @@ -1471,7 +1449,6 @@ }, { "examine": "The living dead.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "4", @@ -1491,7 +1468,6 @@ }, { "examine": "An annoying flappy thing.", - "slayer_task": "5", "melee_animation": "4915", "range_animation": "4915", "combat_audio": "292,294,293", @@ -1516,7 +1492,6 @@ }, { "examine": "A shadowy, barely visible flying entity from some evil place.", - "slayer_task": "5", "melee_animation": "4915", "range_animation": "0", "attack_speed": "4", @@ -1555,7 +1530,6 @@ }, { "examine": "Converts grass to beef.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -1576,7 +1550,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -1601,7 +1574,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -1627,7 +1599,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -1670,7 +1641,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "4933", "combat_audio": "703,705,704", @@ -1692,7 +1662,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -1715,7 +1684,6 @@ }, { "examine": "A dirty rat.", - "slayer_task": "67", "range_animation": "0", "combat_audio": "703,705,704", "attack_speed": "4", @@ -1758,7 +1726,6 @@ }, { "examine": "Not man's best friend.", - "slayer_task": "92", "melee_animation": "6559", "combat_audio": "481,491,490", "attack_speed": "4", @@ -1776,7 +1743,6 @@ }, { "examine": "A vicious mountain wolf.", - "slayer_task": "92", "melee_animation": "6579", "range_animation": "6579", "combat_audio": "909,912,911", @@ -1798,7 +1764,6 @@ }, { "examine": "A vicious mountain wolf.", - "slayer_task": "92", "melee_animation": "6579", "range_animation": "6579", "combat_audio": "909,912,911", @@ -1836,7 +1801,6 @@ }, { "examine": "He doesn't seem pleased to see me.", - "slayer_task": "27", "melee_animation": "6562", "range_animation": "6562", "attack_speed": "4", @@ -1919,7 +1883,6 @@ { "agg_radius": "", "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5540", "range_animation": "5540", "combat_audio": "436,439,438", @@ -1944,7 +1907,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5540", "range_animation": "0", "combat_audio": "436,439,438", @@ -1969,7 +1931,6 @@ }, { "examine": "Eek! A bear!", - "slayer_task": "6", "melee_animation": "4925", "range_animation": "0", "combat_audio": "300,302,301", @@ -1994,7 +1955,6 @@ }, { "examine": "Eek! A bear!", - "slayer_task": "6", "melee_animation": "4925", "range_animation": "0", "combat_audio": "300,302,301", @@ -2037,7 +1997,6 @@ }, { "examine": "It has a very vicious looking tail.", - "slayer_task": "71", "melee_animation": "6254", "range_animation": "0", "combat_audio": "3611,3612,3610", @@ -2047,7 +2006,7 @@ "defence_animation": "6255", "weakness": "0", "slayer_exp": "23", - "poison_amount": "3", + "poison_amount": "15", "magic_animation": "0", "death_animation": "6256", "name": "Poison Scorpion", @@ -2063,7 +2022,6 @@ }, { "examine": "Tiny", - "slayer_task": "71", "melee_animation": "6254", "range_animation": "0", "combat_audio": "3611,3612,3610", @@ -2084,7 +2042,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -2109,7 +2066,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -2134,7 +2090,6 @@ }, { "examine": "His beard seems to have a life of its own.", - "slayer_task": "62", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "449,451,450", @@ -2159,7 +2114,6 @@ }, { "examine": "An aggressive humanoid.", - "slayer_task": "64", "melee_animation": "128", "range_animation": "128", "attack_speed": "5", @@ -2180,7 +2134,6 @@ }, { "examine": "Big, ugly, and smelly.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -2224,7 +2177,6 @@ }, { "examine": "Big, ugly, and smelly.", - "slayer_task": "44", "melee_animation": "359", "range_animation": "359", "attack_speed": "4", @@ -2249,7 +2201,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -2274,7 +2225,6 @@ }, { "examine": "A short angry guy.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "7", @@ -2295,7 +2245,6 @@ }, { "examine": "A dwarf gone bad.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "combat_audio": "2567,419,418", @@ -2320,7 +2269,6 @@ }, { "examine": "A mountain dwelling short angry guy.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "6", @@ -2389,7 +2337,6 @@ }, { "examine": "A strange, inhuman, elemental warrior.", - "slayer_task": "30", "melee_animation": "393", "range_animation": "0", "attack_speed": "4", @@ -2413,7 +2360,6 @@ }, { "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", "melee_animation": "451", "range_animation": "451", "combat_audio": "2500,530,529", @@ -2438,7 +2384,6 @@ }, { "examine": "Looks pretty otherworldly to me!", - "slayer_task": "65", "range_animation": "0", "attack_speed": "5", "magic_level": "45", @@ -2502,7 +2447,6 @@ }, { "examine": "An inhabitant of icy regions.", - "slayer_task": "7", "melee_animation": "5669", "range_animation": "0", "defence_animation": "0", @@ -2520,7 +2464,6 @@ }, { "examine": "Perhaps our oldest relatives?", - "slayer_task": "61", "melee_animation": "220", "range_animation": "0", "combat_audio": "629,631,630", @@ -2566,7 +2509,6 @@ }, { "examine": "A nasty, poisonous arachnid.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -2575,7 +2517,7 @@ "respawn_delay": "30", "defence_animation": "5328", "weakness": "2", - "poison_amount": "6", + "poison_amount": "30", "magic_animation": "0", "death_animation": "5329", "name": "Poison spider", @@ -2606,7 +2548,6 @@ }, { "examine": "A giant raptor.", - "slayer_task": "7", "melee_animation": "1010", "range_animation": "0", "combat_audio": "832,834,833", @@ -2670,7 +2611,6 @@ }, { "examine": "Must be the pack leader.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "respawn_delay": "60", @@ -2705,7 +2645,6 @@ }, { "examine": "A rare jungle wolf - specific to the Kharazi jungle.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "respawn_delay": "60", @@ -2726,7 +2665,6 @@ }, { "examine": "Wow! Scorpions shouldn't grow that big.", - "slayer_task": "71", "melee_animation": "6254", "range_animation": "0", "combat_audio": "3611,3612,3610", @@ -2749,7 +2687,6 @@ }, { "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", "melee_animation": "451", "range_animation": "451", "combat_audio": "2500,530,529", @@ -2774,7 +2711,6 @@ }, { "examine": "A sea bird.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -2791,7 +2727,6 @@ }, { "examine": "A sea bird.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -2808,7 +2743,6 @@ }, { "examine": "A sea bird.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -2857,7 +2791,6 @@ }, { "examine": "A fighter from the supernatural world. He's a shadow of his former self.", - "slayer_task": "74", "melee_animation": "394", "range_animation": "0", "attack_speed": "5", @@ -2877,53 +2810,51 @@ "attack_level": "47" }, { - "examine": "Small", + "examine": "Small, even by gnome standards.", "melee_animation": "191", "range_animation": "0", - "defence_animation": "0", + "defence_animation": "193", "weakness": "9", "magic_animation": "0", "death_animation": "196", "name": "Gnome child", "defence_level": "1", "safespot": null, - "lifepoints": "1", + "lifepoints": "2", "strength_level": "1", "id": "159", "range_level": "1", "attack_level": "1" }, { - "examine": "Seems to crawl the caves.", - "melee_animation": "266", - "range_animation": "266", - "attack_speed": "5", - "defence_animation": "267", - "weakness": "9", - "magic_animation": "266", - "death_animation": "265", - "name": "Gnome child", - "defence_level": "23", - "safespot": null, - "lifepoints": "21", - "strength_level": "23", - "id": "160", - "bonuses": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "Small", + "examine": "Small, even by gnome standards.", "melee_animation": "191", "range_animation": "0", - "defence_animation": "0", + "defence_animation": "193", "weakness": "9", "magic_animation": "0", "death_animation": "196", "name": "Gnome child", "defence_level": "1", "safespot": null, - "lifepoints": "1", + "lifepoints": "2", + "strength_level": "1", + "id": "160", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Small, even by gnome standards.", + "melee_animation": "191", + "range_animation": "0", + "defence_animation": "193", + "weakness": "9", + "magic_animation": "0", + "death_animation": "196", + "name": "Gnome child", + "defence_level": "1", + "safespot": null, + "lifepoints": "2", "strength_level": "1", "id": "161", "range_level": "1", @@ -3399,7 +3330,7 @@ "respawn_delay": "50", "defence_animation": "0", "weakness": "8", - "poison_amount": "11", + "poison_amount": "55", "magic_animation": "0", "death_animation": "836", "name": "Tribesman", @@ -3566,7 +3497,6 @@ }, { "examine": "A rocky slug.", - "slayer_task": "69", "melee_animation": "1595", "range_animation": "1595", "combat_audio": "729,731,730", @@ -3588,7 +3518,6 @@ }, { "examine": "A rocky slug.", - "slayer_task": "69", "melee_animation": "1595", "range_animation": "1595", "combat_audio": "729,731,730", @@ -3667,7 +3596,6 @@ }, { "examine": "A dwarven guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "combat_audio": "511,513,512", @@ -3857,7 +3785,6 @@ }, { "examine": "A dirty rat.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -4857,131 +4784,130 @@ "range_level": "1", "attack_level": "1" }, - { - "examine": "A mourner, or plague healer.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Mourner", - "defence_level": "24", - "safespot": null, - "lifepoints": "34", - "strength_level": "24", - "id": "347", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "A mourner, or plague healer.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Mourner", - "defence_level": "24", - "safespot": null, - "lifepoints": "34", - "strength_level": "24", - "id": "348", - "range_level": "1", - "attack_level": "24" - }, { "examine": "One of Gielinor's many citizens.", "melee_animation": "422", - "range_animation": "0", + "range_animation": "", "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", + "attack_speed": "4", + "defence_animation": "404", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Man", - "defence_level": "2", + "defence_level": "1", "safespot": null, - "lifepoints": "7", - "strength_level": "2", + "lifepoints": "13", + "strength_level": "1", "id": "351", - "clue_level": "0", + "clue_level": "", + "bonuses": "0,0,0,0,0,1,1,1,0,0,0,0,0,0,0", "range_level": "1", "attack_level": "2" }, { "examine": "One of Gielinor's many citizens.", "melee_animation": "422", - "range_animation": "0", + "range_animation": "", "combat_audio": "511,506,505", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", + "attack_speed": "4", + "respawn_delay": "50", + "defence_animation": "404", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Woman", - "defence_level": "2", + "defence_level": "1", "safespot": null, - "lifepoints": "7", - "strength_level": "2", + "lifepoints": "10", + "strength_level": "1", "id": "352", - "clue_level": "0", + "clue_level": "", + "bonuses": "0,0,0,0,0,1,1,1,0,0,0,0,0,0,0", "range_level": "1", "attack_level": "2" }, { "examine": "One of Gielinor's many citizens.", "melee_animation": "422", - "range_animation": "0", + "range_animation": "", "combat_audio": "511,506,505", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", + "attack_speed": "4", + "respawn_delay": "50", + "defence_animation": "404", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Woman", - "defence_level": "2", + "defence_level": "1", "safespot": null, - "lifepoints": "7", - "strength_level": "2", + "lifepoints": "13", + "strength_level": "1", "id": "353", - "clue_level": "0", + "clue_level": "", + "bonuses": "0,0,0,0,0,1,1,1,0,0,0,0,0,0,0", "range_level": "1", "attack_level": "2" }, { "examine": "One of Gielinor's many citizens.", "melee_animation": "422", - "range_animation": "0", + "range_animation": "", "combat_audio": "511,506,505", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", + "attack_speed": "4", + "respawn_delay": "50", + "defence_animation": "404", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Woman", - "defence_level": "2", + "defence_level": "1", "safespot": null, - "lifepoints": "7", - "strength_level": "2", + "lifepoints": "13", + "strength_level": "1", "id": "354", - "clue_level": "0", + "clue_level": "", + "bonuses": "0,0,0,0,0,1,1,1,0,0,0,0,0,0,0", "range_level": "1", "attack_level": "2" }, { "examine": "A child of West Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "magic_level": "", + "defence_animation": "", + "poison_amount": "", + "magic_animation": "", + "death_animation": "", "name": "Child", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "movement_radius": "", + "lifepoints": "", + "strength_level": "", "id": "355", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" + }, + { + "examine": "A child of West Ardougne.", + "melee_animation": "", + "range_animation": "", + "magic_level": "", + "defence_animation": "", + "poison_amount": "", + "magic_animation": "", + "death_animation": "", + "name": "Child", + "defence_level": "", + "safespot": null, + "movement_radius": "", + "lifepoints": "", + "strength_level": "", + "id": "356", + "range_level": "", + "attack_level": "" }, { "examine": "A holy man.", @@ -5024,22 +4950,24 @@ { "examine": "One of Gielinor's many citizens.", "melee_animation": "422", - "range_animation": "422", + "range_animation": "", "combat_audio": "511,506,505", "attack_speed": "4", + "respawn_delay": "50", "defence_animation": "404", - "weakness": "9", - "magic_animation": "422", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Woman", - "defence_level": "3", + "defence_level": "1", "safespot": null, - "lifepoints": "7", + "lifepoints": "13", "strength_level": "1", "id": "360", - "clue_level": "0", + "clue_level": "", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "3" + "attack_level": "2" }, { "examine": "One of Gielinor's many citizens.", @@ -5047,56 +4975,62 @@ "range_animation": "422", "combat_audio": "511,506,505", "attack_speed": "4", + "respawn_delay": "50", "defence_animation": "404", - "weakness": "9", - "magic_animation": "422", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Woman", - "defence_level": "3", + "defence_level": "10", "safespot": null, - "lifepoints": "7", - "strength_level": "1", + "lifepoints": "13", + "strength_level": "10", "id": "361", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "3" + "attack_level": "10" }, { "examine": "One of Gielinor's many citizens.", "melee_animation": "422", - "range_animation": "422", + "range_animation": "", "combat_audio": "511,506,505", "attack_speed": "4", + "respawn_delay": "50", "defence_animation": "404", - "weakness": "9", - "magic_animation": "422", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Woman", - "defence_level": "3", + "defence_level": "2", "safespot": null, - "lifepoints": "7", + "lifepoints": "10", "strength_level": "1", "id": "362", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "3" + "attack_level": "1" }, { "examine": "One of Gielinor's many citizens.", "melee_animation": "422", - "range_animation": "422", + "range_animation": "", "combat_audio": "511,506,505", "attack_speed": "4", + "respawn_delay": "50", "defence_animation": "404", - "weakness": "9", - "magic_animation": "422", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Woman", - "defence_level": "3", + "defence_level": "10", "safespot": null, - "lifepoints": "7", - "strength_level": "1", + "lifepoints": "23", + "strength_level": "10", "id": "363", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "3" + "attack_level": "10" }, { "examine": "King Lathas of East Ardougne.", @@ -5160,23 +5094,6 @@ "range_level": "1", "attack_level": "1" }, - { - "examine": "A mourner, or plague healer.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Mourner", - "defence_level": "24", - "safespot": null, - "lifepoints": "34", - "strength_level": "24", - "id": "369", - "range_level": "1", - "attack_level": "24" - }, { "examine": "She's quite a looker!", "melee_animation": "0", @@ -5195,7 +5112,6 @@ }, { "examine": "Big, ugly, and smelly.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -5287,6 +5203,7 @@ "melee_animation": "0", "range_animation": "0", "combat_audio": "3611,3612,3610", + "respawn_delay": "60", "defence_animation": "0", "magic_animation": "0", "death_animation": "0", @@ -5309,6 +5226,7 @@ "strength_level": "1", "id": "386", "range_level": "1", + "respawn_delay": "60", "attack_level": "1" }, { @@ -5321,6 +5239,7 @@ "strength_level": "1", "id": "387", "range_level": "1", + "respawn_delay": "60", "attack_level": "1" }, { @@ -5378,7 +5297,6 @@ }, { "examine": "Beefy.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -5465,7 +5383,6 @@ }, { "examine": "An annoying flappy thing.", - "slayer_task": "5", "melee_animation": "4915", "range_animation": "4915", "attack_speed": "5", @@ -5657,7 +5574,6 @@ }, { "examine": "Dead but not gone.", - "slayer_task": "73", "melee_animation": "390", "range_animation": "0", "combat_audio": "511,439,438", @@ -5678,7 +5594,6 @@ }, { "examine": "Dead but not gone.", - "slayer_task": "73", "melee_animation": "390", "range_animation": "0", "combat_audio": "511,439,438", @@ -5906,7 +5821,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -6313,9 +6227,24 @@ "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.", - "slayer_task": "38", "melee_animation": "6199", "range_animation": "0", "combat_audio": "469,472,471", @@ -6324,13 +6253,13 @@ "magic_animation": "0", "death_animation": "6190", "name": "Goblin guard", - "defence_level": "20", + "defence_level": "37", "safespot": null, - "lifepoints": "28", - "strength_level": "20", + "lifepoints": "43", + "strength_level": "37", "id": "489", "range_level": "1", - "attack_level": "20" + "attack_level": "32" }, { "examine": "If the mummy is at school", @@ -6384,7 +6313,6 @@ }, { "examine": "A vicious", - "slayer_task": "71", "melee_animation": "6261", "range_animation": "0", "combat_audio": "3611,3612,3610", @@ -6471,7 +6399,6 @@ }, { "examine": "A minion of Rashiliyia.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "attack_speed": "5", @@ -6492,7 +6419,6 @@ }, { "examine": "A minion of Rashiliyia.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "defence_animation": "0", @@ -6510,7 +6436,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -6531,7 +6456,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5571", "range_animation": "0", "attack_speed": "5", @@ -7817,7 +7741,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -8154,32 +8077,43 @@ "attack_level": "1" }, { - "slayer_exp": "0", + "slayer_exp": "", "examine": "A local civilian.", "name": "Edmond", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "combat_audio": "", + "strength_level": "", "id": "714", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" }, { - "examine": "In charge of people with silly outfits.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Head mourner", - "defence_level": "1", + "slayer_exp": "", + "examine": "A local civilian.", + "name": "Edmond", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "716", - "range_level": "1", - "attack_level": "1" + "lifepoints": "", + "combat_audio": "", + "strength_level": "", + "id": "3213", + "range_level": "", + "attack_level": "" + }, + { + "slayer_exp": "", + "examine": "A local civilian.", + "name": "Edmond", + "defence_level": "", + "safespot": null, + "lifepoints": "", + "combat_audio": "", + "strength_level": "", + "id": "3214", + "range_level": "", + "attack_level": "" }, { "examine": "A member of the Ardougne Royal Army.", @@ -8225,39 +8159,42 @@ }, { "examine": "One of Gielinor's many citizens.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", + "melee_animation": "", + "range_animation": "", + "combat_audio": "", + "protect_style": "", + "respawn_delay": "", "defence_animation": "0", - "magic_animation": "0", + "slayer_exp": "", + "magic_animation": "", "death_animation": "0", "name": "Man", "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "safespot": "", + "lifepoints": "", + "strength_level": "", "id": "728", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" + "clue_level": "", + "range_level": "", + "attack_level": "" }, { "examine": "One of Gielinor's many citizens.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "combat_audio": "", + "defence_animation": "", + "magic_animation": "", + "death_animation": "", "name": "Man", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "strength_level": "", "id": "729", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" + "clue_level": "", + "range_level": "", + "attack_level": "" }, { "examine": "One of Gielinor's many citizens.", @@ -8406,7 +8343,6 @@ }, { "examine": "A badly-behaved goblin.", - "slayer_task": "38", "melee_animation": "6184", "range_animation": "0", "defence_animation": "0", @@ -8582,51 +8518,51 @@ }, { "examine": "A citizen of Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "defence_animation": "", + "magic_animation": "", + "death_animation": "", "name": "Civilian", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "strength_level": "", "id": "785", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" }, { "examine": "A citizen of Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "defence_animation": "", + "magic_animation": "", + "death_animation": "", "name": "Civilian", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "strength_level": "", "id": "786", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" }, { "examine": "A citizen of Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "defence_animation": "", + "magic_animation": "", + "death_animation": "", "name": "Civilian", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "strength_level": "", "id": "787", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" }, { "examine": "A gnome who's supposed to be cleaning up a mess.", @@ -8680,20 +8616,21 @@ "examine": "A cold hearted lady.", "melee_animation": "422", "range_animation": "0", - "magic_level": "55", + "magic_level": "1", "respawn_delay": "60", "defence_animation": "0", "weakness": "10", "magic_animation": "0", "death_animation": "836", "name": "Ice Queen", - "defence_level": "55", + "defence_level": "95", "safespot": null, - "lifepoints": "157", - "strength_level": "55", + "lifepoints": "104", + "strength_level": "94", "id": "795", - "range_level": "55", - "attack_level": "55" + "bonuses": "0,0,0,0,0,30,40,20,10,30,0,0,0,0,0", + "range_level": "1", + "attack_level": "95" }, { "examine": "He looks cold and hungry.", @@ -8895,7 +8832,7 @@ "attack_level": "1" }, { - "examine": "Big", + "examine": "Big, noisy, and scary looking!", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9047,7 +8984,6 @@ }, { "examine": "A vicious desert wolf.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "defence_animation": "0", @@ -9177,7 +9113,6 @@ }, { "examine": "Tough-looking.", - "slayer_task": "64", "range_animation": "0", "attack_speed": "4", "magic_level": "1", @@ -9208,7 +9143,6 @@ }, { "examine": "These ogres protect the city.", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9226,7 +9160,6 @@ }, { "examine": "An ogre that guards.", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9244,7 +9177,6 @@ }, { "examine": "Tries to keep the peace.", - "slayer_task": "64", "melee_animation": "2100", "range_animation": "0", "defence_animation": "0", @@ -9294,7 +9226,6 @@ }, { "examine": "Big and ugly looking.", - "slayer_task": "64", "melee_animation": "2100", "range_animation": "0", "respawn_delay": "60", @@ -9329,7 +9260,6 @@ }, { "examine": "Funnily enough", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9346,7 +9276,6 @@ }, { "examine": "Funnily enough", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9363,7 +9292,6 @@ }, { "examine": "Funnily enough", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9502,7 +9430,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", @@ -9917,7 +9845,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -10123,7 +10050,6 @@ }, { "examine": "Converts grass to beef.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -10184,7 +10110,6 @@ }, { "examine": "It's one of Iban's pets.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -10203,7 +10128,6 @@ }, { "examine": "It's one of Iban's pet vermin.", - "slayer_task": "67", "range_animation": "0", "combat_audio": "703,705,704", "defence_animation": "0", @@ -10567,6 +10491,11 @@ "range_level": "1", "attack_level": "22" }, + { + "examine": "A rather nasty looking crustacean.", + "name": "Sea slug", + "id": "1006" + }, { "examine": "A servant of Zamorak.", "combat_style": "2", @@ -10592,7 +10521,6 @@ }, { "examine": "A nasty, poisonous arachnid.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -10601,18 +10529,18 @@ "respawn_delay": "30", "defence_animation": "5328", "weakness": "2", - "poison_amount": "6", + "poison_amount": "30", "magic_animation": "0", "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", @@ -10651,7 +10579,6 @@ }, { "examine": "Yep. Definitely a chicken.", - "slayer_task": "7", "melee_animation": "5387", "range_animation": "5387", "combat_audio": "355,357,356", @@ -10672,7 +10599,6 @@ }, { "examine": "He rules the", - "slayer_task": "7", "melee_animation": "5387", "range_animation": "0", "respawn_delay": "60", @@ -11504,7 +11430,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but mean, ugly and throws rocks.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "7", @@ -11526,7 +11451,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but mean, ugly and throws rocks.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "7", @@ -11548,7 +11472,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but mean, ugly and throws rocks.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "7", @@ -11570,7 +11493,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but mean, ugly and throws rocks.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "7", @@ -11592,7 +11514,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but mean, ugly and throws rocks.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "7", @@ -11613,7 +11534,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11632,7 +11552,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11651,7 +11570,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11670,7 +11588,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11689,7 +11606,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11708,7 +11624,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11727,7 +11642,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11746,7 +11660,6 @@ }, { "examine": "One of the troll generals.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "attack_speed": "5", @@ -11767,7 +11680,6 @@ }, { "examine": "One of the troll generals.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "attack_speed": "5", @@ -11788,7 +11700,6 @@ }, { "examine": "One of the troll generals.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "attack_speed": "5", @@ -11809,7 +11720,6 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", @@ -11828,7 +11738,6 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", @@ -11847,7 +11756,6 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", @@ -11866,7 +11774,6 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", @@ -11885,7 +11792,6 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", @@ -11904,7 +11810,6 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", @@ -11923,7 +11828,6 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", @@ -11942,7 +11846,6 @@ }, { "examine": "An unusually large troll.", - "slayer_task": "83", "melee_animation": "1158", "range_animation": "0", "respawn_delay": "60", @@ -11962,7 +11865,6 @@ }, { "examine": "He's guarding the cells.", - "slayer_task": "83", "range_animation": "0", "melee_animation": "1158", "respawn_delay": "60", @@ -11983,7 +11885,6 @@ }, { "examine": "He's guarding the cells.", - "slayer_task": "83", "range_animation": "0", "melee_animation": "1158", "respawn_delay": "60", @@ -12004,7 +11905,6 @@ }, { "examine": "He's guarding the cells.", - "slayer_task": "83", "range_animation": "0", "defence_animation": "0", "weakness": "7", @@ -12021,7 +11921,6 @@ }, { "examine": "He's guarding the cells.", - "slayer_task": "83", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -12039,7 +11938,6 @@ }, { "examine": "Small for a troll, but it's mean, ugly and throws rocks.", - "slayer_task": "83", "start_gfx": "33", "combat_style": "1", "melee_animation": "284", @@ -12065,7 +11963,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but it's mean, ugly and throws rocks.", - "slayer_task": "83", "start_gfx": "33", "combat_style": "1", "melee_animation": "284", @@ -12091,7 +11988,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but it's mean, ugly and throws rocks.", - "slayer_task": "83", "start_gfx": "33", "combat_style": "1", "melee_animation": "284", @@ -12117,7 +12013,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but it's mean, ugly and throws rocks.", - "slayer_task": "83", "start_gfx": "33", "combat_style": "1", "melee_animation": "284", @@ -12143,7 +12038,6 @@ { "agg_radius": "6", "examine": "Small for a troll, but it's mean, ugly and throws rocks.", - "slayer_task": "83", "start_gfx": "33", "combat_style": "1", "melee_animation": "284", @@ -12216,7 +12110,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -12235,7 +12128,6 @@ }, { "examine": "He's fast asleep.", - "slayer_task": "83", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -12444,7 +12336,6 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6223", "range_animation": "6223", "combat_audio": "571,573,572", @@ -12468,34 +12359,32 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6224", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "60", - "weakness": "7", - "slayer_exp": "90", - "poison_amount": "4", - "magic_animation": "0", - "death_animation": "6228", - "lifepoints": "90", - "id": "1154", - "aggressive": "true", - "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", "range_animation": "0", "combat_audio": "567,569,568", + "attack_speed": "4", + "poisonous": "true", "magic_level": "1", + "respawn_delay": "60", "defence_animation": "6227", + "weakness": "7", + "slayer_exp": "90", + "poison_amount": "20", + "magic_animation": "0", + "death_animation": "6228", "name": "Kalphite Soldier", "defence_level": "70", "safespot": null, + "lifepoints": "90", "strength_level": "70", + "id": "1154", + "aggressive": "true", + "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", "range_level": "1", "attack_level": "70" }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6223", "range_animation": "6223", "attack_speed": "4", @@ -12504,7 +12393,7 @@ "respawn_delay": "65", "defence_animation": "6232", "weakness": "7", - "poison_amount": "6", + "poison_amount": "30", "magic_animation": "6223", "death_animation": "6230", "name": "Kalphite Guardian", @@ -12520,7 +12409,6 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6223", "range_animation": "6223", "combat_audio": "571,573,572", @@ -12544,7 +12432,6 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6223", "range_animation": "6223", "attack_speed": "4", @@ -12553,7 +12440,7 @@ "respawn_delay": "65", "defence_animation": "6232", "weakness": "7", - "poison_amount": "6", + "poison_amount": "30", "magic_animation": "6223", "death_animation": "6230", "name": "Kalphite Guardian", @@ -12570,13 +12457,12 @@ { "agg_radius": "64", "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6241", "range_animation": "6241", "attack_speed": "4", - "poisonous": "true", + "poisonous": "", "magic_level": "150", - "respawn_delay": "45", + "respawn_delay": "0", "defence_animation": "6232", "weakness": "10", "magic_animation": "6241", @@ -12595,12 +12481,11 @@ { "agg_radius": "64", "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6235", "range_animation": "6235", "attack_speed": "4", "magic_level": "150", - "respawn_delay": "0", + "respawn_delay": "45", "defence_animation": "6234", "weakness": "10", "magic_animation": "6235", @@ -12618,7 +12503,6 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -12656,7 +12540,6 @@ }, { "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", "start_gfx": "250", "combat_style": "1", "melee_animation": "428", @@ -12680,7 +12563,6 @@ }, { "examine": "I don't wanna be at the wrong end of that pike.", - "slayer_task": "31", "melee_animation": "428", "range_animation": "0", "combat_audio": "425,427,426", @@ -12784,7 +12666,6 @@ }, { "examine": "Eek! A bear cub!", - "slayer_task": "6", "range_animation": "0", "combat_audio": "498,500,499", "respawn_delay": "60", @@ -12814,7 +12695,6 @@ }, { "examine": "What big teeth you have.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "defence_animation": "0", @@ -12984,7 +12864,6 @@ }, { "examine": "A scary, man eating ghoul.", - "slayer_task": "37", "melee_animation": "422", "range_animation": "422", "combat_audio": "442,444,443", @@ -13028,7 +12907,7 @@ }, { "examine": "It looks very hungry!", - "melee_animation": "7183", + "melee_animation": "1264", "slayer_exp": "50", "death_animation": "7185", "name": "Vampire", @@ -13043,9 +12922,11 @@ }, { "slayer_exp": "40", + "examine": "It looks very hungry!", "name": "Vampire", "defence_level": "1", "safespot": null, + "melee_animation": "1264", "lifepoints": "50", "strength_level": "1", "id": "1223", @@ -13054,6 +12935,7 @@ "attack_level": "1" }, { + "examine": "It looks very hungry!", "name": "Vampire", "defence_level": "1", "safespot": null, @@ -13300,7 +13182,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -13321,7 +13202,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -13342,7 +13222,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -13363,7 +13242,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -13384,7 +13262,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -13405,7 +13282,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -13426,7 +13302,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -13447,7 +13322,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -13468,7 +13342,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -13489,7 +13362,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -13599,6 +13471,7 @@ "respawn_delay": "60", "defence_animation": "0", "weakness": "3", + "poison_amount": "30", "magic_animation": "811", "death_animation": "836", "name": "Saradomin wizard", @@ -14421,7 +14294,6 @@ }, { "examine": "Cute. But deadly.", - "slayer_task": "6", "range_animation": "0", "combat_audio": "498,500,499", "respawn_delay": "60", @@ -14496,7 +14368,6 @@ }, { "examine": "Fearsome predator found only in the Fremennik Province", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "combat_audio": "481,491,490", @@ -14675,7 +14546,6 @@ }, { "examine": "A horror from the ocean depths.", - "slayer_task": "24", "melee_animation": "1341", "range_animation": "0", "magic_level": "60", @@ -15378,7 +15248,6 @@ }, { "examine": "A scimitar wielding ninja monkey.", - "slayer_task": "61", "melee_animation": "1392", "range_animation": "0", "combat_audio": "629,631,630", @@ -15397,7 +15266,6 @@ }, { "examine": "A bow wielding ninja monkey.", - "slayer_task": "61", "combat_style": "1", "melee_animation": "0", "range_animation": "1394", @@ -15453,7 +15321,6 @@ }, { "examine": "A huge brutish gorilla armoured with dangerous looking vambraces.", - "slayer_task": "61", "melee_animation": "1402", "range_animation": "1402", "combat_audio": "629,631,630", @@ -15473,7 +15340,6 @@ }, { "examine": "A huge brutish gorilla armoured with dangerous looking vambraces.", - "slayer_task": "61", "melee_animation": "1402", "range_animation": "0", "combat_audio": "629,631,630", @@ -15495,7 +15361,6 @@ }, { "examine": "A huge brutish gorilla stands here", - "slayer_task": "61", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -15535,7 +15400,6 @@ }, { "examine": "A large and lumbering undead monkey.", - "slayer_task": "61", "melee_animation": "1383", "range_animation": "0", "combat_audio": "629,631,630", @@ -15651,7 +15515,6 @@ }, { "examine": "It's a brightly coloured bird of the jungle.", - "slayer_task": "7", "melee_animation": "6775", "range_animation": "0", "defence_animation": "0", @@ -15669,7 +15532,6 @@ }, { "examine": "It's a brightly coloured bird of the jungle. It flies very quickly.", - "slayer_task": "7", "melee_animation": "6775", "range_animation": "0", "defence_animation": "0", @@ -15699,7 +15561,6 @@ }, { "examine": "A very dangerous looking spider", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -15974,7 +15835,6 @@ }, { "examine": "Well", - "slayer_task": "75", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -15993,7 +15853,6 @@ }, { "examine": "I don't think the pickaxe is for hitting rocks.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "respawn_delay": "60", @@ -16059,7 +15918,6 @@ }, { "examine": "A large boisterous bird, a delicacy for ogres.", - "slayer_task": "7", "melee_animation": "6761", "range_animation": "0", "combat_audio": "1449,1451,1450", @@ -16127,7 +15985,6 @@ }, { "examine": "Not man's best friend.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "respawn_delay": "60", @@ -16147,7 +16004,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -16167,7 +16023,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -16186,7 +16041,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -16206,7 +16060,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -16225,7 +16078,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -16245,7 +16097,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -16265,7 +16116,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -16300,7 +16150,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -16325,7 +16174,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -16350,7 +16198,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -16375,7 +16222,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -16400,7 +16246,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -16425,7 +16270,6 @@ }, { "examine": "His beard seems to have a life of its own.", - "slayer_task": "62", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "449,451,450", @@ -16450,7 +16294,6 @@ }, { "examine": "His beard seems to have a life of its own.", - "slayer_task": "62", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "449,451,450", @@ -16475,7 +16318,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "68", "melee_animation": "25", "range_animation": "25", "combat_audio": "405,407,406", @@ -16499,7 +16341,6 @@ }, { "examine": "Its scales seem to be made of bronze.", - "slayer_task": "13", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -16525,7 +16366,6 @@ }, { "examine": "Its scales seem to be made of iron.", - "slayer_task": "50", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -16551,7 +16391,6 @@ }, { "examine": "Its scales seem to be made of steel.", - "slayer_task": "80", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -16577,7 +16416,6 @@ }, { "examine": "An unsuitable pet.", - "slayer_task": "27", "melee_animation": "6562", "range_animation": "6562", "magic_level": "20", @@ -16624,7 +16462,6 @@ }, { "examine": "A spiky crawling critter. ", - "slayer_task": "16", "melee_animation": "266", "range_animation": "266", "combat_audio": "341,343,342", @@ -16634,6 +16471,7 @@ "defence_animation": "267", "weakness": "1", "slayer_exp": "22", + "poison_amount": "40", "magic_animation": "266", "death_animation": "265", "name": "Cave crawler", @@ -16656,6 +16494,7 @@ "respawn_delay": "25", "defence_animation": "267", "slayer_exp": "22", + "poison_amount": "40", "magic_animation": "266", "death_animation": "265", "name": "Cave crawler", @@ -16678,6 +16517,7 @@ "respawn_delay": "25", "defence_animation": "267", "slayer_exp": "22", + "poison_amount": "40", "magic_animation": "266", "death_animation": "265", "name": "Cave crawler", @@ -16700,6 +16540,7 @@ "respawn_delay": "25", "defence_animation": "267", "slayer_exp": "22", + "poison_amount": "40", "magic_animation": "266", "death_animation": "265", "name": "Cave crawler", @@ -16714,7 +16555,6 @@ }, { "examine": "A very smelly ghost.", - "slayer_task": "0", "start_gfx": "334", "combat_style": "2", "melee_animation": "9466", @@ -16743,7 +16583,6 @@ }, { "examine": "A very smelly ghost.", - "slayer_task": "0", "start_gfx": "334", "combat_style": "2", "melee_animation": "9466", @@ -16772,7 +16611,6 @@ }, { "examine": "A very smelly ghost.", - "slayer_task": "0", "start_gfx": "334", "combat_style": "2", "melee_animation": "9466", @@ -16801,7 +16639,6 @@ }, { "examine": "A very smelly ghost.", - "slayer_task": "0", "start_gfx": "334", "combat_style": "2", "melee_animation": "9466", @@ -16899,7 +16736,6 @@ }, { "examine": "Flies like a rock.", - "slayer_task": "84", "melee_animation": "1516", "range_animation": "1516", "attack_speed": "5", @@ -16987,7 +16823,6 @@ }, { "examine": "A denizen of the Abyss!", - "slayer_task": "1", "melee_animation": "1537", "range_animation": "1537", "combat_audio": "276,278,277", @@ -17101,7 +16936,6 @@ }, { "examine": "The winged reptile.", - "slayer_task": "19", "melee_animation": "7762", "range_animation": "7762", "combat_audio": "363,365,364", @@ -17189,7 +17023,6 @@ }, { "examine": "The vacuumed face of evil.", - "slayer_task": "28", "melee_animation": "1557", "range_animation": "1557", "combat_audio": "414,416,415", @@ -17210,7 +17043,6 @@ }, { "examine": "The cave-dwelling cousin of the dust devils.", - "slayer_task": "28", "melee_animation": "1557", "range_animation": "0", "defence_animation": "0", @@ -17339,7 +17171,6 @@ { "spawn_animation": "8081", "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "8080", "combat_audio": "696,698,697", "attack_speed": "5", @@ -17363,7 +17194,6 @@ { "spawn_animation": "8081", "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "8080", "combat_audio": "696,698,697", "attack_speed": "5", @@ -17387,7 +17217,6 @@ { "spawn_animation": "8081", "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "8080", "combat_audio": "696,698,697", "attack_speed": "5", @@ -17411,7 +17240,6 @@ { "spawn_animation": "8081", "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "8080", "combat_audio": "696,698,697", "attack_speed": "5", @@ -17434,7 +17262,6 @@ }, { "examine": "It's a Jelly.", - "slayer_task": "51", "melee_animation": "8569", "range_animation": "8569", "combat_audio": "547,550,549", @@ -17457,7 +17284,6 @@ }, { "examine": "Doesn't look so tough...", - "slayer_task": "51", "melee_animation": "8569", "range_animation": "0", "combat_audio": "547,550,549", @@ -17480,7 +17306,6 @@ }, { "examine": "Wibbly.", - "slayer_task": "51", "melee_animation": "8569", "range_animation": "0", "combat_audio": "547,550,549", @@ -17503,7 +17328,6 @@ }, { "examine": "There's always room for jelly.", - "slayer_task": "51", "melee_animation": "8569", "range_animation": "0", "combat_audio": "547,550,549", @@ -17560,7 +17384,6 @@ }, { "examine": "An evil magic user.", - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17584,7 +17407,6 @@ "attack_level": "1" }, { - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17609,7 +17431,6 @@ }, { "examine": "An evil magic user.", - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17634,7 +17455,6 @@ }, { "examine": "An evil magic user.", - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17659,7 +17479,6 @@ }, { "examine": "An evil magic user.", - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17684,7 +17503,6 @@ }, { "examine": "Now THAT'S handy.", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "9125", "combat_audio": "377,379,378", @@ -17707,7 +17525,6 @@ }, { "examine": "Gimmie five. Actually", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "0", "combat_audio": "377,379,378", @@ -17729,7 +17546,6 @@ }, { "examine": "Gimmie five. Actually", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "0", "combat_audio": "377,379,378", @@ -17750,7 +17566,6 @@ }, { "examine": "Gimmie five. Actually", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "0", "combat_audio": "377,379,378", @@ -17772,7 +17587,6 @@ }, { "examine": "Gimmie five. Actually", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "0", "combat_audio": "377,379,378", @@ -17794,7 +17608,6 @@ }, { "examine": "Now THAT's handy.", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -17816,7 +17629,6 @@ }, { "examine": "I'm glad it's just the hand I can see...", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -17838,7 +17650,6 @@ }, { "examine": "A big severed hand.", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -17859,7 +17670,6 @@ }, { "examine": "Give the guy a big hand.....", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -17881,7 +17691,6 @@ }, { "examine": "A big severed hand.", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -18070,7 +17879,6 @@ }, { "examine": "It's an undead cow.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "0", "attack_speed": "4", @@ -18093,7 +17901,6 @@ }, { "examine": "Yup, definitely a chicken... an undead chicken.", - "slayer_task": "7", "melee_animation": "5387", "range_animation": "0", "combat_audio": "355,357,356", @@ -18117,7 +17924,6 @@ }, { "examine": "An extremely vicious lobster.", - "slayer_task": "71", "melee_animation": "6265", "range_animation": "0", "defence_animation": "0", @@ -18184,7 +17990,6 @@ }, { "examine": "This poor soul cannot understand why it has not passed to the next world.", - "slayer_task": "36", "melee_animation": "422", "range_animation": "0", "attack_speed": "5", @@ -18693,7 +18498,6 @@ }, { "examine": "These gnomes know how to get around!", - "slayer_task": "7", "melee_animation": "6790", "range_animation": "0", "respawn_delay": "60", @@ -18712,7 +18516,6 @@ }, { "examine": "A farmer's enemy.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -18850,7 +18653,6 @@ }, { "examine": "Where beef comes from.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -18890,7 +18692,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18912,7 +18713,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18934,7 +18734,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18956,7 +18755,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18978,7 +18776,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -19000,7 +18797,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -19022,7 +18818,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -19044,7 +18839,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -19083,7 +18877,6 @@ }, { "examine": "A short stout menacing fellow.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "6", @@ -19103,7 +18896,6 @@ }, { "examine": "A short stout menacing fellow.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "6", @@ -19123,7 +18915,6 @@ }, { "examine": "A short stout menacing fellow.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "6", @@ -19382,7 +19173,6 @@ }, { "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", "melee_animation": "6001", "range_animation": "0", "combat_audio": "469,472,471", @@ -19402,7 +19192,6 @@ }, { "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", "melee_animation": "6001", "range_animation": "0", "combat_audio": "469,472,471", @@ -19422,7 +19211,6 @@ }, { "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", "melee_animation": "6001", "range_animation": "0", "combat_audio": "469,472,471", @@ -19442,7 +19230,6 @@ }, { "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", "melee_animation": "6001", "range_animation": "0", "combat_audio": "469,472,471", @@ -19460,6 +19247,21 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "A human zombie.", + "melee_animation": "5568", + "combat_audio": "931,923,922", + "defence_animation": "5567", + "death_animation": "5569", + "name": "Zombie", + "defence_level": "30", + "safespot": null, + "lifepoints": "50", + "strength_level": "30", + "id": "1826", + "range_level": "1", + "attack_level": "30" + }, { "examine": "Flies like a rock.", "melee_animation": "9454", @@ -19522,34 +19324,32 @@ }, { "examine": "A foul-smelling blob of protoplasm.", - "slayer_task": "18", "melee_animation": "1789", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "15", - "weakness": "1", - "slayer_exp": "25", - "poison_amount": "3", - "magic_animation": "0", - "death_animation": "1792", - "lifepoints": "25", - "id": "1831", - "aggressive": "false", "range_animation": "0", "combat_audio": "784,786,785", + "attack_speed": "4", + "poisonous": "true", "magic_level": "13", + "respawn_delay": "15", "defence_animation": "0", + "weakness": "1", + "slayer_exp": "25", + "poison_amount": "15", + "magic_animation": "0", + "death_animation": "1792", "name": "Cave slime", "defence_level": "35", "safespot": null, + "lifepoints": "25", "strength_level": "13", + "id": "1831", + "aggressive": "false", "clue_level": "0", "range_level": "1", "attack_level": "13" }, { "examine": "A nasty crawling critter.", - "slayer_task": "15", "melee_animation": "6079", "range_animation": "0", "attack_speed": "4", @@ -20172,49 +19972,70 @@ "attack_level": "1" }, { - "examine": "A tough-looking criminal.", - "melee_animation": "412", - "range_animation": "412", + "examine": "Ice warrior.", + "melee_animation": "440", + "range_animation": "440", "attack_speed": "5", "defence_animation": "404", "magic_animation": "412", "death_animation": "9055", "name": "Kamil", - "defence_level": "20", + "defence_level": "135", "safespot": null, - "lifepoints": "50", - "strength_level": "55", + "lifepoints": "130", + "strength_level": "80", "id": "1913", + "bonuses": "0,60,0,0,0,35,60,35,0,0,0,100,0,0,0", "range_level": "1", - "attack_level": "20" + "attack_level": "190" }, { - "melee_animation": "422", + "examine": "A vampyre warrior of Zamorak.", + "melee_animation": "1264", "respawn_delay": "60", "defence_animation": "425", "death_animation": "836", "name": "Dessous", - "defence_level": "1", + "defence_level": "99", "safespot": null, "lifepoints": "200", - "strength_level": "1", + "strength_level": "99", "id": "1914", "aggressive": "true", + "bonuses": "0,0,50,0,0,10,150,150,0,0,0,50,0,0,0", "range_level": "1", - "attack_level": "1" + "attack_level": "99" }, { - "melee_animation": "422", + "examine": "A vampyre warrior of Zamorak.", + "melee_animation": "1264", "respawn_delay": "60", "defence_animation": "425", "death_animation": "836", "name": "Dessous", - "defence_level": "1", + "defence_level": "99", "safespot": null, "lifepoints": "200", - "strength_level": "1", + "strength_level": "99", "id": "1915", "aggressive": "true", + "bonuses": "0,0,50,0,0,10,150,150,0,0,0,50,0,0,0", + "range_level": "1", + "attack_level": "99" + }, + { + "examine": "Luckily, I can't see much of his face.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Ruantun", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "1916", "range_level": "1", "attack_level": "1" }, @@ -20268,6 +20089,22 @@ "range_level": "1", "attack_level": "58" }, + { + "examine": "One of Morytania's vampyric nobility.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Malak", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "1920", + "range_level": "1", + "attack_level": "1" + }, { "examine": "Looks like a rough-and-ready type.", "melee_animation": "0", @@ -20338,7 +20175,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -20358,7 +20194,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -20377,7 +20212,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -20397,7 +20231,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -20416,7 +20249,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -20436,7 +20268,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -20456,7 +20287,6 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", @@ -20473,6 +20303,98 @@ "range_level": "1", "attack_level": "64" }, + { + "examine": "A troll frozen in a block of ice.", + "melee_animation": "0", + "range_animation": "0", + "respawn_delay": "1", + "defence_animation": "0", + "weakness": "9", + "magic_animation": "0", + "death_animation": "0", + "name": "Ice block", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "1943", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A troll frozen in a block of ice.", + "melee_animation": "0", + "range_animation": "0", + "respawn_delay": "1", + "defence_animation": "0", + "weakness": "9", + "magic_animation": "0", + "death_animation": "0", + "name": "Ice block", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "1944", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A troll frozen in a block of ice.", + "melee_animation": "0", + "range_animation": "0", + "respawn_delay": "1", + "defence_animation": "0", + "weakness": "9", + "magic_animation": "0", + "death_animation": "0", + "name": "Ice block", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "1945", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A troll frozen in a block of ice.", + "melee_animation": "0", + "range_animation": "0", + "respawn_delay": "1", + "defence_animation": "0", + "weakness": "9", + "magic_animation": "0", + "death_animation": "0", + "name": "Ice block", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "1946", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "An ice troll.", + "name": "Troll father", + "id": "1947" + }, + { + "examine": "An ice troll.", + "name": "Troll father", + "id": "1948" + }, + { + "examine": "An ice troll.", + "name": "Troll mother", + "id": "1949" + }, + { + "examine": "An ice troll.", + "name": "Troll mother", + "id": "1950" + }, { "examine": "Not a man's best friend.", "melee_animation": "6579", @@ -20619,17 +20541,17 @@ "magic_animation": "0", "death_animation": "5555", "name": "Mummy", - "defence_level": "55", + "defence_level": "90", "safespot": null, - "lifepoints": "78", - "strength_level": "55", + "lifepoints": "90", + "strength_level": "90", "id": "1961", "aggressive": "true", "range_level": "1", - "attack_level": "55" + "attack_level": "90" }, { - "examine": "Spooky", + "examine": "Spooky, bandaged dead dude.", "melee_animation": "5549", "range_animation": "0", "defence_animation": "0", @@ -20637,14 +20559,15 @@ "magic_animation": "0", "death_animation": "5555", "name": "Mummy", - "defence_level": "55", + "defence_level": "90", "poison_immune": "true", "safespot": null, - "lifepoints": "78", - "strength_level": "55", + "lifepoints": "90", + "strength_level": "90", "id": "1962", + "aggressive": "true", "range_level": "1", - "attack_level": "55" + "attack_level": "90" }, { "examine": "A victim of poor first aid.", @@ -20655,14 +20578,15 @@ "magic_animation": "0", "death_animation": "5555", "name": "Mummy", - "defence_level": "55", + "defence_level": "90", "poison_immune": "true", "safespot": null, - "lifepoints": "78", - "strength_level": "55", + "lifepoints": "90", + "strength_level": "90", "id": "1963", + "aggressive": "true", "range_level": "1", - "attack_level": "55" + "attack_level": "90" }, { "examine": "But who's the daddy?", @@ -20673,13 +20597,14 @@ "magic_animation": "0", "death_animation": "5555", "name": "Mummy", - "defence_level": "55", + "defence_level": "90", "safespot": null, - "lifepoints": "78", - "strength_level": "55", + "lifepoints": "90", + "strength_level": "90", "id": "1964", + "aggressive": "true", "range_level": "1", - "attack_level": "55" + "attack_level": "90" }, { "examine": "A tightly-wrapped monster.", @@ -20717,22 +20642,23 @@ }, { "examine": "I think they're some kind of beetle...", - "slayer_task": "70", "melee_animation": "1948", "range_animation": "0", + "attack_speed": "3", "poisonous": "true", - "defence_animation": "0", + "defence_animation": "1946", "weakness": "7", + "poison_amount": "15", "magic_animation": "0", - "death_animation": "1946", + "death_animation": "5464", "name": "Scarabs", - "defence_level": "62", + "defence_level": "10", "safespot": null, - "lifepoints": "88", - "strength_level": "62", + "lifepoints": "25", + "strength_level": "2", "id": "1969", "range_level": "1", - "attack_level": "62" + "attack_level": "255" }, { "examine": "A wandering merchant.", @@ -20747,7 +20673,6 @@ }, { "examine": "A giant skeleton.", - "slayer_task": "75", "melee_animation": "5499", "range_animation": "0", "defence_animation": "0", @@ -20763,9 +20688,47 @@ "range_level": "1", "attack_level": "60" }, + { + "examine": "The warrior of darkness.", + "melee_animation": "440", + "range_animation": "0", + "respawn_delay": "60", + "defence_animation": "0", + "weakness": "0", + "magic_animation": "0", + "death_animation": "836", + "name": "Damis", + "defence_level": "90", + "safespot": null, + "lifepoints": "90", + "strength_level": "90", + "id": "1974", + "bonuses": "0,0,0,0,0,60,60,60,60,60,0,80,0,0,0", + "range_level": "1", + "attack_level": "90" + }, + { + "examine": "The warrior of darkness.", + "melee_animation": "440", + "range_animation": "0", + "attack_speed": "3", + "respawn_delay": "60", + "defence_animation": "0", + "weakness": "0", + "magic_animation": "0", + "death_animation": "836", + "name": "Damis", + "defence_level": "160", + "safespot": null, + "lifepoints": "200", + "strength_level": "100", + "id": "1975", + "bonuses": "0,0,0,0,0,100,100,100,80,120,0,100,0,0,0", + "range_level": "1", + "attack_level": "160" + }, { "examine": "Looks hungry!", - "slayer_task": "27", "melee_animation": "6565", "range_animation": "0", "respawn_delay": "60", @@ -20783,19 +20746,22 @@ "attack_level": "48" }, { + "examine": "Zamorak's warrior of fire.", "melee_animation": "799", + "magic_level": "100", "respawn_delay": "60", "defence_animation": "434", "death_animation": "836", "name": "Fareed", - "defence_level": "1", + "defence_level": "135", "safespot": null, "lifepoints": "130", - "strength_level": "1", + "strength_level": "120", "id": "1977", "aggressive": "true", - "range_level": "1", - "attack_level": "1" + "bonuses": "0,0,0,0,0,100,100,100,0,0,0,120,0,0,0", + "range_level": "100", + "attack_level": "190" }, { "examine": "A malnourished worker.", @@ -20917,7 +20883,6 @@ }, { "examine": "He has had his day.", - "slayer_task": "27", "melee_animation": "6568", "range_animation": "0", "combat_audio": "544,546,545", @@ -20980,7 +20945,6 @@ }, { "examine": "I might give the burgers a miss in this town.", - "slayer_task": "20", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -20998,7 +20962,6 @@ }, { "examine": "I don't fancy eating any part of this.", - "slayer_task": "20", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -21016,7 +20979,6 @@ }, { "examine": "I think they're some kind of beetle.", - "slayer_task": "70", "melee_animation": "1948", "range_animation": "0", "attack_speed": "2", @@ -21024,6 +20986,7 @@ "respawn_delay": "60", "defence_animation": "0", "weakness": "6", + "poison_amount": "15", "magic_animation": "0", "death_animation": "1946", "name": "Scarab swarm", @@ -21330,7 +21293,6 @@ }, { "examine": "A nasty little rodent.", - "slayer_task": "67", "melee_animation": "240", "range_animation": "240", "combat_audio": "703,705,704", @@ -21354,7 +21316,6 @@ }, { "examine": "A nasty overgrown rodent.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "4933", "combat_audio": "703,705,704", @@ -21378,7 +21339,6 @@ }, { "examine": "Incey wincey.", - "slayer_task": "76", "melee_animation": "6249", "range_animation": "6249", "combat_audio": "537,539,538", @@ -21402,7 +21362,6 @@ }, { "examine": "Not very incey wincey.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "5327", "combat_audio": "537,539,538", @@ -21426,7 +21385,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "combat_audio": "", "melee_animation": "359", "range_animation": "359", @@ -21450,7 +21408,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21473,7 +21430,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21496,7 +21452,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21519,7 +21474,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21542,7 +21496,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21565,7 +21518,6 @@ }, { "examine": "It's falling apart!", - "slayer_task": "64", "combat_audio": "", "range_animation": "359", "melee_animation": "359", @@ -21589,7 +21541,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21612,7 +21563,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21635,7 +21585,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21658,7 +21607,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21681,7 +21629,6 @@ }, { "examine": "A partially decomposing zombie ogre.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -21704,7 +21651,6 @@ }, { "examine": "It's falling apart!", - "slayer_task": "64", "combat_audio": "", "melee_animation": "359", "range_animation": "359", @@ -21728,7 +21674,6 @@ }, { "examine": "It's falling apart!", - "slayer_task": "64", "combat_audio": "", "range_animation": "359", "melee_animation": "359", @@ -21752,12 +21697,11 @@ }, { "examine": "What could be hiding in that crack in the wall?", - "slayer_task": "87", "melee_animation": "0", "range_animation": "0", "combat_audio": "931,923,922", "attack_speed": "5", - "respawn_delay": "60", + "respawn_delay": "0", "defence_animation": "0", "weakness": "7", "slayer_exp": "30", @@ -21774,16 +21718,28 @@ "attack_level": "1" }, { + "examine": "A partially decomposing zombie ogre.", + "slayer_task": "64", + "combat_audio": "", + "melee_animation": "359", + "range_animation": "359", + "attack_speed": "6", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "360", + "slayer_exp": "", + "weakness": "7", + "magic_animation": "359", + "death_animation": "361", "name": "Slash Bash", - "defence_level": "1", + "defence_level": "60", "safespot": null, - "lifepoints": "103", - "strength_level": "1", + "lifepoints": "100", + "strength_level": "120", "id": "2060", "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "range_level": "100", + "attack_level": "100" }, { "examine": "I can see fish swimming in the water.", @@ -21803,7 +21759,6 @@ }, { "examine": "This one is slacking off.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21822,7 +21777,6 @@ }, { "examine": "This one is slacking off.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21841,7 +21795,6 @@ }, { "examine": "This one is slacking off.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21860,7 +21813,6 @@ }, { "examine": "This one is slacking off.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21879,7 +21831,6 @@ }, { "examine": "He protects the miners.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21899,7 +21850,6 @@ }, { "examine": "He protects the miners.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -22475,7 +22425,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -22494,7 +22443,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -22513,7 +22461,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -22532,7 +22479,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -22551,7 +22497,6 @@ }, { "examine": "An elite member of the Black Guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "respawn_delay": "60", @@ -22570,7 +22515,6 @@ }, { "examine": "An elite member of the Black Guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "respawn_delay": "60", @@ -22589,7 +22533,6 @@ }, { "examine": "An elite member of the Black Guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "respawn_delay": "60", @@ -23468,7 +23411,7 @@ "name": "Abyssal leech", "defence_level": "52", "safespot": null, - "lifepoints": "74", + "lifepoints": "10", "strength_level": "1", "id": "2263", "aggressive": "true", @@ -23741,6 +23684,54 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "An observer for the Temple Knights.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Sir Spishyus", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2282", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "An observer for the Temple Knights.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Lady Table", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2283", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "An observer for the Temple Knights.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Sir Kuam Ferentse", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2284", + "range_level": "1", + "attack_level": "1" + }, { "examine": "A warrior blessed by Saradomin.", "melee_animation": "400", @@ -23748,13 +23739,45 @@ "defence_animation": "425", "death_animation": "836", "name": "Sir Leye", - "defence_level": "1", + "defence_level": "15", "safespot": null, "lifepoints": "21", - "strength_level": "1", + "strength_level": "18", "id": "2285", "aggressive": "true", "range_level": "1", + "attack_level": "18" + }, + { + "examine": "An observer for the Temple Knights.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Sir Tinley", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2286", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "An observer for the Temple Knights.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Sir Ren Itchood", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2287", + "range_level": "1", "attack_level": "1" }, { @@ -23789,6 +23812,22 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "Head of recruitment for the Temple Knights.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Sir Tiffy Cashien", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2290", + "range_level": "1", + "attack_level": "1" + }, { "examine": "A carpet merchant.", "melee_animation": "0", @@ -23872,7 +23911,6 @@ }, { "examine": "Young, but still beefy.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "366,368,367", @@ -24176,7 +24214,6 @@ }, { "examine": "I don't wanna be at the wrong end of that pike.", - "slayer_task": "31", "melee_animation": "428", "range_animation": "0", "combat_audio": "425,427,426", @@ -24198,7 +24235,6 @@ }, { "examine": "I don't wanna be at the wrong end of that pike.", - "slayer_task": "31", "melee_animation": "428", "range_animation": "0", "combat_audio": "425,427,426", @@ -24220,7 +24256,6 @@ }, { "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", "start_gfx": "250", "combat_style": "1", "melee_animation": "428", @@ -24244,7 +24279,6 @@ }, { "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", "start_gfx": "250", "combat_style": "1", "melee_animation": "428", @@ -24268,40 +24302,233 @@ }, { "examine": "A Mourner showing his true identity.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "defence_animation": "", + "magic_animation": "", + "death_animation": "", "name": "Head mourner", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "strength_level": "", "id": "2372", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" }, { "examine": "A mourner, or plague healer.", - "slayer_task": "31", "melee_animation": "428", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", + "range_animation": "", + "attack_speed": "4", + "defence_animation": "", + "weakness": "", + "magic_animation": "", "death_animation": "836", "name": "Mourner", - "defence_level": "61", + "defence_level": "80", "safespot": null, "lifepoints": "87", "strength_level": "61", "id": "2373", "aggressive": "true", - "range_level": "1", + "range_level": "", "attack_level": "61" }, { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2381", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2382", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2383", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2384", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2385", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2386", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2387", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2388", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2389", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2390", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2391", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2392", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2393", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2394", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2395", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", + "name": "Mysterious ghost", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "combat_audio": "436,439,438", + "strength_level": "1", + "id": "2396", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -24313,6 +24540,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -24324,6 +24552,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -24335,6 +24564,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -24346,6 +24576,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -24357,6 +24588,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -24368,17 +24600,16 @@ "attack_level": "1" }, { - "slayer_task": "29", - "examine": "A dwarf gone bad.", - "respawn_delay": "150", "slayer_exp": "61", "weakness": "6", + "examine": "A dwarf gone bad.", "name": "Chaos dwarf", "defence_level": "1", "safespot": null, "lifepoints": "10", "strength_level": "1", "id": "2423", + "respawn_delay": "150", "range_level": "1", "attack_level": "1" }, @@ -24470,7 +24701,6 @@ }, { "examine": "A teeny-tiny horror from the ocean depths...", - "slayer_task": "24", "melee_animation": "1341", "range_animation": "0", "respawn_delay": "60", @@ -24869,7 +25099,7 @@ "respawn_delay": "60", "defence_animation": "0", "weakness": "1", - "poison_amount": "11", + "poison_amount": "55", "magic_animation": "0", "death_animation": "278", "name": "Bush snake", @@ -24888,7 +25118,7 @@ "poisonous": "true", "respawn_delay": "60", "defence_animation": "276", - "poison_amount": "11", + "poison_amount": "55", "death_animation": "278", "name": "Bush snake", "defence_level": "1", @@ -25004,7 +25234,7 @@ "respawn_delay": "50", "defence_animation": "0", "weakness": "9", - "poison_amount": "11", + "poison_amount": "55", "magic_animation": "0", "death_animation": "836", "name": "Tribesman", @@ -26128,7 +26358,6 @@ }, { "examine": "She quackers.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "respawn_delay": "60", @@ -26148,7 +26377,6 @@ }, { "examine": "Mini quackers.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -26442,6 +26670,8 @@ { "examine": "Hydro-power!", "combat_style": "2", + "start_gfx": "93", + "start_height": "80", "melee_animation": "414", "range_animation": "0", "magic_level": "14", @@ -26457,6 +26687,7 @@ "strength_level": "1", "id": "2710", "range_level": "1", + "projectile": "94", "attack_level": "1" }, { @@ -26964,7 +27195,6 @@ }, { "examine": "From a darker dimension.", - "slayer_task": "25", "melee_animation": "2731", "range_animation": "2731", "combat_audio": "389,391,390", @@ -26989,51 +27219,51 @@ }, { "examine": "Digging.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "defence_animation": "", + "magic_animation": "", + "death_animation": "", "name": "Slave", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "strength_level": "", "id": "2785", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" }, { "examine": "Digging.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "defence_animation": "", + "magic_animation": "", + "death_animation": "", "name": "Slave", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "strength_level": "", "id": "2786", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" }, { "examine": "Confused.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "melee_animation": "", + "range_animation": "", + "defence_animation": "", + "magic_animation": "", + "death_animation": "", "name": "Slave", - "defence_level": "1", + "defence_level": "", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "", + "strength_level": "", "id": "2787", - "range_level": "1", - "attack_level": "1" + "range_level": "", + "attack_level": "" }, { "examine": "Drill Sergeant from heck!", @@ -27068,7 +27298,7 @@ "attack_level": "1" }, { - "examine": "An angry Ogre in a funny hat.", + "examine": "Big, ugly, and smelly.", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -27076,14 +27306,14 @@ "magic_animation": "359", "death_animation": "361", "name": "Ogre", - "defence_level": "30", + "defence_level": "54", "safespot": null, - "lifepoints": "48", - "strength_level": "30", + "lifepoints": "60", + "strength_level": "54", "id": "2801", "aggressive": "true", "range_level": "1", - "attack_level": "30" + "attack_level": "54" }, { "examine": "They just call him 'Coach'.", @@ -27103,7 +27333,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -27124,7 +27353,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -27145,7 +27373,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -27185,7 +27412,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -27205,7 +27431,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -27483,7 +27708,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -27504,7 +27728,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -27525,7 +27748,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -27546,7 +27768,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -27567,7 +27788,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -27588,7 +27808,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -27609,7 +27828,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "defence_animation": "0", @@ -27628,7 +27846,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "defence_animation": "0", @@ -27647,7 +27864,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "defence_animation": "0", @@ -27666,7 +27882,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "defence_animation": "0", @@ -27685,7 +27900,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "defence_animation": "0", @@ -27704,7 +27918,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "defence_animation": "0", @@ -27743,7 +27956,6 @@ }, { "examine": "A bunch of legs, eyes and teeth.", - "slayer_task": "76", "melee_animation": "5319", "range_animation": "0", "combat_audio": "537,539,538", @@ -27942,7 +28154,6 @@ }, { "examine": "A knee-high horror from the ocean depths...", - "slayer_task": "24", "melee_animation": "1579", "range_animation": "0", "defence_animation": "0", @@ -29383,6 +29594,7 @@ "respawn_delay": "60", "defence_animation": "2937", "weakness": "9", + "poison_amount": "40", "magic_animation": "2936", "death_animation": "2938", "name": "Jogre Champion", @@ -29421,7 +29633,7 @@ "examine": "Champion of the jogres.", "melee_animation": "5485", "range_animation": "5493", - "poisonous": "true", + "poisonous": "", "respawn_delay": "60", "defence_animation": "5493", "weakness": "9", @@ -29474,7 +29686,6 @@ }, { "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", "start_gfx": "499", "melee_animation": "2985", "attack_speed": "6", @@ -29503,7 +29714,6 @@ }, { "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", "start_gfx": "499", "melee_animation": "2985", "attack_speed": "6", @@ -29532,7 +29742,6 @@ }, { "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", "start_gfx": "499", "melee_animation": "2985", "attack_speed": "6", @@ -29561,7 +29770,6 @@ }, { "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", "start_gfx": "499", "melee_animation": "2985", "attack_speed": "6", @@ -29590,7 +29798,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -29615,7 +29822,6 @@ }, { "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", "melee_animation": "451", "range_animation": "451", "combat_audio": "2500,530,529", @@ -29995,7 +30201,6 @@ }, { "examine": "A swarm of bugs.", - "slayer_task": "42", "melee_animation": "1584", "range_animation": "0", "combat_audio": "2743,2745,2744", @@ -30541,25 +30746,8 @@ "range_level": "1", "attack_level": "1" }, - { - "examine": "A mourner, or plague healer.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mourner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3216", - "range_level": "1", - "attack_level": "1" - }, { "examine": "Loves mining.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30578,7 +30766,6 @@ }, { "examine": "Loves mining.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30597,7 +30784,6 @@ }, { "examine": "Loves mining.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30871,46 +31057,66 @@ "attack_level": "1" }, { + "agg_radius": "", + "examine": "He looks a bit dodgy.", + "melee_animation": "422", + "respawn_delay": "60", + "defence_animation": "425", "death_animation": "836", "name": "Cuffs", "defence_level": "1", "safespot": null, "lifepoints": "7", - "melee_animation": "422", "strength_level": "1", "id": "3237", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "1" }, { + "agg_radius": "", + "examine": "Looks unpleasant.", + "melee_animation": "422", + "respawn_delay": "60", + "defence_animation": "425", + "death_animation": "836", + "name": "Narf", + "defence_level": "1", + "safespot": null, + "lifepoints": "7", + "strength_level": "1", + "id": "3238", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "Seems to be loitering.", + "melee_animation": "422", + "respawn_delay": "60", + "defence_animation": "425", "death_animation": "836", "name": "Rusty", "defence_level": "1", "safespot": null, "lifepoints": "7", - "melee_animation": "422", "strength_level": "1", "id": "3239", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "1" }, { + "examine": "Untrustworthy.", + "melee_animation": "422", + "respawn_delay": "60", + "defence_animation": "425", "death_animation": "836", "name": "Jeff", "defence_level": "1", "safespot": null, "lifepoints": "7", - "melee_animation": "422", "strength_level": "1", "id": "3240", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "1" }, { "melee_animation": "395", @@ -31408,7 +31614,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31427,7 +31632,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31446,7 +31650,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31465,7 +31668,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31484,7 +31686,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31503,7 +31704,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31522,7 +31722,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31541,7 +31740,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31560,7 +31758,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31579,7 +31776,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31598,7 +31794,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31617,7 +31812,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -31864,7 +32058,6 @@ }, { "examine": "Converts grass to beef.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -32052,7 +32245,6 @@ }, { "examine": "A bouncy fungus.", - "slayer_task": "94", "melee_animation": "2776", "range_animation": "0", "attack_speed": "5", @@ -32202,7 +32394,6 @@ }, { "examine": "A fowl beast.", - "slayer_task": "7", "melee_animation": "2299", "range_animation": "0", "combat_audio": "355,357,356", @@ -32224,7 +32415,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "68", "melee_animation": "25", "range_animation": "25", "combat_audio": "405,407,406", @@ -32265,7 +32455,6 @@ }, { "examine": "Vermin from the underworld.", - "slayer_task": "67", "melee_animation": "0", "range_animation": "0", "combat_audio": "703,705,704", @@ -32397,7 +32586,6 @@ }, { "examine": "A small ice demon.", - "slayer_task": "46", "range_animation": "0", "combat_audio": "531,533,532", "attack_speed": "5", @@ -32467,6 +32655,7 @@ "respawn_delay": "60", "defence_animation": "0", "weakness": "10", + "poison_amount": "35", "magic_animation": "0", "death_animation": "7249", "name": "Wild jade vine", @@ -32488,6 +32677,7 @@ "respawn_delay": "60", "defence_animation": "0", "weakness": "10", + "poison_amount": "35", "magic_animation": "0", "death_animation": "7249", "name": "Wild jade vine", @@ -32509,6 +32699,7 @@ "respawn_delay": "60", "defence_animation": "0", "weakness": "10", + "poison_amount": "35", "magic_animation": "0", "death_animation": "7249", "name": "Wild jade vine", @@ -32530,6 +32721,7 @@ "respawn_delay": "60", "defence_animation": "0", "weakness": "10", + "poison_amount": "35", "magic_animation": "0", "death_animation": "7249", "name": "Wild jade vine", @@ -32623,16 +32815,21 @@ "attack_level": "1" }, { + "examine": "Big, ugly, and smelly.", + "slayer_task": "64", + "melee_animation": "359", + "attack_speed": "7", + "respawn_delay": "60", + "defence_animation": "360", + "weakness": "8", + "death_animation": "361", "name": "Ogre", - "defence_level": "1", + "defence_level": "27", "safespot": null, "lifepoints": "60", - "strength_level": "1", - "attack_speed": "7", + "strength_level": "27", "id": "3419", "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", "attack_level": "1" }, { @@ -33023,7 +33220,6 @@ }, { "examine": "A large boisterous bird", - "slayer_task": "7", "melee_animation": "6800", "range_animation": "0", "defence_animation": "0", @@ -33348,7 +33544,6 @@ }, { "examine": "An initiate juvenile vampyre", - "slayer_task": "86", "melee_animation": "6016", "range_animation": "0", "defence_animation": "0", @@ -33383,7 +33578,6 @@ }, { "examine": "He looks really hungry!", - "slayer_task": "86", "melee_animation": "6276", "range_animation": "0", "respawn_delay": "60", @@ -33402,7 +33596,6 @@ }, { "examine": "She looks really hungry!", - "slayer_task": "86", "melee_animation": "6276", "range_animation": "0", "respawn_delay": "60", @@ -33434,7 +33627,6 @@ }, { "examine": "He looks really hungry!", - "slayer_task": "86", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -33452,7 +33644,6 @@ }, { "examine": "An initiate juvenile vampyre", - "slayer_task": "86", "melee_animation": "6016", "range_animation": "0", "defence_animation": "0", @@ -33533,7 +33724,6 @@ }, { "examine": "Good doggy...", - "slayer_task": "43", "melee_animation": "6562", "range_animation": "0", "combat_audio": "3717,3719,3718", @@ -33611,7 +33801,7 @@ "respawn_delay": "60", "defence_animation": "6227", "weakness": "6", - "poison_amount": "4", + "poison_amount": "20", "magic_animation": "0", "death_animation": "6228", "name": "Kalphite Soldier", @@ -33714,7 +33904,6 @@ }, { "examine": "A large snake that thrives in swamps.", - "slayer_task": "86", "melee_animation": "3538", "range_animation": "3538", "combat_audio": "3609,3608,3610", @@ -34037,7 +34226,6 @@ }, { "examine": "He looks a little on the cross side!", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "0", "combat_audio": "469,472,471", @@ -34247,7 +34435,6 @@ }, { "examine": "Eww", - "slayer_task": "67", "melee_animation": "6117", "range_animation": "0", "combat_audio": "703,705,704", @@ -34285,7 +34472,6 @@ }, { "examine": "An annoying flappy thing.", - "slayer_task": "5", "melee_animation": "4915", "range_animation": "4915", "combat_audio": "292,294,293", @@ -35833,7 +36019,7 @@ "name": "Tortoise", "defence_level": "36", "safespot": null, - "lifepoints": "51", + "lifepoints": "121", "strength_level": "36", "id": "3808", "range_level": "1", @@ -35907,61 +36093,68 @@ "examine": "A Gnome Arrow-chucker", "combat_style": "1", "melee_animation": "190", - "range_animation": "0", + "range_animation": "190", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "193", "weakness": "2", "magic_animation": "0", "death_animation": "196", "name": "Gnome Archer", - "defence_level": "30", + "defence_level": "1", "safespot": null, - "lifepoints": "42", + "lifepoints": "10", "strength_level": "1", "id": "3814", "aggressive": "true", - "range_level": "30", - "attack_level": "1" + "range_level": "5", + "projectile": "10", + "attack_level": "1", + "prj_height": "30" }, { "examine": "Yee haa!", "melee_animation": "3969", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "193", "weakness": "9", "magic_animation": "0", "death_animation": "196", "name": "Gnome Driver", - "defence_level": "33", + "defence_level": "1", "safespot": null, - "lifepoints": "47", - "strength_level": "33", + "lifepoints": "10", + "strength_level": "1", "id": "3815", "aggressive": "true", "range_level": "1", - "attack_level": "33" + "attack_level": "5" }, { "examine": "A battle mage of the gnomish variety.", "combat_style": "2", + "start_gfx": "93", + "start_height": "80", "melee_animation": "3968", "range_animation": "0", - "magic_level": "34", + "magic_level": "5", + "spell_id": "", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "193", "weakness": "5", - "magic_animation": "0", + "magic_animation": "200", "death_animation": "196", "name": "Gnome Mage", - "defence_level": "34", + "defence_level": "1", "safespot": null, - "lifepoints": "48", + "lifepoints": "10", "strength_level": "1", "id": "3816", "aggressive": "true", "range_level": "1", - "attack_level": "1" + "projectile": "94", + "attack_level": "1", + "prj_height": "30" }, { "examine": "The cruel tortoise trainer. Boo!", @@ -35990,7 +36183,7 @@ "name": "Tortoise", "defence_level": "36", "safespot": null, - "lifepoints": "51", + "lifepoints": "101", "strength_level": "36", "id": "3819", "range_level": "1", @@ -36154,7 +36347,6 @@ }, { "examine": "Snake", - "slayer_task": "72", "melee_animation": "3538", "range_animation": "0", "combat_audio": "3609,3608,3610", @@ -36175,7 +36367,6 @@ }, { "examine": "A baby sea snake. Snaaaaaaake!", - "slayer_task": "72", "melee_animation": "3538", "range_animation": "0", "combat_audio": "3609,3608,3610", @@ -36293,7 +36484,6 @@ }, { "examine": "A denizen of the Abyss!", - "slayer_task": "1", "melee_animation": "1537", "range_animation": "1537", "combat_audio": "276,278,277", @@ -36454,7 +36644,6 @@ }, { "examine": "A dwarven guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "combat_audio": "511,513,512", @@ -37010,7 +37199,6 @@ }, { "examine": "A one-eyed man eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "combat_audio": "448,451,450", @@ -37036,7 +37224,6 @@ }, { "examine": "A one-eyed woman eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "combat_audio": "448,451,450", @@ -37363,7 +37550,6 @@ }, { "examine": "It's all white by me.", - "slayer_task": "5", "melee_animation": "4915", "range_animation": "0", "respawn_delay": "60", @@ -37401,7 +37587,6 @@ }, { "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "52", "melee_animation": "4235", "range_animation": "0", "combat_audio": "496,500,499", @@ -37449,7 +37634,6 @@ }, { "examine": "A horrible, emaciated ape like creature with beady green eyes.", - "slayer_task": "52", "melee_animation": "4235", "range_animation": "0", "combat_audio": "496,500,499", @@ -37519,7 +37703,6 @@ }, { "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", "melee_animation": "4234", "range_animation": "4234", "combat_audio": "496,500,499", @@ -37545,7 +37728,6 @@ }, { "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", "melee_animation": "4234", "range_animation": "4234", "combat_audio": "496,500,499", @@ -37571,7 +37753,6 @@ }, { "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", "melee_animation": "4234", "range_animation": "4234", "combat_audio": "496,500,499", @@ -37597,7 +37778,6 @@ }, { "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", "melee_animation": "4234", "range_animation": "4234", "combat_audio": "496,500,499", @@ -37623,7 +37803,6 @@ }, { "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", "melee_animation": "4234", "range_animation": "4234", "combat_audio": "496,500,499", @@ -37843,7 +38022,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -37866,7 +38044,6 @@ }, { "examine": "I don't think insect repellent will work.", - "slayer_task": "34", "melee_animation": "1184", "range_animation": "1184", "combat_audio": "571,573,572", @@ -38000,7 +38177,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "combat_audio": "703,705,704", "melee_animation": "4933", "attack_speed": "5", @@ -38187,7 +38363,6 @@ }, { "examine": "He doesn't look very pleased to see you.", - "slayer_task": "58", "melee_animation": "4266", "range_animation": "4266", "combat_audio": "626,628,627", @@ -38425,7 +38600,6 @@ }, { "examine": "A popular dwarven delicacy.", - "slayer_task": "67", "melee_animation": "2705", "range_animation": "2705", "combat_audio": "710,713,711", @@ -39264,7 +39438,6 @@ }, { "examine": "His beard seems to have a life of its own.", - "slayer_task": "62", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "449,451,450", @@ -40220,6 +40393,7 @@ "examine": "He works evil magic.", "start_gfx": "93", "combat_style": "2", + "start_height": "80", "melee_animation": "810", "range_animation": "0", "combat_audio": "511,513,512", @@ -40244,6 +40418,7 @@ "examine": "He works evil magic.", "start_gfx": "96", "combat_style": "2", + "start_height": "80", "melee_animation": "810", "range_animation": "0", "combat_audio": "511,513,512", @@ -40287,7 +40462,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "11", "melee_animation": "25", "range_animation": "25", "combat_audio": "405,407,406", @@ -40312,7 +40486,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "11", "combat_audio": "405,407,406", "melee_animation": "25", "attack_speed": "4", @@ -40364,7 +40537,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40390,7 +40562,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40416,7 +40587,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40442,7 +40612,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40468,7 +40637,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40494,7 +40662,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40520,7 +40687,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40546,7 +40712,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40572,7 +40737,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40598,7 +40762,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40624,7 +40787,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40650,7 +40812,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40676,7 +40837,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40702,7 +40862,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40728,7 +40887,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40754,7 +40912,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -40780,7 +40937,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -40805,7 +40961,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -40830,7 +40985,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -40855,7 +41009,6 @@ }, { "examine": "His beard seems to have a life of its own.", - "slayer_task": "62", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "449,451,450", @@ -40880,7 +41033,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -40905,7 +41057,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -40930,7 +41081,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -40955,7 +41105,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -40980,7 +41129,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -41005,7 +41153,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -41030,7 +41177,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -41055,7 +41201,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -41080,7 +41225,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -41105,7 +41249,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -41131,7 +41274,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -41157,7 +41299,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -41183,7 +41324,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -41209,7 +41349,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -41234,7 +41373,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -41259,7 +41397,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -41284,7 +41421,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -41309,7 +41445,6 @@ }, { "examine": "His beard seems to have a life of its own.", - "slayer_task": "62", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "449,451,450", @@ -42096,7 +42231,6 @@ }, { "examine": "An enraged vampyre!", - "slayer_task": "86", "melee_animation": "6016", "range_animation": "0", "defence_animation": "0", @@ -42979,7 +43113,6 @@ }, { "examine": "An extremely vicious lobster.", - "slayer_task": "71", "melee_animation": "6265", "range_animation": "0", "respawn_delay": "60", @@ -43211,7 +43344,6 @@ }, { "examine": "A nasty overgrown rodent.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "4933", "combat_audio": "703,705,704", @@ -43235,7 +43367,6 @@ }, { "examine": "A nasty overgrown rodent.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "4933", "combat_audio": "703,705,704", @@ -43259,7 +43390,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -43281,7 +43411,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -43303,7 +43432,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -43340,7 +43468,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -43362,7 +43489,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -43384,7 +43510,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "0", "range_animation": "0", "combat_audio": "703,705,704", @@ -43406,7 +43531,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "0", "range_animation": "0", "combat_audio": "703,705,704", @@ -43524,7 +43648,6 @@ }, { "examine": "A dirty rat.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -43546,7 +43669,6 @@ }, { "examine": "A dirty rat.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -43590,7 +43712,6 @@ }, { "examine": "He looks a little on the cross side!", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -43609,7 +43730,6 @@ }, { "examine": "It's one of Iban's pet vermin.", - "slayer_task": "67", "range_animation": "0", "combat_audio": "703,705,704", "defence_animation": "0", @@ -43678,7 +43798,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "combat_audio": "703,705,704", "melee_animation": "4933", "attack_speed": "5", @@ -43698,7 +43817,6 @@ }, { "examine": "Trollish.", - "slayer_task": "83", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -43747,7 +43865,6 @@ }, { "examine": "A mountain-dwelling bird. Cute", - "slayer_task": "7", "melee_animation": "5031", "range_animation": "0", "respawn_delay": "60", @@ -43767,7 +43884,6 @@ }, { "examine": "A very", - "slayer_task": "7", "melee_animation": "5024", "range_animation": "5025", "magic_level": "55", @@ -44380,7 +44496,6 @@ }, { "examine": "It blends in very well with its surroundings.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -44397,7 +44512,6 @@ }, { "examine": "This bird obviously doesn't believe in subtlety.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -44414,7 +44528,6 @@ }, { "examine": "Best served ice cold.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -44431,7 +44544,6 @@ }, { "examine": "Actually", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -44448,7 +44560,6 @@ }, { "examine": "Nothing much to get in a flap about.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -45220,7 +45331,6 @@ }, { "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", "combat_style": "2", "range_animation": "0", "respawn_delay": "60", @@ -45239,7 +45349,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -45265,7 +45374,6 @@ }, { "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", "range_animation": "0", "defence_animation": "0", "weakness": "7", @@ -45281,7 +45389,6 @@ }, { "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", "combat_style": "2", "range_animation": "0", "respawn_delay": "60", @@ -45300,7 +45407,6 @@ }, { "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", "combat_style": "2", "range_animation": "0", "respawn_delay": "60", @@ -45544,7 +45650,6 @@ }, { "examine": "Part scarab, part man.", - "slayer_task": "70", "combat_style": "2", "melee_animation": "7615", "range_animation": "0", @@ -45566,7 +45671,6 @@ }, { "examine": "A mounted lancer.", - "slayer_task": "70", "melee_animation": "7584", "range_animation": "0", "defence_animation": "0", @@ -45585,7 +45689,6 @@ }, { "examine": "A mounted archer.", - "slayer_task": "70", "melee_animation": "5451", "range_animation": "5451", "defence_animation": "0", @@ -45604,7 +45707,6 @@ }, { "examine": "A huge scarab beast.", - "slayer_task": "70", "melee_animation": "5457", "range_animation": "0", "magic_level": "62", @@ -46446,7 +46548,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -46489,7 +46590,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -46513,7 +46613,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -46556,7 +46655,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -46580,7 +46678,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -46680,7 +46777,6 @@ }, { "examine": "A minion of Rashiliyia.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "attack_speed": "5", @@ -46701,7 +46797,6 @@ }, { "examine": "A minion of Rashiliyia.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "defence_animation": "0", @@ -46719,7 +46814,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -46740,7 +46834,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -46761,7 +46854,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5571", "range_animation": "0", "attack_speed": "5", @@ -46782,7 +46874,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5571", "range_animation": "0", "attack_speed": "5", @@ -46803,7 +46894,6 @@ }, { "examine": "A giant skeleton.", - "slayer_task": "75", "melee_animation": "5499", "range_animation": "0", "defence_animation": "0", @@ -46821,17 +46911,16 @@ }, { "examine": "A fiendish embodiment of water.", - "slayer_task": "90", "combat_style": "1", - "melee_animation": "1582", - "range_animation": "1582", + "melee_animation": "299", + "range_animation": "299", "combat_audio": "3774,3773,3772", "attack_speed": "4", "magic_level": "105", - "defence_animation": "1581", + "defence_animation": "301", "weakness": "4", - "magic_animation": "1582", - "death_animation": "1580", + "magic_animation": "299", + "death_animation": "300", "name": "Waterfiend", "defence_level": "128", "poison_immune": "true", @@ -46847,7 +46936,6 @@ }, { "examine": "It appears to be intelligent and savage.", - "slayer_task": "41", "melee_animation": "91", "range_animation": "91", "combat_audio": "408,410,409", @@ -46872,7 +46960,6 @@ }, { "examine": "Experimenting with mithril gone bad!", - "slayer_task": "57", "melee_animation": "91", "range_animation": "91", "combat_audio": "408,410,409", @@ -47465,7 +47552,6 @@ }, { "examine": "A terrifying dog beast.", - "slayer_task": "82", "melee_animation": "5625", "range_animation": "0", "attack_speed": "5", @@ -47602,7 +47688,6 @@ }, { "examine": "Sturdy cold being.", - "slayer_task": "48", "melee_animation": "5724", "range_animation": "5725", "respawn_delay": "60", @@ -47622,7 +47707,6 @@ }, { "examine": "Sturdy cold being.", - "slayer_task": "48", "melee_animation": "5724", "range_animation": "0", "respawn_delay": "60", @@ -47642,7 +47726,6 @@ }, { "examine": "Sturdy cold being.", - "slayer_task": "48", "melee_animation": "5724", "range_animation": "0", "respawn_delay": "60", @@ -47662,7 +47745,6 @@ }, { "examine": "Sturdy cold being.", - "slayer_task": "48", "melee_animation": "5724", "range_animation": "0", "respawn_delay": "60", @@ -47682,7 +47764,6 @@ }, { "examine": "An impressive-looking troll.", - "slayer_task": "83", "melee_animation": "5374", "range_animation": "0", "magic_level": "65", @@ -48574,7 +48655,6 @@ }, { "examine": "Bedside manner is a little lacking", - "slayer_task": "93", "melee_animation": "5643", "range_animation": "0", "defence_animation": "0", @@ -48592,7 +48672,6 @@ }, { "examine": "I hope his hands don't shake.", - "slayer_task": "93", "melee_animation": "5643", "range_animation": "0", "defence_animation": "0", @@ -48610,7 +48689,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -48647,7 +48725,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -48684,7 +48761,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -48705,7 +48781,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -48726,7 +48801,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -48763,7 +48837,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -48784,7 +48857,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -48805,7 +48877,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -48826,7 +48897,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -48847,7 +48917,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -48868,7 +48937,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -48889,7 +48957,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -48910,7 +48977,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -48931,7 +48997,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -48952,7 +49017,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -48973,7 +49037,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -48994,7 +49057,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -49031,7 +49093,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -49068,7 +49129,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -49105,7 +49165,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -49142,7 +49201,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -49179,7 +49237,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -49200,7 +49257,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -49237,7 +49293,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -49274,7 +49329,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -49311,7 +49365,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -49368,7 +49421,6 @@ }, { "examine": "A big", - "slayer_task": "15", "melee_animation": "6079", "range_animation": "0", "attack_speed": "4", @@ -49802,7 +49854,6 @@ }, { "examine": "He keeps order in the city.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "511,513,512", @@ -49822,7 +49873,6 @@ }, { "examine": "He keeps order in the city.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "511,513,512", @@ -50497,7 +50547,6 @@ }, { "examine": "A goblin with big", - "slayer_task": "38", "melee_animation": "0", "range_animation": "0", "combat_audio": "469,472,471", @@ -50515,7 +50564,6 @@ }, { "examine": "A goblin with big", - "slayer_task": "38", "melee_animation": "0", "range_animation": "0", "combat_audio": "469,472,471", @@ -50566,7 +50614,6 @@ }, { "examine": "He sells tickets to Keldagrim.", - "slayer_task": "38", "melee_animation": "0", "range_animation": "0", "combat_audio": "469,472,471", @@ -51658,707 +51705,844 @@ "attack_level": "1" }, { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6006", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6007", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6008", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6009", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { - "examine": "I wish the moon wasn't out!", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6010", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6011", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6012", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6013", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "20", "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "76", + "defence_level": "70", "safespot": null, - "lifepoints": "87", - "strength_level": "76", + "lifepoints": "100", + "strength_level": "70", "id": "6014", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "76" + "attack_level": "70" }, { "examine": "Eek! A werewolf!", - "slayer_task": "91", "melee_animation": "6536", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", + "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "45", + "defence_level": "70", "safespot": null, - "lifepoints": "64", - "strength_level": "45", + "lifepoints": "100", + "strength_level": "70", "id": "6015", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "45" + "attack_level": "70" }, { "examine": "Eek! A werewolf!", - "slayer_task": "91", "melee_animation": "6536", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", + "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "45", + "defence_level": "70", "safespot": null, - "lifepoints": "64", - "strength_level": "45", + "lifepoints": "100", + "strength_level": "70", "id": "6016", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "45" + "attack_level": "70" }, { "examine": "Eek! A werewolf!", - "slayer_task": "91", "melee_animation": "6536", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", + "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "45", + "defence_level": "70", "safespot": null, - "lifepoints": "64", - "strength_level": "45", + "lifepoints": "100", + "strength_level": "70", "id": "6017", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "45" + "attack_level": "70" }, { "examine": "Eek! A werewolf!", - "slayer_task": "91", "melee_animation": "6536", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", + "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "45", + "defence_level": "70", "safespot": null, - "lifepoints": "64", - "strength_level": "45", + "lifepoints": "100", + "strength_level": "70", "id": "6018", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "45" + "attack_level": "70" }, { + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "1", + "defence_level": "70", "safespot": null, "lifepoints": "100", - "strength_level": "1", + "strength_level": "70", "id": "6019", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "1" + "attack_level": "70" }, { "examine": "Eek! A werewolf!", - "slayer_task": "91", "melee_animation": "6536", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", + "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "45", + "defence_level": "70", "safespot": null, - "lifepoints": "64", - "strength_level": "45", + "lifepoints": "100", + "strength_level": "70", "id": "6020", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "45" + "attack_level": "70" }, { "examine": "Eek! A werewolf!", - "slayer_task": "91", "melee_animation": "6536", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", + "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "45", + "defence_level": "70", "safespot": null, - "lifepoints": "64", - "strength_level": "45", + "lifepoints": "100", + "strength_level": "70", "id": "6021", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "45" + "attack_level": "70" }, { + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "1", + "defence_level": "70", "safespot": null, "lifepoints": "100", - "strength_level": "1", + "strength_level": "70", "id": "6022", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "1" + "attack_level": "70" }, { + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "1", + "defence_level": "70", "safespot": null, "lifepoints": "100", - "strength_level": "1", + "strength_level": "70", "id": "6023", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "1" + "attack_level": "70" }, { + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "1", + "defence_level": "70", "safespot": null, "lifepoints": "100", - "strength_level": "1", + "strength_level": "70", "id": "6024", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "1" + "attack_level": "70" }, { + "examine": "Eek! A werewolf!", "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", + "range_animation": "", + "combat_audio": "481,491,490", + "attack_speed": "4", + "magic_level": "1", + "respawn_delay": "20", "defence_animation": "6538", + "weakness": "", + "magic_animation": "", "death_animation": "6537", "name": "Werewolf", - "defence_level": "1", + "defence_level": "70", "safespot": null, "lifepoints": "100", - "strength_level": "1", + "strength_level": "70", "id": "6025", - "aggressive": "true", + "aggressive": "false", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", "clue_level": "1", "range_level": "1", - "attack_level": "1" + "attack_level": "70" }, { + "examine": "A traveling man. Are those fleas?", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Boris", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6026", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "A well organised shop keeper.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Imre", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6027", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "He's staring at the moon.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Yuri", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6028", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "He keeps calling me comrade.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Joseph", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6029", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "There's a set of brows that mean business.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Nikolai", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6030", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "This is one feral looking innkeeper.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Eduard", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6031", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "A tough looking villager.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Lev", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6032", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "A simple villager.", "melee_animation": "422", - "attack_speed": "5", - "respawn_delay": "60", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", "defence_animation": "425", "death_animation": "836", "name": "Georgy", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "strength_level": "1", + "strength_level": "10", "id": "6033", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "attack_level": "1" + "attack_level": "10" }, { + "examine": "Seems a bit of a drama queen.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Svetlana", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6034", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "I bet she works out.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Irina", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6035", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "She doesn't say much.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Alexis", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6036", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "She looks like she can handle herself.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Milla", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6037", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "She seems light on her feet.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Galina", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6038", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "A wise villager.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Sofiya", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6039", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "Not exactly a warrior princess.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Ksenia", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6040", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "A well off villager.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Yadviga", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6041", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "The woman.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Nikita", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6042", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "She seems sure of herself.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Vera", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6043", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "A healthy villager.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Zoja", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6044", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { + "examine": "She's looking for flowers.", + "melee_animation": "422", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "15", + "defence_animation": "425", "death_animation": "836", "name": "Liliya", - "defence_level": "1", + "defence_level": "10", "safespot": null, "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "10", "id": "6045", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "10" }, { "melee_animation": "6559", @@ -52436,7 +52620,6 @@ }, { "examine": "A vicious desert wolf.", - "slayer_task": "92", "melee_animation": "6579", "range_animation": "6579", "attack_speed": "5", @@ -52561,7 +52744,6 @@ }, { "examine": "A one-eyed man eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -52586,7 +52768,6 @@ }, { "examine": "A one-eyed woman eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -52611,7 +52792,6 @@ }, { "examine": "A one-eyed man eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -52636,7 +52816,6 @@ }, { "examine": "A one-eyed woman eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -52661,7 +52840,6 @@ }, { "examine": "Overgrown undead vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -52681,7 +52859,6 @@ }, { "examine": "Overgrown undead vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -52701,7 +52878,6 @@ }, { "examine": "Overgrown undead vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -52845,7 +53021,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -52888,7 +53063,6 @@ }, { "examine": "Probably not a chicken.", - "slayer_task": "7", "melee_animation": "6811", "range_animation": "0", "respawn_delay": "60", @@ -52899,7 +53073,7 @@ "name": "Entrana firebird", "defence_level": "1", "safespot": null, - "lifepoints": "1", + "lifepoints": "5", "strength_level": "1", "id": "6108", "range_level": "1", @@ -52907,7 +53081,6 @@ }, { "examine": "These gnomes know how to get around!", - "slayer_task": "7", "melee_animation": "6790", "range_animation": "0", "respawn_delay": "60", @@ -52956,7 +53129,6 @@ }, { "examine": "Aww, cute.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -52990,7 +53162,6 @@ }, { "examine": "A messy bird.", - "slayer_task": "7", "melee_animation": "3467", "range_animation": "3467", "combat_audio": "309,311,310", @@ -53107,6 +53278,7 @@ "attack_level": "1" }, { + "examine": "An ugly green creature.", "name": "Goblin", "defence_level": "1", "safespot": null, @@ -53702,14 +53874,13 @@ }, { "examine": "A servant of the god Zamorak. ", - "slayer_task": "40", "melee_animation": "6945", "attack_speed": "6", - "poisonous": "true", + "poisonous": "", "respawn_delay": "150", "weakness": "9", "slayer_exp": "350", - "poison_amount": "16", + "poison_amount": "", "magic_animation": "6945", "death_animation": "6946", "lifepoints": "255", @@ -53732,7 +53903,6 @@ { "agg_radius": "64", "examine": "Destroyer of 1000 planes!", - "slayer_task": "40", "melee_animation": "6945", "range_animation": "6945", "attack_speed": "5", @@ -53767,7 +53937,6 @@ }, { "examine": "Scourge of light.", - "slayer_task": "56", "start_gfx": "1208", "combat_style": "1", "melee_animation": "7033", @@ -53843,7 +54012,6 @@ }, { "examine": "From the maws of hell.", - "slayer_task": "43", "melee_animation": "6579", "range_animation": "6579", "combat_audio": "3717,3719,3718", @@ -53889,7 +54057,6 @@ }, { "examine": "He doesn't look pleased to see me.", - "slayer_task": "91", "melee_animation": "6536", "range_animation": "6536", "respawn_delay": "25", @@ -53911,7 +54078,6 @@ }, { "examine": "He doesn't look pleased to see me.", - "slayer_task": "91", "melee_animation": "6536", "range_animation": "6536", "respawn_delay": "25", @@ -53975,7 +54141,6 @@ }, { "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "1582", "range_animation": "1582", "combat_audio": "696,698,697", @@ -54018,7 +54183,6 @@ }, { "examine": "Those horns look pretty sharp...", - "slayer_task": "39", "melee_animation": "4300", "range_animation": "4300", "respawn_delay": "25", @@ -54040,7 +54204,6 @@ }, { "examine": "Warrior of Zamorak.", - "slayer_task": "79", "melee_animation": "390", "range_animation": "390", "attack_speed": "5", @@ -54063,7 +54226,6 @@ }, { "examine": "A ranger spirit dedicated to Zamorak.", - "slayer_task": "78", "start_gfx": "18", "combat_style": "1", "start_height": "96", @@ -54089,7 +54251,6 @@ }, { "examine": "A deadly servant of Zamorak.", - "slayer_task": "77", "combat_style": "2", "melee_animation": "811", "attack_speed": "5", @@ -54256,7 +54417,6 @@ }, { "examine": "A servant of Armadyl.", - "slayer_task": "79", "combat_style": "1", "melee_animation": "6954", "range_animation": "6954", @@ -54279,7 +54439,6 @@ }, { "examine": "Armadyl's favourite servant.", - "slayer_task": "78", "combat_style": "1", "melee_animation": "6953", "range_animation": "6953", @@ -54304,29 +54463,28 @@ }, { "examine": "A mage who serves Armadyl above all else - even death.", - "slayer_task": "77", "combat_style": "2", "melee_animation": "6952", + "range_animation": "6952", "attack_speed": "5", + "magic_level": "150", "respawn_delay": "25", + "defence_animation": "6955", "weakness": "4", "magic_animation": "6952", "death_animation": "6956", - "lifepoints": "75", - "id": "6231", - "aggressive": "true", - "bonuses": "0,0,0,0,0,9,12,5,45,28,0,0,0,0,0", - "prj_height": "92", - "range_animation": "6952", - "magic_level": "150", - "defence_animation": "6955", "name": "Spiritual mage", "defence_level": "111", "safespot": null, + "lifepoints": "75", "strength_level": "1", + "id": "6231", + "aggressive": "true", + "bonuses": "0,0,0,0,0,9,12,5,45,28,0,0,0,0,0", "clue_level": "2", "range_level": "1", "projectile": "1199", + "prj_height": "92", "attack_level": "1" }, { @@ -54821,7 +54979,7 @@ "combat_style": "2", "melee_animation": "811", "range_animation": "811", - "poisonous": "true", + "poisonous": "", "magic_level": "60", "respawn_delay": "25", "end_gfx": "76", @@ -54842,7 +55000,6 @@ }, { "examine": "Warrior of Saradomin.", - "slayer_task": "79", "melee_animation": "390", "range_animation": "390", "attack_speed": "5", @@ -54865,7 +55022,6 @@ }, { "examine": "A ranger spirit dedicated to Saradomin.", - "slayer_task": "78", "start_gfx": "18", "combat_style": "1", "start_height": "96", @@ -54891,7 +55047,6 @@ }, { "examine": "Saradomin's holy mage.", - "slayer_task": "77", "combat_style": "2", "melee_animation": "811", "attack_speed": "5", @@ -54986,7 +55141,6 @@ { "agg_radius": "64", "examine": "A battle-honed goblin. ", - "slayer_task": "38", "melee_animation": "6154", "range_animation": "6154", "attack_speed": "5", @@ -55019,28 +55173,27 @@ "attack_level": "1" }, { + "agg_radius": "64", "examine": "A battle-honed goblin. ", - "slayer_task": "38", "start_gfx": "1202", "combat_style": "2", "melee_animation": "6154", + "range_animation": "6154", "attack_speed": "5", + "magic_level": "150", "respawn_delay": "50", + "defence_animation": "6155", "weakness": "3", "magic_animation": "6154", "death_animation": "6156", - "lifepoints": "127", - "id": "6263", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,6,0,0,0", - "agg_radius": "64", - "range_animation": "6154", - "magic_level": "150", - "defence_animation": "6155", "name": "Sergeant Steelwill", "defence_level": "150", "safespot": null, + "lifepoints": "127", "strength_level": "50", + "id": "6263", + "aggressive": "true", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,6,0,0,0", "range_level": "1", "projectile": "1203", "attack_level": "80" @@ -55059,7 +55212,6 @@ { "agg_radius": "64", "examine": "A battle-honed goblin. ", - "slayer_task": "38", "combat_style": "1", "melee_animation": "6154", "range_animation": "6154", @@ -55138,7 +55290,6 @@ }, { "examine": "A one-eyed man-eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -55163,7 +55314,6 @@ }, { "examine": "A one-eyed man-eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -55265,7 +55415,6 @@ }, { "examine": "An ugly, smelly creature.", - "slayer_task": "45", "melee_animation": "164", "range_animation": "164", "combat_audio": "469,472,471", @@ -55287,7 +55436,6 @@ }, { "examine": "The spirit of a ranger, serving bandos beyond death.", - "slayer_task": "78", "start_gfx": "95", "combat_style": "1", "melee_animation": "4320", @@ -55312,7 +55460,6 @@ }, { "examine": "A true servant of Bandos.", - "slayer_task": "79", "melee_animation": "4320", "range_animation": "4320", "respawn_delay": "25", @@ -55334,7 +55481,6 @@ }, { "examine": "One of Bandos' chosen.", - "slayer_task": "77", "combat_style": "2", "melee_animation": "4320", "range_animation": "4320", @@ -55465,7 +55611,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55488,7 +55633,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55511,7 +55655,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55534,7 +55677,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55557,7 +55699,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55597,7 +55738,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55620,7 +55760,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55660,7 +55799,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55683,7 +55821,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -55706,7 +55843,6 @@ }, { "examine": "You don't think you can harm this creature.", - "slayer_task": "89", "melee_animation": "7093", "range_animation": "0", "attack_speed": "5", @@ -55742,7 +55878,6 @@ }, { "examine": "Annoyingly easy to squish.", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "2", @@ -56518,7 +56653,6 @@ }, { "examine": "I wouldn't want to be footing that bill.", - "slayer_task": "7", "melee_animation": "6775", "range_animation": "0", "attack_speed": "5", @@ -56595,7 +56729,6 @@ }, { "examine": "Polynomial - a parrot that goes without breakfast", - "slayer_task": "7", "melee_animation": "6775", "range_animation": "0", "attack_speed": "5", @@ -57478,7 +57611,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "respawn_delay": "60", @@ -57498,7 +57630,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "respawn_delay": "60", @@ -57518,7 +57649,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "magic_level": "22", @@ -57538,7 +57668,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "magic_level": "26", @@ -57558,7 +57687,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "magic_level": "26", @@ -58274,11 +58402,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -58430,11 +58558,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -58589,11 +58717,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -58650,11 +58778,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -58730,11 +58858,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -59053,11 +59181,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -59133,11 +59261,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -59335,6 +59463,7 @@ "examine": "A ghost of an ork slain during the god wars.", "melee_animation": "7411", "range_animation": "7518", + "poisonous": "", "magic_level": "70", "defence_animation": "7413", "magic_animation": "7505", @@ -59354,11 +59483,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -59513,11 +59642,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -59528,7 +59657,8 @@ "id": "6673", "clue_level": "2", "range_level": "80", - "attack_level": "80" + "attack_level": "80", + "prj_height": "" }, { "spawn_animation": "7426", @@ -59574,11 +59704,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -59897,11 +60027,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -60334,11 +60464,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -60575,11 +60705,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -60654,11 +60784,11 @@ "examine": "A ghost of a knight slain during the god wars.", "melee_animation": "7441", "range_animation": "7522", - "poisonous": "true", + "poisonous": "", "magic_level": "80", "defence_animation": "7443", "slayer_exp": "143", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "7508", "death_animation": "7442", "name": "Revenant knight", @@ -60786,7 +60916,6 @@ }, { "examine": "Cut-down and dried.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -60806,7 +60935,6 @@ }, { "examine": "Needs moisturising.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -60826,7 +60954,6 @@ }, { "examine": "I bet it's parched.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -60862,7 +60989,6 @@ }, { "examine": "A huge beast of a beetle.", - "slayer_task": "70", "melee_animation": "7596", "range_animation": "0", "defence_animation": "0", @@ -60898,7 +61024,6 @@ }, { "examine": "Bred to shoot you down.", - "slayer_task": "70", "start_gfx": "18", "combat_style": "1", "melee_animation": "7607", @@ -60921,7 +61046,6 @@ }, { "examine": "It might be angry...it's hard to tell.", - "slayer_task": "70", "melee_animation": "7612", "range_animation": "0", "respawn_delay": "60", @@ -60960,7 +61084,6 @@ }, { "examine": "Bouncy", - "slayer_task": "70", "melee_animation": "7584", "range_animation": "0", "respawn_delay": "60", @@ -60980,7 +61103,6 @@ }, { "examine": "He'll have someone's eye out with that.", - "slayer_task": "70", "start_gfx": "18", "combat_style": "1", "melee_animation": "5451", @@ -61021,7 +61143,6 @@ }, { "examine": "Part scarab, part man.", - "slayer_task": "70", "combat_style": "2", "melee_animation": "7615", "range_animation": "0", @@ -61124,7 +61245,6 @@ }, { "examine": "He exudes something like holiness.", - "slayer_task": "70", "melee_animation": "7612", "range_animation": "0", "magic_level": "66", @@ -61143,7 +61263,6 @@ }, { "examine": "He exudes something like holiness.", - "slayer_task": "70", "melee_animation": "7607", "range_animation": "7607", "magic_level": "66", @@ -61163,17 +61282,21 @@ { "examine": "A bird. Literally terrifying.", "melee_animation": "1010", + "range_animation": "0", + "magic_level": "50", "respawn_delay": "0", - "defence_animation": "1011", - "death_animation": "1012", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "1013", "name": "Spirit terrorbird", - "defence_level": "1", + "defence_level": "50", "safespot": null, "lifepoints": "74", - "strength_level": "1", + "strength_level": "50", "id": "6794", - "range_level": "1", - "attack_level": "1" + "range_level": "50", + "attack_level": "50" }, { "examine": "A bird. Literally terrifying.", @@ -61236,18 +61359,23 @@ "attack_level": "18" }, { + "examine": "Wax on", + "melee_animation": "8069", + "range_animation": "0", + "magic_level": "60", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "8065", "name": "Praying mantis", - "defence_level": "1", + "defence_level": "60", "safespot": null, - "lifepoints": "10", - "melee_animation": "8064", - "strength_level": "1", + "lifepoints": "107", + "strength_level": "60", "id": "6798", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "8066" + "range_level": "60", + "attack_level": "60" }, { "examine": "Wax on", @@ -61269,18 +61397,23 @@ "attack_level": "60" }, { + "examine": "He ent such a bad guy.", + "melee_animation": "7853", + "range_animation": "0", + "magic_level": "60", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7854", "name": "Giant ent", - "defence_level": "1", + "defence_level": "60", "safespot": null, - "lifepoints": "10", - "melee_animation": "7853", - "strength_level": "1", + "lifepoints": "111", + "strength_level": "60", "id": "6800", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7852" + "range_level": "60", + "attack_level": "60" }, { "examine": "He ent such a bad guy.", @@ -61302,18 +61435,23 @@ "attack_level": "60" }, { + "examine": "Serpentine.", + "melee_animation": "8152", + "range_animation": "0", + "magic_level": "55", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "8153", "name": "Spirit cobra", - "defence_level": "1", + "defence_level": "55", "safespot": null, - "lifepoints": "10", - "melee_animation": "8152", - "strength_level": "1", + "lifepoints": "90", + "strength_level": "55", "id": "6802", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "8154" + "range_level": "55", + "attack_level": "55" }, { "examine": "Serpentine.", @@ -61335,18 +61473,23 @@ "attack_level": "55" }, { + "examine": "Face the thing that should not be!", + "melee_animation": "7786", + "range_animation": "0", + "magic_level": "65", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7780", "name": "Spirit dagannoth", - "defence_level": "1", + "defence_level": "65", "safespot": null, - "lifepoints": "10", - "melee_animation": "7786", - "strength_level": "1", + "lifepoints": "115", + "strength_level": "65", "id": "6804", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7785" + "range_level": "65", + "attack_level": "65" }, { "examine": "Face the thing that should not be!", @@ -61417,19 +61560,24 @@ "attack_level": "19" }, { + "examine": "Kneel before squid!", "combat_style": "2", - "melee_animation": "7970", + "melee_animation": "7963", + "range_animation": "0", + "magic_level": "50", "respawn_delay": "0", - "defence_animation": "7967", - "death_animation": "7979", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "7964", "name": "Karamthulhu overlord", - "defence_level": "1", + "defence_level": "50", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "82", + "strength_level": "50", "id": "6809", - "range_level": "1", - "attack_level": "1" + "range_level": "50", + "attack_level": "50" }, { "examine": "Kneel before squid!", @@ -61510,23 +61658,28 @@ "defence_animation": "7742" }, { - "combat_style": "1", + "examine": "Definitely not teenaged", + "combat_style": "0", "melee_animation": "8286", + "range_animation": "0", + "magic_level": "55", "respawn_delay": "0", - "defence_animation": "8287", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "8285", "name": "War tortoise", - "defence_level": "1", + "defence_level": "55", "safespot": null, - "lifepoints": "348", - "strength_level": "1", + "lifepoints": "95", + "strength_level": "55", "id": "6815", - "range_level": "1", - "attack_level": "1" + "range_level": "55", + "attack_level": "55" }, { "examine": "Definitely not teenaged", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8286", "range_animation": "0", "magic_level": "55", @@ -61556,19 +61709,24 @@ "attack_level": "1" }, { + "examine": "It's an extra-planar little blood hoover.", "combat_style": "2", - "melee_animation": "7675", + "melee_animation": "8910", + "range_animation": "0", + "magic_level": "50", "respawn_delay": "0", - "defence_animation": "7670", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7671", "name": "Abyssal parasite", - "defence_level": "1", + "defence_level": "50", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "77", + "strength_level": "50", "id": "6818", - "range_level": "1", - "attack_level": "1" + "range_level": "50", + "attack_level": "50" }, { "examine": "It's an extra-planar little blood hoover.", @@ -61591,18 +61749,23 @@ "attack_level": "50" }, { + "examine": "Lurking like only a lurker can.", + "melee_animation": "7680", + "range_animation": "0", + "magic_level": "55", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7684", "name": "Abyssal lurker", - "defence_level": "1", + "defence_level": "55", "safespot": null, - "lifepoints": "10", - "melee_animation": "7680", - "strength_level": "1", + "lifepoints": "88", + "strength_level": "55", "id": "6820", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7681" + "range_level": "55", + "attack_level": "55" }, { "examine": "Lurking like only a lurker can.", @@ -61729,19 +61892,24 @@ "attack_level": "17" }, { - "examine": "It's mean and green.", + "examine": "It's mean and green!", "melee_animation": "8208", + "range_animation": "0", + "attack_speed": "5", + "magic_level": "55", "respawn_delay": "0", - "defence_animation": "8205", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "8209", "name": "Stranger plant", - "defence_level": "1", + "defence_level": "55", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "91", + "strength_level": "55", "id": "6827", - "range_level": "1", - "attack_level": "1" + "range_level": "55", + "attack_level": "55" }, { "examine": "It's mean and green!", @@ -61803,18 +61971,24 @@ "attack_level": "16" }, { + "examine": "Surprisingly slime-free.", + "combat_style": "1", + "melee_animation": "7795", + "range_animation": "0", + "magic_level": "18", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7797", "name": "Desert wyrm", - "defence_level": "1", + "defence_level": "18", "safespot": null, - "lifepoints": "47", - "melee_animation": "7795", - "strength_level": "1", + "lifepoints": "25", + "strength_level": "18", "id": "6831", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7796" + "range_level": "18", + "attack_level": "18" }, { "examine": "Surprisingly slime-free.", @@ -61838,6 +62012,26 @@ }, { "examine": "If you think he's evil", + "combat_style": "1", + "melee_animation": "8248", + "range_animation": "0", + "magic_level": "42", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "8250", + "name": "Evil turnip", + "defence_level": "42", + "safespot": null, + "lifepoints": "60", + "strength_level": "42", + "id": "6833", + "range_level": "42", + "attack_level": "42" + }, + { + "examine": "If you think he's evil", + "combat_style": "1", "melee_animation": "8248", "range_animation": "0", "magic_level": "42", @@ -61855,18 +62049,23 @@ "attack_level": "42" }, { + "examine": "It vants to suck my blood!", + "melee_animation": "8275", + "range_animation": "0", + "magic_level": "31", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "8276", "name": "Vampire bat", - "defence_level": "1", + "defence_level": "31", "safespot": null, - "lifepoints": "20", - "melee_animation": "8275", - "strength_level": "1", + "lifepoints": "44", + "strength_level": "31", "id": "6835", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "8274" + "range_level": "31", + "attack_level": "31" }, { "examine": "It vants to suck my blood!", @@ -61888,19 +62087,24 @@ "attack_level": "31" }, { + "examine": "Salt and vinegaroon.", "melee_animation": "6254", + "range_animation": "0", "combat_audio": "3611,3612,3610", + "magic_level": "19", "respawn_delay": "0", - "defence_animation": "6255", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "6256", "name": "Spirit scorpion", - "defence_level": "1", + "defence_level": "19", "safespot": null, - "lifepoints": "67", - "strength_level": "1", + "lifepoints": "27", + "strength_level": "19", "id": "6837", - "range_level": "1", - "attack_level": "1" + "range_level": "19", + "attack_level": "19" }, { "examine": "Salt and vinegaroon.", @@ -61957,6 +62161,7 @@ "lifepoints": "101", "strength_level": "60", "id": "6840", + "bonuses": "90,50,50,60,90,80,50,30,40,100,0,0,0,0,0", "range_level": "60", "attack_level": "60" }, @@ -62002,18 +62207,23 @@ "attack_level": "17" }, { + "examine": "It's like a little suction pump with eyes.", + "melee_animation": "7657", + "range_animation": "0", + "magic_level": "49", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7656", "name": "Bloated leech", - "defence_level": "1", + "defence_level": "49", "safespot": null, - "lifepoints": "10", - "melee_animation": "7657", - "strength_level": "1", + "lifepoints": "70", + "strength_level": "49", "id": "6843", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7655" + "range_level": "49", + "attack_level": "49" }, { "examine": "It's like a little suction pump with eyes.", @@ -62035,18 +62245,23 @@ "attack_level": "49" }, { + "examine": "Violent little so-and-so.", + "melee_animation": "7928", + "range_animation": "0", + "magic_level": "32", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7925", "name": "Honey badger", - "defence_level": "1", + "defence_level": "32", "safespot": null, - "lifepoints": "10", - "melee_animation": "7928", - "strength_level": "1", + "lifepoints": "45", + "strength_level": "32", "id": "6845", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7927" + "range_level": "32", + "attack_level": "32" }, { "examine": "Violent little so-and-so.", @@ -62132,6 +62347,7 @@ "lifepoints": "105", "strength_level": "60", "id": "6850", + "bonuses": "93,90,65,90,30,40,50,50,60,40,0,0,0,0,0", "range_level": "60", "attack_level": "60" }, @@ -62161,7 +62377,7 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "magic_level": "1", "respawn_delay": "0", @@ -62179,7 +62395,7 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "range_animation": "0", "magic_level": "1", @@ -62200,7 +62416,7 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "magic_level": "25", "respawn_delay": "0", @@ -62219,7 +62435,7 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "range_animation": "0", "magic_level": "25", @@ -62241,7 +62457,7 @@ }, { "examine": "He's just a big bully!", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "magic_level": "28", "respawn_delay": "0", @@ -62259,7 +62475,7 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "range_animation": "0", "magic_level": "28", @@ -62280,7 +62496,7 @@ }, { "examine": "He's just a big bully", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "magic_level": "30", "respawn_delay": "0", @@ -62299,7 +62515,7 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "range_animation": "0", "magic_level": "30", @@ -62320,7 +62536,7 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "attack_speed": "", "magic_level": "35", @@ -62340,7 +62556,7 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "range_animation": "0", "magic_level": "35", @@ -62395,18 +62611,23 @@ "attack_level": "40" }, { + "examine": "Well", + "melee_animation": "7816", + "range_animation": "0", + "magic_level": "55", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7818", "name": "Smoke devil", - "defence_level": "1", + "defence_level": "55", "safespot": null, - "lifepoints": "10", - "melee_animation": "7816", - "strength_level": "1", + "lifepoints": "87", + "strength_level": "55", "id": "6865", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7817" + "range_level": "55", + "attack_level": "55" }, { "examine": "Well", @@ -62428,18 +62649,23 @@ "attack_level": "55" }, { + "examine": "Putting all three of its best feet forward.", + "melee_animation": "7896", + "range_animation": "0", + "magic_level": "40", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7897", "name": "Bull ant", - "defence_level": "1", + "defence_level": "40", "safespot": null, - "lifepoints": "10", - "melee_animation": "7896", - "strength_level": "1", + "lifepoints": "57", + "strength_level": "40", "id": "6867", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7900" + "range_level": "40", + "attack_level": "40" }, { "examine": "Putting all three of its best feet forward.", @@ -62503,19 +62729,24 @@ "attack_level": "1" }, { - "melee_animation": "853", + "examine": "I suppose it could smell worse", + "melee_animation": "7769", + "range_animation": "0", "attack_speed": "5", + "magic_level": "28", "respawn_delay": "0", - "defence_animation": "851", - "death_animation": "852", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "7770", "name": "Compost mound", - "defence_level": "1", + "defence_level": "28", "safespot": null, - "lifepoints": "96", - "strength_level": "1", + "lifepoints": "40", + "strength_level": "28", "id": "6871", - "range_level": "1", - "attack_level": "1" + "range_level": "28", + "attack_level": "28" }, { "examine": "I suppose it could smell worse", @@ -62579,21 +62810,26 @@ "attack_level": "112" }, { + "examine": "If looks could kill... Wait", "start_gfx": "1467", "combat_style": "2", "melee_animation": "7762", + "range_animation": "0", + "magic_level": "43", "respawn_delay": "0", - "defence_animation": "7761", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7763", "name": "Spirit cockatrice", - "defence_level": "1", + "defence_level": "43", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "61", + "strength_level": "43", "id": "6875", - "range_level": "1", + "range_level": "43", "projectile": "1468", - "attack_level": "1" + "attack_level": "43" }, { "examine": "If looks could kill... Wait", @@ -62618,21 +62854,26 @@ "attack_level": "43" }, { + "examine": "If looks could kill... Wait", "start_gfx": "1467", "combat_style": "2", "melee_animation": "7762", + "range_animation": "0", + "magic_level": "43", "respawn_delay": "0", - "defence_animation": "7761", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7763", "name": "Spirit guthatrice", - "defence_level": "1", + "defence_level": "43", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "61", + "strength_level": "43", "id": "6877", - "range_level": "1", + "range_level": "43", "projectile": "1468", - "attack_level": "1" + "attack_level": "43" }, { "examine": "If looks could kill... Wait", @@ -62657,22 +62898,27 @@ "attack_level": "43" }, { + "examine": "If looks could kill... Wait", "start_gfx": "1467", "combat_style": "2", "melee_animation": "7762", + "range_animation": "0", "combat_audio": "703,705,704", + "magic_level": "43", "respawn_delay": "0", - "defence_animation": "7761", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7763", "name": "Spirit saratrice", - "defence_level": "1", + "defence_level": "43", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "61", + "strength_level": "43", "id": "6879", - "range_level": "1", + "range_level": "43", "projectile": "1468", - "attack_level": "1" + "attack_level": "43" }, { "examine": "If looks could kill... Wait", @@ -62698,21 +62944,26 @@ "attack_level": "43" }, { + "examine": "If looks could kill... Wait", "start_gfx": "1467", "combat_style": "2", "melee_animation": "7762", + "range_animation": "0", + "magic_level": "43", "respawn_delay": "0", - "defence_animation": "7761", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7763", "name": "Spirit zamatrice", - "defence_level": "1", + "defence_level": "43", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "61", + "strength_level": "43", "id": "6881", - "range_level": "1", + "range_level": "43", "projectile": "1468", - "attack_level": "1" + "attack_level": "43" }, { "examine": "If looks could kill... Wait", @@ -62737,21 +62988,26 @@ "attack_level": "43" }, { + "examine": "If looks could kill... Wait", "start_gfx": "1467", "combat_style": "2", "melee_animation": "7762", + "range_animation": "0", + "magic_level": "43", "respawn_delay": "0", - "defence_animation": "7761", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7763", "name": "Spirit pengatrice", - "defence_level": "1", + "defence_level": "43", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "61", + "strength_level": "43", "id": "6883", - "range_level": "1", + "range_level": "43", "projectile": "1468", - "attack_level": "1" + "attack_level": "43" }, { "examine": "If looks could kill... Wait", @@ -62776,21 +63032,26 @@ "attack_level": "43" }, { + "examine": "If looks could kill... Wait", "start_gfx": "1467", "combat_style": "2", "melee_animation": "7762", + "range_animation": "0", + "magic_level": "43", "respawn_delay": "0", - "defence_animation": "7761", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7763", "name": "Spirit coraxatrice", - "defence_level": "1", + "defence_level": "43", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "61", + "strength_level": "43", "id": "6885", - "range_level": "1", + "range_level": "43", "projectile": "1468", - "attack_level": "1" + "attack_level": "43" }, { "examine": "If looks could kill... Wait", @@ -62815,21 +63076,26 @@ "attack_level": "43" }, { + "examine": "If looks could kill... Wait", "start_gfx": "1467", "combat_style": "2", "melee_animation": "7762", + "range_animation": "0", + "magic_level": "43", "respawn_delay": "0", - "defence_animation": "7761", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7763", "name": "Spirit vulatrice", - "defence_level": "1", + "defence_level": "43", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "61", + "strength_level": "43", "id": "6887", - "range_level": "1", + "range_level": "43", "projectile": "1468", - "attack_level": "1" + "attack_level": "43" }, { "examine": "If looks could kill... Wait", @@ -62854,20 +63120,23 @@ "attack_level": "43" }, { - "examine": "The only creature with a mouth big enough to hold a cannonball.", + "examine": "The only creature with a mouth big enough to fit a cannon ball into.", "melee_animation": "7260", + "range_animation": "0", "magic_level": "55", "respawn_delay": "0", - "defence_animation": "7257", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7256", "name": "Barker toad", - "defence_level": "1", + "defence_level": "55", "safespot": null, - "lifepoints": "10", + "lifepoints": "94", "strength_level": "55", "id": "6889", "range_level": "55", - "attack_level": "1" + "attack_level": "55" }, { "examine": "The only creature with a mouth big enough to fit a cannon ball into.", @@ -63131,6 +63400,7 @@ "combat_style": "2", "melee_animation": "8304", "attack_speed": "5", + "magic_level": "28", "respawn_delay": "0", "defence_animation": "8306", "death_animation": "8305", @@ -63479,18 +63749,23 @@ "attack_level": "28" }, { + "examine": "On Gielinor", + "melee_animation": "8569", + "range_animation": "0", + "magic_level": "50", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "8570", "name": "Spirit jelly", - "defence_level": "1", + "defence_level": "50", "safespot": null, - "lifepoints": "10", - "melee_animation": "8569", - "strength_level": "1", + "lifepoints": "78", + "strength_level": "50", "id": "6992", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "8571" + "range_level": "50", + "attack_level": "50" }, { "examine": "On Gielinor", @@ -63512,19 +63787,24 @@ "attack_level": "50" }, { + "examine": "Hail to the Queen", "combat_style": "1", - "melee_animation": "8519", + "melee_animation": "6223", + "range_animation": "0", + "magic_level": "25", "respawn_delay": "0", - "defence_animation": "8518", - "death_animation": "8517", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "6228", "name": "Spirit kalphite", - "defence_level": "1", + "defence_level": "25", "safespot": null, - "lifepoints": "77", - "strength_level": "1", + "lifepoints": "35", + "strength_level": "25", "id": "6994", - "range_level": "1", - "attack_level": "1" + "range_level": "25", + "attack_level": "25" }, { "examine": "Hail to the Queen", @@ -63568,9 +63848,9 @@ "melee_animation": "8591", "range_animation": "8594", "combat_audio": "408,410,409", - "poisonous": "true", + "poisonous": "", "defence_animation": "8592", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "8594", "death_animation": "8593", "name": "Revenant dragon", @@ -63589,9 +63869,9 @@ "melee_animation": "8591", "range_animation": "8594", "combat_audio": "408,410,409", - "poisonous": "true", + "poisonous": "", "defence_animation": "8592", - "poison_amount": "8", + "poison_amount": "", "magic_animation": "8594", "death_animation": "8593", "name": "Revenant dragon", @@ -63606,7 +63886,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -63630,7 +63909,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -63654,7 +63932,6 @@ }, { "examine": "Must be the pack leader.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "respawn_delay": "60", @@ -63788,7 +64065,6 @@ }, { "examine": "An ogre snack with wings.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -63855,7 +64131,6 @@ }, { "examine": "A fearsome adversary with no sense of humour.", - "slayer_task": "64", "melee_animation": "8636", "range_animation": "0", "respawn_delay": "60", @@ -63875,7 +64150,6 @@ }, { "examine": "Irascible, belligerent and stupefyingly impolite.", - "slayer_task": "64", "melee_animation": "8636", "range_animation": "0", "respawn_delay": "60", @@ -63895,7 +64169,6 @@ }, { "examine": "Irascible, beliggerent and stupefyingly impolite.", - "slayer_task": "64", "melee_animation": "8637", "range_animation": "0", "respawn_delay": "60", @@ -63915,7 +64188,6 @@ }, { "examine": "A large and contentious lady ogre.", - "slayer_task": "64", "melee_animation": "8636", "range_animation": "0", "attack_speed": "5", @@ -63936,7 +64208,6 @@ }, { "examine": "A large and contentious lady ogre.", - "slayer_task": "64", "melee_animation": "8637", "range_animation": "0", "attack_speed": "5", @@ -64343,6 +64614,7 @@ "death_animation": "8756", "name": "Bork", "defence_level": "80", + "poison_immune": "true", "lifepoints": "300", "strength_level": "90", "id": "7133", @@ -64460,6 +64732,7 @@ "name": "Bork", "defence_level": "1", "safespot": null, + "poison_immune": "true", "lifepoints": "300", "strength_level": "1", "id": "7134", @@ -65298,7 +65571,7 @@ "attack_level": "1" }, { - "combat_style": "1", + "combat_style": "0", "melee_animation": "8222", "magic_level": "70", "respawn_delay": "0", @@ -65316,7 +65589,7 @@ }, { "examine": "Do you hear duelling banjos?", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8222", "range_animation": "0", "magic_level": "65", @@ -65376,6 +65649,24 @@ "range_level": "25", "attack_level": "28" }, + { + "examine": "It spins.", + "melee_animation": "8172", + "range_animation": "0", + "magic_level": "34", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "8176", + "name": "Void spinner", + "defence_level": "34", + "safespot": null, + "lifepoints": "48", + "strength_level": "34", + "id": "7333", + "range_level": "34", + "attack_level": "34" + }, { "examine": "It spins.", "melee_animation": "8172", @@ -65395,21 +65686,28 @@ "attack_level": "34" }, { + "examine": "This one will burn right through the net!", + "combat_style": "1", + "melee_animation": "7863", + "range_animation": "0", + "magic_level": "60", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7864", "name": "Forge regent", - "defence_level": "1", + "defence_level": "60", "safespot": null, - "lifepoints": "10", - "melee_animation": "7863", - "strength_level": "1", + "lifepoints": "108", + "strength_level": "60", "id": "7335", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7865" + "range_level": "60", + "attack_level": "60" }, { "examine": "This one will burn right through the net!", + "combat_style": "1", "melee_animation": "7863", "range_animation": "0", "magic_level": "60", @@ -65428,18 +65726,23 @@ "attack_level": "60" }, { + "examine": "Fast cat is fast.", + "melee_animation": "5228", + "range_animation": "0", + "magic_level": "50", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "5230", "name": "Spirit larupia", - "defence_level": "1", + "defence_level": "50", "safespot": null, - "lifepoints": "10", - "melee_animation": "5228", - "strength_level": "1", + "lifepoints": "81", + "strength_level": "50", "id": "7337", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "5227" + "range_level": "50", + "attack_level": "50" }, { "examine": "Fast cat is fast.", @@ -65462,6 +65765,7 @@ }, { "examine": "It'll kill your enemies, and makes a great cup of tea.", + "combat_style": "1", "melee_animation": "7879", "range_animation": "422", "magic_level": "70", @@ -65483,6 +65787,7 @@ }, { "examine": "It'll kill your enemies, and makes a great cup of tea.", + "combat_style": "1", "melee_animation": "7879", "range_animation": "422", "magic_level": "70", @@ -65504,24 +65809,29 @@ "attack_level": "70" }, { - "combat_style": "1", + "examine": "Made of lava.", + "combat_style": "0", "melee_animation": "7980", + "range_animation": "0", + "magic_level": "65", "respawn_delay": "0", - "defence_animation": "7981", - "death_animation": "7692", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "7979", "name": "Lava titan", - "defence_level": "1", + "defence_level": "65", "safespot": null, - "lifepoints": "528", - "strength_level": "1", + "lifepoints": "115", + "strength_level": "65", "id": "7341", "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" + "range_level": "65", + "attack_level": "65" }, { "examine": "Made of lava.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "7980", "range_animation": "0", "magic_level": "65", @@ -65542,6 +65852,7 @@ }, { "examine": "The King of the Titans!", + "combat_style": "1", "melee_animation": "8183", "range_animation": "8183", "magic_level": "70", @@ -65564,6 +65875,7 @@ }, { "examine": "The King of the Titans!", + "combat_style": "1", "melee_animation": "8183", "range_animation": "8183", "magic_level": "70", @@ -65622,18 +65934,23 @@ "attack_level": "60" }, { + "examine": "If a normal black cat is bad luck", + "melee_animation": "5989", + "range_animation": "0", + "magic_level": "60", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "5990", "name": "Talon beast", - "defence_level": "1", + "defence_level": "60", "safespot": null, - "lifepoints": "10", - "melee_animation": "5989", - "strength_level": "1", + "lifepoints": "110", + "strength_level": "60", "id": "7347", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "5988" + "range_level": "60", + "attack_level": "60" }, { "examine": "If a normal black cat is bad luck", @@ -65655,18 +65972,23 @@ "attack_level": "60" }, { - "death_animation": "7979", - "name": "Abyssal titan", - "defence_level": "1", - "safespot": null, - "lifepoints": "667", + "examine": "Big", "melee_animation": "7693", - "strength_level": "1", - "id": "7349", - "range_level": "1", + "range_animation": "0", + "magic_level": "70", "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7691" + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "7692", + "name": "Abyssal titan", + "defence_level": "70", + "safespot": null, + "lifepoints": "125", + "strength_level": "70", + "id": "7349", + "range_level": "70", + "attack_level": "70" }, { "examine": "Big", @@ -65689,6 +66011,26 @@ }, { "examine": "It torches.", + "combat_style": "2", + "melee_animation": "8235", + "range_animation": "0", + "magic_level": "34", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "8236", + "name": "Void torcher", + "defence_level": "34", + "safespot": null, + "lifepoints": "48", + "strength_level": "34", + "id": "7351", + "range_level": "34", + "attack_level": "34" + }, + { + "examine": "It torches.", + "combat_style": "2", "melee_animation": "8235", "range_animation": "0", "magic_level": "34", @@ -65706,21 +66048,28 @@ "attack_level": "34" }, { + "examine": "Looks a little...volatile.", + "combat_style": "1", + "melee_animation": "7755", + "range_animation": "0", + "magic_level": "29", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7758", "name": "Giant chinchompa", - "defence_level": "1", + "defence_level": "29", "safespot": null, - "lifepoints": "1", - "melee_animation": "7755", - "strength_level": "1", + "lifepoints": "41", + "strength_level": "29", "id": "7353", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7753" + "range_level": "29", + "attack_level": "29" }, { "examine": "Looks a little...volatile.", + "combat_style": "1", "melee_animation": "7755", "range_animation": "0", "magic_level": "29", @@ -65740,6 +66089,7 @@ }, { "examine": "Scorching!", + "combat_style": "2", "melee_animation": "7834", "range_animation": "7834", "attack_speed": "5", @@ -65763,6 +66113,7 @@ }, { "examine": "Scorching!", + "combat_style": "2", "melee_animation": "7834", "range_animation": "7834", "attack_speed": "5", @@ -65877,24 +66228,29 @@ "attack_level": "40" }, { - "combat_style": "1", + "examine": "This bat burned down the belfry.", + "combat_style": "2", "melee_animation": "8257", + "range_animation": "0", "attack_speed": "3", + "magic_level": "22", "respawn_delay": "0", - "defence_animation": "8256", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "8258", "name": "Spirit Tz-Kih", - "defence_level": "1", + "defence_level": "22", "safespot": null, - "lifepoints": "62", - "strength_level": "1", + "lifepoints": "31", + "strength_level": "22", "id": "7361", - "range_level": "1", - "attack_level": "1" + "range_level": "22", + "attack_level": "22" }, { "examine": "This bat burned down the belfry.", - "combat_style": "1", + "combat_style": "2", "melee_animation": "8257", "range_animation": "0", "attack_speed": "3", @@ -65914,19 +66270,24 @@ "attack_level": "22" }, { + "examine": "Those spikes are pretty big!", "start_gfx": "1367", - "melee_animation": "5228", + "melee_animation": "5229", + "range_animation": "0", + "magic_level": "50", "respawn_delay": "0", - "defence_animation": "5227", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "5230", "name": "Spirit graahk", - "defence_level": "1", + "defence_level": "50", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "81", + "strength_level": "50", "id": "7363", - "range_level": "1", - "attack_level": "1" + "range_level": "50", + "attack_level": "50" }, { "examine": "Those spikes are pretty big!", @@ -65949,19 +66310,24 @@ "attack_level": "50" }, { + "examine": "Those teeth are pretty big!", "start_gfx": "1365", "melee_animation": "5228", + "range_animation": "0", + "magic_level": "50", "respawn_delay": "0", - "defence_animation": "5227", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "5230", "name": "Spirit kyatt", - "defence_level": "1", + "defence_level": "50", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "81", + "strength_level": "50", "id": "7365", - "range_level": "1", - "attack_level": "1" + "range_level": "50", + "attack_level": "50" }, { "examine": "Those teeth are pretty big!", @@ -65983,6 +66349,24 @@ "range_level": "50", "attack_level": "50" }, + { + "examine": "It shifts.", + "melee_animation": "8131", + "range_animation": "0", + "magic_level": "34", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "8133", + "name": "Void shifter", + "defence_level": "34", + "safespot": null, + "lifepoints": "48", + "strength_level": "34", + "id": "7367", + "range_level": "34", + "attack_level": "34" + }, { "examine": "It shifts.", "melee_animation": "8131", @@ -66001,6 +66385,24 @@ "range_level": "34", "attack_level": "34" }, + { + "examine": "It ravages.", + "melee_animation": "8086", + "range_animation": "0", + "magic_level": "34", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "8087", + "name": "Void ravager", + "defence_level": "34", + "safespot": null, + "lifepoints": "48", + "strength_level": "34", + "id": "7370", + "range_level": "34", + "attack_level": "34" + }, { "examine": "It ravages.", "melee_animation": "8086", @@ -66039,22 +66441,27 @@ "attack_level": "60" }, { + "examine": "It's like a little stomach on wings.", + "melee_animation": "7994", + "range_animation": "0", + "magic_level": "60", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", "death_animation": "7996", "name": "Ravenous locust", - "defence_level": "1", + "defence_level": "60", "safespot": null, - "lifepoints": "10", - "melee_animation": "7994", - "strength_level": "1", + "lifepoints": "100", + "strength_level": "60", "id": "7374", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7995" + "range_level": "60", + "attack_level": "60" }, { "examine": "He is an iron man!", - "combat_style": "1", + "combat_style": "0", "melee_animation": "7946", "magic_level": "65", "respawn_delay": "0", @@ -66073,7 +66480,7 @@ }, { "examine": "He is an iron man!", - "combat_style": "1", + "combat_style": "0", "melee_animation": "7946", "combat_audio": "0,0,0", "magic_level": "65", @@ -66092,6 +66499,24 @@ "range_level": "65", "attack_level": "65" }, + { + "examine": "Where did I put the marshmallows?", + "melee_animation": "8080", + "range_animation": "0", + "magic_level": "46", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "8078", + "name": "Pyrelord", + "defence_level": "46", + "safespot": null, + "lifepoints": "65", + "strength_level": "46", + "id": "7377", + "range_level": "46", + "attack_level": "46" + }, { "examine": "Where did I put the marshmallows?", "melee_animation": "8080", @@ -66452,7 +66877,6 @@ }, { "examine": "You clearly can't live on treasure alone.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -66469,7 +66893,6 @@ }, { "examine": "A testament to the effect of greed.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -66486,7 +66909,6 @@ }, { "examine": "He wanted loot", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -66503,7 +66925,6 @@ }, { "examine": "Eternally looking for that big payday.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -66520,7 +66941,6 @@ }, { "examine": "No more tea-breaks for this one.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -66537,7 +66957,6 @@ }, { "examine": "Even in death you can smell his feet.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -66554,7 +66973,6 @@ }, { "examine": "An anatomist's dream.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -66571,7 +66989,6 @@ }, { "examine": "A hand seems to be gripping his spine through his chest. Ouch.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -66689,7 +67106,6 @@ }, { "examine": "This dung beetle has mistaken you for its staple diet.", - "slayer_task": "70", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -66707,7 +67123,6 @@ }, { "examine": "It's looking unwell - probably something it ate.", - "slayer_task": "70", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -66725,7 +67140,6 @@ }, { "examine": "Once a valuable contributor to the ecosystem.", - "slayer_task": "70", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -66743,7 +67157,6 @@ }, { "examine": "This is what happens if you play with your food.", - "slayer_task": "70", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -67726,7 +68139,6 @@ }, { "examine": "Ew it's still alive.", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "9125", "attack_speed": "5", @@ -67748,7 +68160,6 @@ }, { "examine": "Ew it's still alive.", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "9125", "attack_speed": "5", @@ -67770,7 +68181,6 @@ }, { "examine": "A bloodveld with a very mixed heritage.", - "slayer_task": "10", "melee_animation": "9102", "range_animation": "0", "attack_speed": "5", @@ -67792,7 +68202,6 @@ }, { "examine": "A bloodveld with a very mixed heritage.", - "slayer_task": "10", "melee_animation": "9102", "range_animation": "0", "attack_speed": "5", @@ -68240,7 +68649,6 @@ }, { "examine": "Looks like a big ugly dog.", - "slayer_task": "27", "melee_animation": "6565", "range_animation": "0", "respawn_delay": "60", @@ -68275,7 +68683,6 @@ }, { "examine": "A small ice demon.", - "slayer_task": "46", "melee_animation": "8080", "range_animation": "0", "combat_audio": "531,533,532", @@ -68299,7 +68706,6 @@ }, { "examine": "The icefiend seems to be melting.", - "slayer_task": "46", "melee_animation": "8080", "range_animation": "0", "combat_audio": "531,533,532", @@ -68323,7 +68729,6 @@ }, { "examine": "The icefiend seems to be melting.", - "slayer_task": "46", "melee_animation": "8080", "range_animation": "0", "attack_speed": "5", @@ -68557,17 +68962,39 @@ "range_level": "38", "attack_level": "28" }, + { + "examine": "Not the best of vocalists.", + "combat_style": "1", + "melee_animation": "9449", + "range_animation": "9382", + "combat_audio": "284,286,285", + "magic_level": "65", + "respawn_delay": "60", + "defence_animation": "9451", + "weakness": "0", + "magic_animation": "9382", + "death_animation": "9450", + "name": "Mighty banshee", + "defence_level": "65", + "safespot": null, + "lifepoints": "85", + "strength_level": "65", + "id": "7786", + "aggressive": "true", + "range_level": "0", + "attack_level": "65" + }, { "examine": "A big, scary hand! ", "melee_animation": "1802", "attack_speed": "6", - "respawn_delay": "60", + "respawn_delay": "0", "defence_animation": "1803", "slayer_exp": "105", "death_animation": "1804", "name": "Wall Beast", "defence_level": "38", - "movement_radius": "1", + "movement_radius": "0", "safespot": null, "lifepoints": "105", "strength_level": "38", @@ -69863,7 +70290,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -69885,7 +70311,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -69906,7 +70331,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -69927,7 +70351,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -69967,7 +70390,6 @@ }, { "examine": "It rattles when it moves.", - "slayer_task": "75", "combat_style": "2", "melee_animation": "5485", "range_animation": "5485", @@ -69990,7 +70412,6 @@ }, { "examine": "It looks just a bit... underfed.", - "slayer_task": "75", "melee_animation": "5519", "range_animation": "5519", "combat_audio": "774,775,777", @@ -70015,7 +70436,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "5487", "combat_audio": "774,775,777", @@ -70039,7 +70459,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "5487", "combat_audio": "774,775,777", @@ -70114,7 +70533,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70136,7 +70554,6 @@ }, { "examine": "He guards the dungeon with the faithfulness of the undead.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -70159,7 +70576,6 @@ }, { "examine": "Could use a good meal.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -70209,7 +70625,6 @@ }, { "examine": "He seems a little underweight.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -70229,7 +70644,6 @@ }, { "examine": "He seems a little underweight.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -70264,7 +70678,6 @@ }, { "examine": "He seems a little underweight.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -70284,7 +70697,6 @@ }, { "examine": "He seems a little underweight.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -70333,7 +70745,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70390,7 +70801,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "combat_audio": "774,775,777", "melee_animation": "5485", "attack_speed": "4", @@ -70411,7 +70821,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "range_animation": "0", "combat_audio": "774,775,777", "melee_animation": "5485", @@ -70434,7 +70843,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "range_animation": "0", "combat_audio": "774,775,777", "melee_animation": "5485", @@ -70493,7 +70901,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -70516,7 +70923,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -70536,7 +70942,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -70556,7 +70961,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -70576,7 +70980,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5499", "range_animation": "0", "combat_audio": "774,775,777", @@ -70598,7 +71001,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70673,7 +71075,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70695,7 +71096,6 @@ }, { "examine": "A skeleton in a dress!", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -70718,7 +71118,6 @@ }, { "examine": "Achingly thin.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -70740,7 +71139,6 @@ }, { "examine": "Look: another skeleton.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70762,7 +71160,6 @@ }, { "examine": "That skeleton's grinning at me.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -70784,7 +71181,6 @@ }, { "examine": "He needs a tan.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -70806,7 +71202,6 @@ }, { "examine": "How do you know if a skeleton's male or female?", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70827,7 +71222,6 @@ }, { "examine": "He obviously hasn't realised he's dead.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70849,7 +71243,6 @@ }, { "examine": "Put some meat on those bones!", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70871,7 +71264,6 @@ }, { "examine": "An angry skeleton.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -70893,7 +71285,6 @@ }, { "examine": "Cross bones.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70915,7 +71306,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -70971,7 +71361,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -70993,7 +71382,6 @@ }, { "examine": "An opponent from the grave. He seems unimpressed by your bone rummaging.", - "slayer_task": "75", "melee_animation": "2067", "range_animation": "0", "combat_audio": "774,775,777", @@ -71014,7 +71402,6 @@ }, { "examine": "An ex-barbarian, willing to make you ex too.", - "slayer_task": "75", "melee_animation": "422", "range_animation": "0", "combat_audio": "774,775,777", @@ -71035,7 +71422,6 @@ }, { "examine": "He's heartless.", - "slayer_task": "75", "melee_animation": "2067", "range_animation": "0", "combat_audio": "774,775,777", @@ -71056,7 +71442,6 @@ }, { "examine": "He's less heavy now.", - "slayer_task": "75", "melee_animation": "390", "range_animation": "0", "combat_audio": "774,775,777", @@ -71077,7 +71462,6 @@ }, { "examine": "Floats like an anvil, hits like a hammer.", - "slayer_task": "75", "melee_animation": "422", "range_animation": "0", "combat_audio": "774,775,777", @@ -71768,11 +72152,6 @@ "name": "Sir Vyvin", "id": "605" }, - { - "examine": "Head of recruitment for the Temple Knights.", - "name": "Sir Tiffy Cashien", - "id": "2290" - }, { "examine": "An armourer.", "name": "Wayne", @@ -71811,6 +72190,7 @@ { "examine": "A sea bird.", "name": "Gull", + "water_npc": "true", "id": "2726" }, { @@ -72488,46 +72868,55 @@ "respawn_delay": "1" }, { + "examine": "A baby impling. Ahhh.", "name": "Baby Impling Puro", "id": "6055", "respawn_delay": "7" }, { + "examine": "A young impling. It's not fair!", "name": "Young Impling Puro", "id": "6056", "respawn_delay": "7" }, { + "examine": "An impling gourmet. Mmmm, tasty.", "name": "Gourmet Impling Puro", "id": "6057", "respawn_delay": "7" }, { + "examine": "An earth impling. Rock on.", "name": "Earth Impling Puro", "id": "6058", "respawn_delay": "50" }, { + "examine": "An impling who likes magic. Magic!", "name": "Essence Impling Puro", "id": "6059", "respawn_delay": "50" }, { + "examine": "An impling with varied tastes.", "name": "Eclectic Impling Puro", "id": "6060", "respawn_delay": "7" }, { + "examine": "A very stealthy impling.", "name": "Ninja Impling Puro", "id": "6063", "respawn_delay": "9999" }, { + "examine": "A nature impling. Right on, maaan.", "name": "Nature Impling Puro", "id": "6061", "respawn_delay": "9999" }, { + "examine": "Ooh, shiny things!", "name": "Magpie Impling Puro", "id": "6062", "respawn_delay": "9999" @@ -72624,5 +73013,14902 @@ "examine": "An intelligent-looking shop owner.", "name": "Obli", "id": "516" + }, + { + "examine": "A person sitting an exam.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Student", + "defence_level": "1", + "lifepoints": "10", + "strength_level": "1", + "id": "7151", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A person sitting an exam.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Student", + "defence_level": "1", + "lifepoints": "10", + "strength_level": "1", + "id": "7153", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A person sitting an exam.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Student", + "defence_level": "1", + "lifepoints": "10", + "strength_level": "1", + "id": "7154", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A person sitting an exam.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Student", + "defence_level": "1", + "lifepoints": "10", + "strength_level": "1", + "id": "7155", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "She looks like she means business.", + "name": "Balnea", + "id": "7047" + }, + { + "name": "Schoolgirl", + "id": "17" + }, + { + "name": "Orc", + "id": "70" + }, + { + "name": "Skeleton Mage", + "id": "94" + }, + { + "name": "Skavid", + "id": "129" + }, + { + "name": "Mammoth", + "id": "135" + }, + { + "name": "Mounted terrorchick gnome", + "id": "136" + }, + { + "name": "Gull", + "id": "149" + }, + { + "name": "Gull", + "id": "150" + }, + { + "name": "Fly trap", + "id": "151" + }, + { + "name": "Butterfly", + "id": "152" + }, + { + "name": "Butterfly", + "id": "155" + }, + { + "name": "Butterfly", + "id": "156" + }, + { + "name": "Butterfly", + "id": "157" + }, + { + "name": "Gnome shop keeper", + "id": "165" + }, + { + "name": "Gnome baller", + "id": "167" + }, + { + "name": "Brimstail", + "id": "171" + }, + { + "name": "Witch", + "id": "177" + }, + { + "name": "Bandit", + "id": "195" + }, + { + "name": "Barbarian guard", + "id": "197" + }, + { + "name": "Lord Daquarius", + "id": "200" + }, + { + "name": "Lollk", + "id": "207" + }, + { + "name": "Nulodion", + "id": "209" + }, + { + "name": "Sir Percival", + "id": "211" + }, + { + "name": "Merlin", + "id": "213" + }, + { + "name": "Peasant", + "id": "215" + }, + { + "name": "Crone", + "id": "217" + }, + { + "name": "Galahad", + "id": "218" + }, + { + "name": "Bonzo", + "id": "225" + }, + { + "name": "Morris", + "id": "226" + }, + { + "name": "Big Dave", + "id": "228" + }, + { + "name": "Joshua", + "id": "229" + }, + { + "name": "Austri", + "id": "232" + }, + { + "name": "Fishing spot", + "id": "234" + }, + { + "name": "Fishing spot", + "id": "235" + }, + { + "name": "Fishing spot", + "id": "236" + }, + { + "name": "Sir Lancelot", + "id": "239" + }, + { + "name": "Sir Gawain", + "id": "240" + }, + { + "name": "Sir Kay", + "id": "241" + }, + { + "name": "Sir Bedivere", + "id": "242" + }, + { + "name": "Sir Tristram", + "id": "243" + }, + { + "name": "Sir Pelleas", + "id": "244" + }, + { + "name": "Sir Lucan", + "id": "245" + }, + { + "name": "Morgan Le Faye", + "id": "248" + }, + { + "name": "Merlin", + "id": "249" + }, + { + "name": "The Lady of the Lake", + "id": "250" + }, + { + "name": "Beggar", + "id": "252" + }, + { + "name": "Kelvin", + "id": "260" + }, + { + "name": "Joe", + "id": "261" + }, + { + "name": "Hengrad", + "id": "263" + }, + { + "name": "Winelda", + "id": "276" + }, + { + "name": "Ernest", + "id": "287" + }, + { + "name": "Guard", + "id": "292" + }, + { + "name": "Twig", + "id": "301" + }, + { + "name": "Hadley", + "id": "302" + }, + { + "name": "Gerald", + "id": "303" + }, + { + "name": "Almera", + "id": "304" + }, + { + "name": "Hudon", + "id": "305" + }, + { + "name": "Golrie", + "id": "306" + }, + { + "name": "Fishing spot", + "id": "309" + }, + { + "name": "Fishing spot", + "id": "310" + }, + { + "name": "Fishing spot", + "id": "311" + }, + { + "name": "Fishing spot", + "id": "312" + }, + { + "name": "Fishing spot", + "id": "313" + }, + { + "name": "Fishing spot", + "id": "314" + }, + { + "name": "Fishing spot", + "id": "315" + }, + { + "name": "Fishing spot", + "id": "316" + }, + { + "name": "Fishing spot", + "id": "317" + }, + { + "name": "Fishing spot", + "id": "318" + }, + { + "name": "Fishing spot", + "id": "319" + }, + { + "name": "Fishing spot", + "id": "320" + }, + { + "name": "Fishing spot", + "id": "321" + }, + { + "name": "Fishing spot", + "id": "322" + }, + { + "name": "Fishing spot", + "id": "324" + }, + { + "name": "Fishing spot", + "id": "325" + }, + { + "name": "Fishing spot", + "id": "326" + }, + { + "name": "Fishing spot", + "id": "327" + }, + { + "name": "Fishing spot", + "id": "328" + }, + { + "name": "Fishing spot", + "id": "329" + }, + { + "name": "Fishing spot", + "id": "330" + }, + { + "name": "Fishing spot", + "id": "331" + }, + { + "name": "Fishing spot", + "id": "332" + }, + { + "name": "Fishing spot", + "id": "333" + }, + { + "name": "Fishing spot", + "id": "334" + }, + { + "name": "Da Vinci", + "id": "337" + }, + { + "name": "Guidor", + "id": "343" + }, + { + "name": "Kilron", + "id": "349" + }, + { + "name": "Omart", + "id": "350" + }, + { + "name": "Jerico", + "id": "366" + }, + { + "name": "Luthas", + "id": "379" + }, + { + "name": "Dwarf", + "id": "381" + }, + { + "name": "Stankers", + "id": "383" + }, + { + "name": "Legends guard", + "id": "399" + }, + { + "name": "Radimus Erkle", + "id": "400" + }, + { + "name": "Fishing spot", + "id": "403" + }, + { + "name": "Fishing spot", + "id": "404" + }, + { + "name": "Fishing spot", + "id": "405" + }, + { + "name": "Fishing spot", + "id": "406" + }, + { + "name": "Genie", + "id": "409" + }, + { + "name": "Mysterious Old Man", + "id": "410" + }, + { + "name": "Cyrisus", + "id": "431" + }, + { + "name": "Cyrisus", + "id": "433" + }, + { + "name": "Cyrisus", + "id": "434" + }, + { + "name": "Fallen Man", + "id": "436" + }, + { + "name": "Murphy", + "id": "463" + }, + { + "name": "Murphy", + "id": "464" + }, + { + "name": "Murphy", + "id": "465" + }, + { + "name": "Murphy", + "id": "466" + }, + { + "name": "Shark", + "id": "467" + }, + { + "name": "Shark", + "id": "468" + }, + { + "name": "Bolkoy", + "id": "471" + }, + { + "name": "Remsai", + "id": "472" + }, + { + "name": "Khazard trooper", + "id": "473" + }, + { + "name": "Gnome troop", + "id": "480" + }, + { + "name": "Local Gnome", + "id": "484" + }, + { + "name": "Kalron", + "id": "486" + }, + { + "name": "Hajedy", + "id": "510" + }, + { + "name": "Vigroy", + "id": "511" + }, + { + "name": "Yohnus", + "id": "513" + }, + { + "name": "Seravel", + "id": "514" + }, + { + "name": "Valaine", + "id": "536" + }, + { + "name": "Scavvo", + "id": "537" + }, + { + "name": "Aubury", + "id": "553" + }, + { + "name": "Jiminua", + "id": "560" + }, + { + "name": "Arhein", + "id": "563" + }, + { + "name": "Lunderwin", + "id": "565" + }, + { + "name": "Zambo", + "id": "568" + }, + { + "name": "Hickton", + "id": "575" + }, + { + "name": "Harry", + "id": "576" + }, + { + "name": "Frincos", + "id": "578" + }, + { + "name": "Herquin", + "id": "584" + }, + { + "name": "Rommik", + "id": "585" + }, + { + "name": "Davon", + "id": "588" + }, + { + "name": "Zenesha", + "id": "589" + }, + { + "name": "Aemad", + "id": "590" + }, + { + "name": "Kortan", + "id": "591" + }, + { + "name": "Roachey", + "id": "592" + }, + { + "name": "Frenita", + "id": "593" + }, + { + "name": "Nurmof", + "id": "594" + }, + { + "name": "Noterazzo", + "id": "597" + }, + { + "name": "Hudo", + "id": "600" + }, + { + "name": "Rometti", + "id": "601" + }, + { + "name": "Gulluck", + "id": "602" + }, + { + "name": "Heckel Funch", + "id": "603" + }, + { + "name": "Gunnjorn", + "id": "607" + }, + { + "name": "Sir Amik Varze", + "id": "608" + }, + { + "name": "Greldo", + "id": "612" + }, + { + "name": "Gnome baller", + "id": "622" + }, + { + "name": "Gnome baller", + "id": "623" + }, + { + "name": "Gnome baller", + "id": "624" + }, + { + "name": "Gnome baller", + "id": "625" + }, + { + "name": "Gnome baller", + "id": "626" + }, + { + "name": "Gnome baller", + "id": "627" + }, + { + "name": "Gnome baller", + "id": "628" + }, + { + "name": "Gnome baller", + "id": "629" + }, + { + "name": "Gnome baller", + "id": "630" + }, + { + "name": "Gnome baller", + "id": "631" + }, + { + "name": "Gnome baller", + "id": "632" + }, + { + "name": "Gnome winger", + "id": "634" + }, + { + "name": "Cheerleader", + "id": "636" + }, + { + "name": "Katrine", + "id": "642" + }, + { + "name": "Straven", + "id": "644" + }, + { + "name": "King Roald", + "id": "647" + }, + { + "name": "Fairy Queen", + "id": "653" + }, + { + "name": "Shamus", + "id": "654" + }, + { + "name": "Megan", + "id": "661" + }, + { + "name": "Lucy", + "id": "662" + }, + { + "name": "Boot", + "id": "664" + }, + { + "name": "Caleb", + "id": "666" + }, + { + "name": "Johnathon", + "id": "668" + }, + { + "name": "Hazelmere", + "id": "669" + }, + { + "name": "Glough", + "id": "671" + }, + { + "name": "Anita", + "id": "672" + }, + { + "name": "Charlie", + "id": "673" + }, + { + "name": "Shipyard worker", + "id": "675" + }, + { + "name": "Femi", + "id": "676" + }, + { + "name": "Tower Advisor", + "id": "685" + }, + { + "name": "Tower Advisor", + "id": "686" + }, + { + "name": "Tower Advisor", + "id": "687" + }, + { + "name": "Competition Judge", + "id": "693" + }, + { + "name": "Bailey", + "id": "695" + }, + { + "name": "Holgart", + "id": "697" + }, + { + "name": "Holgart", + "id": "699" + }, + { + "name": "Holgart", + "id": "700" + }, + { + "name": "Kent", + "id": "701" + }, + { + "name": "Fisherman", + "id": "702" + }, + { + "name": "Fisherman", + "id": "703" + }, + { + "name": "Fisherman", + "id": "704" + }, + { + "name": "Alrena", + "id": "710" + }, + { + "name": "Bravek", + "id": "711" + }, + { + "name": "Carla", + "id": "712" + }, + { + "name": "Ted Rehnison", + "id": "721" + }, + { + "name": "Martha Rehnison", + "id": "722" + }, + { + "name": "Billy Rehnison", + "id": "723" + }, + { + "name": "Milli Rehnison", + "id": "724" + }, + { + "name": "Jethick", + "id": "725" + }, + { + "name": "Trufitus", + "id": "740" + }, + { + "name": "Oracle", + "id": "746" + }, + { + "name": "Angry barbarian spirit", + "id": "748" + }, + { + "name": "Peaceful barbarian spirit", + "id": "754" + }, + { + "name": "Kittens", + "id": "759" + }, + { + "name": "Kitten", + "id": "761" + }, + { + "name": "Kitten", + "id": "762" + }, + { + "name": "Kitten", + "id": "763" + }, + { + "name": "Kitten", + "id": "764" + }, + { + "name": "Kitten", + "id": "765" + }, + { + "name": "Kitten", + "id": "766" + }, + { + "name": "Cat", + "id": "768" + }, + { + "name": "Cat", + "id": "769" + }, + { + "name": "Cat", + "id": "770" + }, + { + "name": "Cat", + "id": "771" + }, + { + "name": "Cat", + "id": "772" + }, + { + "name": "Cat", + "id": "773" + }, + { + "name": "Overgrown cat", + "id": "774" + }, + { + "name": "Overgrown cat", + "id": "775" + }, + { + "name": "Overgrown cat", + "id": "776" + }, + { + "name": "Overgrown cat", + "id": "777" + }, + { + "name": "Overgrown cat", + "id": "779" + }, + { + "name": "Gertrude", + "id": "780" + }, + { + "name": "Philop", + "id": "782" + }, + { + "name": "Kanel", + "id": "784" + }, + { + "name": "Garv", + "id": "788" + }, + { + "name": "Grubor", + "id": "789" + }, + { + "name": "Seth", + "id": "791" + }, + { + "name": "Grip", + "id": "792" + }, + { + "name": "Helemos", + "id": "797" + }, + { + "examine": "His job doesn't look very fun...", + "name": "Pierre", + "id": "807" + }, + { + "examine": "He looks kind of stuck up...", + "name": "Hobbes", + "id": "808" + }, + { + "examine": "She looks like she enjoys her job.", + "name": "Louisa", + "id": "809" + }, + { + "examine": "She looks very nervous...", + "name": "Mary", + "id": "810" + }, + { + "examine": "He looks like he spends a lot of time outdoor.", + "name": "Stanford", + "id": "811" + }, + { + "examine": "She's dressed in a red top and green trousers.", + "name": "Anna", + "id": "814" + }, + { + "examine": "He's dressed all in red.", + "name": "Bob", + "id": "815" + }, + { + "examine": "She's wearing a blue top and red trousers.", + "name": "Carol", + "id": "816" + }, + { + "examine": "He's dressed all in green.", + "name": "David", + "id": "817" + }, + { + "examine": "She's wearing a green top and blue trousers.", + "name": "Elizabeth", + "id": "818" + }, + { + "examine": "He's dressed all in blue.", + "name": "Frank", + "id": "819" + }, + { + "name": "Ana", + "id": "822" + }, + { + "name": "Ana", + "id": "823" + }, + { + "name": "Female slave", + "id": "824" + }, + { + "name": "Escaping slave", + "id": "826" + }, + { + "name": "Shanty Claws", + "id": "828" + }, + { + "name": "Mercenary Captain", + "id": "829" + }, + { + "name": "Al Shabim", + "id": "832" + }, + { + "name": "Horacio", + "id": "844" + }, + { + "name": "Kangai Mau", + "id": "846" + }, + { + "name": "Blurberry", + "id": "848" + }, + { + "name": "Barman", + "id": "849" + }, + { + "name": "Og", + "id": "853" + }, + { + "name": "Grew", + "id": "854" + }, + { + "name": "Toban", + "id": "855" + }, + { + "name": "Ogre guard", + "id": "857" + }, + { + "name": "Ogre guard", + "id": "860" + }, + { + "name": "Ogre guard", + "id": "861" + }, + { + "name": "Skavid", + "id": "865" + }, + { + "name": "Skavid", + "id": "866" + }, + { + "name": "Skavid", + "id": "867" + }, + { + "name": "Skavid", + "id": "868" + }, + { + "name": "Skavid", + "id": "869" + }, + { + "name": "Watchtower Wizard", + "id": "871" + }, + { + "name": "Ogre trader", + "id": "876" + }, + { + "name": "Weakened Delrith", + "id": "880" + }, + { + "name": "Claus the chef", + "id": "886" + }, + { + "name": "Philipe Carnillean", + "id": "888" + }, + { + "name": "Henryeta Carnillean", + "id": "889" + }, + { + "name": "Butler Jones", + "id": "890" + }, + { + "name": "Alomone", + "id": "891" + }, + { + "name": "Hazeel", + "id": "892" + }, + { + "name": "Nora T. Hagg", + "id": "896" + }, + { + "name": "Mouse", + "id": "901" + }, + { + "name": "Gundai", + "id": "902" + }, + { + "name": "Lundail", + "id": "903" + }, + { + "name": "Kolodion", + "id": "906" + }, + { + "name": "Ned", + "id": "918" + }, + { + "name": "Prince Ali", + "id": "921" + }, + { + "name": "Border Guard", + "id": "926" + }, + { + "name": "Fishing spot", + "id": "927" + }, + { + "name": "Gujuo", + "id": "928" + }, + { + "name": "Ungadulu", + "id": "929" + }, + { + "name": "Ungadulu", + "id": "930" + }, + { + "name": "Siegfried Erkle", + "id": "933" + }, + { + "name": "Viyeldi", + "id": "935" + }, + { + "name": "San Tojalon", + "id": "936" + }, + { + "name": "Irvig Senay", + "id": "937" + }, + { + "name": "Echned Zekin", + "id": "940" + }, + { + "name": "Fishing spot", + "id": "952" + }, + { + "name": "Banker", + "id": "953" + }, + { + "name": "Kardia", + "id": "992" + }, + { + "name": "Niloof", + "id": "994" + }, + { + "name": "Lord Iban", + "id": "1003" + }, + { + "name": "Hamid", + "id": "1008" + }, + { + "name": "Fycie", + "id": "1011" + }, + { + "name": "Bugs", + "id": "1012" + }, + { + "name": "Bloated Toad", + "id": "1014" + }, + { + "name": "Chompy bird", + "id": "1016" + }, + { + "name": "Baby impling", + "id": "1024" + }, + { + "name": "Banker", + "id": "1036" + }, + { + "name": "Rufus", + "id": "1038" + }, + { + "name": "Barker", + "id": "1039" + }, + { + "name": "Fidelio", + "id": "1040" + }, + { + "name": "Sbott", + "id": "1041" + }, + { + "name": "Roavar", + "id": "1042" + }, + { + "name": "Will o' the wisp", + "id": "1043" + }, + { + "name": "Drezel", + "id": "1047" + }, + { + "name": "Mime", + "id": "1056" + }, + { + "name": "Saba", + "id": "1070" + }, + { + "name": "Tenzing", + "id": "1071" + }, + { + "name": "Archer", + "id": "1075" + }, + { + "name": "Bob", + "id": "1091" + }, + { + "name": "Billy", + "id": "1093" + }, + { + "name": "Mountain goat", + "id": "1094" + }, + { + "name": "Eadgar", + "id": "1113" + }, + { + "name": "Godric", + "id": "1114" + }, + { + "name": "Kalphite Queen", + "id": "1159" + }, + { + "name": "Timfraku", + "id": "1162" + }, + { + "name": "Tiadeche", + "id": "1163" + }, + { + "name": "Tiadeche", + "id": "1164" + }, + { + "name": "Tinsay", + "id": "1165" + }, + { + "name": "Tinsay", + "id": "1166" + }, + { + "name": "Tamayu", + "id": "1167" + }, + { + "name": "Tamayu", + "id": "1168" + }, + { + "name": "Tamayu", + "id": "1169" + }, + { + "name": "Tamayu", + "id": "1170" + }, + { + "name": "Lubufu", + "id": "1171" + }, + { + "name": "The Shaikahan", + "id": "1173" + }, + { + "name": "Fishing spot", + "id": "1174" + }, + { + "name": "Fishing spot", + "id": "1175" + }, + { + "name": "Fishing spot", + "id": "1177" + }, + { + "name": "Fishing spot", + "id": "1178" + }, + { + "name": "Cormorant", + "id": "1180" + }, + { + "name": "Pelican", + "id": "1181" + }, + { + "name": "Lord Iorwerth", + "id": "1182" + }, + { + "name": "Idris", + "id": "1186" + }, + { + "name": "Essyllt", + "id": "1187" + }, + { + "name": "Morvran", + "id": "1188" + }, + { + "name": "Fishing spot", + "id": "1189" + }, + { + "name": "Fishing spot", + "id": "1190" + }, + { + "name": "Fishing spot", + "id": "1191" + }, + { + "name": "Arianwyn", + "id": "1202" + }, + { + "name": "Tyras guard", + "id": "1205" + }, + { + "name": "Quartermaster", + "id": "1207" + }, + { + "name": "Spider", + "id": "1221" + }, + { + "name": "Mist", + "id": "1222" + }, + { + "name": "Vampyric hound", + "id": "1224" + }, + { + "name": "Tree", + "id": "1226" + }, + { + "name": "Fishing spot", + "id": "1236" + }, + { + "name": "Fishing spot", + "id": "1237" + }, + { + "name": "Fishing spot", + "id": "1238" + }, + { + "name": "Shade Spirit", + "id": "1242" + }, + { + "name": "Afflicted(Ulsquire)", + "id": "1251" + }, + { + "name": "Ulsquire Shauncy", + "id": "1252" + }, + { + "name": "Afflicted(Razmire)", + "id": "1253" + }, + { + "name": "Razmire Keelgan", + "id": "1254" + }, + { + "name": "Mort'ton Local", + "id": "1255" + }, + { + "name": "Mort'ton Local", + "id": "1256" + }, + { + "name": "Mort'ton local", + "id": "1259" + }, + { + "name": "Mort'ton local", + "id": "1260" + }, + { + "name": "Butterfly", + "id": "1280" + }, + { + "name": "Bjorn", + "id": "1284" + }, + { + "name": "Eldgrim", + "id": "1285" + }, + { + "name": "Askeladden", + "id": "1295" + }, + { + "name": "Town Guard", + "id": "1299" + }, + { + "name": "Freygerd", + "id": "1310" + }, + { + "name": "Lensa", + "id": "1311" + }, + { + "name": "Jennella", + "id": "1312" + }, + { + "name": "Gull", + "id": "1322" + }, + { + "name": "Gull", + "id": "1323" + }, + { + "name": "Gull", + "id": "1324" + }, + { + "name": "Gull", + "id": "1325" + }, + { + "name": "Fishing spot", + "id": "1331" + }, + { + "name": "Fishing spot", + "id": "1332" + }, + { + "name": "Fishing spot", + "id": "1333" + }, + { + "name": "Jossik", + "id": "1334" + }, + { + "name": "Jossik", + "id": "1335" + }, + { + "name": "Larrissa", + "id": "1336" + }, + { + "name": "Larrissa", + "id": "1337" + }, + { + "name": "Dagannoth mother", + "id": "1348" + }, + { + "name": "Dagannoth mother", + "id": "1349" + }, + { + "name": "Dagannoth mother", + "id": "1350" + }, + { + "name": "Queen Sigrid", + "id": "1359" + }, + { + "name": "Arnor", + "id": "1361" + }, + { + "name": "Haming", + "id": "1362" + }, + { + "name": "Moldof", + "id": "1363" + }, + { + "name": "Helga", + "id": "1364" + }, + { + "name": "Matilda", + "id": "1365" + }, + { + "name": "Ashild", + "id": "1366" + }, + { + "name": "Prince Brand", + "id": "1371" + }, + { + "name": "Princess Astrid", + "id": "1372" + }, + { + "name": "King Vargas", + "id": "1373" + }, + { + "name": "Advisor Ghrim", + "id": "1375" + }, + { + "name": "Halla", + "id": "1383" + }, + { + "name": "Yrsa", + "id": "1384" + }, + { + "name": "Thora", + "id": "1387" + }, + { + "name": "Fishing spot", + "id": "1399" + }, + { + "name": "Gull", + "id": "1400" + }, + { + "name": "Fishing spot", + "id": "1405" + }, + { + "name": "Fishing spot", + "id": "1406" + }, + { + "name": "Daero", + "id": "1407" + }, + { + "name": "Waydar", + "id": "1408" + }, + { + "name": "Waydar", + "id": "1409" + }, + { + "name": "Waydar", + "id": "1410" + }, + { + "name": "Garkor", + "id": "1411" + }, + { + "name": "Garkor", + "id": "1412" + }, + { + "name": "Lumo", + "id": "1413" + }, + { + "name": "Lumo", + "id": "1414" + }, + { + "name": "Bunkdo", + "id": "1415" + }, + { + "name": "Bunkdo", + "id": "1416" + }, + { + "name": "Carado", + "id": "1417" + }, + { + "name": "Carado", + "id": "1418" + }, + { + "name": "Karam", + "id": "1420" + }, + { + "name": "Karam", + "id": "1421" + }, + { + "name": "Karam", + "id": "1422" + }, + { + "name": "Bunkwicket", + "id": "1423" + }, + { + "name": "Waymottin", + "id": "1424" + }, + { + "name": "Zooknock", + "id": "1425" + }, + { + "name": "Zooknock", + "id": "1426" + }, + { + "name": "G.L.O. Caranock", + "id": "1428" + }, + { + "name": "Dugopul", + "id": "1429" + }, + { + "name": "Salenab", + "id": "1430" + }, + { + "name": "Trefaji", + "id": "1431" + }, + { + "name": "Aberab", + "id": "1432" + }, + { + "name": "Solihib", + "movement_radius": "1", + "id": "1433" + }, + { + "name": "Daga", + "id": "1434" + }, + { + "name": "Tutab", + "id": "1435" + }, + { + "name": "Ifaba", + "id": "1436" + }, + { + "name": "Hamab", + "id": "1437" + }, + { + "name": "Hafuba", + "id": "1438" + }, + { + "name": "Denadu", + "id": "1439" + }, + { + "name": "Lofu", + "id": "1440" + }, + { + "name": "Kruk", + "id": "1441" + }, + { + "name": "Awowogei", + "id": "1448" + }, + { + "name": "Uwogo", + "id": "1449" + }, + { + "name": "Muruwoi", + "id": "1450" + }, + { + "name": "Elder Guard", + "id": "1462" + }, + { + "name": "Bonzara", + "id": "1468" + }, + { + "name": "Foreman", + "id": "1470" + }, + { + "name": "Spider", + "id": "1474" + }, + { + "name": "Gorilla", + "id": "1482" + }, + { + "name": "Dummy", + "id": "1488" + }, + { + "name": "Dummy", + "id": "1489" + }, + { + "name": "Dummy", + "id": "1490" + }, + { + "name": "Dummy", + "id": "1491" + }, + { + "name": "Dummy", + "id": "1492" + }, + { + "name": "Dummy", + "id": "1493" + }, + { + "name": "Dummy", + "id": "1494" + }, + { + "name": "Dummy", + "id": "1495" + }, + { + "name": "Dummy", + "id": "1496" + }, + { + "name": "Dummy", + "id": "1497" + }, + { + "name": "Dummy", + "id": "1498" + }, + { + "name": "Dummy", + "id": "1499" + }, + { + "name": "Dummy", + "id": "1500" + }, + { + "name": "Dummy", + "id": "1501" + }, + { + "name": "Dummy", + "id": "1502" + }, + { + "name": "Dummy", + "id": "1503" + }, + { + "name": "Dummy", + "id": "1504" + }, + { + "name": "Dummy", + "id": "1505" + }, + { + "name": "Dummy", + "id": "1506" + }, + { + "name": "Dummy", + "id": "1507" + }, + { + "name": "Forester", + "id": "1508" + }, + { + "name": "Woman-at-arms", + "id": "1509" + }, + { + "name": "Apprentice", + "id": "1510" + }, + { + "name": "Ranger", + "id": "1511" + }, + { + "name": "Adventurer", + "id": "1512" + }, + { + "name": "Mage", + "id": "1513" + }, + { + "name": "Nail beast", + "id": "1515" + }, + { + "name": "Nail beast", + "id": "1522" + }, + { + "name": "Nail beast", + "id": "1523" + }, + { + "name": "Undead Lumberjack", + "id": "1525" + }, + { + "name": "Lanthus", + "id": "1526" + }, + { + "name": "Mine cart", + "id": "1527" + }, + { + "name": "Sheep", + "id": "1529" + }, + { + "name": "Rabbit", + "id": "1530" + }, + { + "name": "Loading crane", + "id": "1542" + }, + { + "name": "Innocent-looking key", + "id": "1543" + }, + { + "name": "Mine cart", + "id": "1544" + }, + { + "name": "Mine cart", + "id": "1545" + }, + { + "name": "Mine cart", + "id": "1546" + }, + { + "name": "Mine cart", + "id": "1547" + }, + { + "name": "Mine cart", + "id": "1548" + }, + { + "name": "Santa", + "id": "1552" + }, + { + "name": "Aga", + "id": "1554" + }, + { + "name": "Arrg", + "id": "1555" + }, + { + "name": "Ice wolf", + "id": "1559" + }, + { + "name": "Curpile Fyod", + "id": "1568" + }, + { + "name": "Veliaf Hurtz", + "id": "1569" + }, + { + "name": "Sani Piliu", + "id": "1570" + }, + { + "name": "Harold Evans", + "id": "1571" + }, + { + "name": "Radigad Ponfit", + "id": "1572" + }, + { + "name": "Polmafi Ferdygris", + "id": "1573" + }, + { + "name": "Ivan Strom", + "id": "1574" + }, + { + "name": "Skeleton Hellhound", + "id": "1575" + }, + { + "name": "Stranger", + "id": "1576" + }, + { + "name": "Vanstrom Klause", + "id": "1577" + }, + { + "name": "Mist", + "id": "1578" + }, + { + "name": "Vanstrom Klause", + "id": "1579" + }, + { + "name": "Vanstrom Klause", + "id": "1580" + }, + { + "name": "Vanstrom Klause", + "id": "1581" + }, + { + "name": "Saniboch", + "id": "1595" + }, + { + "name": "Vannaka", + "id": "1596" + }, + { + "name": "Cave crawler", + "id": "1599" + }, + { + "name": "Skullball", + "id": "1659" + }, + { + "name": "Agility Trainer", + "id": "1664" + }, + { + "name": "Dr Fenkenstrain", + "id": "1666" + }, + { + "name": "Fenkenstrain's Monster", + "id": "1671" + }, + { + "name": "Lord Rologarth", + "id": "1674" + }, + { + "name": "Eluned", + "id": "1679" + }, + { + "name": "Islwyn", + "id": "1680" + }, + { + "name": "Golrie", + "id": "1682" + }, + { + "name": "Velorina", + "id": "1683" + }, + { + "name": "Gravingas", + "id": "1685" + }, + { + "name": "Ak-Haranu", + "id": "1687" + }, + { + "name": "Undead cow", + "id": "1689" + }, + { + "name": "Robin", + "id": "1694" + }, + { + "name": "Johanhus Ulsbrecht", + "id": "1709" + }, + { + "name": "Tree", + "id": "1719" + }, + { + "name": "Tree", + "id": "1720" + }, + { + "name": "Tree", + "id": "1721" + }, + { + "name": "Dead tree", + "id": "1722" + }, + { + "name": "Dead tree", + "id": "1723" + }, + { + "name": "Dead tree", + "id": "1724" + }, + { + "name": "Dead tree", + "id": "1725" + }, + { + "name": "Dead tree", + "id": "1726" + }, + { + "name": "Dead tree", + "id": "1727" + }, + { + "name": "Dead tree", + "id": "1728" + }, + { + "name": "Dead tree", + "id": "1729" + }, + { + "name": "Dead tree", + "id": "1730" + }, + { + "name": "Dead tree", + "id": "1731" + }, + { + "name": "Dramen tree", + "id": "1732" + }, + { + "name": "Magic tree", + "id": "1734" + }, + { + "name": "Maple tree", + "id": "1735" + }, + { + "name": "Willow", + "id": "1736" + }, + { + "name": "Willow", + "id": "1737" + }, + { + "name": "Willow", + "id": "1738" + }, + { + "name": "Hollow tree", + "id": "1749" + }, + { + "name": "Hollow tree", + "id": "1750" + }, + { + "name": "Mounted terrorbird gnome", + "id": "1753" + }, + { + "name": "Crow", + "id": "1756" + }, + { + "name": "Sheep", + "id": "1762" + }, + { + "name": "Sheep", + "id": "1764" + }, + { + "name": "Sheep", + "id": "1765" + }, + { + "name": "Ilfeen", + "id": "1777" + }, + { + "name": "William", + "id": "1778" + }, + { + "name": "Ian", + "id": "1779" + }, + { + "name": "Larry", + "id": "1780" + }, + { + "name": "Darren", + "id": "1781" + }, + { + "name": "Edward", + "id": "1782" + }, + { + "name": "Neil", + "id": "1784" + }, + { + "name": "Simon", + "id": "1786" + }, + { + "name": "Sam", + "id": "1787" + }, + { + "name": "Lumdo", + "id": "1788" + }, + { + "name": "Bunkwicket", + "id": "1789" + }, + { + "name": "Waymottin", + "id": "1790" + }, + { + "name": "Jungle Tree", + "id": "1791" + }, + { + "name": "Jungle Tree", + "id": "1792" + }, + { + "name": "Tassie Slipcast", + "id": "1793" + }, + { + "name": "Phantuwti Fanstuwi Farsight", + "id": "1798" + }, + { + "name": "Tindel Marchant", + "id": "1799" + }, + { + "name": "Gnormadium Avlafrim", + "id": "1800" + }, + { + "name": "Petra Fiyed", + "id": "1801" + }, + { + "name": "Slagilith", + "id": "1804" + }, + { + "name": "Ragnar", + "id": "1808" + }, + { + "name": "Svidi", + "id": "1809" + }, + { + "name": "Jokul", + "id": "1810" + }, + { + "name": "The Kendal", + "id": "1811" + }, + { + "name": "Bald Headed Eagle", + "id": "1821" + }, + { + "name": "Frog", + "id": "1830" + }, + { + "name": "Easter Bunny", + "id": "1835" + }, + { + "name": "Dondakan the Dwarf", + "id": "1836" + }, + { + "name": "Dondakan the Dwarf", + "id": "1838" + }, + { + "name": "Dondakan the Dwarf", + "id": "1839" + }, + { + "name": "Rolad", + "id": "1841" + }, + { + "name": "Dwarven Boatman", + "id": "1845" + }, + { + "name": "Miodvetnir", + "id": "1847" + }, + { + "name": "Dernu", + "id": "1848" + }, + { + "name": "Derni", + "id": "1849" + }, + { + "name": "Brian", + "id": "1860" + }, + { + "name": "Ali Morrisane", + "id": "1862" + }, + { + "name": "Ali the Mayor", + "id": "1869" + }, + { + "name": "Bandit Leader", + "id": "1876" + }, + { + "name": "Bandit", + "id": "1879" + }, + { + "name": "Bandit", + "id": "1881" + }, + { + "name": "Sir Palomedes", + "id": "1882" + }, + { + "name": "Sir Palomedes", + "id": "1883" + }, + { + "name": "Trobert", + "id": "1884" + }, + { + "name": "Villager", + "id": "1887" + }, + { + "name": "Villager", + "id": "1889" + }, + { + "name": "Villager", + "id": "1890" + }, + { + "name": "Villager", + "id": "1891" + }, + { + "name": "Villager", + "id": "1893" + }, + { + "name": "Villager", + "id": "1894" + }, + { + "name": "Villager", + "id": "1895" + }, + { + "name": "Villager", + "id": "1897" + }, + { + "name": "Villager", + "id": "1898" + }, + { + "name": "Menaphite Leader", + "id": "1899" + }, + { + "name": "Menaphite Thug", + "id": "1903" + }, + { + "name": "Broken clay golem", + "id": "1907" + }, + { + "name": "Damaged clay golem", + "id": "1909" + }, + { + "name": "Clay golem", + "id": "1910" + }, + { + "name": "Elissa", + "id": "1912" + }, + { + "name": "Eblis", + "id": "1922" + }, + { + "name": "Eblis", + "id": "1924" + }, + { + "name": "Bandit", + "id": "1927" + }, + { + "name": "Bandit", + "id": "1928" + }, + { + "name": "Bandit", + "id": "1929" + }, + { + "name": "Bandit", + "id": "1930" + }, + { + "name": "Troll child", + "id": "1932" + }, + { + "name": "Troll child", + "id": "1934" + }, + { + "name": "Mummy", + "id": "1957" + }, + { + "name": "Mummy", + "id": "1959" + }, + { + "name": "Mummy ashes", + "id": "1960" + }, + { + "name": "Mummy", + "id": "1966" + }, + { + "name": "Mummy", + "id": "1968" + }, + { + "name": "Azzanadra", + "id": "1970" + }, + { + "name": "Raetul", + "id": "1982" + }, + { + "name": "Siamun", + "id": "1983" + }, + { + "name": "High Priest", + "id": "1984" + }, + { + "name": "Priest", + "id": "1987" + }, + { + "name": "Priest", + "id": "1989" + }, + { + "name": "Sphinx", + "id": "1990" + }, + { + "name": "Neite", + "id": "1992" + }, + { + "name": "Vulture", + "id": "1996" + }, + { + "name": "Plague cow", + "id": "2000" + }, + { + "name": "Wanderer", + "id": "2002" + }, + { + "name": "Wanderer", + "id": "2006" + }, + { + "name": "Het", + "id": "2007" + }, + { + "name": "Apmeken", + "id": "2008" + }, + { + "name": "Scabaras", + "id": "2009" + }, + { + "name": "Crondis", + "id": "2010" + }, + { + "name": "Icthlarin", + "id": "2011" + }, + { + "name": "Klenter", + "id": "2013" + }, + { + "name": "Light creature", + "id": "2020" + }, + { + "name": "Light creature", + "id": "2022" + }, + { + "name": "Juna", + "id": "2023" + }, + { + "name": "Grish", + "id": "2038" + }, + { + "name": "Uglug Nar", + "id": "2039" + }, + { + "name": "Pilg", + "id": "2040" + }, + { + "name": "Grug", + "id": "2041" + }, + { + "name": "Ogre guard", + "id": "2042" + }, + { + "name": "Ogre guard", + "id": "2043" + }, + { + "name": "Zavistic Rarve", + "id": "2059" + }, + { + "name": "Sithik Ints", + "id": "2061" + }, + { + "name": "Sithik Ints", + "id": "2062" + }, + { + "name": "Gargh", + "id": "2063" + }, + { + "name": "Scarg", + "id": "2064" + }, + { + "name": "Gruh", + "id": "2065" + }, + { + "name": "Irwin Feaselbaum", + "id": "2066" + }, + { + "name": "Fishing spot", + "id": "2068" + }, + { + "name": "Sigmund", + "id": "2079" + }, + { + "name": "Sigmund", + "id": "2083" + }, + { + "name": "Mistag", + "id": "2084" + }, + { + "name": "Kazgar", + "id": "2085" + }, + { + "name": "Ur-tag", + "id": "2087" + }, + { + "name": "Duke Horacio", + "id": "2088" + }, + { + "name": "Mistag", + "id": "2089" + }, + { + "name": "Sigmund", + "id": "2090" + }, + { + "name": "Red Axe Secretary", + "id": "2099" + }, + { + "name": "Red Axe Director", + "id": "2107" + }, + { + "name": "Red Axe Cat", + "id": "2108" + }, + { + "name": "Trader", + "id": "2123" + }, + { + "name": "Trader", + "id": "2125" + }, + { + "name": "Supreme Commander", + "id": "2128" + }, + { + "name": "Commander Veldaban", + "id": "2129" + }, + { + "name": "Gnome emissary", + "id": "2137" + }, + { + "name": "Riki the sculptor's model", + "id": "2142" + }, + { + "name": "Riki the sculptor's model", + "id": "2144" + }, + { + "name": "Riki the sculptor's model", + "id": "2145" + }, + { + "name": "Riki the sculptor's model", + "id": "2146" + }, + { + "name": "Riki the sculptor's model", + "id": "2147" + }, + { + "name": "Riki the sculptor's model", + "id": "2148" + }, + { + "name": "Riki the sculptor's model", + "id": "2149" + }, + { + "name": "Riki the sculptor's model", + "id": "2150" + }, + { + "name": "Vigr", + "id": "2151" + }, + { + "name": "Santiri", + "id": "2152" + }, + { + "name": "Saro", + "id": "2153" + }, + { + "name": "Wemund", + "id": "2155" + }, + { + "name": "Randivor", + "id": "2156" + }, + { + "name": "Hervi", + "id": "2157" + }, + { + "name": "Gulldamar", + "id": "2159" + }, + { + "name": "Agmundi", + "id": "2161" + }, + { + "name": "Vermundi", + "id": "2162" + }, + { + "name": "Assistant", + "id": "2166" + }, + { + "name": "Dromund", + "id": "2169" + }, + { + "name": "Inn Keeper", + "id": "2176" + }, + { + "name": "Barman", + "id": "2179" + }, + { + "name": "Hegir", + "id": "2188" + }, + { + "name": "Haera", + "id": "2189" + }, + { + "name": "Reinald", + "id": "2194" + }, + { + "name": "Gauss", + "id": "2196" + }, + { + "name": "Myndill", + "id": "2197" + }, + { + "name": "Tombar", + "id": "2199" + }, + { + "name": "Odmar", + "id": "2200" + }, + { + "name": "Drunken Dwarf", + "id": "2204" + }, + { + "name": "Dwarven Miner", + "id": "2208" + }, + { + "name": "Dwarven Miner", + "id": "2209" + }, + { + "name": "Dwarven Miner", + "id": "2210" + }, + { + "name": "Dwarven Miner", + "id": "2211" + }, + { + "name": "Dwarven Miner", + "id": "2212" + }, + { + "name": "Dwarven Miner", + "id": "2213" + }, + { + "name": "Dwarven Miner", + "id": "2214" + }, + { + "name": "Dwarven Miner", + "id": "2215" + }, + { + "name": "Dwarven Miner", + "id": "2216" + }, + { + "name": "Dwarven Miner", + "id": "2217" + }, + { + "name": "Dwarven Miner", + "id": "2218" + }, + { + "name": "Purple Pewter Director", + "id": "2219" + }, + { + "name": "Purple Pewter Director", + "id": "2220" + }, + { + "name": "Blue Opal Director", + "id": "2221" + }, + { + "name": "Yellow Fortune Director", + "id": "2222" + }, + { + "name": "Green Gemstone Director", + "id": "2223" + }, + { + "name": "White Chisel Director", + "id": "2224" + }, + { + "name": "Silver Cog Director", + "id": "2225" + }, + { + "name": "Brown Engine Director", + "id": "2226" + }, + { + "name": "Red Axe Director", + "id": "2227" + }, + { + "name": "Commander Veldaban", + "id": "2228" + }, + { + "name": "Red Axe Cat", + "id": "2229" + }, + { + "name": "Red Axe Cat", + "id": "2230" + }, + { + "name": "Black Guard Berserker", + "id": "2231" + }, + { + "name": "Olivia", + "id": "2233" + }, + { + "name": "Donie", + "id": "2238" + }, + { + "name": "Pig", + "id": "2239" + }, + { + "name": "Gnome", + "id": "2251" + }, + { + "name": "Crow", + "id": "2252" + }, + { + "name": "Bed", + "id": "2254" + }, + { + "name": "Thing under the bed", + "id": "2255" + }, + { + "name": "Mage of Zamorak", + "id": "2257" + }, + { + "name": "Mage of Zamorak", + "id": "2259" + }, + { + "name": "Mage of Zamorak", + "id": "2260" + }, + { + "name": "Brian O'Richard", + "id": "2266" + }, + { + "name": "Rogue Guard", + "id": "2268" + }, + { + "name": "Martin Thwait", + "id": "2270" + }, + { + "name": "Spin Blades", + "id": "2273" + }, + { + "name": "Rug Merchant", + "id": "2295" + }, + { + "name": "Rug Merchant", + "id": "2297" + }, + { + "name": "Rug Station Attendant", + "id": "2299" + }, + { + "name": "Sarah", + "id": "2302" + }, + { + "name": "Vanessa", + "id": "2305" + }, + { + "name": "Richard", + "id": "2306" + }, + { + "name": "Alice", + "id": "2307" + }, + { + "name": "Capt' Arnav", + "id": "2308" + }, + { + "name": "Metarialus", + "id": "2322" + }, + { + "name": "Sick-looking sheep (1)", + "id": "2345" + }, + { + "name": "Sick-looking sheep (2)", + "id": "2346" + }, + { + "name": "Sick-looking sheep (3)", + "id": "2347" + }, + { + "name": "Sick-looking sheep (4)", + "id": "2348" + }, + { + "name": "Mourner", + "id": "2349" + }, + { + "name": "Mourner", + "id": "2350" + }, + { + "name": "Mourner", + "id": "2351" + }, + { + "name": "Eudav", + "id": "2352" + }, + { + "name": "Oronwen", + "id": "2353" + }, + { + "name": "Dalldav", + "id": "2356" + }, + { + "name": "Gethin", + "id": "2357" + }, + { + "name": "Arianwyn", + "id": "2358" + }, + { + "name": "Goreu", + "id": "2363" + }, + { + "name": "Ysgawyn", + "id": "2364" + }, + { + "name": "Arvel", + "id": "2365" + }, + { + "name": "Mawrth", + "id": "2366" + }, + { + "name": "Kelyn", + "id": "2367" + }, + { + "name": "Eoin", + "id": "2368" + }, + { + "name": "Iona", + "id": "2369" + }, + { + "name": "Gnome", + "id": "2370" + }, + { + "name": "Eluned", + "id": "2375" + }, + { + "name": "Sick-looking sheep (1)", + "id": "2377" + }, + { + "name": "Sick-looking sheep (2)", + "id": "2378" + }, + { + "name": "Sick-looking sheep (3)", + "id": "2379" + }, + { + "name": "Sick-looking sheep (4)", + "id": "2380" + }, + { + "name": "Cart conductor", + "id": "2403" + }, + { + "name": "Red Axe Director", + "id": "2410" + }, + { + "name": "Red Axe Director", + "id": "2411" + }, + { + "name": "Red Axe Henchman", + "id": "2412" + }, + { + "name": "Red Axe Henchman", + "id": "2413" + }, + { + "name": "Red Axe Henchman", + "id": "2414" + }, + { + "name": "Colonel Grimsson", + "id": "2415" + }, + { + "name": "Colonel Grimsson", + "id": "2416" + }, + { + "name": "Ogre shaman", + "id": "2417" + }, + { + "name": "Ogre shaman", + "id": "2418" + }, + { + "name": "Grunsh", + "id": "2419" + }, + { + "name": "Gnome emissary", + "id": "2420" + }, + { + "name": "Gnome companion", + "id": "2421" + }, + { + "name": "Gnome companion", + "id": "2422" + }, + { + "name": "Gunslik", + "id": "2424" + }, + { + "name": "Nolar", + "id": "2425" + }, + { + "name": "Factory Worker", + "id": "2426" + }, + { + "name": "Cart conductor", + "id": "2427" + }, + { + "name": "Gauss", + "id": "2428" + }, + { + "name": "Drunken Dwarf", + "id": "2429" + }, + { + "name": "Rowdy dwarf", + "id": "2430" + }, + { + "name": "Ulifed", + "id": "2431" + }, + { + "name": "Red Axe Henchman", + "id": "2432" + }, + { + "name": "Red Axe Henchman", + "id": "2433" + }, + { + "name": "Ogre shaman", + "id": "2434" + }, + { + "name": "Jarvald", + "id": "2435" + }, + { + "name": "Jarvald", + "id": "2437" + }, + { + "name": "Jarvald", + "id": "2438" + }, + { + "name": "Door-support", + "id": "2440" + }, + { + "name": "Door", + "id": "2441" + }, + { + "name": "Door", + "id": "2442" + }, + { + "name": "Door", + "id": "2444" + }, + { + "name": "Door", + "id": "2445" + }, + { + "name": "Door-support", + "id": "2446" + }, + { + "name": "Door", + "id": "2447" + }, + { + "name": "Door", + "id": "2448" + }, + { + "name": "Egg", + "id": "2449" + }, + { + "name": "Egg", + "id": "2450" + }, + { + "name": "Egg", + "id": "2451" + }, + { + "name": "Freaky Forester", + "id": "2458" + }, + { + "name": "Frog", + "id": "2469" + }, + { + "name": "Frog", + "id": "2470" + }, + { + "name": "Frog", + "id": "2471" + }, + { + "name": "Frog", + "id": "2472" + }, + { + "name": "Frog", + "id": "2473" + }, + { + "name": "Frog prince", + "id": "2474" + }, + { + "name": "Frog princess", + "id": "2475" + }, + { + "name": "Evil Bob", + "id": "2478" + }, + { + "name": "Servant", + "id": "2480" + }, + { + "name": "Bush snake", + "id": "2483" + }, + { + "name": "Broodoo victim", + "id": "2498" + }, + { + "name": "Broodoo victim", + "id": "2500" + }, + { + "name": "Broodoo victim", + "id": "2502" + }, + { + "name": "Sharimika", + "id": "2504" + }, + { + "name": "Mamma Bufetta", + "id": "2507" + }, + { + "name": "Layleen", + "id": "2510" + }, + { + "name": "Karaday", + "id": "2513" + }, + { + "name": "Safta Doc", + "id": "2516" + }, + { + "name": "Gabooty", + "id": "2519" + }, + { + "name": "Fanellaman", + "id": "2522" + }, + { + "name": "Jagbakoba", + "id": "2525" + }, + { + "name": "Murcaily", + "id": "2528" + }, + { + "name": "Rionasta", + "id": "2531" + }, + { + "name": "Mahogany", + "id": "2534" + }, + { + "name": "Teak", + "id": "2535" + }, + { + "name": "Niles", + "id": "2536" + }, + { + "name": "Miles", + "id": "2537" + }, + { + "name": "Giles", + "id": "2538" + }, + { + "name": "Dr Jekyll", + "id": "2540" + }, + { + "name": "Mr Hyde", + "id": "2541" + }, + { + "name": "Mr Hyde", + "id": "2542" + }, + { + "name": "Mr Hyde", + "id": "2543" + }, + { + "name": "Mr Hyde", + "id": "2544" + }, + { + "name": "Mr Hyde", + "id": "2545" + }, + { + "name": "Mr Hyde", + "id": "2546" + }, + { + "name": "Blackjack seller", + "id": "2548" + }, + { + "name": "Dwarven Miner", + "id": "2550" + }, + { + "name": "Dwarven Miner", + "id": "2551" + }, + { + "name": "Dwarven Miner", + "id": "2552" + }, + { + "name": "Tin ore", + "id": "2554" + }, + { + "name": "Copper ore", + "id": "2555" + }, + { + "name": "Iron ore", + "id": "2556" + }, + { + "name": "Mithril ore", + "id": "2557" + }, + { + "name": "Adamantite ore", + "id": "2558" + }, + { + "name": "Runite ore", + "id": "2559" + }, + { + "name": "Silver ore", + "id": "2560" + }, + { + "name": "Gold ore", + "id": "2561" + }, + { + "name": "Coal", + "id": "2562" + }, + { + "name": "Perfect gold ore", + "id": "2563" + }, + { + "name": "Wise Old Man", + "id": "2566" + }, + { + "name": "Wise Old Man", + "id": "2567" + }, + { + "name": "Banker", + "id": "2568" + }, + { + "name": "Banker", + "id": "2569" + }, + { + "name": "Banker", + "id": "2570" + }, + { + "name": "Pillory Guard", + "id": "2573" + }, + { + "name": "Purepker895", + "id": "2575" + }, + { + "name": "Qutiedoll", + "id": "2576" + }, + { + "name": "1337sp34kr", + "id": "2577" + }, + { + "name": "Elfinlocks", + "id": "2578" + }, + { + "name": "Cool Mom227", + "id": "2579" + }, + { + "name": "Ellamaria", + "id": "2581" + }, + { + "name": "Trolley", + "id": "2582" + }, + { + "name": "Trolley", + "id": "2583" + }, + { + "name": "Trolley", + "id": "2584" + }, + { + "name": "Billy, a guard of Falador", + "id": "2585" + }, + { + "name": "Bob, another guard of Falador", + "id": "2587" + }, + { + "name": "PKMaster0036", + "id": "2589" + }, + { + "name": "King Roald", + "id": "2590" + }, + { + "name": "TzHaar-Mej-Jal", + "id": "2617" + }, + { + "name": "TzHaar-Mej-Kah", + "id": "2618" + }, + { + "name": "TzHaar-Ket-Zuh", + "id": "2619" + }, + { + "name": "TzHaar-Hur-Tel", + "id": "2620" + }, + { + "name": "TzHaar-Hur-Lek", + "id": "2622" + }, + { + "name": "TzHaar-Mej-Roh", + "id": "2623" + }, + { + "name": "TzHaar-Ket", + "id": "2624" + }, + { + "name": "TzHaar-Ket", + "id": "2625" + }, + { + "name": "Rocks", + "id": "2626" + }, + { + "name": "Tok-Xil", + "id": "2632" + }, + { + "name": "Wise Old Man", + "id": "2633" + }, + { + "name": "Bob", + "id": "2635" + }, + { + "name": "Bob", + "id": "2636" + }, + { + "name": "Sphinx", + "id": "2637" + }, + { + "name": "Neite", + "id": "2638" + }, + { + "name": "Robert the Strong", + "id": "2639" + }, + { + "name": "Odysseus", + "id": "2640" + }, + { + "name": "King Black Dragon", + "id": "2642" + }, + { + "name": "R4ng3rNo0b889", + "id": "2643" + }, + { + "name": "Love Cats", + "id": "2644" + }, + { + "name": "Love Cats", + "id": "2645" + }, + { + "name": "Neite", + "id": "2646" + }, + { + "name": "Bob", + "id": "2647" + }, + { + "name": "Beite", + "id": "2648" + }, + { + "name": "Gnome", + "id": "2649" + }, + { + "name": "Gnome", + "id": "2650" + }, + { + "name": "Odysseus", + "id": "2651" + }, + { + "name": "Neite", + "id": "2652" + }, + { + "name": "Unferth", + "id": "2654" + }, + { + "name": "Unferth", + "id": "2656" + }, + { + "name": "Unferth", + "id": "2657" + }, + { + "name": "Unferth", + "id": "2658" + }, + { + "name": "Unferth", + "id": "2659" + }, + { + "name": "Reldo", + "id": "2661" + }, + { + "name": "Lazy cat", + "id": "2662" + }, + { + "name": "Lazy cat", + "id": "2663" + }, + { + "name": "Lazy cat", + "id": "2664" + }, + { + "name": "Lazy cat", + "id": "2665" + }, + { + "name": "Lazy cat", + "id": "2666" + }, + { + "name": "Lazy cat", + "id": "2667" + }, + { + "name": "Wily cat", + "id": "2668" + }, + { + "name": "Wily cat", + "id": "2669" + }, + { + "name": "Wily cat", + "id": "2670" + }, + { + "name": "Wily cat", + "id": "2671" + }, + { + "name": "Wily cat", + "id": "2672" + }, + { + "name": "Wily cat", + "id": "2673" + }, + { + "name": "Make-over Mage", + "id": "2676" + }, + { + "examine": "So what can one do with a drunken sailor?", + "name": "Ahab", + "id": "2692" + }, + { + "name": "Seagull", + "id": "2708" + }, + { + "name": "Grum", + "id": "2719" + }, + { + "name": "Gerrant", + "id": "2720" + }, + { + "name": "Wydin", + "id": "2721" + }, + { + "name": "Fishing spot", + "id": "2722" + }, + { + "name": "Fishing spot", + "id": "2723" + }, + { + "name": "Fishing spot", + "id": "2724" + }, + { + "name": "Gull", + "id": "2727" + }, + { + "name": "Monk of Entrana", + "id": "2730" + }, + { + "name": "Solus Dellagar", + "id": "2747" + }, + { + "name": "Savant", + "id": "2748" + }, + { + "name": "Lord Daquarius", + "id": "2749" + }, + { + "name": "Solus Dellagar", + "id": "2750" + }, + { + "name": "Black Knight", + "id": "2751" + }, + { + "name": "Lord Daquarius", + "id": "2752" + }, + { + "name": "Mage of Zamorak", + "id": "2753" + }, + { + "name": "Mage of Zamorak", + "id": "2754" + }, + { + "name": "Mage of Zamorak", + "id": "2755" + }, + { + "name": "Woman", + "id": "2756" + }, + { + "name": "Black Knight", + "id": "2777" + }, + { + "name": "Black Knight", + "id": "2778" + }, + { + "name": "Solus Dellagar", + "id": "2780" + }, + { + "name": "Gnome guard", + "id": "2781" + }, + { + "name": "Thorgel", + "id": "2788" + }, + { + "name": "Pillory Guard", + "id": "2791" + }, + { + "name": "Tramp", + "id": "2793" + }, + { + "name": "Tramp", + "id": "2794" + }, + { + "name": "Skippy", + "id": "2795" + }, + { + "name": "Skippy", + "id": "2797" + }, + { + "name": "Skippy", + "id": "2798" + }, + { + "name": "Skippy", + "id": "2799" + }, + { + "name": "A pile of broken glass", + "id": "2800" + }, + { + "name": "Alice the Camel", + "id": "2813" + }, + { + "name": "Ali the Smith", + "id": "2816" + }, + { + "name": "Ali the Farmer", + "id": "2821" + }, + { + "name": "Ali the Tailor", + "id": "2822" + }, + { + "name": "Ali the Guard", + "id": "2823" + }, + { + "name": "Davey", + "id": "2829" + }, + { + "name": "Fishing spot", + "id": "2859" + }, + { + "name": "Death", + "id": "2862" + }, + { + "name": "Most of a Zombie", + "id": "2864" + }, + { + "name": "Most of a Zombie", + "id": "2865" + }, + { + "name": "Most of a Zombie", + "id": "2867" + }, + { + "name": "Zombie Head", + "id": "2868" + }, + { + "name": "Half-Zombie", + "id": "2870" + }, + { + "name": "Other Half-Zombie", + "id": "2871" + }, + { + "name": "Child", + "id": "2872" + }, + { + "name": "Child", + "id": "2873" + }, + { + "name": "Child", + "id": "2874" + }, + { + "name": "Child", + "id": "2875" + }, + { + "name": "Child", + "id": "2876" + }, + { + "name": "Child", + "id": "2877" + }, + { + "name": "Bardur", + "id": "2879" + }, + { + "name": "Wallasalki", + "id": "2884" + }, + { + "name": "Suspicious water", + "id": "2891" + }, + { + "name": "Suspicious water", + "id": "2893" + }, + { + "name": "Suspicious water", + "id": "2895" + }, + { + "name": "Father Reen", + "id": "2897" + }, + { + "name": "Father Reen", + "id": "2900" + }, + { + "name": "Father Badden", + "id": "2901" + }, + { + "name": "Father Badden", + "id": "2903" + }, + { + "name": "Denath", + "id": "2904" + }, + { + "name": "Denath", + "id": "2905" + }, + { + "name": "Eric", + "id": "2906" + }, + { + "name": "Eric", + "id": "2907" + }, + { + "name": "Evil Dave", + "id": "2908" + }, + { + "name": "Evil Dave", + "id": "2910" + }, + { + "name": "Matthew", + "id": "2911" + }, + { + "name": "Matthew", + "id": "2912" + }, + { + "name": "Jennifer", + "id": "2913" + }, + { + "name": "Jennifer", + "id": "2914" + }, + { + "name": "Tanya", + "id": "2915" + }, + { + "name": "Tanya", + "id": "2916" + }, + { + "name": "Patrick", + "id": "2917" + }, + { + "name": "Patrick", + "id": "2918" + }, + { + "name": "Sand storm", + "id": "2920" + }, + { + "name": "Clay golem", + "id": "2922" + }, + { + "name": "Clay golem", + "id": "2928" + }, + { + "name": "Ghost", + "id": "2929" + }, + { + "name": "Jorral", + "id": "2932" + }, + { + "name": "Melina", + "id": "2933" + }, + { + "name": "Melina", + "id": "2935" + }, + { + "name": "Droalak", + "id": "2936" + }, + { + "name": "Droalak", + "id": "2938" + }, + { + "name": "Dron", + "id": "2939" + }, + { + "name": "Blanin", + "id": "2940" + }, + { + "name": "Pox", + "id": "2943" + }, + { + "name": "Pox", + "id": "2944" + }, + { + "examine": "Cracking personality.", + "name": "Grimesquit", + "id": "2946" + }, + { + "examine": "Lovely girl, shame about the smell.", + "name": "Phingspet", + "id": "2947" + }, + { + "name": "Felkrash", + "id": "2951" + }, + { + "name": "Ceril Carnillean", + "id": "2953" + }, + { + "name": "Councillor Halgrive", + "id": "2954" + }, + { + "name": "Spice seller", + "id": "2955" + }, + { + "name": "Fur trader", + "id": "2956" + }, + { + "name": "Gem merchant", + "id": "2957" + }, + { + "name": "Silk merchant", + "id": "2959" + }, + { + "name": "Zenesha", + "id": "2960" + }, + { + "name": "Ali Morrisane", + "id": "2961" + }, + { + "name": "Rat", + "id": "2974" + }, + { + "name": "Turbogroomer", + "id": "2983" + }, + { + "name": "Loki", + "id": "2985" + }, + { + "name": "Treacle", + "id": "2987" + }, + { + "name": "Claude", + "id": "2989" + }, + { + "name": "Rauborn", + "id": "2991" + }, + { + "name": "Vaeringk", + "id": "2992" + }, + { + "name": "Oxi", + "id": "2993" + }, + { + "name": "Fior", + "id": "2994" + }, + { + "name": "Sagira", + "id": "2995" + }, + { + "name": "Anleif", + "id": "2996" + }, + { + "name": "Gambler", + "id": "2999" + }, + { + "name": "Barman", + "id": "3000" + }, + { + "name": "Fishing spot", + "id": "3019" + }, + { + "name": "Rug Merchant", + "id": "3020" + }, + { + "name": "Nirrie", + "id": "3023" + }, + { + "name": "Tirrie", + "id": "3024" + }, + { + "name": "Hallak", + "id": "3025" + }, + { + "name": "Usi", + "id": "3031" + }, + { + "name": "Nkuku", + "id": "3032" + }, + { + "name": "Garai", + "id": "3033" + }, + { + "name": "Habibah", + "id": "3034" + }, + { + "name": "Meskhenet", + "id": "3035" + }, + { + "name": "Zahra", + "id": "3036" + }, + { + "name": "Zahur", + "id": "3037" + }, + { + "name": "Seddu", + "id": "3038" + }, + { + "name": "Kazemde", + "id": "3039" + }, + { + "name": "Tarik", + "id": "3041" + }, + { + "name": "Rokuh", + "id": "3045" + }, + { + "name": "Target", + "id": "3047" + }, + { + "name": "Target", + "id": "3048" + }, + { + "name": "Larxus", + "id": "3049" + }, + { + "name": "Dead Monk", + "id": "3076" + }, + { + "name": "High Priest", + "id": "3078" + }, + { + "name": "Assassin", + "id": "3081" + }, + { + "name": "Rosie", + "id": "3082" + }, + { + "name": "Sorcha", + "id": "3083" + }, + { + "name": "Cait", + "id": "3084" + }, + { + "name": "Cormac", + "id": "3085" + }, + { + "name": "Fionn", + "id": "3086" + }, + { + "name": "Donnacha", + "id": "3087" + }, + { + "name": "Ronan", + "id": "3088" + }, + { + "name": "Flying Book", + "id": "3093" + }, + { + "name": "Flying Book", + "id": "3095" + }, + { + "name": "Pizzaz Hat", + "id": "3096" + }, + { + "name": "Charmed Warrior", + "id": "3107" + }, + { + "name": "Bert", + "id": "3108" + }, + { + "name": "Sandy", + "id": "3110" + }, + { + "name": "Sandy", + "id": "3113" + }, + { + "name": "Mazion", + "id": "3114" + }, + { + "name": "Reeso", + "id": "3116" + }, + { + "name": "Prison Pete", + "id": "3118" + }, + { + "name": "Balloon Animal", + "id": "3119" + }, + { + "name": "Balloon Animal", + "id": "3120" + }, + { + "name": "Pyramid block", + "id": "3124" + }, + { + "name": "Pyramid block", + "id": "3125" + }, + { + "name": "Pentyn", + "id": "3126" + }, + { + "name": "Aristarchus", + "id": "3127" + }, + { + "name": "Boneguard", + "id": "3128" + }, + { + "name": "Pile of bones", + "id": "3130" + }, + { + "name": "Desert Spirit", + "id": "3131" + }, + { + "name": "Crust of ice", + "id": "3132" + }, + { + "name": "Furnace grate", + "id": "3134" + }, + { + "name": "Enakhra", + "id": "3136" + }, + { + "name": "Enakhra", + "id": "3138" + }, + { + "name": "Boneguard", + "id": "3139" + }, + { + "name": "Akthanakos", + "id": "3141" + }, + { + "name": "Akthanakos", + "id": "3142" + }, + { + "name": "Lazim", + "id": "3143" + }, + { + "name": "Enakhra", + "id": "3148" + }, + { + "name": "Akthanakos", + "id": "3149" + }, + { + "name": "Harpie Bug Swarm", + "id": "3152" + }, + { + "name": "Count Draynor", + "id": "3154" + }, + { + "name": "Bill Teach", + "id": "3156" + }, + { + "name": "Bill Teach", + "id": "3157" + }, + { + "name": "Bill Teach", + "id": "3158" + }, + { + "name": "Bill Teach", + "id": "3159" + }, + { + "name": "Bill Teach", + "id": "3160" + }, + { + "name": "Charley", + "id": "3161" + }, + { + "name": "Smith", + "id": "3162" + }, + { + "name": "Joe", + "id": "3163" + }, + { + "name": "Mama", + "id": "3164" + }, + { + "name": "Mama", + "id": "3165" + }, + { + "name": "Gull", + "id": "3197" + }, + { + "name": "Romily Weaklax", + "id": "3205" + }, + { + "name": "Priest", + "id": "3206" + }, + { + "name": "Pious Pete", + "id": "3207" + }, + { + "name": "Taper", + "id": "3208" + }, + { + "name": "Alrena", + "id": "3210" + }, + { + "name": "Alrena", + "id": "3211" + }, + { + "name": "Bravek", + "id": "3212" + }, + { + "name": "Tina", + "id": "3218" + }, + { + "name": "Hunding", + "id": "3254" + }, + { + "name": "Engineering assistant", + "id": "3281" + }, + { + "name": "Squirrel", + "id": "3284" + }, + { + "name": "Squirrel", + "id": "3285" + }, + { + "name": "Raccoon", + "id": "3287" + }, + { + "name": "Skeleton", + "id": "3289" + }, + { + "name": "Witch", + "id": "3292" + }, + { + "name": "Frog", + "id": "3300" + }, + { + "name": "Storm cloud", + "id": "3301" + }, + { + "name": "Fairy Nuff", + "id": "3303" + }, + { + "name": "Slim Louie", + "id": "3305" + }, + { + "name": "Fat Rocco", + "id": "3306" + }, + { + "name": "Zandar Horfyre", + "id": "3308" + }, + { + "name": "Sheep", + "id": "3311" + }, + { + "name": "Zanaris choir", + "id": "3312" + }, + { + "name": "Baby tanglefoot", + "id": "3314" + }, + { + "name": "Gatekeeper", + "id": "3321" + }, + { + "name": "Draul Leptoc", + "id": "3323" + }, + { + "name": "Martina Scorsby", + "id": "3326" + }, + { + "name": "Tarquin", + "id": "3328" + }, + { + "name": "Sigurd", + "id": "3329" + }, + { + "name": "Hari", + "id": "3330" + }, + { + "name": "Trees", + "id": "3332" + }, + { + "name": "Trees", + "id": "3333" + }, + { + "name": "Bullrush", + "id": "3335" + }, + { + "name": "Bullrush", + "id": "3336" + }, + { + "name": "Cave scenery", + "id": "3337" + }, + { + "name": "Cave scenery", + "id": "3338" + }, + { + "name": "Cave scenery", + "id": "3339" + }, + { + "name": "Genie", + "id": "3351" + }, + { + "name": "Mysterious Old Man", + "id": "3352" + }, + { + "name": "Swarm", + "id": "3353" + }, + { + "name": "Cap'n Hand", + "id": "3354" + }, + { + "name": "Rick Turpentine", + "id": "3355" + }, + { + "name": "Niles", + "id": "3356" + }, + { + "name": "Miles", + "id": "3357" + }, + { + "name": "Giles", + "id": "3358" + }, + { + "name": "Dr Jekyll", + "id": "3359" + }, + { + "name": "Mr Hyde", + "id": "3360" + }, + { + "name": "Mr Hyde", + "id": "3361" + }, + { + "name": "Mr Hyde", + "id": "3362" + }, + { + "name": "Mr Hyde", + "id": "3363" + }, + { + "name": "Mr Hyde", + "id": "3364" + }, + { + "name": "Mr Hyde", + "id": "3365" + }, + { + "name": "Sir Amik Varze", + "id": "3372" + }, + { + "name": "Sir Amik Varze", + "id": "3373" + }, + { + "name": "K'klik", + "id": "3377" + }, + { + "name": "Evil Dave", + "id": "3379" + }, + { + "name": "Doris", + "id": "3381" + }, + { + "name": "Gypsy", + "id": "3383" + }, + { + "name": "Gypsy", + "id": "3386" + }, + { + "name": "Culinaromancer", + "id": "3387" + }, + { + "name": "Osman", + "id": "3388" + }, + { + "name": "Sir Amik Varze", + "id": "3395" + }, + { + "name": "Awowogei", + "id": "3396" + }, + { + "name": "Awowogei", + "id": "3397" + }, + { + "name": "Skrach Uglogwee", + "id": "3398" + }, + { + "name": "Culinaromancer", + "id": "3399" + }, + { + "name": "An old Dwarf", + "id": "3401" + }, + { + "name": "Rohak", + "id": "3403" + }, + { + "name": "Pirate Pete", + "id": "3417" + }, + { + "name": "Fish", + "id": "3428" + }, + { + "name": "Fish", + "id": "3429" + }, + { + "name": "Fish", + "id": "3430" + }, + { + "name": "Fish", + "id": "3440" + }, + { + "name": "Fish", + "id": "3441" + }, + { + "name": "Fish", + "id": "3442" + }, + { + "name": "Fish", + "id": "3446" + }, + { + "name": "Fish", + "id": "3447" + }, + { + "name": "Fish", + "id": "3448" + }, + { + "name": "? ? ? ?", + "id": "3452" + }, + { + "name": "? ? ? ?", + "id": "3453" + }, + { + "name": "? ? ? ?", + "id": "3454" + }, + { + "name": "? ? ? ?", + "id": "3455" + }, + { + "name": "? ? ? ?", + "id": "3456" + }, + { + "name": "? ? ? ?", + "id": "3457" + }, + { + "name": "? ? ? ?", + "id": "3458" + }, + { + "name": "? ? ? ?", + "id": "3459" + }, + { + "name": "? ? ? ?", + "id": "3460" + }, + { + "name": "? ? ? ?", + "id": "3461" + }, + { + "name": "Skrach Uglogwee", + "id": "3462" + }, + { + "name": "Skrach Uglogwee", + "id": "3464" + }, + { + "name": "Nung", + "id": "3465" + }, + { + "name": "Ogre", + "id": "3466" + }, + { + "name": "Rantz", + "id": "3467" + }, + { + "name": "Rantz", + "id": "3468" + }, + { + "name": "Ogre boat", + "id": "3469" + }, + { + "name": "Ogre boat", + "id": "3472" + }, + { + "name": "Balloon Toad", + "id": "3473" + }, + { + "name": "Balloon Toad", + "id": "3474" + }, + { + "name": "Balloon Toad", + "id": "3475" + }, + { + "name": "Jubbly bird", + "id": "3477" + }, + { + "name": "King Awowogei", + "id": "3479" + }, + { + "name": "Mizaru", + "id": "3481" + }, + { + "name": "Kikazaru", + "id": "3482" + }, + { + "name": "Iwazaru", + "id": "3483" + }, + { + "name": "Culinaromancer", + "id": "3485" + }, + { + "name": "Culinaromancer", + "id": "3486" + }, + { + "name": "Culinaromancer", + "id": "3487" + }, + { + "name": "Culinaromancer", + "id": "3488" + }, + { + "name": "Culinaromancer", + "id": "3489" + }, + { + "name": "Culinaromancer", + "id": "3490" + }, + { + "name": "Culinaromancer", + "id": "3492" + }, + { + "name": "Overgrown hellcat", + "id": "3503" + }, + { + "name": "Hellcat", + "id": "3504" + }, + { + "name": "Hell-kitten", + "id": "3505" + }, + { + "name": "Lazy hellcat", + "id": "3506" + }, + { + "name": "Wily hellcat", + "id": "3507" + }, + { + "name": "Leo", + "id": "3508" + }, + { + "name": "Wiskit", + "id": "3510" + }, + { + "name": "Vampyre Juvinate", + "id": "3512" + }, + { + "name": "Vampyre Juvinate", + "id": "3515" + }, + { + "name": "Gadderanks", + "id": "3516" + }, + { + "name": "Gadderanks", + "id": "3518" + }, + { + "name": "Gadderanks", + "id": "3519" + }, + { + "name": "Vampyre Juvinate", + "id": "3520" + }, + { + "name": "Vampyre Juvinate", + "id": "3528" + }, + { + "name": "Vampyre Juvinate", + "id": "3529" + }, + { + "name": "Mist", + "id": "3530" + }, + { + "name": "Ivan Strom", + "id": "3535" + }, + { + "name": "Ivan Strom", + "id": "3536" + }, + { + "name": "Vampyre Juvinate", + "id": "3537" + }, + { + "name": "Vampyre Juvinate", + "id": "3538" + }, + { + "name": "Veliaf Hurtz", + "id": "3539" + }, + { + "name": "Elisabeta", + "id": "3540" + }, + { + "name": "Aurel", + "id": "3541" + }, + { + "name": "Sorin", + "id": "3542" + }, + { + "name": "Luscion", + "id": "3543" + }, + { + "name": "Sergiu", + "id": "3544" + }, + { + "name": "Radu", + "id": "3545" + }, + { + "name": "Grigore", + "id": "3546" + }, + { + "name": "Ileana", + "id": "3547" + }, + { + "name": "Valeria", + "id": "3548" + }, + { + "name": "Emilia", + "id": "3549" + }, + { + "name": "Florin", + "id": "3550" + }, + { + "name": "Catalina", + "id": "3551" + }, + { + "name": "Ivan", + "id": "3552" + }, + { + "name": "Victor", + "id": "3553" + }, + { + "name": "Helena", + "id": "3554" + }, + { + "name": "Teodor", + "id": "3555" + }, + { + "name": "Marius", + "id": "3556" + }, + { + "name": "Gabriela", + "id": "3557" + }, + { + "name": "Vladimir", + "id": "3558" + }, + { + "name": "Calin", + "id": "3559" + }, + { + "name": "Mihail", + "id": "3560" + }, + { + "name": "Nicoleta", + "id": "3561" + }, + { + "name": "Simona", + "id": "3562" + }, + { + "name": "Vasile", + "id": "3563" + }, + { + "name": "Razvan", + "id": "3564" + }, + { + "name": "Luminata", + "id": "3565" + }, + { + "name": "Cornelius", + "id": "3566" + }, + { + "name": "Cornelius", + "id": "3569" + }, + { + "name": "Benjamin", + "id": "3570" + }, + { + "name": "Liam", + "id": "3571" + }, + { + "name": "Miala", + "id": "3572" + }, + { + "name": "Verak", + "id": "3573" + }, + { + "name": "Fishing spot", + "id": "3574" + }, + { + "name": "Fishing spot", + "id": "3575" + }, + { + "name": "Juvinate", + "id": "3576" + }, + { + "name": "Juvinate", + "id": "3578" + }, + { + "name": "Tentacle", + "id": "3580" + }, + { + "name": "Troll", + "id": "3584" + }, + { + "name": "Tok-Xil", + "id": "3592" + }, + { + "name": "Rocnar", + "id": "3594" + }, + { + "name": "Toy Soldier", + "id": "3595" + }, + { + "name": "Toy Doll", + "id": "3596" + }, + { + "name": "Toy Mouse", + "id": "3597" + }, + { + "name": "Clockwork cat", + "id": "3598" + }, + { + "name": "Ghast", + "id": "3606" + }, + { + "name": "Ghast", + "id": "3607" + }, + { + "name": "Ghast", + "id": "3608" + }, + { + "name": "Ghast", + "id": "3609" + }, + { + "name": "Ghast", + "id": "3610" + }, + { + "name": "Ghast", + "id": "3611" + }, + { + "name": "Giant snail", + "id": "3612" + }, + { + "name": "Giant snail", + "id": "3613" + }, + { + "name": "Giant snail", + "id": "3614" + }, + { + "name": "Riyl shadow", + "id": "3615" + }, + { + "name": "Asyn shadow", + "id": "3616" + }, + { + "name": "Shade", + "id": "3617" + }, + { + "name": "Tentacle", + "id": "3621" + }, + { + "name": "Smiddi Ryak", + "id": "3623" + }, + { + "name": "Rolayne Twickit", + "id": "3625" + }, + { + "name": "Jayene Kliyn", + "id": "3627" + }, + { + "name": "Valantay Eppel", + "id": "3629" + }, + { + "name": "Dalcian Fang", + "id": "3631" + }, + { + "name": "Fyiona Fray", + "id": "3633" + }, + { + "name": "Abidor Crank", + "id": "3635" + }, + { + "name": "Spirit tree", + "id": "3636" + }, + { + "name": "Spirit tree", + "id": "3637" + }, + { + "name": "Launa", + "id": "3638" + }, + { + "name": "Launa", + "id": "3640" + }, + { + "name": "Brana", + "id": "3641" + }, + { + "name": "Mawnis Burowgar", + "id": "3642" + }, + { + "name": "Tolna", + "id": "3643" + }, + { + "name": "Tolna", + "id": "3644" + }, + { + "name": "Confusion beast", + "id": "3651" + }, + { + "name": "Confusion beast", + "id": "3652" + }, + { + "name": "Confusion beast", + "id": "3653" + }, + { + "name": "Confusion beast", + "id": "3654" + }, + { + "name": "Hopeless creature", + "id": "3656" + }, + { + "name": "Hopeless creature", + "id": "3657" + }, + { + "name": "Tolna", + "id": "3658" + }, + { + "name": "Tolna", + "id": "3659" + }, + { + "name": "Tolna", + "id": "3660" + }, + { + "name": "Hopeless beast", + "id": "3668" + }, + { + "name": "Hopeless beast", + "id": "3669" + }, + { + "name": "Sinister Stranger", + "id": "3677" + }, + { + "name": "Sinister Stranger", + "id": "3678" + }, + { + "name": "Vestri", + "id": "3679" + }, + { + "name": "Nigel", + "id": "3681" + }, + { + "name": "Egg", + "id": "3682" + }, + { + "name": "Egg", + "id": "3683" + }, + { + "name": "Egg", + "id": "3684" + }, + { + "name": "Egg", + "id": "3685" + }, + { + "name": "Chocolate kebbit", + "id": "3686" + }, + { + "name": "Chocolate kebbit", + "id": "3687" + }, + { + "name": "Easter Bunny", + "id": "3688" + }, + { + "name": "Egg", + "id": "3689" + }, + { + "name": "Egg", + "id": "3690" + }, + { + "name": "Egg", + "id": "3691" + }, + { + "name": "Egg", + "id": "3692" + }, + { + "name": "Egg", + "id": "3693" + }, + { + "name": "Egg", + "id": "3694" + }, + { + "name": "Volf Olafson", + "id": "3695" + }, + { + "name": "Ingrid Hradson", + "id": "3696" + }, + { + "name": "Boulder", + "id": "3708" + }, + { + "name": "Ulfric", + "id": "3710" + }, + { + "name": "Zanik", + "id": "3712" + }, + { + "name": "Sigmund", + "id": "3713" + }, + { + "name": "Zanik", + "id": "3714" + }, + { + "name": "Sigmund", + "id": "3716" + }, + { + "name": "Sigmund", + "id": "3717" + }, + { + "name": "Sigmund", + "id": "3718" + }, + { + "name": "Sigmund", + "id": "3719" + }, + { + "name": "Sigmund", + "id": "3720" + }, + { + "name": "Zanik", + "id": "3721" + }, + { + "name": "Zanik", + "id": "3722" + }, + { + "name": "General Bentnoze", + "id": "3723" + }, + { + "name": "General Wartface", + "id": "3724" + }, + { + "name": "Grubfoot", + "id": "3725" + }, + { + "name": "Arthur", + "id": "3778" + }, + { + "name": "Squire", + "id": "3780" + }, + { + "name": "Sir Palomedes", + "id": "3787" + }, + { + "name": "Void Knight", + "id": "3789" + }, + { + "name": "Fishing spot", + "id": "3803" + }, + { + "name": "Lieutenant Schepbur", + "id": "3817" + }, + { + "name": "Wise Old Man", + "id": "3820" + }, + { + "name": "Devin Mendelberg", + "id": "3825" + }, + { + "name": "George Laxmeister", + "id": "3826" + }, + { + "name": "Ramara du Croissant", + "id": "3827" + }, + { + "name": "Kathy Corkat", + "id": "3828" + }, + { + "name": "Kathy Corkat", + "id": "3831" + }, + { + "name": "Kalphite Queen", + "id": "3832" + }, + { + "name": "Drunken Dwarf", + "id": "3837" + }, + { + "name": "Wise Old Man", + "id": "3838" + }, + { + "name": "Wise Old Man", + "id": "3839" + }, + { + "name": "Sea troll", + "id": "3841" + }, + { + "name": "Sea troll", + "id": "3842" + }, + { + "name": "Skeleton Mage", + "id": "3844" + }, + { + "name": "Sea troll", + "id": "3845" + }, + { + "name": "Sea Troll General", + "id": "3846" + }, + { + "name": "Fishing spot", + "id": "3848" + }, + { + "name": "Skeleton Mage", + "id": "3850" + }, + { + "name": "Suspect", + "id": "3852" + }, + { + "name": "Suspect", + "id": "3853" + }, + { + "name": "Suspect", + "id": "3854" + }, + { + "name": "Suspect", + "id": "3855" + }, + { + "name": "Suspect", + "id": "3856" + }, + { + "name": "Suspect", + "id": "3857" + }, + { + "name": "Suspect", + "id": "3858" + }, + { + "name": "Suspect", + "id": "3859" + }, + { + "name": "Suspect", + "id": "3860" + }, + { + "name": "Suspect", + "id": "3861" + }, + { + "name": "Suspect", + "id": "3862" + }, + { + "name": "Suspect", + "id": "3863" + }, + { + "name": "Suspect", + "id": "3864" + }, + { + "name": "Suspect", + "id": "3865" + }, + { + "name": "Suspect", + "id": "3866" + }, + { + "name": "Suspect", + "id": "3867" + }, + { + "name": "Suspect", + "id": "3868" + }, + { + "name": "Suspect", + "id": "3869" + }, + { + "name": "Suspect", + "id": "3870" + }, + { + "name": "Suspect", + "id": "3871" + }, + { + "name": "Suspect", + "id": "3872" + }, + { + "name": "Suspect", + "id": "3873" + }, + { + "name": "Suspect", + "id": "3874" + }, + { + "name": "Suspect", + "id": "3875" + }, + { + "name": "Suspect", + "id": "3876" + }, + { + "name": "Suspect", + "id": "3877" + }, + { + "name": "Suspect", + "id": "3878" + }, + { + "name": "Suspect", + "id": "3879" + }, + { + "name": "Suspect", + "id": "3880" + }, + { + "name": "Suspect", + "id": "3881" + }, + { + "name": "Suspect", + "id": "3882" + }, + { + "name": "Suspect", + "id": "3883" + }, + { + "name": "Suspect", + "id": "3884" + }, + { + "name": "Suspect", + "id": "3885" + }, + { + "name": "Suspect", + "id": "3886" + }, + { + "name": "Suspect", + "id": "3887" + }, + { + "name": "Suspect", + "id": "3888" + }, + { + "name": "Suspect", + "id": "3889" + }, + { + "name": "Suspect", + "id": "3890" + }, + { + "name": "Suspect", + "id": "3891" + }, + { + "name": "Molly", + "id": "3892" + }, + { + "name": "Molly", + "id": "3893" + }, + { + "name": "Molly", + "id": "3894" + }, + { + "name": "Molly", + "id": "3895" + }, + { + "name": "Molly", + "id": "3896" + }, + { + "name": "Molly", + "id": "3897" + }, + { + "name": "Molly", + "id": "3898" + }, + { + "name": "Molly", + "id": "3899" + }, + { + "name": "Molly", + "id": "3900" + }, + { + "name": "Molly", + "id": "3901" + }, + { + "name": "Molly", + "id": "3902" + }, + { + "name": "Molly", + "id": "3903" + }, + { + "name": "Molly", + "id": "3904" + }, + { + "name": "Molly", + "id": "3905" + }, + { + "name": "Molly", + "id": "3906" + }, + { + "name": "Molly", + "id": "3907" + }, + { + "name": "Molly", + "id": "3908" + }, + { + "name": "Molly", + "id": "3909" + }, + { + "name": "Molly", + "id": "3910" + }, + { + "name": "Molly", + "id": "3911" + }, + { + "name": "Flippa", + "id": "3912" + }, + { + "name": "Tilt", + "id": "3913" + }, + { + "name": "Gardener", + "id": "3914" + }, + { + "name": "Prince Brand", + "id": "3918" + }, + { + "name": "Princess Astrid", + "id": "3919" + }, + { + "name": "Runa", + "id": "3920" + }, + { + "name": "Osvald", + "id": "3923" + }, + { + "name": "Runolf", + "id": "3924" + }, + { + "name": "Ingrid", + "id": "3926" + }, + { + "name": "Signy", + "id": "3928" + }, + { + "name": "Hild", + "id": "3929" + }, + { + "name": "Armod", + "id": "3930" + }, + { + "name": "Beigarth", + "id": "3931" + }, + { + "name": "Reinn", + "id": "3932" + }, + { + "name": "Thorodin", + "id": "3936" + }, + { + "name": "Hangman game", + "id": "3944" + }, + { + "name": "Hangman game", + "id": "3945" + }, + { + "name": "Hangman game", + "id": "3946" + }, + { + "name": "Hangman game", + "id": "3947" + }, + { + "name": "Hangman game", + "id": "3948" + }, + { + "name": "Hangman game", + "id": "3949" + }, + { + "name": "Hangman game", + "id": "3950" + }, + { + "name": "Hangman game", + "id": "3951" + }, + { + "name": "Hangman game", + "id": "3952" + }, + { + "name": "Hangman game", + "id": "3953" + }, + { + "name": "Treasure fairy", + "id": "3954" + }, + { + "name": "Jacky Jester", + "id": "3955" + }, + { + "name": "Combat stone", + "id": "3956" + }, + { + "name": "Combat stone", + "id": "3957" + }, + { + "name": "Combat stone", + "id": "3958" + }, + { + "name": "Combat stone", + "id": "3959" + }, + { + "name": "Combat stone", + "id": "3960" + }, + { + "name": "Combat stone", + "id": "3961" + }, + { + "name": "Combat stone", + "id": "3962" + }, + { + "name": "Combat stone", + "id": "3963" + }, + { + "name": "Combat stone", + "id": "3964" + }, + { + "name": "Combat stone", + "id": "3965" + }, + { + "name": "Combat stone", + "id": "3966" + }, + { + "name": "Combat stone", + "id": "3967" + }, + { + "name": "Combat stone", + "id": "3968" + }, + { + "name": "Combat stone", + "id": "3969" + }, + { + "name": "Combat stone", + "id": "3970" + }, + { + "name": "Combat stone", + "id": "3971" + }, + { + "name": "Combat stone", + "id": "3972" + }, + { + "name": "Combat stone", + "id": "3973" + }, + { + "name": "Combat stone", + "id": "3974" + }, + { + "name": "Combat stone", + "id": "3975" + }, + { + "name": "Combat stone", + "id": "3976" + }, + { + "name": "Combat stone", + "id": "3977" + }, + { + "name": "Combat stone", + "id": "3978" + }, + { + "name": "Combat stone", + "id": "3979" + }, + { + "name": "Combat stone", + "id": "3980" + }, + { + "name": "Combat stone", + "id": "3981" + }, + { + "name": "Combat stone", + "id": "3982" + }, + { + "name": "Combat stone", + "id": "3983" + }, + { + "name": "Combat stone", + "id": "3984" + }, + { + "name": "Combat stone", + "id": "3985" + }, + { + "name": "Combat stone", + "id": "3986" + }, + { + "name": "Combat stone", + "id": "3987" + }, + { + "name": "Combat stone", + "id": "3988" + }, + { + "name": "Combat stone", + "id": "3989" + }, + { + "name": "Combat stone", + "id": "3990" + }, + { + "name": "Combat stone", + "id": "3991" + }, + { + "name": "Combat stone", + "id": "3992" + }, + { + "name": "Combat stone", + "id": "3993" + }, + { + "name": "Combat stone", + "id": "3994" + }, + { + "name": "Combat stone", + "id": "3995" + }, + { + "name": "Combat stone", + "id": "3996" + }, + { + "name": "Combat stone", + "id": "3997" + }, + { + "name": "Combat stone", + "id": "3998" + }, + { + "name": "Combat stone", + "id": "3999" + }, + { + "name": "Combat stone", + "id": "4000" + }, + { + "name": "Combat stone", + "id": "4001" + }, + { + "name": "Combat stone", + "id": "4002" + }, + { + "name": "Combat stone", + "id": "4003" + }, + { + "name": "Combat stone", + "id": "4004" + }, + { + "name": "Combat stone", + "id": "4005" + }, + { + "name": "Combat stone", + "id": "4006" + }, + { + "name": "Combat stone", + "id": "4007" + }, + { + "name": "Combat stone", + "id": "4008" + }, + { + "name": "Combat stone", + "id": "4009" + }, + { + "name": "Combat stone", + "id": "4010" + }, + { + "name": "Combat stone", + "id": "4011" + }, + { + "name": "Combat stone", + "id": "4012" + }, + { + "name": "Combat stone", + "id": "4013" + }, + { + "name": "Combat stone", + "id": "4014" + }, + { + "name": "Combat stone", + "id": "4015" + }, + { + "name": "Combat stone", + "id": "4016" + }, + { + "name": "Combat stone", + "id": "4017" + }, + { + "name": "Combat stone", + "id": "4018" + }, + { + "name": "Combat stone", + "id": "4019" + }, + { + "name": "Combat stone", + "id": "4020" + }, + { + "name": "Elemental balance", + "id": "4021" + }, + { + "name": "Elemental balance", + "id": "4022" + }, + { + "name": "Elemental balance", + "id": "4023" + }, + { + "name": "Elemental balance", + "id": "4024" + }, + { + "name": "Elemental balance", + "id": "4025" + }, + { + "name": "Elemental balance", + "id": "4026" + }, + { + "name": "Elemental balance", + "id": "4027" + }, + { + "name": "Elemental balance", + "id": "4028" + }, + { + "name": "Elemental balance", + "id": "4029" + }, + { + "name": "Elemental balance", + "id": "4030" + }, + { + "name": "Elemental balance", + "id": "4031" + }, + { + "name": "Elemental balance", + "id": "4032" + }, + { + "name": "Elemental balance", + "id": "4033" + }, + { + "name": "Elemental balance", + "id": "4034" + }, + { + "name": "Elemental balance", + "id": "4035" + }, + { + "name": "Elemental balance", + "id": "4036" + }, + { + "name": "Elemental balance", + "id": "4037" + }, + { + "name": "Elemental balance", + "id": "4038" + }, + { + "name": "Elemental balance", + "id": "4039" + }, + { + "name": "Elemental balance", + "id": "4040" + }, + { + "name": "Elemental balance", + "id": "4041" + }, + { + "name": "Elemental balance", + "id": "4042" + }, + { + "name": "Elemental balance", + "id": "4043" + }, + { + "name": "Elemental balance", + "id": "4044" + }, + { + "name": "Elemental balance", + "id": "4045" + }, + { + "name": "Elemental balance", + "id": "4046" + }, + { + "name": "Elemental balance", + "id": "4047" + }, + { + "name": "Elemental balance", + "id": "4048" + }, + { + "name": "Elemental balance", + "id": "4049" + }, + { + "name": "Elemental balance", + "id": "4050" + }, + { + "name": "Elemental balance", + "id": "4051" + }, + { + "name": "Elemental balance", + "id": "4052" + }, + { + "name": "Elemental balance", + "id": "4053" + }, + { + "name": "Elemental balance", + "id": "4054" + }, + { + "name": "Elemental balance", + "id": "4055" + }, + { + "name": "Elemental balance", + "id": "4056" + }, + { + "name": "Elemental balance", + "id": "4057" + }, + { + "name": "Elemental balance", + "id": "4058" + }, + { + "name": "Elemental balance", + "id": "4059" + }, + { + "name": "Elemental balance", + "id": "4060" + }, + { + "name": "Elemental balance", + "id": "4061" + }, + { + "name": "Elemental balance", + "id": "4062" + }, + { + "name": "Elemental balance", + "id": "4063" + }, + { + "name": "Elemental balance", + "id": "4064" + }, + { + "name": "Elemental balance", + "id": "4065" + }, + { + "name": "Elemental balance", + "id": "4066" + }, + { + "name": "Elemental balance", + "id": "4067" + }, + { + "name": "Elemental balance", + "id": "4068" + }, + { + "name": "Elemental balance", + "id": "4069" + }, + { + "name": "Elemental balance", + "id": "4070" + }, + { + "name": "Elemental balance", + "id": "4071" + }, + { + "name": "Elemental balance", + "id": "4072" + }, + { + "name": "Elemental balance", + "id": "4073" + }, + { + "name": "Elemental balance", + "id": "4074" + }, + { + "name": "Elemental balance", + "id": "4075" + }, + { + "name": "Elemental balance", + "id": "4076" + }, + { + "name": "Elemental balance", + "id": "4077" + }, + { + "name": "Elemental balance", + "id": "4078" + }, + { + "name": "Elemental balance", + "id": "4079" + }, + { + "name": "Elemental balance", + "id": "4080" + }, + { + "name": "Elemental balance", + "id": "4081" + }, + { + "name": "Elemental balance", + "id": "4082" + }, + { + "name": "Elemental balance", + "id": "4083" + }, + { + "name": "Elemental balance", + "id": "4084" + }, + { + "name": "Elemental balance", + "id": "4085" + }, + { + "name": "Elemental balance", + "id": "4086" + }, + { + "name": "Elemental balance", + "id": "4087" + }, + { + "name": "Elemental balance", + "id": "4088" + }, + { + "name": "Elemental balance", + "id": "4089" + }, + { + "name": "Elemental balance", + "id": "4090" + }, + { + "name": "Elemental balance", + "id": "4091" + }, + { + "name": "Elemental balance", + "id": "4092" + }, + { + "name": "Elemental balance", + "id": "4093" + }, + { + "name": "Elemental balance", + "id": "4094" + }, + { + "name": "Elemental balance", + "id": "4095" + }, + { + "name": "Combat stone", + "id": "4096" + }, + { + "name": "Combat stone", + "id": "4097" + }, + { + "name": "Combat stone", + "id": "4098" + }, + { + "name": "Combat stone", + "id": "4099" + }, + { + "name": "Combat stone", + "id": "4100" + }, + { + "name": "Combat stone", + "id": "4101" + }, + { + "name": "Combat stone", + "id": "4102" + }, + { + "name": "Combat stone", + "id": "4103" + }, + { + "name": "Combat stone", + "id": "4104" + }, + { + "name": "Combat stone", + "id": "4105" + }, + { + "name": "Combat stone", + "id": "4106" + }, + { + "name": "Combat stone", + "id": "4107" + }, + { + "name": "Combat stone", + "id": "4108" + }, + { + "name": "Combat stone", + "id": "4109" + }, + { + "name": "Combat stone", + "id": "4110" + }, + { + "name": "Combat stone", + "id": "4111" + }, + { + "name": "Combat stone", + "id": "4112" + }, + { + "name": "Combat stone", + "id": "4113" + }, + { + "name": "Combat stone", + "id": "4114" + }, + { + "name": "Combat stone", + "id": "4115" + }, + { + "name": "Combat stone", + "id": "4116" + }, + { + "name": "Combat stone", + "id": "4117" + }, + { + "name": "Combat stone", + "id": "4118" + }, + { + "name": "Combat stone", + "id": "4119" + }, + { + "name": "Combat stone", + "id": "4120" + }, + { + "name": "Combat stone", + "id": "4121" + }, + { + "name": "Combat stone", + "id": "4122" + }, + { + "name": "Combat stone", + "id": "4123" + }, + { + "name": "Combat stone", + "id": "4124" + }, + { + "name": "Combat stone", + "id": "4125" + }, + { + "name": "Combat stone", + "id": "4126" + }, + { + "name": "Combat stone", + "id": "4127" + }, + { + "name": "Combat stone", + "id": "4128" + }, + { + "name": "Combat stone", + "id": "4129" + }, + { + "name": "Combat stone", + "id": "4130" + }, + { + "name": "Combat stone", + "id": "4131" + }, + { + "name": "Combat stone", + "id": "4132" + }, + { + "name": "Combat stone", + "id": "4133" + }, + { + "name": "Combat stone", + "id": "4134" + }, + { + "name": "Combat stone", + "id": "4135" + }, + { + "name": "Combat stone", + "id": "4136" + }, + { + "name": "Combat stone", + "id": "4137" + }, + { + "name": "Combat stone", + "id": "4138" + }, + { + "name": "Combat stone", + "id": "4139" + }, + { + "name": "Combat stone", + "id": "4140" + }, + { + "name": "Combat stone", + "id": "4141" + }, + { + "name": "Combat stone", + "id": "4142" + }, + { + "name": "Combat stone", + "id": "4143" + }, + { + "name": "Combat stone", + "id": "4144" + }, + { + "name": "Combat stone", + "id": "4145" + }, + { + "name": "Combat stone", + "id": "4146" + }, + { + "name": "Combat stone", + "id": "4147" + }, + { + "name": "Combat stone", + "id": "4148" + }, + { + "name": "Combat stone", + "id": "4149" + }, + { + "name": "Combat stone", + "id": "4150" + }, + { + "name": "Combat stone", + "id": "4151" + }, + { + "name": "Combat stone", + "id": "4152" + }, + { + "name": "Combat stone", + "id": "4153" + }, + { + "name": "Combat stone", + "id": "4154" + }, + { + "name": "Combat stone", + "id": "4155" + }, + { + "name": "Combat stone", + "id": "4156" + }, + { + "name": "Combat stone", + "id": "4157" + }, + { + "name": "Combat stone", + "id": "4158" + }, + { + "name": "Combat stone", + "id": "4159" + }, + { + "name": "Combat stone", + "id": "4160" + }, + { + "name": "Combat stone", + "id": "4161" + }, + { + "name": "Combat stone", + "id": "4162" + }, + { + "name": "Combat stone", + "id": "4163" + }, + { + "name": "Combat stone", + "id": "4164" + }, + { + "name": "Combat stone", + "id": "4165" + }, + { + "name": "Combat stone", + "id": "4166" + }, + { + "name": "Combat stone", + "id": "4167" + }, + { + "name": "Combat stone", + "id": "4168" + }, + { + "name": "Combat stone", + "id": "4169" + }, + { + "name": "Combat stone", + "id": "4170" + }, + { + "name": "Combat stone", + "id": "4171" + }, + { + "name": "Combat stone", + "id": "4172" + }, + { + "name": "Combat stone", + "id": "4173" + }, + { + "name": "Combat stone", + "id": "4174" + }, + { + "name": "Combat stone", + "id": "4175" + }, + { + "name": "Combat stone", + "id": "4176" + }, + { + "name": "Combat stone", + "id": "4177" + }, + { + "name": "Combat stone", + "id": "4178" + }, + { + "name": "Combat stone", + "id": "4179" + }, + { + "name": "Combat stone", + "id": "4180" + }, + { + "name": "Combat stone", + "id": "4181" + }, + { + "name": "Combat stone", + "id": "4182" + }, + { + "name": "Combat stone", + "id": "4183" + }, + { + "name": "Combat stone", + "id": "4184" + }, + { + "name": "Combat stone", + "id": "4185" + }, + { + "name": "Combat stone", + "id": "4186" + }, + { + "name": "Combat stone", + "id": "4187" + }, + { + "name": "Combat stone", + "id": "4188" + }, + { + "name": "Combat stone", + "id": "4189" + }, + { + "name": "Combat stone", + "id": "4190" + }, + { + "name": "Combat stone", + "id": "4191" + }, + { + "name": "Combat stone", + "id": "4192" + }, + { + "name": "Combat stone", + "id": "4193" + }, + { + "name": "Combat stone", + "id": "4194" + }, + { + "name": "Combat stone", + "id": "4195" + }, + { + "name": "Combat stone", + "id": "4196" + }, + { + "name": "Combat stone", + "id": "4197" + }, + { + "name": "Combat stone", + "id": "4198" + }, + { + "name": "Combat stone", + "id": "4199" + }, + { + "name": "Combat stone", + "id": "4200" + }, + { + "name": "Combat stone", + "id": "4201" + }, + { + "name": "Combat stone", + "id": "4202" + }, + { + "name": "Combat stone", + "id": "4203" + }, + { + "name": "Combat stone", + "id": "4204" + }, + { + "name": "Combat stone", + "id": "4205" + }, + { + "name": "Combat stone", + "id": "4206" + }, + { + "name": "Combat stone", + "id": "4207" + }, + { + "name": "Combat stone", + "id": "4208" + }, + { + "name": "Combat stone", + "id": "4209" + }, + { + "name": "Combat stone", + "id": "4210" + }, + { + "name": "Combat stone", + "id": "4211" + }, + { + "name": "Combat stone", + "id": "4212" + }, + { + "name": "Combat stone", + "id": "4213" + }, + { + "name": "Combat stone", + "id": "4214" + }, + { + "name": "Combat stone", + "id": "4215" + }, + { + "name": "Combat stone", + "id": "4216" + }, + { + "name": "Combat stone", + "id": "4217" + }, + { + "name": "Combat stone", + "id": "4218" + }, + { + "name": "Combat stone", + "id": "4219" + }, + { + "name": "Combat stone", + "id": "4220" + }, + { + "name": "Combat stone", + "id": "4221" + }, + { + "name": "Combat stone", + "id": "4222" + }, + { + "name": "Combat stone", + "id": "4223" + }, + { + "name": "Combat stone", + "id": "4224" + }, + { + "name": "Combat stone", + "id": "4225" + }, + { + "name": "Crawling hand", + "id": "4226" + }, + { + "name": "Left head", + "id": "4231" + }, + { + "name": "Middle head", + "id": "4232" + }, + { + "name": "Right head", + "id": "4233" + }, + { + "name": "Kalphite Queen", + "id": "4234" + }, + { + "name": "Rick", + "id": "4235" + }, + { + "name": "Maid", + "id": "4236" + }, + { + "name": "Cook", + "id": "4238" + }, + { + "name": "Butler", + "id": "4240" + }, + { + "name": "Demon butler", + "id": "4242" + }, + { + "name": "Chief servant", + "id": "4244" + }, + { + "name": "Guard", + "id": "4253" + }, + { + "name": "Buinn", + "id": "4277" + }, + { + "name": "Nardok", + "id": "4312" + }, + { + "name": "Dartog", + "id": "4313" + }, + { + "name": "Dwarf", + "id": "4315" + }, + { + "name": "H.A.M. Member", + "id": "4317" + }, + { + "name": "H.A.M. Member", + "id": "4319" + }, + { + "name": "Zanik", + "id": "4321" + }, + { + "name": "Zanik", + "id": "4323" + }, + { + "name": "Zanik", + "id": "4324" + }, + { + "name": "Light creature", + "id": "4325" + }, + { + "name": "Zanik", + "id": "4326" + }, + { + "name": "HAM member", + "id": "4327" + }, + { + "name": "Sigmund", + "id": "4328" + }, + { + "name": "Johanhus Ulsbrecht", + "id": "4330" + }, + { + "name": "Sigmund", + "id": "4331" + }, + { + "name": "Sigmund", + "id": "4332" + }, + { + "name": "Sigmund", + "id": "4333" + }, + { + "name": "Sigmund", + "id": "4334" + }, + { + "name": "Sigmund", + "id": "4335" + }, + { + "name": "Zanik", + "id": "4337" + }, + { + "name": "Zanik", + "id": "4338" + }, + { + "name": "Zanik", + "id": "4340" + }, + { + "name": "Zanik", + "id": "4341" + }, + { + "name": "Zanik", + "id": "4342" + }, + { + "name": "Crab", + "id": "4346" + }, + { + "name": "Cavey Davey", + "id": "4358" + }, + { + "name": "San Fan", + "id": "4360" + }, + { + "name": "Swarm", + "id": "4364" + }, + { + "name": "Blue Monkey", + "id": "4365" + }, + { + "name": "Parrot", + "id": "4374" + }, + { + "name": "Gate of War", + "id": "4377" + }, + { + "name": "Ricketty door", + "id": "4378" + }, + { + "name": "Oozing barrier", + "id": "4379" + }, + { + "name": "Portal of Death", + "id": "4380" + }, + { + "name": "Bee keeper", + "id": "4416" + }, + { + "name": "Bees!", + "id": "4417" + }, + { + "name": "Fairy Godfather", + "id": "4420" + }, + { + "name": "Fairy Queen", + "id": "4435" + }, + { + "name": "Fairy Very Wise", + "id": "4442" + }, + { + "name": "Fairy", + "id": "4444" + }, + { + "name": "Fairy", + "id": "4445" + }, + { + "name": "Fairy", + "id": "4446" + }, + { + "name": "Rabbit", + "id": "4447" + }, + { + "name": "Rabbit", + "id": "4448" + }, + { + "name": "Butterfly", + "id": "4449" + }, + { + "name": "Butterfly", + "id": "4450" + }, + { + "name": "Starflower", + "id": "4451" + }, + { + "name": "Starflower", + "id": "4452" + }, + { + "name": "Fairy Fixit", + "id": "4453" + }, + { + "name": "Ork", + "id": "4456" + }, + { + "name": "Fake Man", + "id": "4460" + }, + { + "name": "Held vampyre juvinate", + "id": "4461" + }, + { + "name": "Held vampyre juvinate", + "id": "4462" + }, + { + "name": "Angry juvinate", + "id": "4463" + }, + { + "name": "Angry juvinate", + "id": "4464" + }, + { + "name": "Angry juvinate", + "id": "4465" + }, + { + "name": "Benjamin", + "id": "4466" + }, + { + "name": "Liam", + "id": "4467" + }, + { + "name": "Miala", + "id": "4468" + }, + { + "name": "Verak", + "id": "4469" + }, + { + "name": "Bogrog", + "id": "4471" + }, + { + "name": "Woman", + "id": "4473" + }, + { + "name": "Ned", + "id": "4475" + }, + { + "name": "Annoyed guardian mummy", + "id": "4477" + }, + { + "name": "Tarik", + "id": "4478" + }, + { + "name": "General Bentnoze", + "id": "4493" + }, + { + "name": "Grubfoot", + "id": "4495" + }, + { + "name": "Grubfoot", + "id": "4497" + }, + { + "name": "Grubfoot", + "id": "4498" + }, + { + "name": "Scarab swarm", + "id": "4500" + }, + { + "name": "Ethereal Mimic", + "id": "4508" + }, + { + "name": "Baba Yaga", + "id": "4513" + }, + { + "name": "Pauline Polaris", + "id": "4514" + }, + { + "name": "Meteora", + "id": "4515" + }, + { + "name": "Melana Moonlander", + "id": "4516" + }, + { + "name": "Selene", + "id": "4517" + }, + { + "name": "Rimae Sirsalis", + "id": "4518" + }, + { + "name": "Sirsal Banker", + "id": "4519" + }, + { + "name": "Clan Guard", + "id": "4520" + }, + { + "name": "Enchanted Broom", + "id": "4522" + }, + { + "name": "Enchanted Broom", + "id": "4523" + }, + { + "name": "Enchanted Bucket", + "id": "4525" + }, + { + "name": "Bouquet Mac Hyacinth", + "id": "4526" + }, + { + "name": "Parrot", + "id": "4535" + }, + { + "name": "Lokar Searunner", + "id": "4536" + }, + { + "name": "Cabin boy", + "id": "4538" + }, + { + "name": "'Bird's-Eye' Jack", + "id": "4544" + }, + { + "name": "Palmer", + "id": "4552" + }, + { + "name": "'Betty' B.Boppin", + "id": "4553" + }, + { + "name": "Hirko", + "id": "4558" + }, + { + "name": "Holoy", + "id": "4559" + }, + { + "name": "Hura", + "id": "4563" + }, + { + "name": "Nick", + "id": "4569" + }, + { + "name": "Crow", + "id": "4570" + }, + { + "name": "Crow", + "id": "4571" + }, + { + "name": "Gianne jnr.", + "id": "4572" + }, + { + "name": "Timble", + "id": "4573" + }, + { + "name": "Tamble", + "id": "4574" + }, + { + "name": "Spang", + "id": "4575" + }, + { + "name": "Brambickle", + "id": "4576" + }, + { + "name": "Wingstone", + "id": "4577" + }, + { + "name": "Penwie", + "id": "4578" + }, + { + "name": "Professor Manglethorp", + "id": "4583" + }, + { + "name": "Damwin", + "id": "4584" + }, + { + "name": "Professor Imblewyn", + "id": "4586" + }, + { + "name": "Perrdur", + "id": "4587" + }, + { + "name": "Dalila", + "id": "4588" + }, + { + "name": "Eebel", + "id": "4591" + }, + { + "name": "Ermin", + "id": "4592" + }, + { + "name": "Captain Lamdoo", + "id": "4596" + }, + { + "name": "Meegle", + "id": "4597" + }, + { + "name": "Wurbel", + "id": "4598" + }, + { + "name": "Sarble", + "id": "4599" + }, + { + "name": "Burkor", + "id": "4601" + }, + { + "name": "Froono", + "id": "4602" + }, + { + "name": "Brimstail", + "id": "4609" + }, + { + "name": "Gnome shop keeper", + "id": "4612" + }, + { + "name": "Cute creature", + "id": "4613" + }, + { + "name": "Cute creature", + "id": "4616" + }, + { + "name": "Evil creature", + "id": "4618" + }, + { + "name": "Cute creature", + "id": "4619" + }, + { + "name": "Evil creature", + "id": "4621" + }, + { + "name": "Cute creature", + "id": "4622" + }, + { + "name": "Evil creature", + "id": "4624" + }, + { + "name": "Cute creature", + "id": "4625" + }, + { + "name": "Evil creature", + "id": "4627" + }, + { + "name": "Cute creature", + "id": "4628" + }, + { + "name": "Evil creature", + "id": "4630" + }, + { + "name": "fluffie", + "id": "4631" + }, + { + "name": "fluffie", + "id": "4632" + }, + { + "name": "Oaknock the Engineer", + "id": "4644" + }, + { + "name": "King Healthorg", + "id": "4646" + }, + { + "name": "Hazelmere", + "id": "4647" + }, + { + "name": "Nisha", + "id": "4648" + }, + { + "name": "Tyras guard", + "id": "4649" + }, + { + "name": "Sir Prysin", + "id": "4657" + }, + { + "name": "Dark wizard", + "id": "4658" + }, + { + "name": "Denath", + "id": "4662" + }, + { + "name": "Denath", + "id": "4663" + }, + { + "name": "Wally", + "id": "4664" + }, + { + "name": "Vertida Sefalatis", + "id": "4709" + }, + { + "name": "Aeonisig Raispher", + "id": "4710" + }, + { + "name": "Safalaan", + "id": "4711" + }, + { + "name": "Sarius Guile", + "id": "4714" + }, + { + "name": "Meiyerditch citizen", + "id": "4727" + }, + { + "name": "Meiyerditch citizen", + "id": "4728" + }, + { + "name": "Meiyerditch citizen", + "id": "4729" + }, + { + "name": "Meiyerditch citizen", + "id": "4730" + }, + { + "name": "Meiyerditch citizen", + "id": "4731" + }, + { + "name": "Meiyerditch citizen", + "id": "4732" + }, + { + "name": "Meiyerditch citizen", + "id": "4742" + }, + { + "name": "Meiyerditch citizen", + "id": "4743" + }, + { + "name": "Meiyerditch citizen", + "id": "4744" + }, + { + "name": "Meiyerditch citizen", + "id": "4745" + }, + { + "name": "Meiyerditch miner", + "id": "4758" + }, + { + "name": "Meiyerditch miner", + "id": "4760" + }, + { + "name": "Meiyerditch miner", + "id": "4761" + }, + { + "name": "Shadowy figure", + "id": "4762" + }, + { + "name": "Shadowy figure", + "id": "4763" + }, + { + "name": "Shadowy figure", + "id": "4764" + }, + { + "name": "Stray dog", + "id": "4767" + }, + { + "name": "Cat", + "id": "4769" + }, + { + "name": "Boat", + "id": "4770" + }, + { + "name": "Boat", + "id": "4771" + }, + { + "name": "Held vampyre juvinate", + "id": "4781" + }, + { + "name": "Vampyre juvinate", + "id": "4782" + }, + { + "name": "Former vampyre", + "id": "4783" + }, + { + "name": "Former vampyre", + "id": "4784" + }, + { + "name": "Former vampyre", + "id": "4785" + }, + { + "name": "Former vampyre", + "id": "4786" + }, + { + "name": "Former vampyre", + "id": "4787" + }, + { + "name": "Former vampyre", + "id": "4788" + }, + { + "name": "Angry vampyre", + "id": "4790" + }, + { + "name": "Vanstrom Klause", + "id": "4791" + }, + { + "name": "Vanstrom Klause", + "id": "4792" + }, + { + "name": "Vanstrom Klause", + "id": "4794" + }, + { + "name": "Vanstrom Klause", + "id": "4795" + }, + { + "name": "Vanescula Drakan", + "id": "4797" + }, + { + "name": "Vanescula Drakan", + "id": "4798" + }, + { + "name": "Vanescula Drakan", + "id": "4799" + }, + { + "name": "Vanescula Drakan", + "id": "4800" + }, + { + "name": "Ranis Drakan", + "id": "4801" + }, + { + "name": "Ranis Drakan", + "id": "4802" + }, + { + "name": "Ranis Drakan", + "id": "4803" + }, + { + "name": "Ranis Drakan", + "id": "4804" + }, + { + "name": "Flying female vampire", + "id": "4809" + }, + { + "name": "Flying female vampire", + "id": "4846" + }, + { + "name": "Ezekial Lovecraft", + "id": "4853" + }, + { + "name": "Sarius Guile", + "id": "4857" + }, + { + "name": "Vanstrom Klause", + "id": "4862" + }, + { + "name": "Kennith", + "id": "4863" + }, + { + "name": "Kennith", + "id": "4864" + }, + { + "name": "Holgart", + "id": "4865" + }, + { + "name": "Holgart", + "id": "4867" + }, + { + "name": "Fisherman", + "id": "4869" + }, + { + "name": "Col. O'Niall", + "id": "4871" + }, + { + "name": "Col. O'Niall", + "id": "4873" + }, + { + "name": "Mayor Hobb", + "id": "4875" + }, + { + "name": "Brother Maledict", + "id": "4876" + }, + { + "name": "Brother Maledict", + "id": "4879" + }, + { + "name": "Witchaven villager", + "id": "4880" + }, + { + "name": "Witchaven villager", + "id": "4884" + }, + { + "name": "Witchaven villager", + "id": "4886" + }, + { + "name": "Witchaven villager", + "id": "4888" + }, + { + "name": "Mother Mallum", + "id": "4889" + }, + { + "name": "Giant lobster", + "id": "4891" + }, + { + "name": "Jeb", + "id": "4896" + }, + { + "name": "Sir Tinley", + "id": "4897" + }, + { + "name": "Smithing Tutor", + "id": "4905" + }, + { + "name": "Fishing spot", + "id": "4908" + }, + { + "name": "Jig cart", + "id": "4912" + }, + { + "name": "Jig cart", + "id": "4914" + }, + { + "name": "Jig cart", + "id": "4915" + }, + { + "name": "Jig cart", + "id": "4916" + }, + { + "name": "Jig cart", + "id": "4917" + }, + { + "name": "Jig cart", + "id": "4918" + }, + { + "name": "Abidor Crank", + "id": "4919" + }, + { + "name": "Ignatius Vulcan", + "id": "4946" + }, + { + "name": "My Arm", + "id": "4948" + }, + { + "name": "My Arm", + "id": "4949" + }, + { + "name": "My Arm", + "id": "4950" + }, + { + "name": "My Arm", + "id": "4958" + }, + { + "name": "My Arm", + "id": "4959" + }, + { + "name": "Adventurer", + "id": "4960" + }, + { + "name": "Captain Barnaby", + "id": "4961" + }, + { + "name": "Murcaily", + "id": "4963" + }, + { + "name": "Jagbakoba", + "id": "4964" + }, + { + "name": "Flies", + "id": "4966" + }, + { + "name": "Unnamed troll child", + "id": "4968" + }, + { + "name": "Drunken dwarf's leg", + "id": "4969" + }, + { + "name": "Baby Roc", + "id": "4970" + }, + { + "name": "Shadow", + "id": "4973" + }, + { + "name": "Captain Barnaby", + "id": "4974" + }, + { + "name": "Male slave", + "id": "4976" + }, + { + "name": "Female slave", + "id": "4978" + }, + { + "name": "Cart Camel", + "id": "4979" + }, + { + "name": "Mine Cart", + "id": "4980" + }, + { + "name": "Mine Cart", + "id": "4981" + }, + { + "name": "Ana", + "id": "4982" + }, + { + "name": "Mercenary", + "id": "4983" + }, + { + "name": "Irena", + "id": "4984" + }, + { + "name": "Gublinch", + "id": "5003" + }, + { + "name": "Gublinch", + "id": "5018" + }, + { + "name": "Gublinch", + "id": "5019" + }, + { + "name": "Jack", + "id": "5020" + }, + { + "name": "Jill", + "id": "5024" + }, + { + "name": "Jeff", + "id": "5025" + }, + { + "name": "Penance Fighter", + "id": "5044" + }, + { + "name": "Penance Fighter", + "id": "5045" + }, + { + "name": "Jack", + "id": "5046" + }, + { + "name": "Jill", + "id": "5047" + }, + { + "name": "Auguste", + "id": "5049" + }, + { + "name": "Auguste", + "id": "5050" + }, + { + "name": "Auguste", + "id": "5051" + }, + { + "name": "Auguste", + "id": "5052" + }, + { + "name": "Assistant Serf", + "id": "5053" + }, + { + "name": "Assistant Brock", + "id": "5054" + }, + { + "name": "Assistant Marrow", + "id": "5055" + }, + { + "name": "Assistant Le Smith", + "id": "5056" + }, + { + "name": "Assistant Stan", + "id": "5057" + }, + { + "name": "Bob", + "id": "5058" + }, + { + "name": "Curly", + "id": "5059" + }, + { + "name": "Moe", + "id": "5060" + }, + { + "name": "Larry", + "id": "5061" + }, + { + "name": "Shark", + "id": "5062" + }, + { + "name": "Shark", + "id": "5068" + }, + { + "name": "Shark", + "id": "5069" + }, + { + "name": "Tropical wagtail", + "id": "5070" + }, + { + "name": "Chinchompa", + "id": "5077" + }, + { + "name": "Matthias", + "id": "5090" + }, + { + "name": "Matthias", + "id": "5093" + }, + { + "name": "Gyr Falcon", + "id": "5094" + }, + { + "name": "Gyr Falcon", + "id": "5095" + }, + { + "name": "Gyr Falcon", + "id": "5096" + }, + { + "name": "Sabre-toothed kyatt", + "id": "5101" + }, + { + "name": "Aleck", + "id": "5110" + }, + { + "name": "Eagle", + "id": "5118" + }, + { + "name": "Eagle", + "id": "5121" + }, + { + "name": "Eagle", + "id": "5122" + }, + { + "name": "Eagle", + "id": "5123" + }, + { + "name": "Nickolaus", + "id": "5124" + }, + { + "name": "Nickolaus", + "id": "5127" + }, + { + "name": "Nickolaus", + "id": "5128" + }, + { + "name": "Nickolaus", + "id": "5129" + }, + { + "name": "Kebbit", + "id": "5134" + }, + { + "name": "Charlie", + "id": "5138" + }, + { + "name": "Boulder", + "id": "5139" + }, + { + "name": "Uri", + "id": "5141" + }, + { + "name": "Uri", + "id": "5142" + }, + { + "name": "Uri", + "id": "5143" + }, + { + "name": "Sheep", + "id": "5148" + }, + { + "name": "Sheep", + "id": "5149" + }, + { + "name": "Sheep", + "id": "5150" + }, + { + "name": "Sheep", + "id": "5151" + }, + { + "name": "Sheep", + "id": "5152" + }, + { + "name": "Sheep", + "id": "5153" + }, + { + "name": "Sheep", + "id": "5154" + }, + { + "name": "Sheep", + "id": "5155" + }, + { + "name": "Sheep", + "id": "5156" + }, + { + "name": "Sheep", + "id": "5157" + }, + { + "name": "Sheep", + "id": "5158" + }, + { + "name": "Sheep", + "id": "5159" + }, + { + "name": "Sheep", + "id": "5160" + }, + { + "name": "Sheep", + "id": "5161" + }, + { + "name": "Sheep", + "id": "5165" + }, + { + "name": "Ogre chieftain", + "id": "5174" + }, + { + "name": "Ogre shaman", + "id": "5175" + }, + { + "name": "Ogre shaman", + "id": "5177" + }, + { + "name": "Elkoy", + "id": "5179" + }, + { + "name": "Ogre shaman", + "id": "5180" + }, + { + "name": "Elkoy", + "id": "5182" + }, + { + "name": "Ogre shaman", + "id": "5183" + }, + { + "name": "Biggleswade", + "id": "5185" + }, + { + "name": "Ogre shaman", + "id": "5186" + }, + { + "name": "Ogre shaman", + "id": "5189" + }, + { + "name": "Blaze Sharpeye", + "id": "5191" + }, + { + "name": "Ogre shaman", + "id": "5192" + }, + { + "name": "Blaze Sharpeye", + "id": "5194" + }, + { + "name": "Witch", + "id": "5199" + }, + { + "name": "Alice's husband", + "id": "5201" + }, + { + "name": "Alice's husband", + "id": "5203" + }, + { + "name": "Alice's husband", + "id": "5205" + }, + { + "name": "Tree", + "id": "5206" + }, + { + "name": "Undead tree", + "id": "5208" + }, + { + "name": " Sneaky undead fowl", + "id": "5209" + }, + { + "name": "Cow1337killr", + "id": "5210" + }, + { + "name": "Alice", + "id": "5212" + }, + { + "name": "Penance Fighter", + "id": "5213" + }, + { + "name": "Penance Fighter", + "id": "5214" + }, + { + "name": "Penance Fighter", + "id": "5215" + }, + { + "name": "Penance Fighter", + "id": "5216" + }, + { + "name": "Penance Fighter", + "id": "5217" + }, + { + "name": "Penance Fighter", + "id": "5218" + }, + { + "name": "Penance Fighter", + "id": "5219" + }, + { + "name": "Penance Runner", + "id": "5220" + }, + { + "name": "Penance Runner", + "id": "5221" + }, + { + "name": "Penance Runner", + "id": "5222" + }, + { + "name": "Penance Runner", + "id": "5223" + }, + { + "name": "Penance Runner", + "id": "5224" + }, + { + "name": "Penance Runner", + "id": "5225" + }, + { + "name": "Penance Runner", + "id": "5226" + }, + { + "name": "Penance Runner", + "id": "5227" + }, + { + "name": "Penance Runner", + "id": "5228" + }, + { + "name": "Penance Ranger", + "id": "5230" + }, + { + "name": "Penance Ranger", + "id": "5231" + }, + { + "name": "Penance Ranger", + "id": "5232" + }, + { + "name": "Penance Ranger", + "id": "5233" + }, + { + "name": "Penance Ranger", + "id": "5234" + }, + { + "name": "Penance Ranger", + "id": "5235" + }, + { + "name": "Penance Ranger", + "id": "5236" + }, + { + "name": "Penance Healer", + "id": "5238" + }, + { + "name": "Penance Healer", + "id": "5239" + }, + { + "name": "Penance Healer", + "id": "5240" + }, + { + "name": "Penance Healer", + "id": "5241" + }, + { + "name": "Penance Healer", + "id": "5242" + }, + { + "name": "Penance Healer", + "id": "5243" + }, + { + "name": "Penance Healer", + "id": "5244" + }, + { + "name": "Penance Healer", + "id": "5245" + }, + { + "name": "Penance Healer", + "id": "5246" + }, + { + "name": "Locust rider", + "id": "5255" + }, + { + "name": "Locust rider", + "id": "5256" + }, + { + "name": "Banker", + "id": "5257" + }, + { + "name": "Banker", + "id": "5259" + }, + { + "name": "Stonemason", + "id": "5261" + }, + { + "name": "Nathifa", + "id": "5263" + }, + { + "name": "Urbi", + "id": "5265" + }, + { + "name": "Jamila", + "id": "5267" + }, + { + "name": "Sophanem guard", + "id": "5269" + }, + { + "name": "Sophanem guard", + "id": "5271" + }, + { + "name": "Sophanem guard", + "id": "5273" + }, + { + "name": "Sophanem guard", + "id": "5275" + }, + { + "name": "Coenus", + "id": "5278" + }, + { + "name": "Jex", + "id": "5279" + }, + { + "name": "Maisa", + "id": "5280" + }, + { + "name": "Osman", + "id": "5282" + }, + { + "name": "Osman", + "id": "5286" + }, + { + "name": "Osman", + "id": "5287" + }, + { + "name": "Embalmer", + "id": "5288" + }, + { + "name": "Carpenter", + "id": "5289" + }, + { + "name": "Linen worker", + "id": "5290" + }, + { + "name": "Priest", + "id": "5291" + }, + { + "name": "Giant scarab", + "id": "5292" + }, + { + "name": "Mummy ashes", + "id": "5360" + }, + { + "name": "Odovacar", + "id": "5383" + }, + { + "name": "Terror dog statue", + "id": "5415" + }, + { + "name": "Terror dog statue", + "id": "5416" + }, + { + "name": "Tarn", + "id": "5419" + }, + { + "name": "Larry", + "id": "5423" + }, + { + "name": "Larry", + "id": "5425" + }, + { + "name": "Larry", + "id": "5426" + }, + { + "name": "Penguin", + "id": "5427" + }, + { + "name": "Penguin", + "id": "5429" + }, + { + "name": "Penguin", + "id": "5430" + }, + { + "name": "KGP Guard", + "id": "5431" + }, + { + "name": "Pescaling Pax", + "id": "5432" + }, + { + "name": "Ping", + "id": "5433" + }, + { + "name": "Ping", + "id": "5434" + }, + { + "name": "Pong", + "id": "5435" + }, + { + "name": "Pong", + "id": "5436" + }, + { + "name": "Ping", + "id": "5437" + }, + { + "name": "Pong", + "id": "5438" + }, + { + "name": "Noodle", + "id": "5443" + }, + { + "name": "Penguin", + "id": "5445" + }, + { + "name": "Penguin suit", + "id": "5446" + }, + { + "name": "Penguin", + "id": "5449" + }, + { + "name": "Penguin", + "id": "5450" + }, + { + "name": "Penguin", + "id": "5451" + }, + { + "name": "Crusher", + "id": "5456" + }, + { + "name": "Crusher", + "id": "5457" + }, + { + "name": "Crusher", + "id": "5458" + }, + { + "name": "Crusher", + "id": "5459" + }, + { + "name": "Tree", + "id": "5460" + }, + { + "name": "Jungle Tree", + "id": "5461" + }, + { + "name": "Tolna", + "id": "5462" + }, + { + "name": "Honour guard", + "id": "5463" + }, + { + "name": "Honour guard", + "id": "5464" + }, + { + "name": "Fridleif Shieldson", + "id": "5465" + }, + { + "name": "Thakkrad Sigmundson", + "id": "5466" + }, + { + "name": "Iceberg", + "id": "5467" + }, + { + "name": "Iceberg", + "id": "5468" + }, + { + "name": "Arctic Pine", + "id": "5469" + }, + { + "name": "Fishing spot", + "id": "5470" + }, + { + "name": "Fishing spot", + "id": "5471" + }, + { + "name": "Bork Sigmundson", + "id": "5477" + }, + { + "name": "Mord Gunnars", + "id": "5481" + }, + { + "name": "Mord Gunnars", + "id": "5482" + }, + { + "name": "Miner", + "id": "5498" + }, + { + "name": "Grundt", + "id": "5502" + }, + { + "name": "Mawnis Burowgar", + "id": "5503" + }, + { + "name": "Mawnis Burowgar", + "id": "5504" + }, + { + "name": "Fridleif Shieldson", + "id": "5505" + }, + { + "name": "Thakkrad Sigmundson", + "id": "5506" + }, + { + "name": "Maria Gunnars", + "id": "5507" + }, + { + "name": "Maria Gunnars", + "id": "5508" + }, + { + "name": "Jofridr Mordstatter", + "id": "5509" + }, + { + "name": "Morten Holdstrom", + "id": "5510" + }, + { + "name": "Gunnar Holdstrom", + "id": "5511" + }, + { + "name": "Anne Isaakson", + "id": "5512" + }, + { + "name": "Lisse Isaakson", + "id": "5513" + }, + { + "name": "Kjedelig Uppsen", + "id": "5518" + }, + { + "name": "Trogen Konungarde", + "id": "5519" + }, + { + "name": "Slug Hemligssen", + "id": "5520" + }, + { + "name": "Ice troll grunt", + "id": "5528" + }, + { + "name": "Sorceress", + "id": "5530" + }, + { + "name": "Osman", + "id": "5560" + }, + { + "name": "Del-Monty", + "id": "5562" + }, + { + "name": "Bouncer", + "id": "5564" + }, + { + "name": "Bouncer", + "id": "5565" + }, + { + "name": "General Khazard", + "id": "5566" + }, + { + "name": "Scout", + "id": "5567" + }, + { + "name": "Scout", + "id": "5568" + }, + { + "name": "Scout", + "id": "5569" + }, + { + "name": "Scout", + "id": "5570" + }, + { + "name": "Effigy", + "id": "5573" + }, + { + "name": "Effigy", + "id": "5579" + }, + { + "name": "Bonafido", + "id": "5580" + }, + { + "name": "Homunculus", + "id": "5582" + }, + { + "name": "Homunculus", + "id": "5583" + }, + { + "name": "'Transmute' The Alchemist", + "id": "5585" + }, + { + "name": "'Transmute' The Alchemist", + "id": "5586" + }, + { + "name": "'Currency' The Alchemist", + "id": "5587" + }, + { + "name": "'Currency' The Alchemist", + "id": "5588" + }, + { + "name": "'The Guns'", + "id": "5592" + }, + { + "name": "Unicow", + "id": "5598" + }, + { + "name": "Elfinlocks", + "id": "5604" + }, + { + "name": "Clockwork cat", + "id": "5605" + }, + { + "name": "Clockwork cat", + "id": "5606" + }, + { + "name": "Rufus", + "id": "5612" + }, + { + "name": "Mi-Gor", + "id": "5613" + }, + { + "name": "Puffin", + "id": "5615" + }, + { + "name": "Brother Tranquility", + "id": "5616" + }, + { + "name": "Brother Tranquility", + "id": "5617" + }, + { + "name": "Brother Tranquility", + "id": "5618" + }, + { + "name": "Zombie monk", + "id": "5623" + }, + { + "name": "Zombie monk", + "id": "5624" + }, + { + "name": "Zombie monk", + "id": "5625" + }, + { + "name": "Zombie monk", + "id": "5626" + }, + { + "name": "Undead Lumberjack", + "id": "5667" + }, + { + "name": "Undead Lumberjack", + "id": "5679" + }, + { + "name": "Undead Lumberjack", + "id": "5681" + }, + { + "name": "Undead Lumberjack", + "id": "5682" + }, + { + "name": "Undead Lumberjack", + "id": "5683" + }, + { + "name": "Undead Lumberjack", + "id": "5684" + }, + { + "name": "Undead Lumberjack", + "id": "5685" + }, + { + "name": "Undead Lumberjack", + "id": "5686" + }, + { + "name": "Undead Lumberjack", + "id": "5687" + }, + { + "name": "Undead Lumberjack", + "id": "5688" + }, + { + "name": "Undead Lumberjack", + "id": "5689" + }, + { + "name": "Undead Lumberjack", + "id": "5690" + }, + { + "name": "Undead Lumberjack", + "id": "5691" + }, + { + "name": "Undead Lumberjack", + "id": "5692" + }, + { + "name": "Undead Lumberjack", + "id": "5693" + }, + { + "name": "Undead Lumberjack", + "id": "5694" + }, + { + "name": "Undead Lumberjack", + "id": "5695" + }, + { + "name": "Undead Lumberjack", + "id": "5696" + }, + { + "name": "Undead Lumberjack", + "id": "5697" + }, + { + "name": "Undead Lumberjack", + "id": "5698" + }, + { + "name": "Undead Lumberjack", + "id": "5699" + }, + { + "name": "Undead Lumberjack", + "id": "5700" + }, + { + "name": "Undead Lumberjack", + "id": "5701" + }, + { + "name": "Undead Lumberjack", + "id": "5702" + }, + { + "name": "Undead Lumberjack", + "id": "5703" + }, + { + "name": "Undead Lumberjack", + "id": "5704" + }, + { + "name": "Undead Lumberjack", + "id": "5705" + }, + { + "name": "Undead Lumberjack", + "id": "5706" + }, + { + "name": "Undead Lumberjack", + "id": "5707" + }, + { + "name": "Undead Lumberjack", + "id": "5708" + }, + { + "name": "Undead Lumberjack", + "id": "5709" + }, + { + "name": "Undead Lumberjack", + "id": "5710" + }, + { + "name": "Undead Lumberjack", + "id": "5711" + }, + { + "name": "Undead Lumberjack", + "id": "5712" + }, + { + "name": "Undead Lumberjack", + "id": "5713" + }, + { + "name": "Undead Lumberjack", + "id": "5714" + }, + { + "name": "Undead Lumberjack", + "id": "5715" + }, + { + "name": "Undead Lumberjack", + "id": "5716" + }, + { + "name": "Undead Lumberjack", + "id": "5717" + }, + { + "name": "Undead Lumberjack", + "id": "5718" + }, + { + "name": "Undead Lumberjack", + "id": "5719" + }, + { + "name": "Undead Lumberjack", + "id": "5720" + }, + { + "name": "Undead Lumberjack", + "id": "5721" + }, + { + "name": "Undead Lumberjack", + "id": "5722" + }, + { + "name": "Undead Lumberjack", + "id": "5723" + }, + { + "name": "Undead Lumberjack", + "id": "5724" + }, + { + "name": "Undead Lumberjack", + "id": "5725" + }, + { + "name": "Undead Lumberjack", + "id": "5726" + }, + { + "name": "Undead Lumberjack", + "id": "5727" + }, + { + "name": "Undead Lumberjack", + "id": "5728" + }, + { + "name": "Undead Lumberjack", + "id": "5729" + }, + { + "name": "Undead Lumberjack", + "id": "5730" + }, + { + "name": "Undead Lumberjack", + "id": "5731" + }, + { + "name": "Undead Lumberjack", + "id": "5732" + }, + { + "name": "Undead Lumberjack", + "id": "5733" + }, + { + "name": "Undead Lumberjack", + "id": "5734" + }, + { + "name": "Undead Lumberjack", + "id": "5735" + }, + { + "name": "Undead Lumberjack", + "id": "5736" + }, + { + "name": "Undead Lumberjack", + "id": "5737" + }, + { + "name": "Undead Lumberjack", + "id": "5738" + }, + { + "name": "Undead Lumberjack", + "id": "5739" + }, + { + "name": "Undead Lumberjack", + "id": "5740" + }, + { + "name": "Undead Lumberjack", + "id": "5741" + }, + { + "name": "Undead Lumberjack", + "id": "5742" + }, + { + "name": "Undead Lumberjack", + "id": "5743" + }, + { + "name": "Undead Lumberjack", + "id": "5744" + }, + { + "name": "Undead Lumberjack", + "id": "5745" + }, + { + "name": "Undead Lumberjack", + "id": "5746" + }, + { + "name": "Undead Lumberjack", + "id": "5747" + }, + { + "name": "Fishing spot", + "id": "5748" + }, + { + "name": "Fishing spot", + "id": "5749" + }, + { + "name": "Ur-zek", + "id": "5770" + }, + { + "name": "Ur-vass", + "id": "5771" + }, + { + "name": "Ur-taal", + "id": "5772" + }, + { + "name": "Ur-meg", + "id": "5773" + }, + { + "name": "Ur-lun", + "id": "5774" + }, + { + "name": "Ur-pel", + "id": "5775" + }, + { + "name": "Bartak", + "id": "5778" + }, + { + "name": "Turgall", + "id": "5779" + }, + { + "name": "Reldak", + "id": "5780" + }, + { + "name": "Miltog", + "id": "5781" + }, + { + "name": "Mernik", + "id": "5782" + }, + { + "name": "Gourmet", + "id": "5787" + }, + { + "name": "Gourmet", + "id": "5789" + }, + { + "name": "Gourmet", + "id": "5790" + }, + { + "name": "Gourmet", + "id": "5791" + }, + { + "name": "Turgok", + "id": "5792" + }, + { + "name": "Markog", + "id": "5793" + }, + { + "name": "Durgok", + "id": "5794" + }, + { + "name": "Tindar", + "id": "5795" + }, + { + "name": "Gundik", + "id": "5796" + }, + { + "name": "Zenkog", + "id": "5797" + }, + { + "name": "Lurgon", + "id": "5798" + }, + { + "name": "Ur-tag", + "id": "5799" + }, + { + "name": "Young 'un", + "id": "5802" + }, + { + "name": "Tyke", + "id": "5804" + }, + { + "name": "Nipper", + "id": "5806" + }, + { + "name": "Movario", + "id": "5825" + }, + { + "name": "Darve", + "id": "5826" + }, + { + "name": "Barlak", + "id": "5828" + }, + { + "name": "Rat Burgiss", + "id": "5830" + }, + { + "name": "Surok Magis", + "id": "5835" + }, + { + "name": "Zaff", + "id": "5836" + }, + { + "name": "Anna Jones", + "id": "5837" + }, + { + "name": "King Roald", + "id": "5838" + }, + { + "name": "Mishkal'un Dorn", + "id": "5839" + }, + { + "name": "Dakh'thoulan Aegis", + "id": "5840" + }, + { + "name": "Sil'as Dahcsnu", + "id": "5841" + }, + { + "name": "Bench", + "id": "5853" + }, + { + "name": "Zanik", + "id": "5857" + }, + { + "name": "Ur-tag", + "id": "5858" + }, + { + "name": "Sigmund and Zanik", + "id": "5862" + }, + { + "name": "Ambassador Alvijar", + "id": "5863" + }, + { + "name": "Tegdak", + "id": "5868" + }, + { + "name": "Zanik", + "id": "5869" + }, + { + "name": "Sergeant Mossfists", + "id": "5871" + }, + { + "name": "Sergeant Slimetoes", + "id": "5872" + }, + { + "name": "Cyrisus", + "id": "5887" + }, + { + "name": "Cyrisus", + "id": "5894" + }, + { + "name": "Cyrisus", + "id": "5895" + }, + { + "name": "Cyrisus", + "id": "5896" + }, + { + "name": "Cyrisus", + "id": "5897" + }, + { + "name": "'Bird's-Eye' Jack", + "id": "5898" + }, + { + "name": "The Inadequacy", + "id": "5899" + }, + { + "name": "The Illusive", + "id": "5907" + }, + { + "name": "Banker", + "id": "5912" + }, + { + "name": "Banker", + "id": "5913" + }, + { + "name": "Elsie", + "id": "5915" + }, + { + "name": "Stray dog", + "id": "5918" + }, + { + "name": "Barnabus Hurma", + "id": "5932" + }, + { + "name": "Marius Giste", + "id": "5933" + }, + { + "name": "Caden Azro", + "id": "5934" + }, + { + "name": "Thias Leacke", + "id": "5935" + }, + { + "name": "Sinco Doar", + "id": "5936" + }, + { + "name": "Tinse Torpe", + "id": "5937" + }, + { + "name": "Torrcs", + "id": "5939" + }, + { + "name": "Marfet", + "id": "5940" + }, + { + "name": "Museum guard", + "id": "5941" + }, + { + "name": "Museum guard", + "id": "5942" + }, + { + "name": "Museum guard", + "id": "5943" + }, + { + "name": "Schoolboy", + "id": "5946" + }, + { + "name": "Teacher and pupil", + "id": "5948" + }, + { + "name": "Schoolboy", + "id": "5949" + }, + { + "name": "Teacher", + "id": "5950" + }, + { + "name": "Schoolgirl", + "id": "5951" + }, + { + "name": "Workman", + "id": "5953" + }, + { + "name": "Workman", + "id": "5955" + }, + { + "name": "Schoolgirl", + "id": "5957" + }, + { + "name": "Ed Wood", + "id": "5964" + }, + { + "name": "Orlando Smith", + "id": "5965" + }, + { + "name": "Natural historian", + "id": "5967" + }, + { + "name": "Natural historian", + "id": "5968" + }, + { + "name": "Natural historian", + "id": "5969" + }, + { + "name": "Natural historian", + "id": "5970" + }, + { + "name": "Schoolgirl", + "id": "5983" + }, + { + "name": "Schoolgirl", + "id": "5984" + }, + { + "name": "Schoolgirl", + "id": "5985" + }, + { + "name": "Miazrqa", + "id": "5988" + }, + { + "name": "Grimgnash", + "id": "5989" + }, + { + "name": "Drain pipe", + "id": "5991" + }, + { + "name": "Mouse", + "id": "5995" + }, + { + "name": "Rupert the Beard", + "id": "5997" + }, + { + "name": "Rupert the Beard", + "id": "6000" + }, + { + "name": "Gnome", + "id": "6002" + }, + { + "name": "Winkin", + "id": "6003" + }, + { + "name": "Gnome", + "id": "6004" + }, + { + "name": "Cage", + "id": "6005" + }, + { + "name": "Immenizz", + "id": "6071" + }, + { + "name": "Cyclops", + "id": "6075" + }, + { + "name": "Captain Ned", + "id": "6082" + }, + { + "name": "Cabin boy Jenkins", + "id": "6085" + }, + { + "name": "Cabin boy Jenkins", + "id": "6086" + }, + { + "name": "Elvarg", + "id": "6087" + }, + { + "name": "Drake", + "id": "6114" + }, + { + "examine": "A man, learned in the ways of the stars.", + "name": "Observatory professor", + "id": "6119" + }, + { + "name": "Clothears", + "id": "6130" + }, + { + "name": "Town crier", + "id": "6138" + }, + { + "name": "Town crier", + "id": "6139" + }, + { + "name": "Sir Lucan", + "id": "6158" + }, + { + "name": "Sir Lancelot", + "id": "6160" + }, + { + "name": "Sir Bedivere", + "id": "6161" + }, + { + "name": "Sir Tristram", + "id": "6162" + }, + { + "name": "Sir Pelleas", + "id": "6163" + }, + { + "name": "Sir Gawain", + "id": "6164" + }, + { + "name": "Sir Kay", + "id": "6165" + }, + { + "name": "Sir Pelleas", + "id": "6166" + }, + { + "name": "Sir Gawain", + "id": "6167" + }, + { + "name": "Sir Kay", + "id": "6168" + }, + { + "name": "Sir Kay", + "id": "6171" + }, + { + "name": "Sir Gawain", + "id": "6172" + }, + { + "name": "Sir Lucan", + "id": "6173" + }, + { + "name": "Bandit", + "id": "6174" + }, + { + "name": "Sir Tristram", + "id": "6175" + }, + { + "name": "Sir Pelleas", + "id": "6176" + }, + { + "name": "Sir Bedivere", + "id": "6177" + }, + { + "name": "Anna", + "id": "6178" + }, + { + "name": "David", + "id": "6179" + }, + { + "name": "Anna", + "id": "6180" + }, + { + "name": "Court judge", + "id": "6181" + }, + { + "name": "Jury", + "id": "6182" + }, + { + "name": "Prosecutor", + "id": "6185" + }, + { + "name": "Morgan Le Faye", + "id": "6186" + }, + { + "name": "Sinclair", + "id": "6191" + }, + { + "name": "Banker", + "id": "6199" + }, + { + "name": "K'ril Tsutsaroth", + "id": "6202" + }, + { + "name": "Warped terrorbird", + "id": "6284" + }, + { + "name": "Jeffery", + "id": "6298" + }, + { + "name": "Big monolith", + "id": "6299" + }, + { + "name": "Small monolith", + "id": "6300" + }, + { + "name": "Cute creature", + "id": "6301" + }, + { + "name": "Evil creature", + "id": "6302" + }, + { + "name": "Yewnock the engineer", + "id": "6303" + }, + { + "name": "Bolrie", + "id": "6304" + }, + { + "name": "Hazelmere", + "id": "6305" + }, + { + "name": "Advisor", + "id": "6307" + }, + { + "name": "King Argenthorg", + "id": "6308" + }, + { + "name": "Prince Argenthorg", + "id": "6309" + }, + { + "name": "Cute creature", + "id": "6310" + }, + { + "name": "Evil creature", + "id": "6312" + }, + { + "name": "Terrorbird servant", + "id": "6313" + }, + { + "name": "Guard no. 21", + "id": "6316" + }, + { + "name": "Guard no. 72", + "id": "6317" + }, + { + "name": "Longramble", + "id": "6318" + }, + { + "name": "Spirit tree", + "id": "6319" + }, + { + "name": "Spirit tree", + "id": "6321" + }, + { + "name": "Child", + "id": "6333" + }, + { + "name": "Child", + "id": "6340" + }, + { + "name": "Child", + "id": "6341" + }, + { + "name": "Child", + "id": "6342" + }, + { + "name": "Child", + "id": "6343" + }, + { + "name": "Child", + "id": "6345" + }, + { + "name": "Street urchin", + "id": "6357" + }, + { + "name": "Street urchin", + "id": "6359" + }, + { + "name": "Street urchin", + "id": "6360" + }, + { + "name": "Street urchin", + "id": "6361" + }, + { + "name": "Eniola", + "id": "6362" + }, + { + "name": "Kennith", + "id": "6373" + }, + { + "name": "Wizard Cromperty", + "id": "6375" + }, + { + "name": "Karamjan Jungle Eagle", + "id": "6384" + }, + { + "name": "Bandit", + "id": "6386" + }, + { + "name": "Glough", + "id": "6391" + }, + { + "name": "Oldak", + "id": "6392" + }, + { + "name": "Zanik", + "id": "6393" + }, + { + "name": "Zanik", + "id": "6394" + }, + { + "name": "Zanik", + "id": "6396" + }, + { + "name": "Zanik", + "id": "6398" + }, + { + "name": "Oldak", + "id": "6400" + }, + { + "name": "Skoblin", + "id": "6468" + }, + { + "name": "Snothead", + "id": "6474" + }, + { + "name": "Snailfeet", + "id": "6475" + }, + { + "name": "Mosschin", + "id": "6476" + }, + { + "name": "Redeyes", + "id": "6477" + }, + { + "name": "Strongbones", + "id": "6478" + }, + { + "name": "Grubfoot", + "id": "6479" + }, + { + "name": "Grubfoot", + "id": "6480" + }, + { + "name": "Priest", + "id": "6483" + }, + { + "name": "Priest", + "id": "6484" + }, + { + "name": "Priest", + "id": "6485" + }, + { + "name": "Priest", + "id": "6486" + }, + { + "name": "Priest", + "id": "6487" + }, + { + "name": "High priest", + "id": "6489" + }, + { + "name": "Registrar 1", + "id": "6505" + }, + { + "name": "Jury", + "id": "6511" + }, + { + "name": "Spectator", + "id": "6513" + }, + { + "name": "Spectator", + "id": "6515" + }, + { + "name": "Spectator", + "id": "6517" + }, + { + "name": "Spectator", + "id": "6519" + }, + { + "name": "Head Registrar", + "id": "6520" + }, + { + "name": "Mandrith", + "id": "6537" + }, + { + "name": "Banker", + "id": "6538" + }, + { + "name": "Charley the Cleaner", + "id": "6539" + }, + { + "name": "Scotty the Cleaner", + "id": "6540" + }, + { + "name": "Sanchez the Cleaner", + "id": "6541" + }, + { + "name": "Summer Bonde", + "id": "6542" + }, + { + "name": "Broken grave marker", + "id": "6566" + }, + { + "name": "Collapsing grave marker", + "id": "6567" + }, + { + "name": "Grave marker", + "id": "6568" + }, + { + "name": "Broken grave marker", + "id": "6569" + }, + { + "name": "Collapsing grave marker", + "id": "6570" + }, + { + "name": "Gravestone", + "id": "6571" + }, + { + "name": "Broken gravestone", + "id": "6572" + }, + { + "name": "Collapsing gravestone", + "id": "6573" + }, + { + "name": "Gravestone", + "id": "6574" + }, + { + "name": "Broken gravestone", + "id": "6575" + }, + { + "name": "Collapsing gravestone", + "id": "6576" + }, + { + "name": "Gravestone", + "id": "6577" + }, + { + "name": "Broken gravestone", + "id": "6578" + }, + { + "name": "Collapsing gravestone", + "id": "6579" + }, + { + "name": "Stele", + "id": "6580" + }, + { + "name": "Broken stele", + "id": "6581" + }, + { + "name": "Collapsing stele", + "id": "6582" + }, + { + "name": "Saradomin symbol", + "id": "6583" + }, + { + "name": "Broken Saradomin symbol", + "id": "6584" + }, + { + "name": "Collapsing Saradomin symbol", + "id": "6585" + }, + { + "name": "Zamorak symbol", + "id": "6586" + }, + { + "name": "Broken Zamorak symbol", + "id": "6587" + }, + { + "name": "Collapsing Zamorak symbol", + "id": "6588" + }, + { + "name": "Guthix symbol", + "id": "6589" + }, + { + "name": "Broken Guthix symbol", + "id": "6590" + }, + { + "name": "Collapsing Guthix symbol", + "id": "6591" + }, + { + "name": "Bandos symbol", + "id": "6592" + }, + { + "name": "Broken Bandos symbol", + "id": "6593" + }, + { + "name": "Collapsing Bandos symbol", + "id": "6594" + }, + { + "name": "Armadyl symbol", + "id": "6595" + }, + { + "name": "Broken Armadyl symbol", + "id": "6596" + }, + { + "name": "Collapsing Armadyl symbol", + "id": "6597" + }, + { + "name": "Memorial stone", + "id": "6598" + }, + { + "name": "Broken memorial stone", + "id": "6599" + }, + { + "name": "Collapsing memorial stone", + "id": "6600" + }, + { + "name": "Memorial stone", + "id": "6601" + }, + { + "name": "Broken memorial stone", + "id": "6602" + }, + { + "name": "Collapsing memorial stone", + "id": "6603" + }, + { + "name": "Snow imp", + "id": "6732" + }, + { + "name": "Snow imp", + "id": "6733" + }, + { + "name": "Snow imp", + "id": "6734" + }, + { + "name": "Snow imp", + "id": "6735" + }, + { + "name": "Snow imp", + "id": "6736" + }, + { + "name": "Snow imp", + "id": "6737" + }, + { + "name": "Snow imp", + "id": "6738" + }, + { + "name": "Snow", + "id": "6741" + }, + { + "name": "Snowman", + "id": "6746" + }, + { + "name": "Bulldog", + "id": "6751" + }, + { + "name": "Mummy", + "id": "6753" + }, + { + "name": "Mummy", + "id": "6754" + }, + { + "name": "Mummy", + "id": "6755" + }, + { + "name": "Mummy", + "id": "6756" + }, + { + "name": "Mummy", + "id": "6757" + }, + { + "name": "Mummy", + "id": "6758" + }, + { + "name": "Mummy", + "id": "6759" + }, + { + "name": "Mummy", + "id": "6760" + }, + { + "name": "Giant scarab", + "id": "6771" + }, + { + "name": "Scabaras priest", + "id": "6775" + }, + { + "name": "Maisa", + "id": "6782" + }, + { + "name": "Maisa", + "id": "6783" + }, + { + "name": "Priest", + "id": "6784" + }, + { + "name": "High Priest of Scabaras", + "id": "6791" + }, + { + "name": "Vulture", + "id": "6792" + }, + { + "name": "Spirit terrorbird", + "id": "6793" + }, + { + "name": "Beaver", + "id": "6808" + }, + { + "name": "Pet shop owner", + "id": "6898" + }, + { + "name": "Bulldog", + "id": "6899" + }, + { + "name": "Baby penguin", + "id": "6908" + }, + { + "name": "Penguin", + "id": "6909" + }, + { + "name": "Penguin", + "id": "6910" + }, + { + "name": "Raven chick", + "id": "6911" + }, + { + "name": "Raven", + "id": "6912" + }, + { + "name": "Baby raccoon", + "id": "6913" + }, + { + "name": "Raccoon", + "id": "6914" + }, + { + "name": "Baby gecko", + "id": "6915" + }, + { + "name": "Gecko", + "id": "6916" + }, + { + "name": "Gecko", + "id": "6918" + }, + { + "name": "Baby squirrel", + "id": "6919" + }, + { + "name": "Squirrel", + "id": "6920" + }, + { + "name": "Baby chameleon", + "id": "6922" + }, + { + "name": "Chameleon", + "id": "6923" + }, + { + "name": "Baby chameleon", + "id": "6924" + }, + { + "name": "Chameleon", + "id": "6925" + }, + { + "name": "Baby chameleon", + "id": "6926" + }, + { + "name": "Chameleon", + "id": "6927" + }, + { + "name": "Baby chameleon", + "id": "6928" + }, + { + "name": "Chameleon", + "id": "6929" + }, + { + "name": "Baby chameleon", + "id": "6930" + }, + { + "name": "Chameleon", + "id": "6931" + }, + { + "name": "Baby chameleon", + "id": "6932" + }, + { + "name": "Chameleon", + "id": "6933" + }, + { + "name": "Baby chameleon", + "id": "6934" + }, + { + "name": "Chameleon", + "id": "6935" + }, + { + "name": "Baby chameleon", + "id": "6936" + }, + { + "name": "Chameleon", + "id": "6937" + }, + { + "name": "Baby chameleon", + "id": "6938" + }, + { + "name": "Chameleon", + "id": "6939" + }, + { + "name": "Baby chameleon", + "id": "6940" + }, + { + "name": "Chameleon", + "id": "6941" + }, + { + "name": "Vulture chick", + "id": "6945" + }, + { + "name": "Vulture", + "id": "6946" + }, + { + "name": "Baby giant crab", + "id": "6947" + }, + { + "name": "Giant crab", + "id": "6948" + }, + { + "name": "Saradomin chick", + "id": "6949" + }, + { + "name": "Saradomin bird", + "id": "6950" + }, + { + "name": "Saradomin owl", + "id": "6951" + }, + { + "name": "Zamorak chick", + "id": "6952" + }, + { + "name": "Zamorak bird", + "id": "6953" + }, + { + "name": "Zamorak hawk", + "id": "6954" + }, + { + "name": "Guthix chick", + "id": "6955" + }, + { + "name": "Guthix bird", + "id": "6956" + }, + { + "name": "Guthix raptor", + "id": "6957" + }, + { + "name": "Pikkupstix", + "id": "6970" + }, + { + "name": "Giant wolpertinger", + "id": "6988" + }, + { + "name": "Ibis", + "id": "6991" + }, + { + "name": "Fishing spot", + "id": "6996" + }, + { + "name": "Dark Squall", + "id": "7000" + }, + { + "name": "Surok Magis", + "id": "7002" + }, + { + "name": "Grenwall", + "id": "7006" + }, + { + "name": "Platypus", + "id": "7015" + }, + { + "name": "Platypus", + "id": "7016" + }, + { + "name": "Platypus", + "id": "7017" + }, + { + "name": "Baby platypus", + "id": "7018" + }, + { + "name": "Baby platypus", + "id": "7019" + }, + { + "name": "Baby platypus", + "id": "7020" + }, + { + "name": "Platypus", + "id": "7022" + }, + { + "name": "Platypus", + "id": "7023" + }, + { + "name": "Baby platypus", + "id": "7025" + }, + { + "name": "Baby platypus", + "id": "7026" + }, + { + "name": "Patrick", + "id": "7027" + }, + { + "name": "Penelope", + "id": "7028" + }, + { + "name": "Peter", + "id": "7029" + }, + { + "name": "Peanut", + "id": "7030" + }, + { + "name": "Diseased kebbit", + "id": "7032" + }, + { + "name": "Fishing spot", + "id": "7040" + }, + { + "name": "Fishing spot", + "id": "7045" + }, + { + "name": "Fishing spot", + "id": "7046" + }, + { + "name": "Frawd", + "id": "7048" + }, + { + "name": "Ogress banker", + "id": "7050" + }, + { + "name": "Seegud", + "id": "7052" + }, + { + "name": "Chargurr", + "id": "7053" + }, + { + "name": "Chargurr", + "id": "7054" + }, + { + "name": "Snurgh", + "id": "7055" + }, + { + "name": "Kringk", + "id": "7058" + }, + { + "name": "Thump", + "id": "7059" + }, + { + "name": "Massage table", + "id": "7060" + }, + { + "name": "Thump", + "id": "7061" + }, + { + "name": "Muggh", + "id": "7062" + }, + { + "name": "Kringk", + "id": "7063" + }, + { + "name": "Hairdryer", + "id": "7064" + }, + { + "name": "Snert", + "id": "7065" + }, + { + "name": "Tyke", + "id": "7068" + }, + { + "name": "Snarrl", + "id": "7069" + }, + { + "name": "Snarrk", + "id": "7070" + }, + { + "name": "I'rk", + "id": "7071" + }, + { + "name": "Thuddley", + "id": "7072" + }, + { + "name": "Grr'bah", + "id": "7073" + }, + { + "name": "Chomp", + "id": "7074" + }, + { + "name": "Grubb", + "id": "7075" + }, + { + "name": "Grunther", + "id": "7076" + }, + { + "name": "Glum", + "id": "7077" + }, + { + "name": "Flying bugs", + "id": "7083" + }, + { + "name": "Balnea", + "id": "7087" + }, + { + "name": "Chief Tess", + "id": "7088" + }, + { + "name": "Chargurr", + "id": "7089" + }, + { + "name": "Wise Old Man", + "id": "7090" + }, + { + "name": "Dawg", + "id": "7091" + }, + { + "name": "Drunken sailor", + "id": "7116" + }, + { + "name": "Drunken sailor", + "id": "7117" + }, + { + "name": "Catapult engineer", + "id": "7118" + }, + { + "name": "Tyras guard", + "id": "7119" + }, + { + "name": "General Hining", + "id": "7121" + }, + { + "name": "Githan", + "id": "7122" + }, + { + "name": "Amulet of Nature", + "id": "7123" + }, + { + "name": "Mud", + "id": "7124" + }, + { + "name": "Surok Magis", + "id": "7136" + }, + { + "name": "Professor Henry", + "id": "7143" + }, + { + "name": "Villager", + "id": "7163" + }, + { + "name": "Villager", + "id": "7165" + }, + { + "name": "Villager", + "id": "7166" + }, + { + "name": "Villager", + "id": "7167" + }, + { + "name": "Kimberly", + "id": "7169" + }, + { + "name": "Kimberly", + "id": "7170" + }, + { + "name": "Kennith", + "id": "7171" + }, + { + "name": "Kennith", + "id": "7172" + }, + { + "name": "Kennith", + "id": "7173" + }, + { + "name": "Kennith", + "id": "7174" + }, + { + "name": "Kennith", + "id": "7175" + }, + { + "name": "Villager", + "id": "7176" + }, + { + "name": "Ezekial Lovecraft", + "id": "7184" + }, + { + "name": "Clive", + "id": "7185" + }, + { + "name": "Katherine", + "id": "7186" + }, + { + "name": "Katherine", + "id": "7187" + }, + { + "name": "Clive", + "id": "7188" + }, + { + "name": "Kent", + "id": "7189" + }, + { + "name": "Rabbit hole", + "id": "7190" + }, + { + "name": "Easter Bunny", + "id": "7197" + }, + { + "name": "Bunny", + "id": "7198" + }, + { + "name": "Bunny", + "id": "7199" + }, + { + "name": "Guard bunny", + "id": "7200" + }, + { + "name": "Chocatrice", + "id": "7201" + }, + { + "name": "Void Knight", + "id": "7203" + }, + { + "name": "Mouse", + "id": "7205" + }, + { + "name": "Rabbit", + "id": "7208" + }, + { + "name": "Raven chick", + "id": "7261" + }, + { + "name": "Raven", + "id": "7262" + }, + { + "name": "Raven chick", + "id": "7263" + }, + { + "name": "Raven", + "id": "7264" + }, + { + "name": "Raven chick", + "id": "7265" + }, + { + "name": "Raven", + "id": "7266" + }, + { + "name": "Raven chick", + "id": "7267" + }, + { + "name": "Raven", + "id": "7268" + }, + { + "name": "Raven chick", + "id": "7269" + }, + { + "name": "Raven", + "id": "7270" + }, + { + "name": "Baby raccoon", + "id": "7271" + }, + { + "name": "Raccoon", + "id": "7272" + }, + { + "name": "Baby raccoon", + "id": "7273" + }, + { + "name": "Raccoon", + "id": "7274" + }, + { + "name": "Baby raccoon", + "id": "7276" + }, + { + "name": "Baby gecko", + "id": "7277" + }, + { + "name": "Baby gecko", + "id": "7278" + }, + { + "name": "Baby gecko", + "id": "7279" + }, + { + "name": "Baby gecko", + "id": "7280" + }, + { + "name": "Gecko", + "id": "7281" + }, + { + "name": "Gecko", + "id": "7282" + }, + { + "name": "Gecko", + "id": "7283" + }, + { + "name": "Gecko", + "id": "7284" + }, + { + "name": "Baby gecko", + "id": "7286" + }, + { + "name": "Baby gecko", + "id": "7287" + }, + { + "name": "Baby gecko", + "id": "7288" + }, + { + "name": "Gecko", + "id": "7289" + }, + { + "name": "Gecko", + "id": "7290" + }, + { + "name": "Gecko", + "id": "7291" + }, + { + "name": "Gecko", + "id": "7292" + }, + { + "name": "Baby giant crab", + "id": "7293" + }, + { + "name": "Giant crab", + "id": "7294" + }, + { + "name": "Baby giant crab", + "id": "7295" + }, + { + "name": "Giant crab", + "id": "7296" + }, + { + "name": "Baby giant crab", + "id": "7297" + }, + { + "name": "Giant crab", + "id": "7298" + }, + { + "name": "Baby giant crab", + "id": "7299" + }, + { + "name": "Giant crab", + "id": "7300" + }, + { + "name": "Baby squirrel", + "id": "7301" + }, + { + "name": "Squirrel", + "id": "7302" + }, + { + "name": "Baby squirrel", + "id": "7303" + }, + { + "name": "Squirrel", + "id": "7304" + }, + { + "name": "Baby squirrel", + "id": "7305" + }, + { + "name": "Squirrel", + "id": "7306" + }, + { + "name": "Baby squirrel", + "id": "7307" + }, + { + "name": "Squirrel", + "id": "7308" + }, + { + "name": "Baby squirrel", + "id": "7309" + }, + { + "name": "Baby squirrel", + "id": "7310" + }, + { + "name": "Baby squirrel", + "id": "7311" + }, + { + "name": "Baby squirrel", + "id": "7312" + }, + { + "name": "Baby penguin", + "id": "7313" + }, + { + "name": "Penguin", + "id": "7314" + }, + { + "name": "Penguin", + "id": "7315" + }, + { + "name": "Baby penguin", + "id": "7316" + }, + { + "name": "Penguin", + "id": "7317" + }, + { + "name": "Penguin", + "id": "7318" + }, + { + "name": "Vulture chick", + "id": "7319" + }, + { + "name": "Vulture", + "id": "7320" + }, + { + "name": "Vulture chick", + "id": "7321" + }, + { + "name": "Vulture", + "id": "7322" + }, + { + "name": "Vulture chick", + "id": "7323" + }, + { + "name": "Vulture", + "id": "7324" + }, + { + "name": "Vulture chick", + "id": "7325" + }, + { + "name": "Vulture", + "id": "7326" + }, + { + "name": "Vulture chick", + "id": "7327" + }, + { + "name": "Vulture", + "id": "7328" + }, + { + "name": "Void shifter", + "id": "7369" + }, + { + "name": "Ravenous locust", + "id": "7372" + }, + { + "name": "Uberlass", + "id": "7383" + }, + { + "name": "Uberlass", + "id": "7384" + }, + { + "name": "Uberlass", + "id": "7385" + }, + { + "name": "Uberlass", + "id": "7386" + }, + { + "name": "Sannytea", + "id": "7387" + }, + { + "name": "Sannytea", + "id": "7388" + }, + { + "name": "Irollsixes", + "id": "7389" + }, + { + "name": "Irollsixes", + "id": "7390" + }, + { + "name": "Foxyhunter", + "id": "7391" + }, + { + "name": "Foxyhunter", + "id": "7392" + }, + { + "name": "Foxyhunter", + "id": "7393" + }, + { + "name": "Gabriela", + "id": "7394" + }, + { + "name": "Teodor", + "id": "7395" + }, + { + "name": "Aurel", + "id": "7396" + }, + { + "name": "Cornelius", + "id": "7397" + }, + { + "name": "Sorin", + "id": "7399" + }, + { + "name": "Luscion", + "id": "7400" + }, + { + "name": "Sergiu", + "id": "7401" + }, + { + "name": "Radu", + "id": "7402" + }, + { + "name": "Grigore", + "id": "7403" + }, + { + "name": "Ileana", + "id": "7404" + }, + { + "name": "Valeria", + "id": "7405" + }, + { + "name": "Emilia", + "id": "7406" + }, + { + "name": "Florin", + "id": "7407" + }, + { + "name": "Catalina", + "id": "7408" + }, + { + "name": "Ivan", + "id": "7409" + }, + { + "name": "Victor", + "id": "7410" + }, + { + "name": "Helena", + "id": "7411" + }, + { + "name": "Mihail", + "id": "7412" + }, + { + "name": "Nicoleta", + "id": "7413" + }, + { + "name": "Vasile", + "id": "7414" + }, + { + "name": "Razvan", + "id": "7415" + }, + { + "name": "Luminata", + "id": "7416" + }, + { + "name": "Juvinate", + "id": "7418" + }, + { + "name": "Held vampyre juvinate", + "id": "7419" + }, + { + "name": "Hieronymus Avlafrim", + "id": "7420" + }, + { + "name": "Sasquine Huburns", + "id": "7421" + }, + { + "name": "Sasquine Huburns", + "id": "7422" + }, + { + "name": "Lollery", + "id": "7423" + }, + { + "name": "Wigglewoo", + "id": "7424" + }, + { + "name": "Orangeowns", + "id": "7427" + }, + { + "name": "I like m0m", + "id": "7428" + }, + { + "name": "I like m0m", + "id": "7429" + }, + { + "name": "Qutiedoll", + "id": "7430" + }, + { + "name": "Goreu", + "id": "7431" + }, + { + "name": "Ysgawyn", + "id": "7432" + }, + { + "name": "Arvel", + "id": "7433" + }, + { + "name": "Mawrth", + "id": "7434" + }, + { + "name": "Kelyn", + "id": "7435" + }, + { + "name": "Eoin", + "id": "7436" + }, + { + "name": "Iona", + "id": "7437" + }, + { + "name": "Arianwyn", + "id": "7442" + }, + { + "name": "Oronwen", + "id": "7444" + }, + { + "name": "Banker", + "id": "7445" + }, + { + "name": "Banker", + "id": "7446" + }, + { + "name": "Dalldav", + "id": "7447" + }, + { + "name": "Gethin", + "id": "7448" + }, + { + "name": "Amaethwr", + "id": "7449" + }, + { + "name": "Teclyn", + "id": "7450" + }, + { + "name": "Butterfly", + "id": "7451" + }, + { + "name": "Lapsang", + "id": "7452" + }, + { + "name": "Lapsang", + "id": "7453" + }, + { + "name": "Souchong", + "id": "7454" + }, + { + "name": "Souchong", + "id": "7455" + }, + { + "name": "Earlgrey", + "id": "7456" + }, + { + "name": "Earlgrey", + "id": "7457" + }, + { + "name": "Fairtrade", + "id": "7458" + }, + { + "name": "Sliceoflemon", + "id": "7464" + }, + { + "name": "Sliceoflemon", + "id": "7465" + }, + { + "name": "Milknosugar", + "id": "7466" + }, + { + "name": "Milknosugar", + "id": "7467" + }, + { + "name": "Randomdood", + "id": "7468" + }, + { + "name": "Employedman", + "id": "7469" + }, + { + "name": "Heidiggle", + "id": "7470" + }, + { + "name": "Dodgy Penny", + "id": "7471" + }, + { + "name": "Shadydude98", + "id": "7472" + }, + { + "name": "Madam C", + "id": "7473" + }, + { + "name": "M0m Online", + "id": "7474" + }, + { + "name": "Learking", + "id": "7478" + }, + { + "name": "Moglewee", + "id": "7489" + }, + { + "name": "Moglewee", + "id": "7490" + }, + { + "name": "Sarah Domin", + "id": "7491" + }, + { + "name": "Snowy knight", + "id": "7498" + }, + { + "name": "Sapphire glacialis", + "id": "7499" + }, + { + "name": "Evil Wibbler", + "id": "7509" + }, + { + "name": "Heresjohnny", + "id": "7516" + }, + { + "name": "Mogglewump", + "id": "7517" + }, + { + "name": "Renderorder", + "id": "7521" + }, + { + "name": "Boolean", + "id": "7522" + }, + { + "name": "Stress Diva", + "id": "7523" + }, + { + "name": "Treadsoftly", + "id": "7524" + }, + { + "name": "Helphelphelp", + "id": "7525" + }, + { + "name": "Doorbellpl0x", + "id": "7526" + }, + { + "name": "Fixmydoorup", + "id": "7527" + }, + { + "name": "Wallscaler", + "id": "7529" + }, + { + "name": "2scompany", + "id": "7530" + }, + { + "name": "2scompany", + "id": "7531" + }, + { + "name": "4sjustsilly", + "id": "7533" + }, + { + "name": "Roadblocked", + "id": "7534" + }, + { + "name": "Barricade", + "id": "7535" + }, + { + "name": "Barricade", + "id": "7536" + }, + { + "name": "Yoinker", + "id": "7537" + }, + { + "name": "Yoinker", + "id": "7538" + }, + { + "name": "Stopthief", + "id": "7539" + }, + { + "name": "Stopthief", + "id": "7540" + }, + { + "name": "Knickknack", + "id": "7541" + }, + { + "name": "Paddywhack", + "id": "7542" + }, + { + "name": "Giveadog", + "id": "7543" + }, + { + "name": "Spacebadgers", + "id": "7544" + }, + { + "name": "Freakypeaky", + "id": "7545" + }, + { + "name": "Rollinghome", + "id": "7546" + }, + { + "name": "Nullpointer", + "id": "7547" + }, + { + "name": "Badgerfreak", + "id": "7548" + }, + { + "name": "Windstrike32", + "id": "7549" + }, + { + "name": "Void Knight", + "id": "7550" + }, + { + "name": "Wraithboss", + "id": "7567" + }, + { + "name": "What Goudron", + "id": "7568" + }, + { + "name": "Lvzyoda", + "id": "7574" + }, + { + "name": "Airstriker", + "id": "7575" + }, + { + "name": "Droepedoff", + "id": "7576" + }, + { + "name": "Plzpudding", + "id": "7577" + }, + { + "name": "Assassin10", + "id": "7578" + }, + { + "name": "Steallake", + "id": "7579" + }, + { + "name": "Deepkiwi", + "id": "7581" + }, + { + "name": "Bigface Oz", + "id": "7584" + }, + { + "name": "Torcher", + "id": "7587" + }, + { + "name": "Torcher", + "id": "7588" + }, + { + "name": "Defiler", + "id": "7589" + }, + { + "name": "Defiler", + "id": "7590" + }, + { + "name": "Shifter", + "id": "7591" + }, + { + "name": "Shifter", + "id": "7592" + }, + { + "name": "Shifter", + "id": "7594" + }, + { + "name": "Splatter", + "id": "7595" + }, + { + "name": "Splatter", + "id": "7596" + }, + { + "name": "Splatter", + "id": "7597" + }, + { + "name": "Spinner", + "id": "7598" + }, + { + "name": "Ravager", + "id": "7599" + }, + { + "name": "Fiara", + "id": "7600" + }, + { + "name": "Reggie", + "id": "7601" + }, + { + "name": "Getorix", + "id": "7602" + }, + { + "name": "Pontimer", + "id": "7603" + }, + { + "name": "Alran", + "id": "7604" + }, + { + "name": "Flying female vampire", + "id": "7610" + }, + { + "name": "Flying female vampire", + "id": "7611" + }, + { + "name": "Flying female vampire", + "id": "7612" + }, + { + "name": "Flying female vampire", + "id": "7613" + }, + { + "name": "Vyrewatch", + "id": "7622" + }, + { + "name": "Vyrewatch", + "id": "7623" + }, + { + "name": "Vyrewatch", + "id": "7624" + }, + { + "name": "Vyrewatch", + "id": "7625" + }, + { + "name": "Vyrewatch", + "id": "7630" + }, + { + "name": "Vyrewatch", + "id": "7633" + }, + { + "name": "Fishing spot", + "id": "7636" + }, + { + "name": "Skeletal hand", + "id": "7637" + }, + { + "name": "Zaromark Sliver", + "id": "7652" + }, + { + "name": "Zaromark Sliver", + "id": "7653" + }, + { + "name": "Fistandantilus", + "id": "7662" + }, + { + "name": "Fistandantilus", + "id": "7663" + }, + { + "name": "Mercenary Adventurer", + "id": "7664" + }, + { + "name": "Ivan Strom", + "id": "7665" + }, + { + "name": "Mysterious person", + "id": "7666" + }, + { + "name": "Mysterious person", + "id": "7667" + }, + { + "name": "Mysterious person", + "id": "7668" + }, + { + "name": "Mysterious person", + "id": "7669" + }, + { + "name": "Mysterious person", + "id": "7670" + }, + { + "name": "Mysterious person", + "id": "7671" + }, + { + "name": "Flaygian Screwte", + "id": "7672" + }, + { + "name": "Mekritus A'hara", + "id": "7673" + }, + { + "name": "Andiess Juip", + "id": "7674" + }, + { + "name": "Kael Forshaw", + "id": "7675" + }, + { + "name": "Andiess Juip", + "id": "7676" + }, + { + "name": "Kael Forshaw", + "id": "7677" + }, + { + "name": "Safalaan", + "id": "7678" + }, + { + "name": "Andiess Juip", + "id": "7679" + }, + { + "name": "Kael Forshaw", + "id": "7680" + }, + { + "name": "Safalaan", + "id": "7681" + }, + { + "name": "Vyrewatch", + "id": "7684" + }, + { + "name": "Vyrewatch", + "id": "7685" + }, + { + "name": "Safalaan", + "id": "7686" + }, + { + "name": "Spectral Vyrewatch", + "id": "7687" + }, + { + "name": "Drezel", + "id": "7688" + }, + { + "name": "Temple guardian", + "id": "7708" + }, + { + "name": "Baby icefiend", + "id": "7712" + }, + { + "name": "Brother Bordiss", + "id": "7718" + }, + { + "name": "Brother Althric", + "id": "7719" + }, + { + "name": "Drorkar", + "id": "7720" + }, + { + "name": "Nurmof", + "id": "7721" + }, + { + "name": "Lakki the delivery dwarf", + "id": "7722" + }, + { + "name": "Drorkar", + "id": "7723" + }, + { + "name": "Brother Bordiss", + "id": "7724" + }, + { + "name": "Professor Arblenap", + "id": "7725" + }, + { + "name": "Assistant", + "id": "7726" + }, + { + "name": "Baby icefiend", + "id": "7728" + }, + { + "name": "Will", + "id": "7737" + }, + { + "name": "Will", + "id": "7738" + }, + { + "name": "Phil", + "id": "7739" + }, + { + "name": "Fluffs", + "id": "7742" + }, + { + "name": "Fluffs", + "id": "7743" + }, + { + "name": "Kittens", + "id": "7744" + }, + { + "name": "TzHaar-Mej-Malk", + "id": "7746" + }, + { + "name": "TzHaar-Hur-Frok", + "id": "7748" + }, + { + "name": "TzHaar-Ket-Grol", + "id": "7749" + }, + { + "name": "TzHaar-Ket-Rok", + "id": "7750" + }, + { + "name": "TzHaar-Ket-Lurk", + "id": "7751" + }, + { + "name": "TzHaar-Mej", + "id": "7753" + }, + { + "name": "TzHaar-Hur", + "id": "7754" + }, + { + "name": "TzHaar-Xil", + "id": "7755" + }, + { + "name": "TzHaar-Hur-Brekt", + "id": "7757" + }, + { + "name": "TzHaar-Hur-Brekt", + "id": "7758" + }, + { + "name": "TzHaar-Ket-Jok", + "id": "7759" + }, + { + "name": "TzHaar-Ket-Jok", + "id": "7760" + }, + { + "name": "TzHaar-Xil-Mor", + "id": "7761" + }, + { + "name": "TzHaar-Xil-Mor", + "id": "7762" + }, + { + "name": "TzHaar-Mej-Kol", + "id": "7763" + }, + { + "name": "TzHaar-Mej-Kol", + "id": "7764" + }, + { + "name": "TzHaar-Hur-Klag", + "id": "7765" + }, + { + "name": "TzHaar-Hur-Klag", + "id": "7766" + }, + { + "name": "TokTz-Ket-Dill", + "id": "7770" + }, + { + "name": "TokTz-Ket-Dill", + "id": "7771" + }, + { + "name": "Skeleton", + "id": "7774" + }, + { + "name": "Skeleton", + "id": "7775" + }, + { + "name": "Skeleton", + "id": "7776" + }, + { + "name": "Skeleton", + "id": "7777" + }, + { + "name": "Skeleton", + "id": "7778" + }, + { + "name": "Sumona", + "id": "7779" + }, + { + "name": "Jesmona", + "id": "7781" + }, + { + "name": "Catolax", + "id": "7782" + }, + { + "name": "Ali Cat", + "id": "7783" + }, + { + "name": "Cave crawler", + "id": "7787" + }, + { + "name": "Skeleton", + "id": "7788" + }, + { + "name": "Zombie", + "id": "7789" + }, + { + "name": "Zombie", + "id": "7790" + }, + { + "name": "Zombie", + "id": "7791" + }, + { + "name": "Zombie", + "id": "7792" + }, + { + "name": "Banshee mistress", + "id": "7793" + }, + { + "name": "Banshee mistress", + "id": "7794" + }, + { + "name": "Insectoid assassin", + "id": "7795" + }, + { + "name": "Insectoid assassin", + "id": "7796" + }, + { + "name": "Kurask overlord", + "id": "7797" + }, + { + "name": "Monstrous cave crawler", + "id": "7798" + }, + { + "name": "Basilisk boss", + "id": "7799" + }, + { + "name": "Mightiest turoth", + "id": "7800" + }, + { + "name": "Aberrant spectre", + "id": "7801" + }, + { + "name": "Aberrant spectre", + "id": "7802" + }, + { + "name": "Aberrant spectre", + "id": "7803" + }, + { + "name": "Aberrant spectre", + "id": "7804" + }, + { + "name": "Kurask minion", + "id": "7805" + }, + { + "name": "Mummy warrior", + "id": "7806" + }, + { + "name": "Mummy warrior", + "id": "7807" + }, + { + "name": "Mummy warrior", + "id": "7808" + }, + { + "name": "Cat", + "id": "7809" + }, + { + "name": "Banshee", + "id": "7810" + }, + { + "name": "Kurask", + "id": "7811" + }, + { + "name": "Cave crawler", + "id": "7812" + }, + { + "name": "Basilisk", + "id": "7813" + }, + { + "name": "Turoth", + "id": "7814" + }, + { + "name": "Skeleton", + "id": "7815" + }, + { + "name": "Master", + "id": "7816" + }, + { + "name": "Zombie", + "id": "7817" + }, + { + "name": "Zombie", + "id": "7818" + }, + { + "name": "Zombie", + "id": "7819" + }, + { + "name": "Zombie", + "id": "7820" + }, + { + "name": "A wanderer.", + "id": "7821" + }, + { + "name": "A wanderer.", + "id": "7822" + }, + { + "name": "Osman", + "id": "7824" + }, + { + "name": "50 Ships Mufassah", + "id": "7825" + }, + { + "name": "Karamthulhu", + "id": "7826" + }, + { + "name": "Karamthulhu", + "id": "7827" + }, + { + "name": "Giant lobster", + "id": "7828" + }, + { + "name": "Giant crab", + "id": "7829" + }, + { + "name": "Customs Sergeant", + "id": "7830" + }, + { + "name": "Customs Sergeant", + "id": "7831" + }, + { + "name": "Young Ralph", + "id": "7832" + }, + { + "name": "Young Ralph", + "id": "7833" + }, + { + "name": "Young Ralph", + "id": "7834" + }, + { + "name": "Young Ralph", + "id": "7835" + }, + { + "name": "Young Ralph", + "id": "7836" + }, + { + "name": "Customs Officer", + "id": "7837" + }, + { + "name": "Customs Officer", + "id": "7838" + }, + { + "name": "Customs Officer", + "id": "7839" + }, + { + "name": "Heavy-Handed Harry", + "id": "7840" + }, + { + "name": "Player", + "id": "7841" + }, + { + "name": "Locker Officer", + "id": "7842" + }, + { + "name": "Locker Officer", + "id": "7843" + }, + { + "name": "Ex-ex-parrot", + "id": "7844" + }, + { + "name": "Pirate impling", + "id": "7845" + }, + { + "name": "Pirate impling", + "id": "7846" + }, + { + "name": "Captain Rabid Jack", + "id": "7847" + }, + { + "name": "Jack", + "id": "7848" + }, + { + "name": "Bosun Giles", + "id": "7849" + }, + { + "name": "Pirate", + "id": "7850" + }, + { + "name": "Lizzie", + "id": "7851" + }, + { + "name": "Cap'n Izzy No-Beard", + "id": "7852" + }, + { + "name": "Ralph", + "id": "7853" + }, + { + "name": "Brass Hand Harry", + "id": "7854" + }, + { + "name": "Bill Teach", + "id": "7855" + }, + { + "name": "Pirate", + "id": "7856" + }, + { + "name": "Brass Hand Harry", + "id": "7857" + }, + { + "name": "Gull", + "id": "7858" + }, + { + "name": "Jack", + "id": "7861" + }, + { + "name": "Fishing spot", + "id": "7862" + }, + { + "name": "Fishing spot", + "id": "7863" + }, + { + "name": "Fishing spot", + "id": "7864" + }, + { + "name": "Guardsman Dante", + "id": "7865" + }, + { + "name": "Guardsman DeShawn", + "id": "7866" + }, + { + "name": "Guardsman Brawn", + "id": "7867" + }, + { + "name": "Iain", + "id": "7868" + }, + { + "name": "Julian", + "id": "7869" + }, + { + "name": "Lachtopher", + "id": "7870" + }, + { + "name": "Samuel", + "id": "7871" + }, + { + "name": "Victoria", + "id": "7872" + }, + { + "name": "Man", + "id": "7873" + }, + { + "name": "Man", + "id": "7874" + }, + { + "name": "Man", + "id": "7875" + }, + { + "name": "Man", + "id": "7876" + }, + { + "name": "Man", + "id": "7877" + }, + { + "name": "Man", + "id": "7878" + }, + { + "name": "Man", + "id": "7879" + }, + { + "name": "Woman", + "id": "7880" + }, + { + "name": "Woman", + "id": "7881" + }, + { + "name": "Woman", + "id": "7882" + }, + { + "name": "Woman", + "id": "7883" + }, + { + "name": "Woman", + "id": "7884" + }, + { + "name": "Guardsman Dante", + "id": "7885" + }, + { + "name": "Guardsman DeShawn", + "id": "7886" + }, + { + "name": "Guardsman Brawn", + "id": "7887" + }, + { + "name": "Sergeant Abram", + "id": "7888" + }, + { + "name": "Guardsman Pazel", + "id": "7889" + }, + { + "name": "Guardsman Peale", + "id": "7890" + }, + { + "name": "General Wartface", + "id": "7892" + }, + { + "name": "General Bentnoze", + "id": "7893" + }, + { + "name": "General Wartface", + "id": "7894" + }, + { + "name": "General Bentnoze", + "id": "7896" + }, + { + "name": "Loar Shadow", + "id": "7898" + }, + { + "name": "Loar Shadow", + "id": "7901" + }, + { + "name": "Sergeant Abram", + "id": "7902" + }, + { + "name": "Guardsman Pazel", + "id": "7903" + }, + { + "name": "Guardsman Peale", + "id": "7904" + }, + { + "name": "Barricade guard", + "id": "7905" + }, + { + "name": "Barricade guard", + "id": "7906" + }, + { + "name": "Barricade guard", + "id": "7907" + }, + { + "name": "Barricade guard", + "id": "7908" + }, + { + "name": "Barricade guard", + "id": "7909" + }, + { + "name": "Barricade guard", + "id": "7910" + }, + { + "name": "Barricade guard", + "id": "7911" + }, + { + "name": "Border guard", + "id": "7912" + }, + { + "name": "Iain", + "id": "7913" + }, + { + "name": "Julian", + "id": "7914" + }, + { + "name": "Lachtopher", + "id": "7915" + }, + { + "name": "Samuel", + "id": "7916" + }, + { + "name": "Victoria", + "id": "7917" + }, + { + "name": "Man", + "id": "7918" + }, + { + "name": "Man", + "id": "7919" + }, + { + "name": "Man", + "id": "7920" + }, + { + "name": "Man", + "id": "7921" + }, + { + "name": "Man", + "id": "7922" + }, + { + "name": "Man", + "id": "7923" + }, + { + "name": "Man", + "id": "7924" + }, + { + "name": "Woman", + "id": "7925" + }, + { + "name": "Woman", + "id": "7926" + }, + { + "name": "Woman", + "id": "7927" + }, + { + "name": "Woman", + "id": "7928" + }, + { + "name": "Lumbridge Guide", + "id": "7929" + }, + { + "name": "Doomsayer", + "id": "7930" + }, + { + "name": "Donie", + "id": "7931" + }, + { + "name": "Gee", + "id": "7932" + }, + { + "name": "Duke Horacio", + "id": "7933" + }, + { + "name": "Sigmund", + "id": "7934" + }, + { + "name": "Hans", + "id": "7935" + }, + { + "name": "Cook", + "id": "7936" + }, + { + "name": "Father Aereck", + "id": "7937" + }, + { + "name": "Sir Vant", + "id": "7938" + }, + { + "name": "Sir Vant", + "id": "7939" + }, + { + "name": "Sir Vant", + "id": "7940" + }, + { + "name": "Sir Vant", + "id": "7941" + }, + { + "name": "Sir Vant", + "id": "7942" + }, + { + "name": "Dragon", + "id": "7943" + }, + { + "name": "Dragon", + "id": "7944" + }, + { + "name": "Dragon", + "id": "7945" + }, + { + "name": "Dragon", + "id": "7946" + }, + { + "name": "Dragon", + "id": "7947" + }, + { + "name": "Dragon", + "id": "7948" + }, + { + "name": "Lumbridge Guide", + "id": "7949" + }, + { + "name": "Melee Tutor", + "id": "7950" + }, + { + "name": "Ranged Tutor", + "id": "7951" + }, + { + "name": "Magic Tutor", + "id": "7952" + }, + { + "name": "Cooking Tutor", + "id": "7953" + }, + { + "name": "Crafting Tutor", + "id": "7954" + }, + { + "name": "Fishing Tutor", + "id": "7955" + }, + { + "name": "Mining Tutor", + "id": "7956" + }, + { + "name": "Prayer Tutor", + "id": "7957" + }, + { + "name": "Woodcutting Tutor", + "id": "7960" + }, + { + "name": "Bank Tutor", + "id": "7961" + }, + { + "name": "Gee", + "id": "7962" + }, + { + "name": "Spirit", + "id": "7963" + }, + { + "name": "Goblin", + "id": "7964" + }, + { + "name": "Goblin", + "id": "7965" + }, + { + "name": "Chaos druid", + "id": "7966" + }, + { + "name": "Shopkeeper", + "id": "7967" + }, + { + "name": "Explorer Jack", + "id": "7968" + }, + { + "name": "Smithing Tutor", + "id": "7970" + }, + { + "name": "Mining Tutor", + "id": "7971" + }, + { + "name": "Spirit", + "id": "7972" + }, + { + "name": "Spirit", + "id": "7977" + }, + { + "name": "Spirit", + "id": "7978" + }, + { + "name": "Spirit", + "id": "7979" + }, + { + "name": "Spirit", + "id": "7980" + }, + { + "name": "Spirit", + "id": "7981" + }, + { + "name": "Spirit", + "id": "7982" + }, + { + "name": "Spirit", + "id": "7983" + }, + { + "name": "Spirit", + "id": "7984" + }, + { + "name": "Summer Bonde", + "id": "7985" + }, + { + "name": "Spirit", + "id": "7987" + }, + { + "name": "Spirit", + "id": "7988" + }, + { + "name": "Erik Bonde", + "id": "7989" + }, + { + "name": "Spirit", + "id": "7990" + }, + { + "name": "Jallek Lenkin", + "id": "7991" + }, + { + "name": "Spirit", + "id": "7992" + }, + { + "name": "Meranek Thanatos", + "id": "7993" + }, + { + "name": "Ghostly warrior", + "id": "7994" + }, + { + "name": "Spirit Beast", + "id": "7995" + }, + { + "name": "Spirit Beast", + "id": "7996" + }, + { + "name": "Spirit Beast", + "id": "7997" + }, + { + "name": "Ghost", + "id": "7998" + }, + { + "name": "Ghost", + "id": "7999" + }, + { + "name": "Goth leprechaun", + "id": "8000" + }, + { + "name": "Jack", + "id": "8001" + }, + { + "name": "Sarah", + "id": "8003" + }, + { + "name": "Laura", + "id": "8004" + }, + { + "name": "Laura", + "id": "8005" + }, + { + "name": "Roger", + "id": "8006" + }, + { + "name": "Jorral", + "id": "8007" + }, + { + "name": "Guthix", + "id": "8008" + }, + { + "name": "Max the traveller", + "id": "8009" + }, + { + "name": "Man", + "id": "8010" + }, + { + "name": "Man", + "id": "8011" + }, + { + "name": "Woman", + "id": "8012" + }, + { + "name": "Woman", + "id": "8013" + }, + { + "name": "Haluned", + "id": "8014" + }, + { + "name": "Jack", + "id": "8015" + }, + { + "name": "Baby Sarah", + "id": "8016" + }, + { + "name": "Snowy", + "id": "8017" + }, + { + "name": "Roger", + "id": "8018" + }, + { + "name": "Portal", + "id": "8019" + }, + { + "name": "Portal", + "id": "8020" + }, + { + "name": "Yellow orb", + "id": "8021" + }, + { + "name": "Yellow orb", + "id": "8023" + }, + { + "name": "Yellow orb", + "id": "8024" + }, + { + "name": "Green orb", + "id": "8025" + }, + { + "name": "Green orb", + "id": "8027" + }, + { + "name": "Green orb", + "id": "8028" + }, + { + "name": "Wizard Korvak", + "id": "8029" + }, + { + "name": "Wizard Vief", + "id": "8030" + }, + { + "name": "Wizard Acantha", + "id": "8031" + }, + { + "name": "Wizard Elriss", + "id": "8032" + }, + { + "name": "Wizard", + "id": "8033" + }, + { + "name": "Wizard", + "id": "8034" + }, + { + "name": "Wizard", + "id": "8035" + }, + { + "name": "Wizard", + "id": "8036" + }, + { + "name": "Wizard", + "id": "8037" + }, + { + "name": "Wizard", + "id": "8038" + }, + { + "name": "Wizard", + "id": "8039" + }, + { + "name": "Wizard", + "id": "8040" + }, + { + "name": "Squire Fyre", + "id": "8042" + }, + { + "name": "Squire Fyre", + "id": "8043" + }, + { + "name": "Orry", + "id": "8044" + }, + { + "name": "Orry", + "id": "8045" + }, + { + "name": "Ube", + "id": "8046" + }, + { + "name": "Ube", + "id": "8047" + }, + { + "name": "Waldo", + "id": "8048" + }, + { + "name": "Waldo", + "id": "8049" + }, + { + "name": "Gjalp", + "id": "8050" + }, + { + "name": "Gjalp", + "id": "8051" + }, + { + "name": "Brother Fintan", + "id": "8052" + }, + { + "name": "Brother Fintan", + "id": "8053" + }, + { + "name": "Stubthumb", + "id": "8054" + }, + { + "name": "Stubthumb", + "id": "8055" + }, + { + "name": "Stubthumb", + "id": "8056" + }, + { + "name": "Doronbol", + "id": "8057" + }, + { + "name": "Doronbol", + "id": "8058" + }, + { + "name": "Crate", + "id": "8059" + }, + { + "name": "Crate", + "id": "8060" + }, + { + "name": "Egg", + "id": "8061" + }, + { + "name": "Egg", + "id": "8062" + }, + { + "name": "Nanuq", + "id": "8063" + }, + { + "name": "Nanuq", + "id": "8064" + }, + { + "name": "Pumpkin", + "id": "8065" + }, + { + "name": "Maggie", + "id": "8078" + }, + { + "name": "Circus barker", + "id": "8079" + }, + { + "name": "Circus barker", + "id": "8080" + }, + { + "name": "Circus barker", + "id": "8081" + }, + { + "name": "Agility assistant", + "id": "8082" + }, + { + "name": "Magic assistant", + "id": "8083" + }, + { + "name": "Ranged assistant", + "id": "8084" + }, + { + "name": "Ringmaster", + "id": "8085" + }, + { + "name": "Audience", + "id": "8086" + }, + { + "name": "Audience", + "id": "8087" + }, + { + "name": "Ticket vendor", + "id": "8088" + }, + { + "name": "Rock", + "id": "8089" + }, + { + "name": "Star sprite", + "id": "8091" + }, + { + "name": "Shooting star shadow", + "id": "8092" + }, + { + "name": "Penguin", + "id": "8093" + }, + { + "name": "Penguin", + "id": "8094" + }, + { + "name": "Penguin", + "id": "8095" + }, + { + "name": "Penguin", + "id": "8096" + }, + { + "name": "Penguin", + "id": "8097" + }, + { + "name": "Penguin", + "id": "8098" + }, + { + "name": "Penguin", + "id": "8099" + }, + { + "name": "Penguin", + "id": "8100" + }, + { + "name": "Penguin", + "id": "8101" + }, + { + "name": "Penguin", + "id": "8102" + }, + { + "name": "Penguin", + "id": "8103" + }, + { + "name": "Barrel", + "id": "8104" + }, + { + "name": "Bush", + "id": "8105" + }, + { + "name": "Bush", + "id": "8106" + }, + { + "name": "Cactus", + "id": "8107" + }, + { + "name": "Crate", + "id": "8108" + }, + { + "name": "Rock", + "id": "8109" + }, + { + "name": "Toadstool", + "id": "8110" + }, + { + "name": "Calladin", + "id": "8111" + }, + { + "name": "Summer Bonde", + "id": "8112" + }, + { + "name": "Summer Bonde", + "id": "8114" + }, + { + "name": "Erik Bonde", + "id": "8115" + }, + { + "name": "Erik Bonde", + "id": "8116" + }, + { + "name": "Jallek Lenkin", + "id": "8117" + }, + { + "name": "Jallek Lenkin", + "id": "8118" + }, + { + "name": "Meranek Thanatos", + "id": "8119" + }, + { + "name": "Meranek Thanatos", + "id": "8120" + }, + { + "name": "Spirit", + "id": "8121" + }, + { + "name": "Rogue", + "id": "8122" + }, + { + "name": "Tormented wraith", + "id": "8123" + }, + { + "name": "Dark energy core", + "id": "8126" + }, + { + "name": "Dark energy core", + "id": "8127" + }, + { + "name": "Spirit Beast", + "id": "8128" + }, + { + "name": "Spirit Beast", + "id": "8129" + }, + { + "name": "Spirit Beast", + "id": "8130" + }, + { + "name": "Spirit Beast", + "id": "8131" + }, + { + "name": "Spirit Beast", + "id": "8132" + }, + { + "name": "Spirit", + "id": "8134" + }, + { + "name": "Summer Bonde", + "id": "8135" + }, + { + "name": "Erik Bonde", + "id": "8136" + }, + { + "name": "Jallek Lenkin", + "id": "8137" + }, + { + "name": "Meranek Thanatos", + "id": "8138" + }, + { + "name": "Hartwin", + "id": "8139" + }, + { + "name": "Hartwin", + "id": "8140" + }, + { + "name": "Zombie", + "id": "8141" + }, + { + "name": "Zombie", + "id": "8143" + }, + { + "name": "Zombie", + "id": "8144" + }, + { + "name": "Zombie", + "id": "8145" + }, + { + "name": "Zemouregal", + "id": "8146" + }, + { + "name": "Zemouregal", + "id": "8147" + }, + { + "name": "Armoured zombie", + "id": "8152" + }, + { + "name": "Armoured zombie", + "id": "8153" + }, + { + "name": "Armoured zombie", + "id": "8154" + }, + { + "name": "Armoured zombie", + "id": "8155" + }, + { + "name": "Armoured zombie", + "id": "8156" + }, + { + "name": "Armoured zombie", + "id": "8157" + }, + { + "name": "Armoured zombie", + "id": "8158" + }, + { + "name": "Armoured zombie", + "id": "8159" + }, + { + "name": "Armoured zombie", + "id": "8160" + }, + { + "name": "Armoured zombie", + "id": "8161" + }, + { + "name": "Armoured zombie", + "id": "8162" + }, + { + "name": "Armoured zombie", + "id": "8163" + }, + { + "name": "Armoured zombie", + "id": "8164" + }, + { + "name": "Sharathteerk", + "id": "8165" + }, + { + "name": "Sharathteerk", + "id": "8166" + }, + { + "name": "Arrav", + "id": "8168" + }, + { + "name": "Arrav", + "id": "8169" + }, + { + "name": "Ramarno", + "id": "8170" + }, + { + "name": "Dimintheis", + "id": "8171" + }, + { + "name": "Dimintheis", + "id": "8172" + }, + { + "name": "Guard", + "id": "8173" + }, + { + "name": "Clive", + "id": "8174" + }, + { + "name": "Ellamaria", + "id": "8175" + }, + { + "name": "Fish", + "id": "8176" + }, + { + "name": "Fish", + "id": "8177" + }, + { + "name": "Monk of Zamorak", + "id": "8178" + }, + { + "name": "Ellamaria", + "id": "8179" + }, + { + "name": "Traiborn", + "id": "8180" + }, + { + "name": "Fenkenstrain's Monster", + "id": "8181" + }, + { + "name": "Wise Old Man", + "id": "8182" + }, + { + "name": "Acrobat", + "id": "8183" + }, + { + "name": "Acrobat", + "id": "8186" + }, + { + "name": "Acrobat", + "id": "8187" + }, + { + "name": "Acrobat", + "id": "8188" + }, + { + "name": "Acrobat", + "id": "8189" + }, + { + "name": "Acrobat", + "id": "8190" + }, + { + "name": "Acrobat", + "id": "8191" + }, + { + "name": "Acrobat", + "id": "8192" + }, + { + "name": "Acrobat", + "id": "8193" + }, + { + "name": "Acrobat", + "id": "8194" + }, + { + "name": "Acrobat", + "id": "8195" + }, + { + "name": "Acrobat", + "id": "8196" + }, + { + "name": "Acrobat", + "id": "8197" + }, + { + "name": "Acrobat", + "id": "8198" + }, + { + "name": "Acrobat", + "id": "8199" + }, + { + "name": "Acrobat", + "id": "8200" + }, + { + "name": "Wendy", + "id": "8201" + }, + { + "name": "Trogs", + "id": "8202" + }, + { + "name": "Norman", + "id": "8203" + }, + { + "name": "Babe", + "id": "8204" + }, + { + "name": "Gus", + "id": "8205" + }, + { + "name": "Lottie", + "id": "8206" + }, + { + "name": "Aggie", + "id": "8207" + }, + { + "name": "Bat", + "id": "8208" + }, + { + "name": "Rat", + "id": "8209" + }, + { + "name": "Lizard", + "id": "8210" + }, + { + "name": "Blackbird", + "id": "8211" + }, + { + "name": "Spider", + "id": "8212" + }, + { + "name": "Snail", + "id": "8213" + }, + { + "name": "Cat", + "id": "8214" + }, + { + "name": "Lazy cat", + "id": "8215" + }, + { + "name": "Overgrown cat", + "id": "8216" + }, + { + "name": "Kitten", + "id": "8217" + }, + { + "name": "Wily cat", + "id": "8218" + }, + { + "name": "Guardian of Armadyl", + "id": "8219" + }, + { + "name": "Head mystic", + "id": "8227" + }, + { + "name": "Rewards mystic", + "id": "8228" + }, + { + "name": "Mystic", + "id": "8229" + }, + { + "name": "Mystic", + "id": "8230" + }, + { + "name": "Mystic", + "id": "8231" + }, + { + "name": "Mystic", + "id": "8232" + }, + { + "name": "Mystic", + "id": "8233" + }, + { + "name": "Mystic", + "id": "8234" + }, + { + "name": "Mystic", + "id": "8235" + }, + { + "name": "Mystic", + "id": "8236" + }, + { + "name": "Mystic", + "id": "8237" + }, + { + "name": "Mystic", + "id": "8238" + }, + { + "name": "Head mystic", + "id": "8239" + }, + { + "name": "Clay familiar (class 1)", + "id": "8240" + }, + { + "name": "Clay familiar (class 1)", + "id": "8241" + }, + { + "name": "Clay familiar (class 2)", + "id": "8242" + }, + { + "name": "Clay familiar (class 2)", + "id": "8243" + }, + { + "name": "Clay familiar (class 3)", + "id": "8244" + }, + { + "name": "Clay familiar (class 3)", + "id": "8245" + }, + { + "name": "Clay familiar (class 4)", + "id": "8246" + }, + { + "name": "Clay familiar (class 4)", + "id": "8247" + }, + { + "name": "Clay familiar (class 5)", + "id": "8248" + }, + { + "name": "Clay familiar (class 5)", + "id": "8249" + }, + { + "name": "Grindxplox", + "id": "8250" + }, + { + "name": "Grindxplox", + "id": "8251" + }, + { + "name": "Stealing Creation team", + "id": "8252" + }, + { + "name": "Resource NPC", + "id": "8253" + }, + { + "name": "Game NPC", + "id": "8254" + }, + { + "name": "Resource NPC", + "id": "8255" + }, + { + "name": "Harrallak Menarous", + "id": "8257" + }, + { + "name": "Yadech Strongarm", + "id": "8269" + }, + { + "name": "Turael", + "id": "8271" + }, + { + "name": "Mazchna", + "id": "8274" + }, + { + "name": "Duradel", + "id": "8275" + }, + { + "name": "Cyrisus", + "id": "8276" + }, + { + "name": "Sloane", + "id": "8280" + }, + { + "name": "Balance Elemental", + "id": "8281" + }, + { + "name": "Balance Elemental", + "id": "8282" + }, + { + "name": "Balance Elemental", + "id": "8283" + }, + { + "name": "Balance Elemental", + "id": "8284" + }, + { + "name": "Balance Elemental", + "id": "8285" + }, + { + "name": "Undead hero", + "id": "8286" + }, + { + "name": "Undead hero", + "id": "8287" + }, + { + "name": "Undead hero", + "id": "8288" + }, + { + "name": "Undead hero", + "id": "8289" + }, + { + "name": "Undead hero", + "id": "8290" + }, + { + "name": "Undead hero", + "id": "8291" + }, + { + "name": "Undead hero", + "id": "8292" + }, + { + "name": "Undead hero", + "id": "8293" + }, + { + "name": "Undead hero", + "id": "8294" + }, + { + "name": "Undead hero", + "id": "8295" + }, + { + "name": "Undead hero", + "id": "8296" + }, + { + "name": "Undead hero", + "id": "8297" + }, + { + "name": "Stone block", + "id": "8298" + }, + { + "name": "Harrallak Menarous", + "id": "8299" + }, + { + "name": "Harrallak Menarous", + "id": "8300" + }, + { + "name": "Harrallak Menarous", + "id": "8301" + }, + { + "name": "Turael", + "id": "8302" + }, + { + "name": "Turael", + "id": "8303" + }, + { + "name": "Cyrisus", + "id": "8304" + }, + { + "name": "Cyrisus", + "id": "8305" + }, + { + "name": "Ghommal", + "id": "8306" + }, + { + "name": "Mazchna", + "id": "8307" + }, + { + "name": "Mazchna", + "id": "8308" + }, + { + "name": "Mazchna", + "id": "8309" + }, + { + "name": "Duradel", + "id": "8310" + }, + { + "name": "Sloane", + "id": "8311" + }, + { + "name": "Elite Dark Ranger", + "id": "8317" + }, + { + "name": "Elite Dark Ranger", + "id": "8318" + }, + { + "name": "Elite Dark Ranger", + "id": "8319" + }, + { + "name": "Elite Dark Mage", + "id": "8320" + }, + { + "name": "Elite Dark Mage", + "id": "8321" + }, + { + "name": "Elite Dark Mage", + "id": "8322" + }, + { + "name": "Elite Dark Mage", + "id": "8323" + }, + { + "name": "Elite Black Knight", + "id": "8324" + }, + { + "name": "Elite Black Knight", + "id": "8325" + }, + { + "name": "Elite Black Knight", + "id": "8326" + }, + { + "name": "Elite Black Knight", + "id": "8327" + }, + { + "name": "Elite Black Knight", + "id": "8328" + }, + { + "name": "Elite Black Knight", + "id": "8329" + }, + { + "name": "Elite Black Knight", + "id": "8330" + }, + { + "name": "Guardian of Armadyl", + "id": "8331" + }, + { + "name": "Guardian of Armadyl", + "id": "8332" + }, + { + "name": "Guardian of Armadyl", + "id": "8333" + }, + { + "name": "Mercenary axeman", + "id": "8334" + }, + { + "name": "Mercenary mage", + "id": "8335" + }, + { + "name": "Idria", + "id": "8336" + }, + { + "name": "Idria", + "id": "8337" + }, + { + "name": "Akrisae", + "id": "8338" + }, + { + "name": "Shady stranger", + "id": "8339" + }, + { + "name": "Shady stranger", + "id": "8340" + }, + { + "name": "Shady stranger", + "id": "8341" + }, + { + "name": "Guardian of Armadyl", + "id": "8342" + }, + { + "name": "Guardian of Armadyl", + "id": "8344" + }, + { + "name": "Local thug", + "id": "8345" + }, + { + "name": "Local mage", + "id": "8347" + }, + { + "name": "Undead troll", + "id": "8367" + }, + { + "name": "Undead troll", + "id": "8378" + }, + { + "name": "Undead troll", + "id": "8379" + }, + { + "name": "Undead troll", + "id": "8380" + }, + { + "name": "Undead troll", + "id": "8381" + }, + { + "name": "Undead troll", + "id": "8382" + }, + { + "name": "Undead troll", + "id": "8383" + }, + { + "name": "Undead troll", + "id": "8384" + }, + { + "name": "Undead troll", + "id": "8385" + }, + { + "name": "Undead troll", + "id": "8386" + }, + { + "name": "Undead troll", + "id": "8387" + }, + { + "name": "Undead troll", + "id": "8388" + }, + { + "name": "Undead troll", + "id": "8389" + }, + { + "name": "Undead troll", + "id": "8390" + }, + { + "name": "Undead troll", + "id": "8391" + }, + { + "name": "Undead troll", + "id": "8392" + }, + { + "name": "Vasador", + "id": "8393" + }, + { + "name": "Valator", + "id": "8394" + }, + { + "name": "Vaasdor", + "id": "8395" + }, + { + "name": "Varosad", + "id": "8396" + }, + { + "name": "Verisad", + "id": "8397" + }, + { + "name": "Vudisor", + "id": "8398" + }, + { + "name": "Vlatoad", + "id": "8399" + }, + { + "name": "Vedolas", + "id": "8400" + }, + { + "name": "Vundiar", + "id": "8401" + }, + { + "name": "Versita", + "id": "8402" + }, + { + "name": "Vislota", + "id": "8403" + }, + { + "name": "Vanjuta", + "id": "8404" + }, + { + "name": "Vasador", + "id": "8405" + }, + { + "name": "Valator", + "id": "8406" + }, + { + "name": "Vaasdor", + "id": "8407" + }, + { + "name": "Varosad", + "id": "8408" + }, + { + "name": "Verisad", + "id": "8409" + }, + { + "name": "Vudisor", + "id": "8410" + }, + { + "name": "Vlatoad", + "id": "8411" + }, + { + "name": "Vedolas", + "id": "8412" + }, + { + "name": "Vundiar", + "id": "8413" + }, + { + "name": "Versita", + "id": "8414" + }, + { + "name": "Vislota", + "id": "8415" + }, + { + "name": "Vanjuta", + "id": "8416" + }, + { + "name": "Ivy Sophista", + "id": "8417" + }, + { + "name": "Thaerisk Cemphier", + "id": "8418" + }, + { + "name": "Thaerisk Cemphier", + "id": "8419" + }, + { + "name": "Assassin", + "id": "8420" + }, + { + "name": "Assassin", + "id": "8423" + }, + { + "name": "Mithril dragon", + "id": "8424" + }, + { + "name": "Dragon head", + "id": "8425" + }, + { + "name": "Dragon head", + "id": "8426" + }, + { + "name": "Dragon head", + "id": "8427" + }, + { + "name": "Khazard launderer", + "id": "8428" + }, + { + "name": "Khazard cook", + "id": "8429" + }, + { + "name": "Silif", + "id": "8430" + }, + { + "name": "Silif", + "id": "8431" + }, + { + "name": "Silif", + "id": "8432" + }, + { + "name": "Silif", + "id": "8433" + }, + { + "name": "Silif", + "id": "8434" + }, + { + "name": "Shady stranger", + "id": "8435" + }, + { + "name": "Suspicious outsider", + "id": "8436" + }, + { + "name": "Elite Khazard guard", + "id": "8437" + }, + { + "name": "Elite Khazard guard", + "id": "8438" + }, + { + "name": "Elite Khazard guard", + "id": "8439" + }, + { + "name": "Elite Khazard guard", + "id": "8440" + }, + { + "name": "Dark Squall", + "id": "8441" + }, + { + "name": "Surok Magis", + "id": "8442" + }, + { + "name": "Lucien", + "id": "8443" + }, + { + "name": "Druid", + "id": "8444" + }, + { + "name": "Druid", + "id": "8445" + }, + { + "name": "Druid", + "id": "8446" + }, + { + "name": "Druid bodyguard", + "id": "8447" + }, + { + "name": "Druid bodyguard", + "id": "8448" + }, + { + "name": "Movario", + "id": "8449" + }, + { + "name": "Darve", + "id": "8450" + }, + { + "name": "Cave goblin", + "id": "8451" + }, + { + "name": "Movario", + "id": "8452" + }, + { + "name": "Darve", + "id": "8453" + }, + { + "name": "Light creature", + "id": "8454" + }, + { + "name": "Light creature", + "id": "8455" + }, + { + "name": "Druid spirit", + "id": "8456" + }, + { + "name": "Druid spirit", + "id": "8457" + }, + { + "name": "Druid spirit", + "id": "8458" + }, + { + "name": "Druid spirit", + "id": "8459" + }, + { + "name": "Turael", + "id": "8460" + }, + { + "name": "Spria", + "id": "8462" + }, + { + "name": "Mazchna", + "id": "8463" + }, + { + "name": "Mazchna", + "id": "8464" + }, + { + "name": "Achtryn", + "id": "8465" + }, + { + "name": "Duradel", + "id": "8466" + }, + { + "name": "Lapalok", + "id": "8467" + }, + { + "name": "Laidee Gnonock", + "id": "8468" + }, + { + "name": "Hazelmere", + "id": "8485" + }, + { + "name": "Undead mage", + "id": "8486" + }, + { + "name": "Wild broav", + "id": "8487" + }, + { + "name": "Hazelmere", + "id": "8488" + }, + { + "name": "Hazelmere", + "id": "8490" + }, + { + "name": "Broav", + "id": "8491" + }, + { + "name": "Sithaph", + "id": "8492" + }, + { + "name": "Strisath", + "id": "8494" + }, + { + "name": "Sakirth", + "id": "8495" + }, + { + "name": "Hazelmere's hat", + "id": "8496" + }, + { + "name": "Lucien", + "id": "8497" + }, + { + "name": "A lazy Khazard guard", + "id": "8498" + }, + { + "name": "Thanksgiving Turkey", + "id": "8499" + }, + { + "name": "Cook's brother", + "id": "8500" + }, + { + "name": "Turkey", + "id": "8501" + }, + { + "name": "Cactus", + "id": "8502" + }, + { + "name": "Cactus", + "id": "8503" + }, + { + "name": "Bush", + "id": "8504" + }, + { + "name": "Toadstool", + "id": "8505" + }, + { + "name": "Barrel", + "id": "8506" + }, + { + "name": "Bush", + "id": "8507" + }, + { + "name": "Crate", + "id": "8508" + }, + { + "name": "Bush", + "id": "8509" + }, + { + "name": "Rock", + "id": "8510" + }, + { + "name": "Bush", + "id": "8511" + }, + { + "name": "Marvin", + "id": "8512" + }, + { + "name": "Marius", + "id": "8513" + }, + { + "name": "Benny", + "id": "8514" + }, + { + "name": "Yeti", + "id": "8515" + }, + { + "name": "Yeti", + "id": "8516" + }, + { + "name": "Jack Frost", + "id": "8517" + }, + { + "name": "Jack Frost", + "id": "8518" + }, + { + "name": "Jack Frost", + "id": "8519" + }, + { + "name": "Jack Frost", + "id": "8520" + }, + { + "name": "Snow imp", + "id": "8521" + }, + { + "name": "Snow imp", + "id": "8537" + }, + { + "name": "Snow imp", + "id": "8538" + }, + { + "name": "Queen of Snow", + "id": "8539" + }, + { + "name": "Santa Claus", + "id": "8540" + }, + { + "name": "Head snow imp", + "id": "8541" + }, + { + "name": "Head snow imp", + "id": "8542" + }, + { + "name": "Head snow imp", + "id": "8543" + }, + { + "name": "Isidor", + "id": "8544" + }, + { + "name": "Mystic", + "id": "8545" + }, + { + "name": "Balance Elemental", + "id": "8546" + }, + { + "name": "Wounded phoenix", + "id": "8547" + }, + { + "name": "Phoenix", + "id": "8548" + }, + { + "name": "Phoenix", + "id": "8549" + }, + { + "name": "Phoenix eggling", + "id": "8550" + }, + { + "name": "Phoenix eggling", + "id": "8551" + }, + { + "name": "Large egg", + "id": "8552" + }, + { + "name": "Priest of Guthix", + "id": "8553" + }, + { + "name": "Brian Twitcher", + "id": "8556" + }, + { + "name": "Lesser reborn warrior", + "id": "8557" + }, + { + "name": "Lesser reborn warrior", + "id": "8558" + }, + { + "name": "Greater reborn warrior", + "id": "8559" + }, + { + "name": "Greater reborn warrior", + "id": "8560" + }, + { + "name": "Lesser reborn ranger", + "id": "8561" + }, + { + "name": "Lesser reborn ranger", + "id": "8562" + }, + { + "name": "Greater reborn ranger", + "id": "8563" + }, + { + "name": "Greater reborn ranger", + "id": "8564" + }, + { + "name": "Lesser reborn mage", + "id": "8565" + }, + { + "name": "Lesser reborn mage", + "id": "8566" + }, + { + "name": "Greater reborn mage", + "id": "8567" + }, + { + "name": "Greater reborn mage", + "id": "8568" + }, + { + "name": "Lesser reborn warrior", + "id": "8569" + }, + { + "name": "Greater reborn warrior", + "id": "8570" + }, + { + "name": "Lesser reborn ranger", + "id": "8571" + }, + { + "name": "Greater reborn ranger", + "id": "8572" + }, + { + "name": "Lesser reborn mage", + "id": "8573" + }, + { + "name": "Greater reborn mage", + "id": "8574" + }, + { + "name": "Phoenix", + "id": "8575" + }, + { + "name": "Phoenix", + "id": "8576" + }, + { + "name": "Phoenix eggling", + "id": "8577" + }, + { + "name": "Phoenix eggling", + "id": "8578" + }, + { + "name": "Karma the chameleon", + "id": "8579" + }, + { + "name": "Karma the chameleon", + "id": "8581" + }, + { + "name": "Karma the chameleon", + "id": "8582" + }, + { + "name": "Karma the chameleon", + "id": "8583" + }, + { + "name": "Karma the chameleon", + "id": "8584" + }, + { + "name": "Karma the chameleon", + "id": "8585" + }, + { + "name": "Karma the chameleon", + "id": "8586" + }, + { + "name": "Karma the chameleon", + "id": "8587" + }, + { + "name": "Karma the chameleon", + "id": "8588" + }, + { + "name": "Karma the chameleon", + "id": "8589" + }, + { + "name": "Geoffrey", + "id": "8590" + }, + { + "examine": "Ooh, shiny things!", + "name": "Magpie Impling", + "id": "1035" + }, + { + "examine": "An earth impling. Rock on.", + "name": "Earth Impling", + "id": "1031" + }, + { + "examine": "A baby impling. Ahhh.", + "name": "Baby Impling", + "id": "1028" + }, + { + "examine": "A young impling. It's not fair!", + "name": "Young Impling", + "id": "1029" + }, + { + "examine": "An impling gourmet. Mmmm, tasty.", + "name": "Gourmet Impling", + "id": "1030" + }, + { + "examine": "An impling who likes magic. Magic!", + "name": "Essence Impling", + "id": "1032" + }, + { + "examine": "Dodgy Mike.", + "name": "Mike", + "id": "3166" + }, + { + "examine": "An impling with varied tastes.", + "name": "Eclectic Impling", + "id": "1033" + }, + { + "examine": "He seems to be making odd sucking noises with his teeth.", + "name": "Leon", + "id": "5111" + }, + { + "examine": "A very stealthy impling.", + "name": "Ninja Impling", + "id": "6053" + }, + { + "examine": "A rare bird.", + "name": "Black Swan", + "water_npc": "true", + "id": "3297" + }, + { + "examine": "A nature impling. Right on, maaan.", + "name": "Nature Impling", + "id": "1034" + }, + { + "examine": "A mourner, or plague healer.", + "death_animation": "836", + "name": "Mourner (boss)", + "defence_level": "10", + "lifepoints": "19", + "melee_animation": "428", + "attack_speed": "4", + "strength_level": "10", + "id": "370", + "bonuses": "0,0,0,0,0,3,2,4,0,0,0,0,0,0,0", + "attack_level": "10" + }, + { + "examine": "A mourner, or plague healer.", + "name": "Mourner", + "id": "717" + }, + { + "examine": "A mourner, or plague healer.", + "name": "Mourner", + "id": "718" + }, + { + "examine": "A mourner, or plague healer.", + "name": "Mourner", + "id": "719" + }, + { + "examine": "A mourner, or plague healer.", + "name": "Mourner", + "id": "3216" + }, + { + "examine": "A mourner, or plague healer.", + "death_animation": "836", + "name": "Mourner", + "defence_level": "14", + "melee_animation": "428", + "lifepoints": "24", + "attack_speed": "4", + "strength_level": "14", + "id": "357", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "attack_level": "14" + }, + { + "examine": "A mourner, or plague healer.", + "death_animation": "836", + "name": "Mourner", + "defence_level": "19", + "melee_animation": "422", + "lifepoints": "30", + "attack_speed": "4", + "strength_level": "19", + "id": "348", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "attack_level": "19" + }, + { + "examine": "A mourner, or plague healer.", + "death_animation": "836", + "name": "Mourner", + "defence_level": "19", + "melee_animation": "422", + "lifepoints": "30", + "attack_speed": "4", + "strength_level": "19", + "id": "369", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "attack_level": "19" + }, + { + "examine": "A mourner, or plague healer.", + "death_animation": "836", + "name": "Mourner", + "defence_level": "8", + "lifepoints": "18", + "melee_animation": "428", + "strength_level": "8", + "attack_speed": "4", + "id": "347", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "attack_level": "8" + }, + { + "examine": "A Mourner, or plague healer.", + "name": "Mourner", + "id": "2374" + }, + { + "examine": "A mourner, or plague healer.", + "name": "Mourner", + "id": "372" + }, + { + "examine": "A mourner, or plague healer.", + "death_animation": "836", + "name": "Mourner", + "defence_level": "8", + "lifepoints": "20", + "melee_animation": "422", + "attack_speed": "4", + "strength_level": "8", + "id": "371", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "attack_level": "8" + }, + { + "examine": "In charge of people with silly outfits.", + "name": "Head Mourner", + "id": "716" + }, + { + "examine": "A mourner, or plague healer.", + "name": "Mourner", + "id": "2784" + }, + { + "examine": "She looks concerned.", + "name": "Elena", + "id": "3215" + }, + { + "examine": "She looks concerned.", + "name": "Elena", + "id": "715" + }, + { + "examine": "She doesn't look too happy.", + "name": "Elena", + "id": "3209" + }, + { + "examine": "She doesn't look too happy.", + "name": "Elena", + "id": "335" + }, + { + "examine": "He looks angry and smells drunk.", + "name": "Skippy", + "id": "2796" } -] +] \ No newline at end of file diff --git a/Server/data/configs/npc_spawns.json b/Server/data/configs/npc_spawns.json index e6bbbb288..46a5a18e7 100644 --- a/Server/data/configs/npc_spawns.json +++ b/Server/data/configs/npc_spawns.json @@ -1,11891 +1,12418 @@ -[ - { - "npc_id": "0", - "loc_data": "{3221,3218,0,1,0}-{2522,5000,0,0,4}" - }, - { - "npc_id": "1", - "loc_data": "{3222,3221,0,1,3}-{2804,3427,0,1,2}-{3285,3208,0,1,4}-{3093,3509,0,1,3}-{3098,3508,0,1,7}-{3096,3510,0,1,3}-{3017,3239,0,1,2}-{2712,3484,0,1,4}-{2693,3495,0,1,1}-{3237,3404,0,1,6}-{3237,3408,0,1,0}-{3247,3396,0,1,3}-{3263,3403,0,1,3}-{3210,3223,1,1,4}-{3230,3208,0,1,1}" - }, - { - "npc_id": "2", - "loc_data": "{3298,3185,0,1,3}-{3101,3511,0,1,4}-{2690,3489,0,1,3}-{3236,3203,0,1,4}-{3217,3209,0,1,4}-{3237,3217,0,1,6}-{3282,3503,0,1,6}" - }, - { - "npc_id": "3", - "loc_data": "{2818,3443,0,1,3}-{3306,3200,0,1,6}-{3300,3208,0,1,3}-{3095,3511,0,1,3}-{3094,3513,0,1,4}-{3097,3510,0,1,4}-{2699,3491,0,1,1}-{3231,3239,0,1,4}-{3278,3502,0,1,6}" - }, - { - "npc_id": "4", - "loc_data": "{3010,3236,0,1,6}-{2693,3493,0,1,7}-{2693,3492,0,1,0}-{2695,3497,0,1,1}-{2700,3496,0,1,5}-{3230,3204,0,1,3}-{3281,3499,0,1,4}" - }, - { - "npc_id": "5", - "loc_data": "{2817,3447,0,1,5}-{2697,3493,0,1,4}-{3236,3205,0,1,2}-{3230,3238,0,1,1}-{3279,3497,0,1,3}" - }, - { - "npc_id": "6", - "loc_data": "{3097,3259,0,1,4}-{3280,3491,0,1,0}-{3246,3207,0,1,4}" - }, - { - "npc_id": "7", - "loc_data": "{2920,3434,0,1,0}-{3226,3290,0,1,3}-{2629,3360,0,1,4}-{3158, 3301, 0, 1, 0}" - }, - { - "npc_id": "8", - "loc_data": "{3019,3230,0,1,4}-{2804,3186,0,1,4}" - }, - { - "npc_id": "9", - "loc_data": "{2964,3392,0,1,6}-{2967,3393,0,1,1}-{3008,3324,0,1,2}-{3007,3321,0,1,6}-{3005,3321,0,1,4}-{2965,3389,0,1,6}-{2966,3392,0,1,1}-{2964,3392,0,1,1}" - }, - { - "npc_id": "11", - "loc_data": "{3235,3399,0,1,5}" - }, - { - "npc_id": "13", - "loc_data": "{3108,3157,1,1,3}-{3113,3157,1,1,4}-{3107,3170,0,1,0}-{3112,3167,0,1,0}-{3106,3157,0,1,3}-{3110,3155,0,1,6}-{2588,3087,0,1,4}-{2592,3086,0,1,6}-{2590,3083,1,1,4}-{2587,3086,1,1,4}-{2594,3084,1,1,6}-{2593,3090,2,1,5}-{2594,3085,2,1,1}-{2588,3089,2,1,4}" - }, - { - "npc_id": "14", - "loc_data": "{2914,3444,0,1,3}-{2905,3450,1,1,0}" - }, - { - "npc_id": "15", - "loc_data": "{2584,3304,0,1,3}-{2588,3291,0,1,7}-{2584,3288,0,1,0}-{2631,3294,0,1,6}-{2629,3296,0,1,4}-{2563,3382,0,1,6}-{2569,3383,0,1,6}-{2571,3381,0,1,1}-{2575,3384,0,1,4}-{2572,3385,0,1,4}" - }, - { - "npc_id": "16", - "loc_data": "{3279,3191,0,1,4}-{3296,3202,1,1,3}" - }, - { - "npc_id": "18", - "loc_data": "{3282,3176,0,1,0}-{3284,3170,0,1,7}-{3284,3174,0,1,0}-{3288,3168,0,1,5}-{3292,3169,0,1,3}-{3295,3168,0,1,3}-{3301,3170,0,1,6}-{3301,3174,0,1,6}-{3301,3177,0,1,0}" - }, - { - "npc_id": "19", - "loc_data": "{2971,3349,0,1,4}-{2979,3350,0,1,2}-{2973,3337,0,1,3}-{2959,3335,0,1,6}-{2982,3339,0,1,6}-{2983,3346,0,1,4}-{2980,3329,0,1,4}-{2987,3330,0,1,6}-{2958,3340,1,1,1}-{2970,3333,1,1,3}-{2966,3329,1,1,4}-{2961,3353,1,1,4}-{2973,3337,1,1,6}-{2998,3341,0,1,6}-{2983,3342,2,1,6}" - }, - { - "npc_id": "20", - "loc_data": "{2588,3301,1,0,0}" - }, - { - "npc_id": "21", - "loc_data": "{2630,3288,0,1,1}-{2647,3306,0,1,1}-{2667,3315,0,1,4}" - }, - { - "npc_id": "23", - "loc_data": "{2582,3297,0,1,6}-{2652,3318,0,1,0}-{2653,3300,0,1,5}-{2669,3298,0,1,4}-{2671,3313,0,1,6}" - }, - { - "npc_id": "24", - "loc_data": "{2614,3318,0,1,6}-{2621,3293,0,1,3}" - }, - { - "npc_id": "25", - "loc_data": "{2612,3316,0,1,1}-{2574,3321,1,1,4}-{2621,3293,1,1,3}" - }, - { - "npc_id": "27", - "loc_data": "{2574,3285,2,0,0}-{2574,3308,2,0,0}-{2582,3284,2,0,0}-{2582,3309,2,0,0}-{2588,3290,2,0,0}-{2588,3303,2,0,0}" - }, - { - "npc_id": "28", - "loc_data": "{2611,3269,0,0,0}" - }, - { - "npc_id": "31", - "loc_data": "{2616,3308,0,1,1}" - }, - { - "npc_id": "32", - "loc_data": "{2651,3307,0,1,1}-{2659,3309,0,1,6}-{2660,3309,0,1,3}-{2661,3309,0,1,1}-{2661,3317,0,1,5}-{2663,3301,0,1,3}-{2665,3300,0,1,3}-{2636,3342,0,1,4}-{2634,3342,0,1,3}-{2635,3339,0,1,3}" - }, - { - "npc_id": "34", - "loc_data": "{2548,3111,1,1,3}-{2545,3116,1,1,7}-{2549,3115,1,1,1}-{2549,3112,1,1,4}" - }, - { - "npc_id": "35", - "loc_data": "{2545,3094,0,1,3}-{2549,3093,0,1,2}-{2543,3089,0,1,3}-{2540,3098,0,1,3}" - }, - { - "npc_id": "36", - "loc_data": "{3020,3373,0,1,0}" - }, - { - "npc_id": "38", - "loc_data": "{2952,3063,0,0,0}-{2955,3057,0,0,0}-{2956,3035,0,0,0}-{2969,3034,0,0,0}-{2984,3048,0,0,0}" - }, - { - "npc_id": "39", - "loc_data": "{2958,3055,0,0,0}-{2954,3029,0,0,0}-{2968,3039,0,0,0}-{2984,3048,0,0,0}-{2989,3058,0,0,0}-{2982,3060,0,0,0}-{2972,3060,0,0,0}-{2998,3061,0,0,0}-{2999,3049,0,0,0}" - }, - { - "npc_id": "40", - "loc_data": "{3005,3060,0,0,0}-{3008,3067,0,0,0}" - }, - { - "npc_id": "41", - "loc_data": "{3197,3356,0,1,4}-{3234,3297,0,1,3}-{3230,3296,0,1,5}-{3236,3288,0,1,6}-{3236,3300,0,1,2}-{2965,3336,0,1,4}-{3110,9573,0,1,6}-{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}-{2784,3062,0,1,4}-{2789,3063,0,1,4}-{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}-{3187,3276,0,1,5}-{3188,3278,0,1,4}-{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}-{3234,3293,0,1,4}-{3229,3298,0,1,1}-{3228,3296,0,1,4}-{2815,3560,0,1,7}-{2819,3561,0,1,4}-{2818,3559,0,1,6}-{3140,3093,0,1,1}-{2674,3652,0,1,3}-{2672,3655,0,1,6}-{2677,3655,0,1,6}-{2694,3274,0,1,6}-{2695,3275,0,1,3}" - }, - { - "npc_id": "42", - "loc_data": "{3237,3346,0,1,1}-{3185,3220,0,1,0}-{3183,3222,0,1,0}" - }, - { - "npc_id": "43", - "loc_data": "{3241,3354,0,1,3}-{3200,3263,0,1,0}-{3212,3262,0,1,1}-{2926,3323,0,1,0}-{2922,3325,0,1,3}-{3204,3268,0,1,0}" - }, - { - "npc_id": "44", - "loc_data": "{3251,3418,0,0,1}-{3252,3418,0,0,1}-{3253,3418,0,0,1}-{3096,3489,0,0,3}-{3254,3418,0,0,1}-{3255,3418,0,0,1}-{3256,3418,0,0,1}-{3147,3448,0,0,1}-{3180,3436,0,0,4}-{3180,3440,0,0,4}-{3180,3444,0,0,4}-{3096,3493,0,0,3}-{3097,3494,0,0,1}-{3191,3437,0,0,3}-{3191,3441,0,0,3}-{3191,3445,0,0,3}" - }, - { - "npc_id": "45", - "loc_data": "{3096,3491,0,0,3}-{3148,3448,0,0,0}-{3191,3443,0,0,3}-{3191,3439,0,0,3}-{3191,3435,0,0,3}-{3180,3442,0,0,4}-{3180,3434,0,0,4}-{2843,5214,0,0,4}-{3180,3438,0,0,4}" - }, - { - "npc_id": "46", - "loc_data": "{3179,3348,0,1,0}-{3178,3353,0,1,0}-{3211,3322,0,1,0}-{3214,3314,0,1,0}-{3214,3318,0,1,0}-{3216,3314,0,1,0}-{3218,3287,0,1,0}-{3223,3281,0,1,0}-{3231,3271,0,1,0}-{3233,3267,0,1,0}-{3233,3270,0,1,0}-{3235,3265,0,1,0}-{3235,3259,0,1,0}-{3238,3244,0,1,0}-{3241,3230,0,1,0}-{3241,3234,0,1,0}-{3243,3228,0,1,0}-{3247,3221,0,1,0}-{3248,3220,0,1,0}-{3259,3212,0,1,0}-{2979,3359,0,1,0}-{2979,3357,0,1,0}-{2993,3384,0,1,6}-{3015,3285,0,1,0}" - }, - { - "npc_id": "47", - "loc_data": "{2521,3331,0,1,3}-{2531,3325,0,1,3}-{2526,3328,0,1,3}-{2523,3331,0,1,4}-{2523,3334,0,1,1}-{2530,3327,0,1,5}-{2531,3329,0,1,5}-{2532,3333,0,1,5}-{3108,9754,0,1,5}-{3110,9754,0,1,5}-{3108,9750,0,1,5}-{2930,9699,0,1,0}-{2933,9697,0,1,0}-{2932,9685,0,1,0}-{2930,9693,0,1,0}-{3097,3364,0,1,3}-{3102,3363,0,1,5}-{3341,3267,0,1,5}-{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}-{3127,3487,0,1,4}-{3125,3486,0,1,6}-{3127,3486,0,1,4}-{3019,3292,0,1,7}-{3018,3295,0,1,7}-{3001,3202,0,1,5}-{3021,3205,0,1,6}-{3026,3174,0,1,5}-{3019,3176,0,1,7}-{3076,3282,0,1,5}-{3089,3266,0,1,4}-{3091,3266,0,1,4}-{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}-{3276,9871,0,1,1}-{3277,9871,0,1,3}-{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}-{3236,3217,0,1,4}-{3259,3230,0,1,4}-{3158,3226,0,1,5}-{3160,3202,0,1,4}-{3233,3237,0,1,7}-{2821,3170,0,1,1}-{2801,3158,0,1,2}-{2852,9642,0,1,6}-{2858,9632,0,1,3}-{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}-{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}-{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}-{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}-{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}-{2565,9505,0,1,1}-{2566,9510,0,1,6}-{2594,9497,0,1,4}-{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}-{3192,3203,0,1,0}-{3194,3204,0,1,0}-{3196,3206,0,1,0}-{3197,3204,0,1,0}-{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}-{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}" - }, - { - "npc_id": "48", - "loc_data": "{2775,2929,0,1,2}-{2789,2926,0,1,5}-{2795,2932,0,1,6}-{2775,2933,0,1,0}-{2769,2928,0,1,3}-{2817,2921,0,1,2}" - }, - { - "npc_id": "49", - "loc_data": "{2871,9819,0,1,0}-{2871,9822,0,1,0}-{2870,9826,0,1,0}-{2869,9829,0,1,0}-{2867,9834,0,1,0}-{2867,9841,0,1,0}-{2864,9853,0,1,0}-{2859,9852,0,1,0}-{2851,9850,0,1,0}-{2856,9846,0,1,0}-{2857,9841,0,1,0}-{2861,9838,0,1,0}-{2854,9838,0,1,0}-{3133,3678,0,1,0}-{3127,3677,0,1,0}-{3127,3667,0,1,0}-{3117,3674,0,1,0}-{3121,3679,0,1,0}-{3104,3678,0,1,0}-{3110,3687,0,1,0}-{3116,3688,0,1,0}-{3103,3704,0,1,0}-{3178,3929,0,1,0}-{3193,3926,0,1,0}-{3195,3915,0,1,0}-{3186,3913,0,1,0}-{3175,3906,0,1,0}-{2735,9692,0,1,1}-{2732,9687,0,1,1}-{2733,9688,0,1,1}-{2734,9690,0,1,1}-{2733,9686,0,1,1}-{2732,9692,0,1,1}-{2736,9685,0,1,1}-{2730,9691,0,1,1}" - }, - { - "npc_id": "50", - "loc_data": "{2273,4698,0,0,0}" - }, - { - "npc_id": "52", - "loc_data": "{2898,9764,0,1,0}-{2892,9800,0,1,0}-{2912,9808,0,1,0}" - }, - { - "npc_id": "53", - "loc_data": "{2694,9508,0,0,3}-{2703,9505,0,0,6}-{2710,9499,0,0,1}-{2704,9520,0,0,5}-{2714,9523,0,0,3}-{2722,9520,0,0,7}-{3212,3820,0,1,0}-{3222,3829,0,1,0}" - }, - { - "npc_id": "54", - "loc_data": "{2834,9824,0,1,0}" - }, - { - "npc_id": "55", - "loc_data": "{2897,9802,0,1,0}-{2909,9906,0,1,4}" - }, - { - "npc_id": "58", - "loc_data": "{3289,5482,0,1,1}-{3296,5472,0,1,0}-{3291,5466,0,1,6}-{3294,5481,0,1,0}-{3300,5479,0,1,7}-{3288,5480,0,1,3}-{3293,5475,0,1,3}-{3293,5481,0,1,2}-{3286,5465,0,1,3}-{3279,5478,0,1,4}-{3270,5464,0,1,2}-{3270,5468,0,1,1}-{2574,9874,0,1,0}-{2577,9877,0,1,3}-{2574,9878,0,1,0}-{2575,9871,0,1,4}" - }, - { - "npc_id": "59", - "loc_data": "{3182,3244,0,1,1}-{3250,3239,0,1,1}-{3241,3241,0,1,1}-{3082,3362,0,1,4}-{3146,3347,0,1,3}-{3249,3249,0,1,1}-{3102,9881,0,1,3}-{3095,9883,0,1,2}-{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}-{3162,3223,0,1,7}-{2461,2880,0,1,5}-{2489,2935,0,1,7}-{2492,2907,0,1,1}-{2496,2890,0,1,4}-{2487,2894,0,1,2}-{2487,2888,0,1,7}-{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}-{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}-{2503,2889,0,1,1}-{2491,2927,0,1,1}-{2497,2939,0,1,2}-{2648,9766,0,1,4}-{2653,9761,0,1,3}-{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}-{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}" - }, - { - "npc_id": "60", - "loc_data": "{3189,3883,0,1,5}-{3182,3882,0,1,5}-{3177,3871,0,1,0}-{3176,3878,0,1,0}-{3173,3892,0,1,6}-{3171,3884,0,1,4}-{3166,3874,0,1,1}-{3169,3900,0,1,1}-{3165,3881,0,1,3}-{3164,3888,0,1,6}-{3163,3894,0,1,7}-{3162,3894,0,1,4}-{2547,2982,0,1,6}-{2477,2866,0,1,5}-{2484,2897,0,1,3}-{2455,2857,0,1,6}-{2434,2845,0,1,4}-{2544,2904,0,1,2}-{2548,2981,0,1,1}-{2448,2784,0,1,7}-{2460,2869,0,1,3}-{2471,2890,0,1,6}-{2491,2906,0,1,0}-{2523,2975,0,1,1}" - }, - { - "npc_id": "61", - "loc_data": "{2893,9835,0,1,0}-{2895,9828,0,1,0}-{2891,9831,0,1,0}-{3111, 9747, 0, 1, 5}-{3108, 9747, 0, 1, 5}-{3321,3145,0,1,3}-{3327,3131,0,1,5}-{3322,3143,0,1,3}-{3182,3878,0,1,6}-{3182,3886,0,1,7}-{3182,3888,0,1,5}-{3175,3888,0,1,4}-{3175,3879,0,1,7}-{3174,3879,0,1,3}-{3174,3882,0,1,0}-{3175,3885,0,1,3}-{3172,3876,0,1,2}-{3172,3894,0,1,1}-{3172,3885,0,1,3}-{3165,3886,0,1,3}-{3165,3896,0,1,1}-{3166,3886,0,1,1}-{3164,3893,0,1,4}-{3163,3881,0,1,4}-{3159,3889,0,1,0}-{3213,9890,0,1,5}-{3210,9886,0,1,4}-{3213,9887,0,1,1}-{3207,9880,0,1,0}-{3255,9870,0,1,4}-{3258,9872,0,1,3}-{3261,9870,0,1,4}-{3156,3248,0,1,4}-{3143,3241,0,1,4}-{3156,3249,0,1,4}-{3175,3248,0,1,7}-{3169,3260,0,1,4}-{3171,3254,0,1,5}-{3162,3246,0,1,6}-{3179,3262,0,1,1}-{3162,3226,0,1,1}-{3166,3222,0,1,1}-{3157,3229,0,1,5}-{3168,3222,0,1,4}-{3211,9622,0,1,2}-{3215,9620,0,1,4}-{3212,9618,0,1,3}-{3217,9617,0,1,3}-{3214,9620,0,1,7}-{2541,3189,0,1,3}-{2542,3187,0,1,6}-{2543,3186,0,1,4}-{2545,3183,0,1,3}-{2545,3183,0,1,3}-{2545,3182,0,1,3}-{2543,3183,0,1,0}-{2543,3182,0,1,3}-{2539,3182,0,1,0}" - }, - { - "npc_id": "62", - "loc_data": "{2769,3198,0,1,7}-{2768,3206,0,1,1}-{2776,3107,0,1,1}-{2794,3121,0,1,3}-{2795,3118,0,1,1}-{2796,3109,0,1,1}-{2808,3112,0,1,6}-{2791,3124,0,1,1}-{2860,3094,0,1,6}-{2854,3086,0,1,6}-{2863,3083,0,1,3}-{2853,3081,0,1,4}-{2859,3079,0,1,4}-{2859,3075,0,1,6}-{2871,3092,0,1,1}-{2867,3094,0,1,1}-{2861,3082,0,1,3}-{2861,3094,0,1,3}-{2863,3081,0,1,2}-{2864,3089,0,1,3}-{2859,3084,0,1,0}-{2850,3060,0,1,6}-{2857,3044,0,1,7}-{2856,3066,0,1,4}-{2784,3030,0,1,4}-{2783,3017,0,1,3}-{2811,2975,0,1,7}-{2815,2987,0,1,5}-{2808,3007,0,1,6}-{2812,3007,0,1,1}-{2885,2950,0,1,4}-{2891,2951,0,1,6}-{2894,2946,0,1,4}-{2928,3022,0,1,3}-{2930,3029,0,1,4}-{2927,3039,0,1,6}-{2928,3043,0,1,0}-{2929,3036,0,1,7}-{2919,3040,0,1,4}-{2934,3045,0,1,2}-{2929,3035,0,1,4}-{2914,3055,0,1,4}-{2922,3059,0,1,3}-{2930,3049,0,1,4}-{2914,3047,0,1,4}-{2935,3049,0,1,1}-{2927,3058,0,1,3}" - }, - { - "npc_id": "63", - "loc_data": "{3128,9954,0,1,0}-{3127,9956,0,1,0}-{3125,9958,0,1,0}-{3117,9956,0,1,0}-{3122,9956,0,1,0}-{3120,9952,0,1,0}-{3123,9951,0,1,0}-{3125,9948,0,1,0}-{3118,9950,0,1,0}-{3074,3892,0,1,3}-{3071,3894,0,1,7}-{3062,3897,0,1,3}-{3058,3889,0,1,4}-{3054,3880,0,1,2}-{3054,3895,0,1,7}-{3051,3879,0,1,6}-{3051,3886,0,1,6}-{3047,3893,0,1,1}-{3047,3881,0,1,4}-{3044,3893,0,1,5}-{3041,3894,0,1,1}-{3038,3890,0,1,1}-{3046,3872,0,1,4}-{3213,3728,0,1,0}-{3211,3734,0,1,0}-{3214,3738,0,1,0}-{3215,3748,0,1,0}-{3218,3746,0,1,0}-{3226,3750,0,1,0}-{3239,3749,0,1,0}-{3247,3748,0,1,0}-{3248,3746,0,1,0}-{3244,3743,0,1,0}-{3245,3737,0,1,0}-{3165,9891,0,1,6}-{3177,9886,0,1,4}-{3175,9889,0,1,4}-{3177,9879,0,1,4}-{3177,9888,0,1,1}-{3178,9895,0,1,4}-{3182,9884,0,1,3}-{2841,9583,0,1,4}-{2841,9582,0,1,4}-{2839,9582,0,1,1}-{2837,9572,0,1,6}-{2832,9580,0,1,1}-{3164,9888,0,1,3}-{3169,9892,0,1,6}-{3258,5560,0,1,2}-{3258,5555,0,1,3}-{3260,5557,0,1,3}-{3257,5552,0,1,1}-{3260,5558,0,1,5}-{3258,5556,0,1,6}-{3257,5555,0,1,6}-{3260,5559,0,1,7}-{3257,5561,0,1,1}" - }, - { - "npc_id": "64", - "loc_data": "{2959,3917,0,1,0}-{2960,3925,0,1,0}-{2961,3920,0,1,0}-{2962,3915,0,1,0}-{2962,3933,0,1,0}-{2963,3923,0,1,0}-{2963,3929,0,1,0}-{2965,3927,0,1,0}-{2965,3931,0,1,0}-{2684,9798,0,1,2}-{2691,9814,0,1,5}-{2694,9819,0,1,6}-{2696,9831,0,1,3}-{2702,9837,0,1,6}-{2709,9844,0,1,3}-{2720,9847,0,1,4}-{2724,9843,0,1,6}-{2733,9846,0,1,4}-{2740,9841,0,1,3}-{2745,9836,0,1,4}-{2745,9828,0,1,5}-{2747,9826,0,1,1}" - }, - { - "npc_id": "66", - "loc_data": "{2507,3204,0,1,5}-{2506,3205,0,1,6}-{2526,3169,0,1,6}-{2540,3226,0,1,6}-{2528,3164,0,1,6}-{2529,3169,0,1,1}-{2537,3170,0,1,1}" - }, - { - "npc_id": "67", - "loc_data": "{2507,3200,0,1,6}-{2516,3212,0,1,1}" - }, - { - "npc_id": "68", - "loc_data": "{2518,3209,0,1,3}-{2533,3209,0,1,5}-{2530,3212,0,1,1}" - }, - { - "npc_id": "73", - "loc_data": "{3268,9893,0,1,3}-{3251,9896,0,1,6}-{3087,9672,0,1,1}-{3103,9672,0,1,1}-{3119,9670,0,1,1}-{3123,9657,0,1,2}" - }, - { - "npc_id": "74", - "loc_data": "{3148,9899,0,1,3}-{3147,9905,0,1,6}-{3141,9901,0,1,5}-{3142,9891,0,1,6}-{3139,9886,0,1,2}-{3148,9883,0,1,1}-{3151,9889,0,1,3}-{3126,9865,0,1,7}-{3129,9859,0,1,3}-{3121,9863,0,1,3}-{3126,9862,0,1,5}-{3082,9671,0,1,3}-{3105,9672,0,1,1}-{3113,9673,0,1,3}-{3124,9660,0,1,3}-{3122,9656,0,1,4}-{3117,9646,0,1,1}-{3124,9651,0,1,2}-{3117,9645,0,1,3}-{3094,9671,0,1,3}-{2843,9767,0,1,3}-{2845,9762,0,1,7}-{2842,9759,0,1,6}" - }, - { - "npc_id": "75", - "loc_data": "{2931,9641,0,1,6}-{3226,9909,0,1,4}-{3225,9905,0,1,0}-{3234,9907,0,1,4}-{2931,9643,0,1,6}-{2588,9493,0,1,3}-{2584,9491,0,1,3}-{2589,9492,0,1,4}-{2591,9492,0,1,6}-{2593,9491,0,1,6}-{2588,9491,0,1,3}-{2839,9774,0,1,4}" - }, - { - "npc_id": "76", - "loc_data": "{2843,9774,0,1,6}-{2842,9769,0,1,4}-{2842,9764,0,1,7}-{2845,9754,0,1,4}-{2849,9751,0,1,1}-{2850,9747,0,1,3}-{2844,9758,0,1,7}-{2540,9814,0,1,6}-{2547,9817,0,1,6}" - }, - { - "npc_id": "78", - "loc_data": "{2905,9832,0,1,0}-{2907,9831,0,1,0}-{2913,9828,0,1,0}-{2921,9826,0,1,0}-{2924,9831,0,1,0}-{2921,9834,0,1,0}-{2918,9831,0,1,0}-{2908,9838,0,1,0}-{2913,9841,0,1,0}-{3073,3961,0,1,0}-{3074,3952,0,1,0}-{3075,3955,0,1,0}-{3076,3961,0,1,0}-{3077,3949,0,1,0}-{3079,3957,0,1,0}-{3080,3961,0,1,0}-{2862,9569,0,1,4}-{2857,9571,0,1,7}-{2837,9561,0,1,1}-{3787,9459,0,1,1}-{3725,9369,0,1,1}-{3737,9390,0,1,4}-{3738,9399,0,1,0}-{3740,9407,0,1,1}-{3755,9424,0,1,0}-{3803,9447,0,1,4}-{3765,9407,0,1,1}-{3762,9395,0,1,3}-{3760,9440,0,1,4}-{3737,9425,0,1,7}-{3720,9430,0,1,4}-{3736,9451,0,1,3}-{3728,9463,0,1,2}-{3740,9462,0,1,4}-{3770,9463,0,1,1}-{3778,9438,0,1,1}-{3786,9422,0,1,4}-{3800,9419,0,1,3}-{3797,9427,0,1,4}-{3820,9432,0,1,5}-{3825,9414,0,1,3}-{3833,9415,0,1,0}-{3752,9450,0,1,6}-{3825,9449,0,1,3}-{3827,9459,0,1,1}-{3818,9466,0,1,4}-{3799,9460,0,1,1}-{3794,9451,0,1,1}-{3762,9432,0,1,1}-{3725,9390,0,1,3}-{2672,9805,0,1,3}-{2663,9804,0,1,4}-{2658,9809,0,1,5}-{3753,9387,0,1,0}-{3728,9382,0,1,3}-{3733,9421,0,1,7}-{2568,9528,0,1,5}-{2573,9530,0,1,3}-{2575,9525,0,1,6}-{2571,9528,0,1,7}-{2608,9818,0,1,7}-{2609,9825,0,1,5}-{2607,9830,0,1,1}-{2605,9837,0,1,1}-{2607,9833,0,1,6}-{3729,9360,0,1,6}-{2759,3402,0,1,2}-{2756,3400,0,1,0}-{2543,9564,0,1,1}-{2543,9562,0,1,1}-{2542,9557,0,1,1}-{2542,9569,0,1,4}-{2540,9566,0,1,0}-{2549,9568,0,1,6}-{2538,9568,0,1,3}-{2585,3479,0,1,1}-{2589,3480,0,1,2}-{2582,3476,0,1,3}-{2588,3485,0,1,4}-{2580,3490,0,1,5}-{2577,3482,0,1,6}-{2583,3484,0,1,7}" - }, - { - "npc_id": "80", - "loc_data": "{2619,3265,0,1,0}-{2619,3265,0,1,3}-{2615,3267,0,1,4}" - }, - { - "npc_id": "81", - "loc_data": "{2436,4443,0,1,3}-{2441,4449,0,1,6}-{3255,3255,0,1,3}-{3255,3256,0,1,4}-{3038,3310,0,1,5}-{3028,3312,0,1,6}-{3028,3299,0,1,6}-{3042,3300,0,1,6}-{2923,3277,0,1,1}-{2921,3289,0,1,1}-{2923,3280,0,1,6}-{2589,3120,0,1,4}-{2605,3116,0,1,5}-{3259,3261,0,1,1}-{3263,3283,0,1,6}-{3257,3268,0,1,4}" - }, - { - "npc_id": "82", - "loc_data": "{3096,3703,0,1,0}-{2936,9652,0,1,3}-{2926,9803,0,1,0}-{2935,9794,0,1,0}-{3052,3117,2,1,1}-{2841,9557,0,1,3}-{2831,9562,0,1,3}-{2838,9605,0,1,3}-{3110,3157,2,1,5}-{3311,3845,0,1,0}" - }, - { - "npc_id": "83", - "loc_data": "{3169,3711,0,1,7}-{2639,9477,2,0,3}-{2635,9497,2,0,2}" - }, - { - "npc_id": "84", - "loc_data": "{2875,9776,0,1,0}-{2862,9776,0,1,0}-{2860,9762,0,1,0}-{2854,9780,0,1,0}-{2866,9781,0,1,0}-{2700,9481,0,0,5}-{2729,9483,0,0,5}" - }, - { - "npc_id": "86", - "loc_data": "{2632,9696,0,1,1}-{2639,9697,0,1,3}-{2646,9696,0,1,3}-{2650,9697,0,1,4}-{2646,9702,0,1,6}-{3098,9880,0,1,5}-{3094,9883,0,1,1}-{3196,3208,0,1,1}-{3196,3205,0,1,0}-{3195,3212,0,1,0}-{3119,9891,0,1,4}-{3249,9868,0,1,4}-{3237,9868,0,1,3}-{3221,9873,0,1,6}-{3225,9862,0,1,2}-{3105,9517,0,1,7}-{3101,9517,0,1,6}-{3102,9521,0,1,4}-{3106,9519,0,1,1}-{3104,9514,0,1,4}-{3104,9518,0,1,1}-{3107,9519,0,1,1}-{3109,9516,0,1,1}-{3107,9515,0,1,7}-{3105,9512,0,1,6}-{3102,9513,0,1,3}-{3100,9514,0,1,1}-{3100,9516,0,1,7}-{3099,9521,0,1,0}-{3103,9523,0,1,1}-{3212,3191,0,1,6}-{3207,3192,0,1,2}-{3216,3184,0,1,2}-{3224,3174,0,1,4}-{3229,3183,0,1,5}-{3222,3185,0,1,3}-{3216,3177,0,1,4}-{3200,3182,0,1,5}-{3202,3189,0,1,6}-{3188,3181,0,1,7}-{3181,3191,0,1,7}-{3185,3199,0,1,4}-{3176,3198,0,1,6}-{3168,3188,0,1,4}-{3172,3178,0,1,1}-{3179,3174,0,1,1}-{3189,3168,0,1,5}-{3237,9866,0,1,6}-{3232,9867,0,1,7}-{3219,9862,0,1,7}-{3227,9875,0,1,4}-{3276,9870,0,1,3}-{3291,3388,0,1,6}-{2647,9713,0,1,3}-{2641,9719,0,1,1}-{2650,9716,0,1,1}-{2518,3187,0,1,3}-{3495,9812,0,1,1}-{3502,9807,0,0,4}-{3049,3691,0,1,0}-{2977,3700,0,1,0}-{2967,3694,0,1,0}-{2973,3694,0,1,0}" - }, - { - "npc_id": "87", - "loc_data": "{3242,3530,0,1,7}-{3256,3541,0,1,7}-{3248,3553,0,1,7}-{3103,9881,0,1,3}-{3122,9889,0,1,2}-{3117,9890,0,1,2}-{3293,3382,0,1,4}-{2520,3188,0,1,3}" - }, - { - "npc_id": "88", - "loc_data": "{2601,9802,0,1,6}-{2594,9805,0,1,1}-{2587,9806,0,1,2}-{2585,9803,0,1,1}-{2584,9801,0,1,5}-{2573,9630,0,1,0}" - }, - { - "npc_id": "89", - "loc_data": "{2783,3463,0,1,7}-{2629,3266,0,1,2}-{2634,3265,0,1,1}-{2575,3058,0,0,6}-{3080,3451,0,1,1}-{3096,3460,0,1,3}-{3142,3211,0,1,6}-{2557,3065,0,0,1}-{3294,3344,0,1,3}-{3289,3349,0,1,6}-{3286,3345,0,1,1}" - }, - { - "npc_id": "90", - "loc_data": "{2885,9812,0,1,0}-{2883,9836,0,1,0}-{2925,3254,2,0,3}-{3116,3534,0,0,4}-{3110,3367,0,0,3}-{3110,9909,0,1,4}-{3121,9910,0,1,4}-{3133,9909,0,1,5}-{3133,9904,0,1,6}-{3129,9916,0,1,3}-{3133,9916,0,1,0}-{3141,9875,0,1,6}-{3208,9905,0,1,4}-{2852,9577,0,1,1}-{2865,9567,0,1,3}-{2924,3252,2,1,3}-{2926,3255,2,1,4}-{2932,3255,2,1,3}-{2931,3251,2,1,4}-{3194,5448,0,1,3}-{3183,5447,0,1,3}-{3177,5457,0,1,4}-{3187,5456,0,1,1}-{3011,3590,0,1,0}-{3016,3596,0,1,0}-{3022,3586,0,1,0}-{3072,3531,0,1,0}-{3112,3538,0,1,0}-{3373,9748,0,1,0}-{3378,9748,0,1,0}-{3382,9753,0,1,0}-{3373,9812,0,1,0}-{3378,9812,0,1,0}-{3382,9817,0,1,0}" - }, - { - "npc_id": "91", - "loc_data": "{3099,9910,0,1,3}-{3100,9906,0,1,6}-{3212,9906,0,1,6}-{3252,9916,0,1,3}-{3259,9915,0,1,3}-{3272,9913,0,1,6}-{3274,9914,0,1,1}-{3278,9908,0,1,1}-{3275,9911,0,1,5}-{3194,5454,0,1,4}" - }, - { - "npc_id": "92", - "loc_data": "{3111,3561,0,1,3}-{3132,9915,0,1,5}-{3095,9909,0,1,3}-{3149,9869,0,1,6}-{3138,9878,0,1,5}-{3137,9869,0,1,3}-{2840,9652,0,1,1}-{2829,9657,0,1,4}-{2819,3288,0,1,1}-{2848,3248,0,1,3}-{2850,3235,0,1,3}-{3117,9671,0,1,1}-{3122,9661,0,1,6}-{3123,9648,0,1,6}-{3005,10362,0,1,1}-{3003,10358,0,1,1}-{3005,10351,0,1,1}-{3003,10345,0,1,5}-{2992,3942,0,1,4}-{2593,9883,0,1,1}-{2590,9885,0,1,4}-{2586,9883,0,1,1}-{2585,9878,0,1,3}-{2537,9845,0,1,1}-{2540,9846,0,1,4}-{2552,9844,0,1,6}-{2556,9846,0,1,4}-{2535,9844,0,1,3}-{2532,9843,0,1,4}-{2530,9845,0,1,3}-{2540,9820,0,1,4}-{2544,9824,0,1,4}-{2980,3763,0,1,0}-{2664,9877,0,1,0}-{2977,3753,0,1,0}-{3143,3802,0,1,0}-{3145,3779,0,1,0}" - }, - { - "npc_id": "93", - "loc_data": "{3101,9956,0,1,3}-{3104,9948,0,1,0}-{3104,9955,0,1,4}-{3108,9951,0,1,4}-{3108,9954,0,1,1}-{3111,9954,0,1,6}-{3112,9958,0,1,4}-{2841,9625,0,1,4}-{2845,9628,0,1,3}-{2841,9638,0,1,6}-{2845,9645,0,1,1}-{2655,9824,0,1,1}-{2669,9824,0,1,4}-{2667,9823,0,1,5}-{2658,9830,0,1,6}-{2666,9829,0,1,6}-{3194,5460,0,1,6}-{3192,5466,0,1,7}-{2566,9507,0,1,3}-{2562,9505,0,1,1}-{2568,9509,0,1,3}-{3185,5493,0,1,4}-{3182,5494,0,1,6}-{3185,5486,0,1,7}-{3175,5492,0,1,3}-{3169,5496,0,1,4}-{3170,5492,0,1,3}-{3186,5487,0,1,4}-{3169,5491,0,1,2}-{2571,9509,0,1,1}-{2564,9504,0,1,1}-{2566,9505,0,1,1}-{2544,9841,0,1,1}-{2536,9844,0,1,6}-{2527,9844,0,1,3}-{2530,9842,0,1,4}-{2543,9815,0,1,3}-{2637,9891,0,1,0}-{2645,9891,0,1,0}-{2653,9892,0,1,0}-{2653,9892,0,1,0}" - }, - { - "npc_id": "94", - "loc_data": "{2589,9881,0,1,6}-{2591,9882,0,1,4}" - }, - { - "npc_id": "95", - "loc_data": "{2462,3274,0,1,0}-{2465,3272,0,1,0}-{2465,3275,0,1,0}-{2470,3272,0,1,0}-{2472,3274,0,1,0}-{2477,3275,0,1,0}-{2437,3336,0,1,0}-{2439,3333,0,1,0}-{2439,3335,0,1,0}-{2440,3333,0,1,0}-{2440,3335,0,1,0}-{3207,3919,0,1,0}-{3218,3924,0,1,0}-{3229,3917,0,1,0}-{3241,3921,0,1,0}-{2706,3580,0,1,0}-{2717,3569,0,1,0}-{2482,2923,0,1,0}-{2496,2959,0,1,0}-{2498,2968,0,1,0}-{2500,2959,0,1,0}-{2502,2969,0,1,0}-{2509,2964,0,1,0}-{2647,3584,0,1,0}-{2680,3585,0,1,0}-{2682,3592,0,1,0}-{2602,2955,0,1,0}-{2605,2963,0,1,0}-{2607,2967,0,1,0}-{2610,2958,0,1,0}-{2610,2961,0,1,0}-{2610,2965,0,1,0}-{2516,3197,0,1,0}-{2548,3179,0,1,0}-{2616,3283,0,1,0}-{2620,3283,0,1,0}-{2379,3344,0,1,0}-{2395,3340,0,1,0}-{2398,3330,0,1,0}-{2406,3329,0,1,0}" - }, - { - "npc_id": "96", - "loc_data": "{2846,3477,0,1,0}-{2847,3474,0,1,0}-{2860,3492,0,1,0}-{2866,3498,0,1,0}-{2842,3450,0,1,0}-{2864,3453,0,1,0}-{2870,3438,0,1,0}-{2872,3445,0,1,0}" - }, - { - "npc_id": "97", - "loc_data": "{2988,3921,0,1,0}-{2991,3924,0,1,0}-{2992,3917,0,1,0}-{2994,3921,0,1,0}-{3002,3924,0,1,0}-{3004,3917,0,1,0}-{3005,3926,0,1,0}-{3006,3921,0,1,0}-{2831,3515,0,1,0}-{2833,3513,0,1,0}-{2848,3487,0,1,0}-{2850,3484,0,1,0}-{2854,3509,0,1,0}-{2856,3508,0,1,0}" - }, - { - "npc_id": "98", - "loc_data": "{3014,3294,0,1,1}" - }, - { - "npc_id": "99", - "loc_data": "{2624,3319,0,1,1}-{2624,3327,0,1,6}-{2642,3314,0,1,4}-{2646,3325,0,1,1}-{2790,3199,0,1,2}-{2784,3188,0,1,1}-{2779,3207,0,1,1}-{2646,3504,0,1,4}-{2634,3476,0,1,5}-{2651,3477,0,1,5}-{2665,3472,0,1,5}-{2662,3488,0,1,5}-{2672,3505,0,1,5}" - }, - { - "npc_id": "100", - "loc_data": "{2551,3408,0,1,6}-{2553,3405,0,1,3}-{2559,3452,0,1,3}-{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}" - }, - { - "npc_id": "101", - "loc_data": "{2555,3407,0,1,3}-{2624,3391,0,1,1}-{2622,3389,0,1,4}-{2619,3390,0,1,4}-{2553,3457,0,1,3}-{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}" - }, - { - "npc_id": "102", - "loc_data": "{2622,3390,0,1,7}-{2622,3390,0,1,7}-{2562,9657,0,1,0}-{2571,9653,0,1,0}" - }, - { - "npc_id": "103", - "loc_data": "{2888,9850,0,1,0}-{2901,9848,0,1,0}-{2909,9848,0,1,0}-{2919,9848,0,1,0}-{2937,9837,0,1,0}-{2906,9818,0,1,0}-{2920,9818,0,1,0}-{2501,3289,0,1,1}-{2928,3251,1,1,3}-{3272,3664,0,1,1}}-{3241,9907,0,1,4}-{3243,9914,0,1,1}-{3242,9913,0,1,6}-{3242,9916,0,1,5}-{3239,9914,0,1,1}-{2928,3244,1,1,4}-{2924,3253,1,1,3}-{2927,3252,1,1,4}-{2927,3254,1,1,3}-{2713,4892,0,1,3}-{2711,4899,0,1,3}-{2732,4901,0,1,3}-{2729,4893,0,1,3}-{2713,4907,0,1,3}-{2968,3761,0,1,0}-{2985,3761,0,1,0}-{2962,3755,0,1,0}-{2990,3755,0,1,0}-{2968,3749,0,1,0}-{2985,3749,0,1,0}" - }, - { - "npc_id": "104", - "loc_data": "{2894,9849,0,1,0}-{2905,9851,0,1,0}-{2915,9852,0,1,0}-{2934,9837,0,1,0}-{2935,9828,0,1,0}-{2912,9820,0,1,0}-{2900,9819,0,1,0}-{3093,3357,1,1,6}" - }, - { - "npc_id": "105", - "loc_data": "{3230,3500,0,1,5}-{2632,3280,0,1,1}-{2633,3274,0,1,3}-{3100,3594,0,1,6}-{3107,3608,0,1,2}-{3099,3602,0,1,0}-{2988,3671,0,1,0}-{3001,3674,0,1,0}-{2497,3164,0,1,2}-{2696,3329,0,1,4}-{2708,3336,0,1,4}" - }, - { - "npc_id": "106", - "loc_data": "{2968,3480,0,1,6}-{2968,3490,0,1,3}-{2978,3507,0,1,1}-{2959,3445,0,1,3}-{2807,3377,0,1,6}-{3289,3353,0,1,3}" - }, - { - "npc_id": "107", - "loc_data": "{2602,3267,0,1,6}-{2603,3270,0,1,4}-{2607,3268,0,1,5}-{3275,3154,0,1,6}-{3038,9802,0,1,5}-{3041,9804,0,1,6}-{3040,9795,0,1,7}-{3055,9776,0,1,4}-{3039,9769,0,1,6}-{3049,9764,0,1,3}-{3050,9770,0,1,1}-{3039,9765,0,1,1}-{3048,9762,0,1,4}-{3044,9760,0,1,6}-{3048,9779,0,1,1}-{2870,3166,0,1,3}-{2868,3159,0,1,4}-{2867,3172,0,1,5}-{2813,3112,0,1,4}-{2858,3167,0,1,3}-{2846,3153,0,1,3}-{2851,3149,0,1,1}-{2789,2949,0,1,3}-{2787,2943,0,1,3}-{2781,2949,0,1,3}-{3253,9909,0,1,4}-{3300,3312,0,1,1}-{3300,3305,0,1,3}-{3298,3300,0,1,5}-{3298,3294,0,1,0}-{3299,3290,0,1,6}-{2645,9823,0,1,1}-{2641,9819,0,1,7}-{2641,9817,0,1,2}-{2642,9813,0,1,5}-{2639,9809,0,1,2}-{3256,3954,0,1,1}-{3249,3949,0,1,1}-{3246,3945,0,1,1}-{3239,3943,0,1,1}-{3231,3940,0,1,1}-{3225,3943,0,1,1}-{3234,3945,0,1,1}-{3025,3568,0,1,0}-{3028,3577,0,1,0}-{3032,3570,0,1,0}-{3040,3581,0,1,0}-{3041,3572,0,1,0}-{3047,3567,0,1,0}-{3055,3574,0,1,0}-{3056,3566,0,1,0}-{3062,3555,0,1,0}-{3062,3570,0,1,0}" - }, - { - "npc_id": "108", - "loc_data": "{2941,9779,0,1,0}-{2934,9776,0,1,0}-{2934,9768,0,1,0}-{2934,9757,0,1,0}-{2930,9752,0,1,0}-{2732,3225,0,1,0}-{2729,3224,0,1,0}-{2719,3223,0,1,0}-{2722,3220,0,1,0}-{2724,3216,0,1,0}-{2721,3213,0,1,0}-{2713,3217,0,1,0}" - }, - { - "npc_id": "109", - "loc_data": "{2601,3269,0,0,0}-{2604,3272,0,0,0}-{2605,3268,0,0,0}-{2606,3271,0,0,0}" - }, - { - "npc_id": "110", - "loc_data": "{3050,10337,0,1,4}-{3047,10340,0,1,4}-{3048,10346,0,1,4}-{3048,10346,0,1,3}-{3305,9400,0,1,1}-{3296,9378,0,1,3}-{3234,5497,0,1,1}-{2564,9887,0,1,4}-{2581,9897,0,1,1}-{2577,9888,0,1,1}" - }, - { - "npc_id": "111", - "loc_data": "{2920,3800,0,1,3}-{2947,3921,0,1,6}-{2952,3902,0,1,6}-{2953,3889,0,1,6}-{3043,9581,0,1,1}-{3055,9577,0,1,6}-{3061,9576,0,1,4}-{3055,9571,0,1,7}-{3052,9566,0,1,6}" - }, - { - "npc_id": "112", - "loc_data": "{2699,3206,0,1,1}-{3165,9880,0,1,3}-{3168,9880,0,1,1}-{3159,9903,0,1,4}-{2825,3249,0,1,6}-{3156,9907,0,1,0}-{2554,3411,0,1,3}-{2560,3406,0,1,3}-{2554,3400,0,1,3}-{2551,3401,0,1,4}-{3202,5490,0,1,4}-{3237,5555,0,1,5}-{2543,9823,0,1,3}-{2540,9819,0,1,5}-{2543,9817,0,1,4}-{3144,3822,0,1,0}" - }, - { - "npc_id": "113", - "loc_data": "{2622,3272,0,0,0}-{2896,2946,0,0,0}-{2918,2961,0,0,0}-{2926,2986,0,0,0}-{2930,3044,0,0,0}-{2926,3056,0,0,0}-{2916,3065,0,0,0}-{2835,9526,0,1,4}-{2832,9514,0,1,4}-{2825,9521,0,1,5}-{2846,9520,0,1,3}-{2838,9508,0,1,0}-{2832,9495,0,1,6}-{2838,9492,0,1,6}-{2851,9485,0,1,3}-{2847,9481,0,1,3}-{2865,9494,0,1,3}-{2855,9502,0,1,2}-{2870,9496,0,1,1}-{2851,9511,0,1,2}-{2853,9519,0,1,3}-{2857,9520,0,1,6}-{2863,9525,0,1,1}-{2869,9530,0,1,5}-{2865,9515,0,1,4}-{2868,9507,0,1,3}-{2874,9512,0,1,6}" - }, - { - "npc_id": "115", - "loc_data": "{2502,3114,0,1,0}-{2505,3111,0,1,0}-{2507,3108,0,1,0}-{2508,3119,0,1,0}-{2509,3087,0,1,0}-{2510,3084,0,1,0}-{2513,3087,0,1,0}-{2514,3081,0,1,0}-{2571,3027,0,1,0}-{2572,3031,0,1,0}-{2575,3024,0,1,0}-{2578,3031,0,1,0}-{2510,3044,0,1,0}-{2520,3046,0,1,0}-{2526,3039,0,1,0}" - }, - { - "npc_id": "116", - "loc_data": "{2622,3277,0,1,5}" - }, - { - "npc_id": "117", - "loc_data": "{2904,9734,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}-{3044,10321,0,1,2}-{3044,10316,0,1,4}-{3045,10308,0,1,4}-{3048,10317,0,1,2}-{2548,3146,0,1,1}-{2542,3145,0,1,4}-{2503,3150,0,1,4}-{3300,3649,0,1,0}" - }, - { - "npc_id": "118", - "loc_data": "{2930,9703,0,1,0}-{2933,9702,0,1,0}-{3042,3465,0,1,1}-{3035,3443,0,1,6}-{3042,3468,0,1,3}-{3008,3449,0,1,4}-{3000,9842,0,1,2}-{2996,9844,0,1,4}-{3003,9828,0,1,1}-{2995,9811,0,1,3}-{3002,9811,0,1,3}-{2994,9809,0,1,5}-{3004,9813,0,1,6}-{2998,9828,0,1,4}-{3004,9799,0,1,4}-{2997,9809,0,1,0}-{3019,9813,0,1,4}-{2983,9807,0,1,6}-{3012,9812,0,1,3}-{3010,9811,0,1,3}-{2964,9811,0,1,1}-{3025,9801,0,1,4}-{3026,9824,0,1,1}-{3018,9819,0,1,6}-{3028,9815,0,1,3}-{3024,9811,0,1,4}-{3021,9826,0,1,5}-{3022,9832,0,1,1}-{3023,9833,0,1,6}-{3027,9833,0,1,3}-{3028,9828,0,1,6}-{3023,9814,0,1,6}-{3042,9831,0,1,4}-{3024,9824,0,1,6}-{3019,9847,0,1,0}-{3034,9847,0,1,7}-{3021,9851,0,1,5}-{3043,9824,0,1,3}-{3038,9821,0,1,1}-{3044,9818,0,1,4}-{3047,9814,0,1,4}-{3037,9814,0,1,6}-{2603,3056,0,1,0}-{2598,3064,0,1,3}-{2589,3068,0,1,5}" - }, - { - "npc_id": "119", - "loc_data": "{2921,9757,0,1,0}-{2915,9759,0,1,0}-{2927,9761,0,1,0}-{2925,9769,0,1,0}-{2931,9784,0,1,0}-{2938,9788,0,1,0}-{3029,10313,0,1,4}-{3028,10309,0,1,4}-{3033,10311,0,1,6}-{3035,10309,0,1,1}-{3035,10313,0,1,1}-{3233,3783,0,1,0}-{3241,3796,0,1,0}-{3253,3782,0,1,0}-{3019,3760,0,1,0}-{3019,3766,0,1,0}-{3021,3755,0,1,0}-{3025,3763,0,1,0}" - }, - { - "npc_id": "120", - "loc_data": "{2874,9880,0,1,0}-{2861,9874,0,1,0}-{2863,9874,0,1,0}-{2866,9874,0,1,0}-{2865,9877,0,1,0}-{2864,9877,0,1,0}-{2861,9877,0,1,0}-{2867,9877,0,1,0}-{2867,9879,0,1,0}" - }, - { - "npc_id": "122", - "loc_data": "{2996,9574,0,1,6}-{3007,9579,0,1,7}-{3001,9580,0,1,4}-{3018,9580,0,1,3}-{3021,9585,0,1,4}-{3024,9582,0,1,4}-{2507,3726,0,1,2}-{2523,3724,0,1,3}-{2535,9556,0,1,4}-{2566,9632,0,1,0}-{2573,9626,0,1,0}" - }, - { - "npc_id": "123", - "loc_data": "{2533,9554,0,1,4}-{2531,9556,0,1,5}-{2522,9562,0,1,4}-{2524,9558,0,1,4}-{2518,9562,0,1,6}-{2515,9566,0,1,3}-{2517,9573,0,1,1}-{2675,3731,0,1,6}-{2669,3726,0,1,6}-{2655,3727,0,1,6}-{2661,3732,0,1,6}-{2656,3720,0,1,6}--{3014,3787,0,1,0}-{3015,3813,0,1,0}-{3017,3781,0,1,0}-{3017,3793,0,1,0}-{3017,3803,0,1,0}-{3019,3789,0,1,0}-{3019,3798,0,1,0}-{3020,3809,0,1,0}-{3022,3805,0,1,0}-{3024,3781,0,1,0}-{3024,3796,0,1,0}-{3024,3812,0,1,0}-{3026,3791,0,1,0}-{3027,3797,0,1,0}-{3027,3804,0,1,0}-{3028,3809,0,1,0}-{3031,3795,0,1,0}-{3031,3802,0,1,0}-{3033,3785,0,1,0}-{3033,3811,0,1,0}-{3035,3806,0,1,0}-{3036,3795,0,1,0}-{3036,3800,0,1,0}-{3038,3809,0,1,0}-{3044,3802,0,1,0}-{3046,3797,0,1,0}" - }, - { - "npc_id": "124", - "loc_data": "{3121,9997,0,1,0}-{3124,9996,0,1,0}-{3125,9993,0,1,0}-{3121,9993,0,1,0}-{3115,9992,0,1,0}-{3118,9990,0,1,0}-{3122,9989,0,1,0}-{3124,9974,0,1,0}-{3119,9976,0,1,0}-{3116,9974,0,1,0}-{3118,9971,0,1,0}-{3142,5532,0,1,6}-{3141,5531,0,1,6}-{3139,5526,0,1,3}-{3143,5524,0,1,3}-{3148,5523,0,1,4}-{3148,5531,0,1,4}" - }, - { - "npc_id": "125", - "loc_data": "{2947,3934,0,1,0}-{2948,3917,0,1,0}-{2949,3926,0,1,0}-{2952,3913,0,1,0}-{2952,3936,0,1,0}-{2954,3921,0,1,0}-{2956,3930,0,1,0}-{2964,3944,0,1,0}-{2970,3947,0,1,0}-{2971,3938,0,1,0}-{2977,3953,0,1,0}-{2978,3942,0,1,0}-{2984,3933,0,1,0}-{2845,3517,0,1,4}-{2848,3515,0,1,4}-{3050,9570,0,1,3}-{2958,3867,0,1,3}-{2956,3857,0,1,6}-{2952,3862,0,1,3}-{2949,3858,0,1,4}-{2955,3875,0,1,7}-{2954,3874,0,1,5}-{2962,3876,0,1,1}-{2961,3877,0,1,3}-{2947,3878,0,1,6}-{2959,3884,0,1,4}-{2956,3885,0,1,3}-{2956,3886,0,1,3}-{2948,3886,0,1,1} -{3043,9579,0,1,2}-{3056,9585,0,1,0}-{3058,9575,0,1,1}-{3052,9582,0,1,0}-{3049,9577,0,1,4}-{3042,9586,0,1,6}-{3052,9588,0,1,1}-{3049,9590,0,1,5}-{3060,9578,0,1,3}-{3054,9566,0,1,7}-{3227,5443,0,1,5}-{3220,5448,0,1,5}-{3208,5443,0,1,6}-{3207,5448,0,1,4}" - }, - { - "npc_id": "126", - "loc_data": "{2381,4422,0,1,5}-{2381,4425,0,1,4}-{2382,4429,0,1,0}-{2385,4419,0,1,0}-{2388,4421,0,1,1}-{2388,4428,0,1,4}" - }, - { - "npc_id": "127", - "loc_data": "{2955,9795,0,1,0}-{2956,9791,0,1,0}-{2962,9792,0,1,0}-{2966,9788,0,1,0}-{2959,9783,0,1,0}-{2964,9775,0,1,0}-{2954,9776,0,1,0}-{3193,3959,0,1,0}-{3188,3960,0,1,0}-{3190,3962,0,1,0}-{3187,3959,0,1,0}-{3192,3961,0,1,0}-{3187,3960,0,1,0}-{3193,3959,0,1,0}-{3187,3961,0,1,0}-{3193,3958,0,1,0}" - }, - { - "npc_id": "128", - "loc_data": "{2610,3274,0,1,0}-{2610,3275,0,1,0}-{2615,3276,0,1,0}-{2616,3275,0,1,0}-{2616,3276,0,1,0}-{2877,3154,0,1,0}-{2878,3153,0,1,0}-{2870,3161,0,1,0}-{2822,3192,0,1,5}-{2770,3213,0,0,0}-{2759,3179,0,0,0}-{2741,3173,0,0,0}-{2757,3170,0,0,0}-{2746,3167,0,0,0}-{2781,3173,0,1,0}-{2746,3147,0,1,0}-{2733,3162,0,1,0}-{2730,3146,0,1,0}-{2782,3104,0,1,0}-{2782,3103,0,1,0}-{2777,3090,0,1,0}-{2773,3089,0,1,0}-{2803,3115,0,1,0}-{2808,3112,0,1,0}-{2887,3145,0,1,0}-{2824,3084,0,1,0}-{2821,3078,0,1,0}-{2817,3078,0,1,0}-{2837,3076,0,1,0}-{2844,3040,0,1,0}-{2840,3049,0,1,0}-{2837,3043,0,1,0}-{2834,3042,0,1,0}-{2827,3026,0,1,0}-{2824,3018,0,1,0}-{2830,3021,0,1,0}-{2832,3018,0,1,0}-{2925,2969,0,1,0}-{2939,2977,0,1,0}-{2936,2980,0,1,0}-{2933,2976,0,1,0}-{2930,2971,0,1,0}-{2928,2984,0,1,0}-{2942,2983,0,1,0}-{2936,2989,0,1,0}" - }, - { - "npc_id": "131", - "loc_data": "{2594,3271,0,1,4}-{2593,3269,0,1,1}-{2592,3272,0,1,6}" - }, - { - "npc_id": "132", - "loc_data": "{2600,3275,0,1,6}-{2600,3279,0,1,4}-{2603,3279,0,1,3}-{2603,3281,0,1,3}-{2604,3277,0,1,6}-{2867,3163,0,1,3}-{2883,3147,0,1,6}-{2856,3144,0,1,7}-{2812,3180,0,1,1}-{2791,3181,0,1,3}-{2749,3173,0,1,3}-{2758,3170,0,1,4}-{2754,3167,0,1,3}-{2754,3175,0,1,1}-{2891,3079,0,1,1}-{2897,3064,0,1,0}-{2896,3071,0,1,1}-{2837,3038,0,1,0}-{2837,3041,0,1,3}-{2831,3031,0,1,3}-{2818,3054,0,1,4}-{2832,3050,0,1,6}-{2832,3011,0,1,4}-{2784,3005,0,1,1}-{2782,3008,0,1,1}-{2791,2999,0,1,2}-{2791,2988,0,1,1}-{2795,2987,0,1,6}-{2797,2985,0,1,3}-{2876,3011,0,1,3}-{2863,3021,0,1,3}-{2867,3016,0,1,1}-{2871,3022,0,1,4}-{2877,3028,0,1,3}-{2868,3012,0,1,4}-{2910,3026,0,1,5}-{2902,3010,0,1,1}-{2928,3029,0,1,4}-{2920,3027,0,1,6}-{2875,2936,0,1,2}-{2945,2979,0,1,3}-{2953,2965,0,1,4}-{2952,2982,0,1,4}-{2955,3003,0,1,3}-{2944,2999,0,1,5}-{2932,3023,0,1,3}-{2933,3024,0,1,3}-{2929,3009,0,1,0}-{2927,3022,0,1,1}-{2926,3023,0,1,6}-{2927,3020,0,1,7}-{2908,3078,0,1,4}-{2920,3081,0,1,4}-{2890,3082,0,1,1}-{2916,3082,0,1,1}" - }, - { - "npc_id": "133", - "loc_data": "{2730,3659,0,1,7}-{2708,3672,0,1,7}-{3072,3625,0,1,7}-{3079,3627,0,1,3}-{3083,3632,0,1,6}-{3082,3643,0,1,0}-{3079,3637,0,1,5}-{3086,3636,0,1,3}-{3096,3641,0,1,2}-{3115,3644,1,0,5}-{3112,3614,0,1,5}-{3124,3600,0,1,1}-{3132,3597,0,1,0}" - }, - { - "npc_id": "134", - "loc_data": "{2871,9793,0,1,0}-{2869,9799,0,1,0}-{2876,9806,0,1,0}-{2864,9819,0,1,0}-{2857,9822,0,1,0}-{2858,9814,0,1,0}-{2859,9802,0,1,0}-{2849,9809,0,1,0}-{3088,9945,0,1,3}-{3089,9943,0,1,0}-{3090,9944,0,1,7}-{3043,10262,0,1,1}-{3044,10252,0,1,4}-{3051,10257,0,1,3}-{3055,10253,0,1,3}-{3067,10255,0,1,3}-{3067,10257,0,1,3}-{3068,10253,0,1,2}-{3069,10257,0,1,4}-{3069,10259,0,1,0}-{3278,5477,0,1,1}-{3278,5476,0,1,2}-{3277,5451,0,1,5}-{3283,5449,0,1,4}-{3280,5476,0,1,3}-{3274,5463,0,1,1}-{3272,5477,0,1,1}-{3271,5473,0,1,4}-{3270,5459,0,1,1}-{3271,5454,0,1,3}-{3278,5525,0,1,3}-{3272,5529,0,1,3}-{3270,5536,0,1,6}-{3277,5536,0,1,4}-{3277,5512,0,1,3}-{3279,5516,0,1,6}-{3280,5531,0,1,6}-{2598,9558,0,1,6}-{2599,9557,0,1,4}-{2598,9556,0,1,4}-{2600,9555,0,1,3}-{2601,9553,0,1,1}-{2572,9569,0,1,3}-{2599,9552,0,1,6}-{2601,9560,0,1,1}-{2599,9564,0,1,6}-{2600,9563,0,1,4}-{2597,9569,0,1,1}-{2594,9571,0,1,2}-{2589,9571,0,1,4}-{2589,9574,0,1,3}-{2584,9572,0,1,6}-{2581,9575,0,1,6}-{2583,9577,0,1,6}-{2582,9572,0,1,3}-{2572,9568,0,1,1}-{2570,9567,0,1,1}-{2572,9571,0,1,3}-{2575,9572,0,1,0}-{2577,9574,0,1,3}-{2580,9578,0,1,3}-{2597,9572,0,1,6}" - }, - { - "npc_id": "138", - "loc_data": "{2375,3434,0,1,4}-{2379,3438,0,1,6}" - }, - { - "npc_id": "139", - "loc_data": "{2378,3432,0,1,2}-{2375,3438,0,1,7}-{2376,3431,0,1,1}-{2380,3438,0,1,4}" - }, - { - "npc_id": "141", - "loc_data": "{3324,5510,0,1,3}-{3286,5512,0,1,1}-{3310,5524,0,1,2}-{3289,5527,0,1,7}-{3296,5519,0,1,4}" - }, - { - "npc_id": "142", - "loc_data": "{2514,3194,0,1,3}-{2547,3178,0,1,1}" - }, - { - "npc_id": "143", - "loc_data": "{2787,2929,0,1,6}-{2835,2934,0,1,4}-{2889,2936,0,1,2}-{2921,2937,0,1,1}" - }, - { - "npc_id": "144", - "loc_data": "{3040,9775,0,1,4}-{3048,9770,0,1,1}-{2842,3298,0,1,6}-{2844,3291,0,1,6}-{2851,3298,0,1,4}-{2855,3303,0,1,4}-{2859,3283,0,1,1}-{2861,3295,0,1,5}-{3070,3829,0,1,0}-{3070,3836,0,1,0}-{3075,3848,0,1,0}-{3090,3841,0,1,0}" - }, - { - "npc_id": "146", - "loc_data": "{2827,3139,0,1,0}-{2976,3053,0,1,0}-{2985,3038,0,1,0}-{2994,3048,0,1,5}-{2992,3030,0,1,0}-{2896,2992,0,1,7}" - }, - { - "npc_id": "151", - "loc_data": "{2756,3156,0,0,4}-{2760,3152,0,0,6}" - }, - { - "npc_id": "152", - "loc_data": "{3103,3347,0,0,0}-{3107,3337,0,0,0}-{3107,3342,0,0,0}-{3107,3344,0,0,0}-{3108,3346,0,0,0}-{3111,3339,0,0,0}-{3111,3348,0,0,0}-{3115,3344,0,0,0}-{3120,3344,0,0,0}-{3122,3335,0,0,0}" - }, - { - "npc_id": "153", - "loc_data": "{2415,4466,0,0,0}-{2419,4427,0,0,0}-{2421,4468,0,0,0}-{2426,4431,0,0,0}-{2500,3492,0,1,4}-{2245,3147,0,1,4}-{2246,3143,0,1,0}-{2242,3140,0,1,3}-{2247,3149,0,1,1}" - }, - { - "npc_id": "154", - "loc_data": "{2394,4453,0,0,0}-{2394,4457,0,0,0}-{2406,4447,0,0,0}-{2568,3470,0,1,5}-{2523,3164,0,1,4}-{2534,3173,0,1,3}-{2538,3160,0,1,3}-{2246,3144,0,1,4}-{2245,3147,0,1,4}-{2240,3147,0,1,3}-{2247,3142,0,1,5}" - }, - { - "npc_id": "155", - "loc_data": "{3235,3222,0,0,0}-{3255,3226,0,0,0}-{2561,3471,0,1,0}" - }, - { - "npc_id": "157", - "loc_data": "{2502,3521,0,1,5}" - }, - { - "npc_id": "162", - "loc_data": "{2480,3426,0,1,6}-{2489,3429,0,1,1}-{2480,3431,0,1,0}-{2475,3418,0,1,4}-{2470,3423,0,1,1}" - }, - { - "npc_id": "163", - "loc_data": "{2465,9899,0,1,6}-{2477,9896,0,1,6}-{2469,9887,0,1,6}-{2463,9893,0,1,6}" - }, - { - "npc_id": "166", - "loc_data": "{2450,3480,1,0,1}-{2449,3480,1,0,1}-{2448,3480,1,0,1}-{2440,3488,1,0,4}-{2440,3487,1,0,4}-{2448,3427,1,0,6}-{2448,3424,1,0,6}-{2443,3424,1,0,3}-{2443,3425,1,0,3}" - }, - { - "npc_id": "170", - "loc_data": "{2890,3175,0,1,4}" - }, - { - "npc_id": "175", - "loc_data": "{3084,3495,0,1,4}-{2994,9549,0,1,6}-{2993,9551,0,1,6}-{2995,9555,0,1,6}-{2999,9548,0,1,0}-{3182,3365,0,1,6}-{3179,3215,0,1,0}" - }, - { - "npc_id": "178", - "loc_data": "{2909,9690,0,1,0}-{2911,9689,0,1,0}-{2905,9687,0,1,0}-{2912,9682,0,1,0}-{2917,9681,0,1,0}-{2918,9679,0,1,0}-{2916,9677,0,1,0}-{2911,9675,0,1,0}-{2906,9675,0,1,0}-{2905,9673,0,1,0}-{2897,9675,0,1,0}-{2899,9678,0,1,0}-{2898,9682,0,1,0}-{2897,9687,0,1,0}-{2903,9690,0,1,0}-{2904,9692,0,1,0}-{2902,9693,0,1,0}-{2915,9691,0,1,0}-{2916,9694,0,1,0}-{2913,9694,0,1,0}-{2908,9694,0,1,0}-{2908,9702,0,1,0}-{2906,9706,0,1,0}-{2915,9703,0,1,0}-{2915,9711,0,1,0}-{2899,9700,0,1,0}-{2898,9703,0,1,0}-{2898,9708,0,1,0}-{2900,9710,0,1,0}-{2907,9711,0,1,0}-{2923,9710,0,1,0}-{2920,9708,0,1,0}-{2919,9703,0,1,0}-{2891,9679,0,1,0}-{2891,9682,0,1,0}-{2890,9692,0,1,0}-{2891,9695,0,1,0}-{2892,9701,0,1,0}-{2893,9701,0,1,0}-{2892,9709,0,1,0}-{2895,9710,0,1,0}-{2942,9801,0,1,0}-{2938,9812,0,1,0}-{3096,3220,0,1,1}-{3273,3507,0,1,6}" - }, - { - "npc_id": "179", - "loc_data": "{3025,3511,1,1,3}-{3038,3505,0,1,4}-{3026,3494,0,1,6}-{3026,3505,0,1,4}-{3026,3512,0,1,6}-{3025,3512,0,1,4}-{3027,3512,0,1,6}-{3030,3511,0,1,6}-{3028,3511,0,1,1}-{3038,3854,0,1,4}-{3029,3852,0,1,1}-{3032,3851,0,1,3}-{3023,3849,0,1,6}-{3042,3856,0,1,0}-{3050,3849,0,1,0}-{3054,3852,0,1,0}-{3057,3842,0,1,0}-{3058,3854,0,1,0}-{3023,3514,1,1,3}-{3014,3516,2,1,1}" - }, - { - "npc_id": "180", - "loc_data": "{2744,3519,0,0,0}-{2957,3238,0,1,4}-{2988,3423,0,1,7}-{2953,3240,0,1,3}-{3011,3277,0,1,1}-{3009,3280,0,1,1}-{3110,3295,0,1,4}" - }, - { - "npc_id": "181", - "loc_data": "{2937,9847,0,1,0}-{2934,9846,0,1,0}-{2929,9848,0,1,0}-{2931,9846,0,1,0}-{2937,9849,0,1,0}-{2936,9850,0,1,0}-{3105,9942,0,1,3}-{3106,9934,0,1,3}-{3109,9930,0,1,1}-{2563,3355,0,1,4}-{2561,3355,0,1,3}-{2561,3357,0,1,1}-{2563,3358,0,1,4}" - }, - { - "npc_id": "182", - "loc_data": "{3690,2966,0,1,4}" - }, - { - "npc_id": "183", - "loc_data": "{2805,3160,0,0,0}-{2803,3190,0,1,4}-{2795,3167,0,0,0}-{2793,3184,0,1,3}-{3670,2977,0,1,1}-{3654,2960,0,1,0}-{3698,2969,0,1,2}-{3676,2991,0,1,3}-{3666,2994,0,1,7}-{3666,2981,1,1,4}" - }, - { - "npc_id": "184", - "loc_data": "{2994,9584,0,1,6}-{2992,9573,0,1,1}-{2990,9583,0,1,4}-{2998,9572,0,1,2}-{2998,9573,0,1,4}-{2989,9578,0,1,0}-{2993,9566,0,1,4}-{2994,9570,0,1,3}-{3659,2952,0,1,6}-{3669,2961,0,1,4}-{3683,2977,0,1,6}-{3668,2981,0,1,3}-{3655,2973,0,1,1}-{3675,2967,0,1,3}-{3039,3958,0,1,0}" - }, - { - "npc_id": "186", - "loc_data": "{3160,3568,0,1,0}-{3167,3576,0,1,0}-{3171,3579,0,1,0}-{3172,3561,0,1,0}-{3175,3572,0,1,0}-{3179,3564,0,1,0}-{3184,3580,0,1,0}-{3189,3563,0,1,0}-{3190,3570,0,1,0}" - }, - { - "npc_id": "187", - "loc_data": "{3291,3924,0,1,4}-{3294,3930,0,1,4}-{3286,3933,0,1,6}-{3285,3928,0,1,6}-{3287,3935,1,1,6}-{3286,3927,1,1,6}-{3283,3936,2,1,6}-{3292,3932,1,1,4}-{3291,3940,0,1,6}-{3280,3943,0,1,6}-{3278,3926,0,1,4}-{3076,3916,0,1,0}-{3079,3909,0,1,0}" - }, - { - "npc_id": "188", - "loc_data": "{2940,3517,0,1,3}-{2938,3517,0,1,3}-{3158,5497,0,1,6}-{3157,5494,0,1,1}-{3155,5498,0,1,6}-{3140,5494,0,1,6}-{3143,5493,0,1,5}-{3142,5497,0,1,4}-{3156,5494,0,1,5}-{3148,5496,0,1,4}" - }, - { - "npc_id": "189", - "loc_data": "{2936,3514,0,1,3}-{2931,3514,0,1,3}-{2933,3517,0,1,3}-{2936,3517,0,1,3}-{3151,5489,0,1,7}-{3151,5489,0,1,7}-{3145,5495,0,1,3}-{3142,5493,0,1,3}-{3149,5489,0,1,6}-{3155,5491,0,1,2}-{3142,5497,0,1,3}" - }, - { - "npc_id": "190", - "loc_data": "{2833,9814,0,1,0}-{2829,9812,0,1,0}-{2825,9811,0,1,0}-{2829,9808,0,1,0}-{2833,9808,0,1,0}-{2827,9805,0,1,0}" - }, - { - "npc_id": "191", - "loc_data": "{2730,3161,0,1,0}-{2729,3159,0,1,0}-{2727,3163,0,1,0}-{2725,3157,0,1,0}-{2726,3161,0,1,0}-{2725,3161,0,1,0}-{2723,3155,0,1,0}-{2723,3156,0,1,0}-{2725,3162,0,1,0}-{2775,3066,0,1,0}-{2802,3071,0,1,0}-{2868,3053,0,1,0}-{2770,3018,0,1,0}-{2770,3014,0,1,0}-{2840,3018,0,1,0}-{2919,2983,0,1,0}-{2937,2993,0,1,0}-{2940,2994,0,1,0}-{2940,2997,0,1,0}-{2942,2997,0,1,0}" - }, - { - "npc_id": "192", - "loc_data": "{3022,3624,0,1,0}-{3022,3639,0,1,0}-{3029,3638,0,1,0}-{3030,3626,0,1,0}-{3035,3632,0,1,0}-{3036,3624,0,1,0}-{3036,3639,0,1,0}-{3022,3624,1,1,0}-{3022,3639,1,1,0}-{3023,3632,1,1,0}-{3028,3626,1,1,0}-{3029,3637,1,1,0}-{3035,3631,1,1,0}-{3036,3624,1,1,0}-{3036,3639,1,1,0}" - }, - { - "npc_id": "193", - "loc_data": "{2581,9505,0,1,4}-{2594,9497,0,1,1}-{2593,9499,0,1,7}-{2587,9498,0,1,3}-{2573,9500,0,1,5}-{2579,9503,0,1,5}-{2580,9500,0,1,4}-{2578,9497,0,1,7}" - }, - { - "npc_id": "195", - "loc_data": "{3029,3701,0,1,6}-{3031,3685,0,1,6}-{3032,3695,0,1,6}-{3033,3705,0,1,6}-{3037,3683,0,1,6}-{3040,3703,0,1,6}" - }, - { - "npc_id": "196", - "loc_data": "{3038,3670,0,1,1}-{3036,3672,0,1,2}-{3037,3661,0,1,6}-{3039,3660,0,1,6}" - }, - { - "npc_id": "198", - "loc_data": "{3190,3358,0,1,1}" - }, - { - "npc_id": "199", - "loc_data": "{3079,3443,0,1,3}" - }, - { - "npc_id": "201", - "loc_data": "{2931,9692,0,0,0}" - }, - { - "npc_id": "202", - "loc_data": "{3039,3700,0,1,0}" - }, - { - "npc_id": "203", - "loc_data": "{3038,3695,0,1,0}" - }, - { - "npc_id": "204", - "loc_data": "{3035,3696,0,1,0}" - }, - { - "npc_id": "205", - "loc_data": "{2613,9523,0,1,0}" - }, - { - "npc_id": "206", - "loc_data": "{3026,3455,0,1,6}-{3015,3458,0,1,5}" - }, - { - "npc_id": "208", - "loc_data": "{2568,3459,0,1,4}" - }, - { - "npc_id": "209", - "loc_data": "{3013,3452,0,1,6}" - }, - { - "npc_id": "216", - "loc_data": "{2851,3348,0,0,7}" - }, - { - "npc_id": "217", - "loc_data": "{2852,3343,0,1,2}" - }, - { - "npc_id": "218", - "loc_data": "{2613,3475,0,1,2}" - }, - { - "npc_id": "222", - "loc_data": "{2851,3351,0,0,7}-{2849,3348,0,0,7}" - }, - { - "npc_id": "223", - "loc_data": "{2569,3250,0,1,0}" - }, - { - "npc_id": "224", - "loc_data": "{2579,9655,0,1,0}-{2581,9656,0,1,0}-{2581,9659,0,1,0}-{2583,9659,0,1,0}-{2584,9656,0,1,0}-{2586,9659,0,1,0}-{2587,9656,0,1,0}-{2589,9654,0,1,0}-{2589,9658,0,1,0}-{2591,9656,0,1,0}-{2591,9659,0,1,0}" - }, - { - "npc_id": "225", - "loc_data": "{2641,3437,0,0,0}" - }, - { - "npc_id": "227", - "loc_data": "{2643,3440,0,0,0}" - }, - { - "npc_id": "228", - "loc_data": "{2634,3427,0,0,0}" - }, - { - "npc_id": "229", - "loc_data": "{2628,3413,0,1,4}" - }, - { - "npc_id": "230", - "loc_data": "{2649,3454,0,0,0}" - }, - { - "npc_id": "231", - "loc_data": "{2650,3468,0,0,0}" - }, - { - "npc_id": "232", - "loc_data": "{2819,3487,0,0,4}" - }, - { - "npc_id": "237", - "loc_data": "{2769,3403,0,1,0}-{2766,3402,0,1,3}-{2770,3402,0,1,0}-{2772,3404,0,1,1}-{2767,3405,1,1,0}-{2771,3403,1,1,6}-{2767,3401,1,1,7}" - }, - { - "npc_id": "239", - "loc_data": "{2758,3516,1,0,0}" - }, - { - "npc_id": "240", - "loc_data": "{2760,3501,0,1,1}" - }, - { - "npc_id": "241", - "loc_data": "{2762,3498,0,0,0}" - }, - { - "npc_id": "242", - "loc_data": "{2764,3509,1,0,0}" - }, - { - "npc_id": "243", - "loc_data": "{2756,3513,1,0,0}" - }, - { - "npc_id": "244", - "loc_data": "{2765,3504,1,0,0}" - }, - { - "npc_id": "245", - "loc_data": "{2755,3517,0,0,0}" - }, - { - "npc_id": "247", - "loc_data": "{2769,3402,2,1,5}" - }, - { - "npc_id": "250", - "loc_data": "{2924,3405,0,0,1}" - }, - { - "npc_id": "251", - "loc_data": "{2761,3512,0,0,0}" - }, - { - "npc_id": "253", - "loc_data": "{2580,3163,0,1,1}-{2590,3184,0,1,1}-{2589,3140,0,1,1}-{2601,3177,0,1,1}-{2607,3182,0,1,1}-{2612,3187,0,1,1}" - }, - { - "npc_id": "254", - "loc_data": "{2569,3149,0,0,1}-{2614,3169,0,1,1}-{2564,3153,0,1,1}-{2568,3147,0,1,1}-{2579,3176,0,1,3}-{2580,3174,0,1,1}-{2584,3149,0,1,3}-{2590,3190,0,1,1}-{2594,3181,0,1,1}-{2595,3186,0,1,1}-{2597,3175,0,1,1}-{2597,3181,0,1,1}-{2607,3185,0,1,1}-{2614,3150,0,1,1}-{2619,3168,0,1,1}" - }, - { - "npc_id": "255", - "loc_data": "{2571,3142,0,1,1}-{2617,3150,0,1,1}-{2609,3171,0,0,1}-{2617,3157,0,1,1}-{2577,3167,0,1,1}-{2597,3176,0,1,1}-{2607,3181,0,1,1}-{2608,3148,0,1,1}-{2600,3190,0,1,1}-{2610,3150,0,1,1}-{2609,3171,0,1,1}" - }, - { - "npc_id": "256", - "loc_data": "{2609,3194,0,1,2}" - }, - { - "npc_id": "257", - "loc_data": "" - }, - { - "npc_id": "258", - "loc_data": "" - }, - { - "npc_id": "259", - "loc_data": "{2566,3140,0,0,1}" - }, - { - "npc_id": "260", - "loc_data": "{2589,3142,0,1,4}" - }, - { - "npc_id": "261", - "loc_data": "{2587,3142,0,1,3}" - }, - { - "npc_id": "262", - "loc_data": "{2615,3160,0,1,4}-{2616,3164,0,1,1}-{2594,3143,0,0,2}" - }, - { - "npc_id": "263", - "loc_data": "{2597,3143,0,0,4}" - }, - { - "npc_id": "264", - "loc_data": "{2568,3200,0,1,0}" - }, - { - "npc_id": "265", - "loc_data": "" - }, - { - "npc_id": "266", - "loc_data": "{2601,3154,0,1,0}" - }, - { - "npc_id": "267", - "loc_data": "{2601,3168,0,1,3}" - }, - { - "npc_id": "268", - "loc_data": "{2582,3172,0,1,1}" - }, - { - "npc_id": "269", - "loc_data": "{2608,3163,0,1,3}" - }, - { - "npc_id": "270", - "loc_data": "{2608,3165,0,1,3}" - }, - { - "npc_id": "271", - "loc_data": "{2608,3158,0,1,3}" - }, - { - "npc_id": "272", - "loc_data": "{3127,3485,0,1,1}" - }, - { - "npc_id": "273", - "loc_data": "{2573,3321,0,1,3}" - }, - { - "npc_id": "274", - "loc_data": "{2638,9899,0,1,6}-{2639,9911,0,1,6}-{2648,9901,0,1,6}-{2649,9912,0,1,6}-{2649,9905,0,1,6}-{2650,9897,0,1,6}" - }, - { - "npc_id": "275", - "loc_data": "{2636,9910,0,1,4}-{2637,9897,0,1,4}-{2643,9913,0,1,6}-{2646,9906,0,1,6}" - }, - { - "npc_id": "276", - "loc_data": "{2656,9875,0,1,3}" - }, - { - "npc_id": "278", - "loc_data": "{3209,3215,0,0,0}" - }, - { - "npc_id": "279", - "loc_data": "{2604,3209,0,0,0}" - }, - { - "npc_id": "280", - "loc_data": "{2615,3258,0,1,6}" - }, - { - "npc_id": "281", - "loc_data": "{2604,3219,0,1,6}-{2609,3207,0,1,6}-{2618,3211,0,1,6}" - }, - { - "npc_id": "282", - "loc_data": "{3280,3494,0,1,6}-{2562,9608,0,1,6}-{2564,9604,0,1,6}" - }, - { - "npc_id": "283", - "loc_data": "{2569,9602,0,1,6}" - }, - { - "npc_id": "284", - "loc_data": "{2953,3450,0,0,0}" - }, - { - "npc_id": "285", - "loc_data": "{3110,3330,0,0,0}" - }, - { - "npc_id": "286", - "loc_data": "{3111,3367,2,0,4}" - }, - { - "npc_id": "287", - "loc_data": "{2770,3403,2,1,3}" - }, - { - "npc_id": "288", - "loc_data": "{3110,3366,2,0,2}-{2677,3655,0,1,3}-{2681,3653,0,1,6}" - }, - { - "npc_id": "289", - "loc_data": "{2616,3302,0,1,3}" - }, - { - "npc_id": "290", - "loc_data": "{2614,3306,0,1,4}" - }, - { - "npc_id": "296", - "loc_data": "{3084,3518,0,1,1}" - }, - { - "npc_id": "297", - "loc_data": "{3093,3518,0,1,5}" - }, - { - "npc_id": "298", - "loc_data": "{3113,3513,0,1,3}" - }, - { - "npc_id": "299", - "loc_data": "{3113,3511,0,1,3}" - }, - { - "npc_id": "300", - "loc_data": "{3105,9569,0,0,0}" - }, - { - "npc_id": "302", - "loc_data": "{2519,3429,0,1,1}" - }, - { - "npc_id": "304", - "loc_data": "{2520,3496,0,1,3}" - }, - { - "npc_id": "305", - "loc_data": "{2511,3484,0,0,4}" - }, - { - "npc_id": "306", - "loc_data": "{2514,9580,0,1,1}" - }, - { - "npc_id": "307", - "loc_data": "{2968,3206,0,0,0}" - }, - { - "npc_id": "308", - "loc_data": "{2611,3393,0,1,4}" - }, - { - "npc_id": "309", - "loc_data": "{2860,2976,0,0,4}-{2856,2977,0,0,4}-{2852,2973,0,0,4}-{2267,3253,0,0,4}-{2266,3253,0,0,4}-{2262,3249,0,0,4}-{2716,3530,0,0,0}-{2561,3374,0,0,4}-{2562,3374,0,0,4}-{2633,3693,0,0,4}-{2633,3694,0,0,4}-{2215,3248,0,0,4}-{2217,3245,0,0,4}-{2213,3241,0,0,4}-{3104,3424,0,0,0}-{3104,3425,0,0,0}-{2566,3370,0,0,0}-{2637,3444,0,0,0}-{2471,3146,0,0,4}-{2465,3156,0,0,4}-{2461,3150,0,0,4}-{2678,3595,0,0,4}" - }, - { - "npc_id": "310", - "loc_data": "{2382,3414,0,0,4}-{2537,3406,0,0,4}-{2527,3412,0,0,4}-{2507,3421,0,0,4}-{2508,3421,0,0,4}-{3239,3243,0,0,6}-{3239,3241,0,0,6}-{3238,3252,0,0,6}-{2860,2972,0,0,6}-{2865,2972,0,0,4}-{2382,3415,0,0,0}-{2392,3420,0,0,0}" - }, - { - "npc_id": "312", - "loc_data": "{2860,3426,0,0,1}" - }, - { - "npc_id": "313", - "loc_data": "{2853,3423,0,0,1}-{2840,3356,0,0,1}-{2843,3359,0,0,1}-{2848,3361,0,0,1}-{2879,3339,0,0,1}-{2879,3338,0,0,1}-{2879,3335,0,0,1}-{2879,3334,0,0,1}-{2876,3331,0,0,1}" - }, - { - "npc_id": "316", - "loc_data": "{2986,3176,0,0,4}-{3086,3227,0,0,1}-{2996,3157,0,0,1}-{3085,3230,0,0,0}" - }, - { - "npc_id": "322", - "loc_data": "{2641,3700,0,0,4}-{2640,3700,0,0,4}-{2777,2740,0,0,4}-{2780,2741,0,0,4}-{2788,2741,0,0,4}-{2771,2733,0,0,4}-{2771,2734,0,0,4}" - }, - { - "npc_id": "323", - "loc_data": "{2926,3177,0,0,6}-{3248,3161,0,0,4}-{2516,3575,0,0,7}-{2511,3562,0,0,7}-{2498,3547,0,0,7}" - }, - { - "npc_id": "324", - "loc_data": "{2645,3708,0,0,1}-{2648,3708,0,0,1}-{2632,3427,0,0,0}-{2647,3711,0,0,1}" - }, - { - "npc_id": "325", - "loc_data": "{2671,3160,0,0,4}-{2672,3160,0,0,4}-{2678,3160,0,0,4}-{2682,3160,0,0,4}-{2683,3160,0,0,4}" - }, - { - "npc_id": "326", - "loc_data": "{2840,3431,0,0,0}-{3246,3156,0,0,0}-{3245,3152,0,0,0}-{3242,3148,0,0,0}-{2855,3423,0,0,1}-{3267,3148,0,0,6}-{3276,3140,0,0,6}-{3275,3140,0,0,6}-{2607,3416,0,0,1}-{2604,3417,0,0,1}-{2602,3419,0,0,1}" - }, - { - "npc_id": "333", - "loc_data": "{2845,3429,0,0,6}-{2923,3178,0,0,6}-{2923,3180,0,0,6}-{2926,3180,0,0,6}-{2602,3411,0,0,3}-{2605,3413,0,0,3}-{2609,3416,0,0,3}-{2610,3413,0,0,3}-{2605,3421,0,0,1}-{2602,3423,0,0,1}-{2603,3426,0,0,1}" - }, - { - "npc_id": "334", - "loc_data": "{2165,3268,0,0,4}-{2162,3274,0,0,4}-{2639,3698,0,0,4}-{2642,3698,0,0,4}-{2699,2702,0,0,4}-{2694,2706,0,0,4}-{2700,2702,0,0,4}-{2707,2698,0,0,4}-{2612,3411,0,0,0}-{2627,3415,0,0,0}-{2700,2702,0,0,0}-{2700,2702,0,0,0}-{2700,2702,0,0,0}-{2707,2698,0,0,0}-{2707,2698,0,0,0}-{2707,2698,0,0,0}" - }, - { - "npc_id": "336", - "loc_data": "{2928,3218,0,1,4}" - }, - { - "npc_id": "338", - "loc_data": "{2929,3222,0,0,6}" - }, - { - "npc_id": "340", - "loc_data": "{2929,3217,0,1,6}" - }, - { - "npc_id": "342", - "loc_data": "{3281,3382,0,0,0}" - }, - { - "npc_id": "343", - "loc_data": "{3284,3382,0,0,0}" - }, - { - "npc_id": "344", - "loc_data": "{3016,3187,1,1,0}-{3015,3183,1,1,0}-{3014,3181,1,1,0}-{3011,3185,1,1,0}" - }, - { - "npc_id": "345", - "loc_data": "{3018,3185,2,1,0}" - }, - { - "npc_id": "347", - "loc_data": "{2523,3292,0,1,0}-{2536,3294,0,1,0}-{2538,3321,0,1,0}" - }, - { - "npc_id": "348", - "loc_data": "{2501,3315,0,1,0}-{2513,3325,0,1,0}-{2526,3279,0,1,0}-{2528,3297,0,1,0}-{2535,3288,0,1,0}-{2548,3287,0,1,0}" - }, - { - "npc_id": "349", - "loc_data": "{2556,3266,0,0,0}" - }, - { - "npc_id": "350", - "loc_data": "{2559,3266,0,0,0}" - }, - { - "npc_id": "351", - "loc_data": "{2507,3325,0,1,4}" - }, - { - "npc_id": "352", - "loc_data": "{2511,3322,0,1,2}-{2540,3279,0,1,6}" - }, - { - "npc_id": "353", - "loc_data": "{2504,3326,0,1,1}-{2509,3314,0,1,3}-{2545,3278,0,1,3}" - }, - { - "npc_id": "354", - "loc_data": "{2510,3318,0,1,6}-{2524,3271,0,1,3}" - }, - { - "npc_id": "355", - "loc_data": "{2504,3318,0,1,0}" - }, - { - "npc_id": "356", - "loc_data": "{2523,3307,0,1,0}" - }, - { - "npc_id": "357", - "loc_data": "{2518,3309,0,1,0}-{2526,3303,0,1,0}-{2543,3309,0,1,0}-{2550,3319,0,1,0}-{2552,3319,0,1,0}" - }, - { - "npc_id": "358", - "loc_data": "{2529,3285,0,1,0}" - }, - { - "npc_id": "360", - "loc_data": "{2550,3272,0,1,4}" - }, - { - "npc_id": "361", - "loc_data": "{2519,3277,0,1,6}" - }, - { - "npc_id": "362", - "loc_data": "{2537,3324,0,1,6}" - }, - { - "npc_id": "363", - "loc_data": "{2513,3315,0,1,4}" - }, - { - "npc_id": "364", - "loc_data": "{2577,3298,1,0,0}" - }, - { - "npc_id": "366", - "loc_data": "{2612,3324,0,0,0}" - }, - { - "npc_id": "367", - "loc_data": "{2935,3210,0,1,2}" - }, - { - "npc_id": "368", - "loc_data": "{3263,3407,0,1,2}" - }, - { - "npc_id": "369", - "loc_data": "{2542,3326,0,1,0}-{2545,3324,0,1,0}-{2545,3327,0,1,0}-{2552,3326,0,1,0}-{2553,3322,0,1,0}-{2553,3324,0,1,0}" - }, - { - "npc_id": "370", - "loc_data": "{2551,3324,1,1,0}-{2551,3327,1,1,0}" - }, - { - "npc_id": "371", - "loc_data": "{2547,3326,0,1,0}-{2549,3322,0,1,0}-{2550,3326,0,1,0}-{2552,3323,0,1,0}" - }, - { - "npc_id": "372", - "loc_data": "{2559,3303,0,1,6}-{2561,3303,0,1,6}-{2561,3305,0,1,4}-{2559,3305,0,1,1}" - }, - { - "npc_id": "373", - "loc_data": "{2516,3274,0,1,0}" - }, - { - "npc_id": "375", - "loc_data": "{3053,3249,0,0,0}" - }, - { - "npc_id": "376", - "loc_data": "{3027,3217,0,1,4}" - }, - { - "npc_id": "377", - "loc_data": "{3028,3223,0,1,2}" - }, - { - "npc_id": "378", - "loc_data": "{3028,3220,0,1,4}" - }, - { - "npc_id": "379", - "loc_data": "{2939,3156,0,1,4}" - }, - { - "npc_id": "380", - "loc_data": "{2953,3149,0,0,0}-{2770,3226,0,0,0}" - }, - { - "npc_id": "381", - "loc_data": "{2684,3272,0,1,4}" - }, - { - "npc_id": "382", - "loc_data": "{3044,9758,0,1,3}" - }, - { - "npc_id": "383", - "loc_data": "{2582,3485,0,1,7}" - }, - { - "npc_id": "384", - "loc_data": "{2545,3568,0,1,7}-{2545,3571,0,1,6}" - }, - { - "npc_id": "385", - "loc_data": "{2549,3568,0,1,6}" - }, - { - "npc_id": "386", - "loc_data": "{2877,9797,0,1,0}" - }, - { - "npc_id": "387", - "loc_data": "{3058,3488,1,0,0}" - }, - { - "npc_id": "388", - "loc_data": "{2709,3485,0,1,1}-{2716,3475,0,1,3}-{2697,3472,0,1,6}-{2702,3469,0,1,3}" - }, - { - "npc_id": "389", - "loc_data": "{2704,3405,3,1,3}" - }, - { - "npc_id": "397", - "loc_data": "{3254,3258,0,1,1}-{3264,3258,0,1,2}-{3026,3303,0,1,4}-{3259,3279,0,1,6}-{3247,3292,0,1,3}-{3260,3291,0,1,2}-{3247,3282,0,1,5}-{3261,3260,0,1,1}-{3252,3283,0,1,6}" - }, - { - "npc_id": "398", - "loc_data": "{2849,5091,0,0,0}-{2726,3349,0,1,6}" - }, - { - "npc_id": "399", - "loc_data": "{2846,5081,0,0,0}-{2730,3349,0,1,1}" - }, - { - "npc_id": "400", - "loc_data": "{2728,3377,0,1,1}" - }, - { - "npc_id": "401", - "loc_data": "{2800,2943,0,0,0}-{2762,2944,0,0,0}-{2823,2941,0,0,0}-{2861,2941,0,0,0}" - }, - { - "npc_id": "402", - "loc_data": "{2791,2944,0,0,0}-{2762,2943,0,0,0}-{2818,2939,0,0,0}-{2870,2943,0,0,0}-{2933,2944,0,0,0}" - }, - { - "npc_id": "407", - "loc_data": "{3059,4507,0,0,3}" - }, - { - "npc_id": "411", - "loc_data": "{2887,3164,0,0,0}-{2878,3161,0,0,0}" - }, - { - "npc_id": "412", - "loc_data": "{2728,10122,0,1,1}-{2735,10123,0,1,3}-{2733,10129,0,1,1}-{2728,10133,0,1,6}-{2722,10133,0,1,6}-{2736,10137,0,1,1}-{2732,10142,0,1,0}-{2726,10142,0,1,6}-{2729,10140,0,1,3}-{2720,10144,0,1,6}-{2712,10142,0,1,3}-{3341,3478,0,1,0}-{3343,3484,0,1,0}-{3345,3484,0,1,0}-{3350,3493,0,1,0}-{3353,3494,0,1,0}-{3361,3484,0,1,0}-{3364,3489,0,1,0}-{3370,3497,0,1,0}-{3379,3478,0,1,0}-{3379,3484,0,1,0}-{3387,3483,0,1,0}-{3388,3494,0,1,0}-{3389,3483,0,1,0}-{3411,3536,0,1,0}-{3418,3522,0,1,0}-{3425,3525,0,1,0}-{3429,3522,0,1,0}-{3432,3527,0,1,0}-{3434,3521,0,1,0}-{3445,3526,0,1,0}-{3565,3503,0,1,0}-{3567,3481,0,1,0}-{3580,3491,0,1,0}-{3588,3472,0,1,0}-{3598,3509,0,1,0}-{3599,3486,0,1,0}-{3607,3462,0,1,0}-{3611,3499,0,1,0}-{3613,3482,0,1,0}-{3627,3511,0,1,0}-{3628,3473,0,1,0}-{3636,3466,0,1,0}-{3639,3510,0,1,0}" - }, - { - "npc_id": "419", - "loc_data": "{3195,5450,0,1,6}-{3181,5449,0,1,1}" - }, - { - "npc_id": "420", - "loc_data": "{3175,5454,0,1,4}" - }, - { - "npc_id": "421", - "loc_data": "{3195,5459,0,1,6}-{3186,5454,0,1,5}-{3173,5494,0,1,6}-{3179,5491,0,1,1}-{3185,5492,0,1,4}-{3184,5487,0,1,3}" - }, - { - "npc_id": "423", - "loc_data": "{2731,5340,0,1,7}" - }, - { - "npc_id": "424", - "loc_data": "{2724,5359,0,1,3}" - }, - { - "npc_id": "425", - "loc_data": "{2717,5332,0,1,7}" - }, - { - "npc_id": "429", - "loc_data": "{2363,5214,0,1,6}-{2358,5214,0,1,7}-{2361,5212,0,1,6}-{2361,5209,0,1,4}-{2359,5213,0,1,1}-{2364,5215,0,1,6}-{2361,5216,0,1,1}-{2361,5212,0,1,0}" - }, - { - "npc_id": "437", - "loc_data": "{2807,3191,0,0,0}" - }, - { - "npc_id": "444", - "loc_data": "{2551,3197,0,1,1}-{2555,3194,0,1,4}-{2551,3157,0,1,3}-{2525,3155,0,1,3}-{2518,3149,0,1,3}-{2502,3175,0,1,1}" - }, - { - "npc_id": "445", - "loc_data": "{2550,3196,0,1,4}-{2557,3197,0,1,1}-{2538,3155,0,1,7}" - }, - { - "npc_id": "446", - "loc_data": "{2994,3194,0,1,1}-{3285,9896,0,1,1}-{3279,9895,0,1,6}" - }, - { - "npc_id": "447", - "loc_data": "{3114,3242,0,1,1}" - }, - { - "npc_id": "448", - "loc_data": "{3120,3238,0,1,6}" - }, - { - "npc_id": "449", - "loc_data": "{3127,3248,0,1,1}" - }, - { - "npc_id": "450", - "loc_data": "{3081,3220,0,1,0}-{3081,3222,0,1,0}" - }, - { - "npc_id": "451", - "loc_data": "{2994,3215,0,1,2}-{3077,3234,0,1,0}" - }, - { - "npc_id": "452", - "loc_data": "{3223,3293,0,0,0}" - }, - { - "npc_id": "454", - "loc_data": "{2899,3430,1,1,3}" - }, - { - "npc_id": "455", - "loc_data": "{2925,3485,0,1,5}" - }, - { - "npc_id": "456", - "loc_data": "{3244,3205,0,0,0}" - }, - { - "npc_id": "458", - "loc_data": "{3148,3175,0,1,4}" - }, - { - "npc_id": "459", - "loc_data": "{2566,9507,0,1,6}-{2565,9506,0,0,5}-{2566,9507,0,1,3}" - }, - { - "npc_id": "460", - "loc_data": "{2590,9488,0,1,3}" - }, - { - "npc_id": "461", - "loc_data": "{2588,3091,1,1,3}" - }, - { - "npc_id": "462", - "loc_data": "{2590,3084,0,1,5}" - }, - { - "npc_id": "469", - "loc_data": "{2542,3169,0,1,4}" - }, - { - "npc_id": "470", - "loc_data": "{2519,3212,0,1,3}" - }, - { - "npc_id": "471", - "loc_data": "{2526,3160,1,1,6}" - }, - { - "npc_id": "472", - "loc_data": "{2521,3168,0,1,4}" - }, - { - "npc_id": "473", - "loc_data": "{2503,3192,0,0,0}-{2514,3159,0,0,6}" - }, - { - "npc_id": "475", - "loc_data": "{2513,3262,0,1,3}-{2522,3251,0,1,1}-{2454,3300,0,1,7}-{2455,3301,0,1,7}" - }, - { - "npc_id": "477", - "loc_data": "{2455,3301,0,1,6}" - }, - { - "npc_id": "478", - "loc_data": "{2509,3255,0,1,3}-{2503,3254,1,1,3}" - }, - { - "npc_id": "481", - "loc_data": "{2499,3266,0,0,4}" - }, - { - "npc_id": "482", - "loc_data": "{2524,3258,0,1,4}" - }, - { - "npc_id": "483", - "loc_data": "{2501,3234,0,0,4}" - }, - { - "npc_id": "484", - "loc_data": "{2537,3168,0,1,5}-{2542,3167,0,0,1}-{2541,3168,0,0,2}-{2541,3171,0,0,3}-{2542,3172,0,0,4}" - }, - { - "npc_id": "491", - "loc_data": "" - }, - { - "npc_id": "494", - "loc_data": "{2807,3443,0,0,6}-{2810,3443,0,0,6}-{2657,3283,0,0,3}-{2657,3286,0,0,3}-{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}-{2584,3422,0,0,4}-{2584,3419,0,0,4}-{2618,3330,0,0,0}-{2619,3330,0,0,0}-{2584,3418,0,0,4}" - }, - { - "npc_id": "495", - "loc_data": "{2809,3443,0,0,6}-{2811,3443,0,0,6}-{2615,3091,0,0,3}-{2615,3091,0,0,3}-{2615,3330,0,0,0}-{2584,3421,0,0,4}" - }, - { - "npc_id": "496", - "loc_data": "{3267,3164,0,0,4}-{3267,3167,0,0,4}-{3267,3169,0,0,4}" - }, - { - "npc_id": "497", - "loc_data": "{3267,3166,0,0,4}-{3267,3168,0,0,4}" - }, - { - "npc_id": "498", - "loc_data": "{2384,4457,0,0,7}-{2385,4461,0,0,1}" - }, - { - "npc_id": "499", - "loc_data": "{2853,2953,0,0,1}-{2852,2953,0,0,1}" - }, - { - "npc_id": "500", - "loc_data": "{2880,2951,0,0,6}" - }, - { - "npc_id": "504", - "loc_data": "{2870,2950,0,0,0}-{2877,2959,0,0,0}" - }, - { - "npc_id": "505", - "loc_data": "{2868,2954,0,0,6}-{2876,2948,0,0,1}" - }, - { - "npc_id": "510", - "loc_data": "{2780,3211,0,0,0}" - }, - { - "npc_id": "511", - "loc_data": "{2834,2956,0,0,0}" - }, - { - "npc_id": "512", - "loc_data": "{2862,2995,1,0,0}" - }, - { - "npc_id": "513", - "loc_data": "{2857,2962,0,0,6}" - }, - { - "npc_id": "514", - "loc_data": "{2870,2974,1,0,6}" - }, - { - "npc_id": "515", - "loc_data": "{2834,2987,0,0,0}" - }, - { - "npc_id": "516", - "loc_data": "{2826,2958,0,1,3}" - }, - { - "npc_id": "517", - "loc_data": "{2870,2970,0,0,0}" - }, - { - "npc_id": "518", - "loc_data": "{2764,2959,1,0,0}" - }, - { - "npc_id": "519", - "loc_data": "{3228,3203,0,1,6}" - }, - { - "npc_id": "520", - "loc_data": "{3217,3241,0,1,4}" - }, - { - "npc_id": "521", - "loc_data": "{3217,3240,0,1,3}" - }, - { - "npc_id": "522", - "loc_data": "{3218,3415,0,1,3}" - }, - { - "npc_id": "523", - "loc_data": "{3217,3411,0,1,3}" - }, - { - "npc_id": "524", - "loc_data": "{3316,3184,0,1,3}" - }, - { - "npc_id": "525", - "loc_data": "{3316,3182,0,1,3}" - }, - { - "npc_id": "527", - "loc_data": "{2959,3390,0,1,1}-{2957,3388,0,1,3}" - }, - { - "npc_id": "528", - "loc_data": "{3078,3512,0,1,3}" - }, - { - "npc_id": "529", - "loc_data": "{3079,3507,0,1,7}" - }, - { - "npc_id": "530", - "loc_data": "{2947,3217,0,1,1}" - }, - { - "npc_id": "531", - "loc_data": "{2947,3212,0,1,1}" - }, - { - "npc_id": "532", - "loc_data": "{2906,3145,0,1,3}" - }, - { - "npc_id": "533", - "loc_data": "{2906,3144,0,1,3}" - }, - { - "npc_id": "534", - "loc_data": "{2375,4449,0,1,5}" - }, - { - "npc_id": "535", - "loc_data": "{2376,4447,0,0,0}" - }, - { - "npc_id": "536", - "loc_data": "{3192,3359,1,1,3}" - }, - { - "npc_id": "537", - "loc_data": "{3192,3355,1,1,6}" - }, - { - "npc_id": "538", - "loc_data": "{3076,3429,0,1,3}" - }, - { - "npc_id": "539", - "loc_data": "{3299,3204,0,0,0}" - }, - { - "npc_id": "540", - "loc_data": "{3288,3212,0,1,6}" - }, - { - "npc_id": "541", - "loc_data": "{3288,3190,0,1,3}" - }, - { - "npc_id": "542", - "loc_data": "{3316,3175,0,1,3}" - }, - { - "npc_id": "543", - "loc_data": "{3272,3182,0,1,6}" - }, - { - "npc_id": "544", - "loc_data": "{3316,3164,0,1,3}" - }, - { - "npc_id": "545", - "loc_data": "{3322,3195,0,1,3}" - }, - { - "npc_id": "546", - "loc_data": "{3203,3433,0,1,3}" - }, - { - "npc_id": "547", - "loc_data": "{3217,3435,0,0,3}" - }, - { - "npc_id": "548", - "loc_data": "{3205,3416,0,1,6}" - }, - { - "npc_id": "549", - "loc_data": "{3229,3438,0,1,6}" - }, - { - "npc_id": "550", - "loc_data": "{3232,3423,0,1,6}" - }, - { - "npc_id": "551", - "loc_data": "{3210,3400,0,1,3}" - }, - { - "npc_id": "552", - "loc_data": "{3206,3400,0,1,3}" - }, - { - "npc_id": "553", - "loc_data": "{3253,3403,0,1,1}" - }, - { - "npc_id": "554", - "loc_data": "{3281,3398,0,1,3}" - }, - { - "npc_id": "556", - "loc_data": "{3012,3244,0,1,6}" - }, - { - "npc_id": "557", - "loc_data": "{3012,3203,0,1,1}" - }, - { - "npc_id": "558", - "loc_data": "{3013,3225,0,1,1}" - }, - { - "npc_id": "559", - "loc_data": "{3026,3252,0,1,3}" - }, - { - "npc_id": "560", - "loc_data": "{2767,3122,0,1,6}" - }, - { - "npc_id": "562", - "loc_data": "{2799,3438,0,1,1}" - }, - { - "npc_id": "563", - "loc_data": "{2803,3430,0,1,6}" - }, - { - "npc_id": "564", - "loc_data": "{2485,4450,0,1,3}" - }, - { - "npc_id": "565", - "loc_data": "{2476,4467,0,1,2}" - }, - { - "npc_id": "566", - "loc_data": "{2480,4471,0,1,4}" - }, - { - "npc_id": "568", - "loc_data": "{2925,3143,0,1,3}" - }, - { - "npc_id": "569", - "loc_data": "{2659,3316,0,1,2}" - }, - { - "npc_id": "570", - "loc_data": "{2669,3303,0,1,5}" - }, - { - "npc_id": "571", - "loc_data": "{2655,3310,0,1,1}-{2669,3310,0,1,3}" - }, - { - "npc_id": "572", - "loc_data": "{2659,3296,0,1,4}" - }, - { - "npc_id": "573", - "loc_data": "{2663,3296,0,1,4}" - }, - { - "npc_id": "574", - "loc_data": "{2656,3300,0,1,3}" - }, - { - "npc_id": "575", - "loc_data": "{2821,3442,0,1,6}" - }, - { - "npc_id": "576", - "loc_data": "{2836,3447,0,1,4}" - }, - { - "npc_id": "577", - "loc_data": "{2975,3384,0,1,3}" - }, - { - "npc_id": "578", - "loc_data": "{2807,3342,0,1,6}" - }, - { - "npc_id": "579", - "loc_data": "{3034,9845,0,1,3}" - }, - { - "npc_id": "580", - "loc_data": "{2952,3388,0,1,3}" - }, - { - "npc_id": "581", - "loc_data": "{2975,3314,0,1,3}" - }, - { - "npc_id": "582", - "loc_data": "{2998,9828,0,1,1}" - }, - { - "npc_id": "583", - "loc_data": "{3011,3257,0,1,3}" - }, - { - "npc_id": "584", - "loc_data": "{2944,3332,0,1,3}" - }, - { - "npc_id": "585", - "loc_data": "{2946,3205,0,1,6}" - }, - { - "npc_id": "586", - "loc_data": "{2884,3450,0,1,3}" - }, - { - "npc_id": "587", - "loc_data": "{2897,3428,0,1,3}" - }, - { - "npc_id": "588", - "loc_data": "{2803,3152,0,1,3}" - }, - { - "npc_id": "589", - "loc_data": "{2660,3291,0,1,3}" - }, - { - "npc_id": "590", - "loc_data": "{2613,3294,0,1,3}" - }, - { - "npc_id": "591", - "loc_data": "{2615,3292,0,1,6}" - }, - { - "npc_id": "592", - "loc_data": "{2597,3401,0,1,2}" - }, - { - "npc_id": "593", - "loc_data": "{2569,3098,0,1,3}" - }, - { - "npc_id": "594", - "loc_data": "{2998,9840,0,1,3}" - }, - { - "npc_id": "595", - "loc_data": "{3271,3412,0,1,3}" - }, - { - "npc_id": "596", - "loc_data": "{3037,3705,0,0,0}" - }, - { - "npc_id": "597", - "loc_data": "{3025,3701,0,1,5}" - }, - { - "npc_id": "598", - "loc_data": "{2943,3384,0,1,3}" - }, - { - "npc_id": "599", - "loc_data": "{2522,5001,0,0,4}" - }, - { - "npc_id": "600", - "loc_data": "{2449,3511,1,1,6}" - }, - { - "npc_id": "601", - "loc_data": "{2482,3510,1,1,0}" - }, - { - "npc_id": "602", - "loc_data": "{2469,3488,2,1,6}" - }, - { - "npc_id": "603", - "loc_data": "{2491,3488,1,1,6}" - }, - { - "npc_id": "604", - "loc_data": "{3001,3144,0,0,0}" - }, - { - "npc_id": "605", - "loc_data": "{2985,3343,2,1,2}" - }, - { - "npc_id": "606", - "loc_data": "{2974,3343,0,1,0}" - }, - { - "npc_id": "607", - "loc_data": "{2539,3547,0,1,4}" - }, - { - "npc_id": "608", - "loc_data": "{2962,3337,2,1,4}" - }, - { - "npc_id": "610", - "loc_data": "{3027,3506,0,0,0}" - }, - { - "npc_id": "611", - "loc_data": "{3029,3505,0,1,0}" - }, - { - "npc_id": "612", - "loc_data": "{3028,3510,0,0,0}" - }, - { - "npc_id": "613", - "loc_data": "{3362,3416,0,1,6}-{3370,3387,0,1,6}" - }, - { - "npc_id": "614", - "loc_data": "{3351,9820,0,1,1}-{3351,9756,0,1,1}" - }, - { - "npc_id": "615", - "loc_data": "{3363,3397,0,1,3}" - }, - { - "npc_id": "616", - "loc_data": "{3370,3416,0,1,3}" - }, - { - "npc_id": "617", - "loc_data": "{3348,3424,0,1,3}" - }, - { - "npc_id": "618", - "loc_data": "{3364,3342,0,1,3}" - }, - { - "npc_id": "619", - "loc_data": "{3355,3333,0,1,3}" - }, - { - "npc_id": "620", - "loc_data": "{3376,3377,0,1,0}" - }, - { - "npc_id": "636", - "loc_data": "{2395,3494,0,0,6}-{2396,3494,0,0,6}-{2397,3494,0,0,6}-{2396,3493,0,0,6}" - }, - { - "npc_id": "637", - "loc_data": "{3158,3425,1,1,5}" - }, - { - "npc_id": "638", - "loc_data": "{3195,3404,0,0,0}" - }, - { - "npc_id": "639", - "loc_data": "{3211,3425,0,1,7}" - }, - { - "npc_id": "640", - "loc_data": "{3253,3487,0,1,6}" - }, - { - "npc_id": "641", - "loc_data": "{3207,3392,0,1,6}" - }, - { - "npc_id": "642", - "loc_data": "{3185,3385,0,1,6}" - }, - { - "npc_id": "643", - "loc_data": "{3246,3383,1,1,5}" - }, - { - "npc_id": "644", - "loc_data": "{3247,9781,0,1,6}" - }, - { - "npc_id": "645", - "loc_data": "{3223,3401,0,1,2}" - }, - { - "npc_id": "646", - "loc_data": "{3257,3447,0,1,0}" - }, - { - "npc_id": "648", - "loc_data": "{3220,3471,0,0,6}" - }, - { - "npc_id": "649", - "loc_data": "{3149,3210,0,1,0}" - }, - { - "npc_id": "650", - "loc_data": "{3148,3205,0,1,0}" - }, - { - "npc_id": "651", - "loc_data": "{3148,3207,0,1,0}" - }, - { - "npc_id": "652", - "loc_data": "{3147,3209,0,1,0}" - }, - { - "npc_id": "656", - "loc_data": "{2822,3374,0,0,3}" - }, - { - "npc_id": "657", - "loc_data": "{3048,3236,0,1,2}" - }, - { - "npc_id": "658", - "loc_data": "{2832,3336,0,1,2}" - }, - { - "npc_id": "659", - "loc_data": "{3052,3373,0,0,0}" - }, - { - "npc_id": "663", - "loc_data": "{3290,3290,0,1,2}" - }, - { - "npc_id": "665", - "loc_data": "{2986,9811,0,0,0}" - }, - { - "npc_id": "666", - "loc_data": "{2819,3451,0,0,3}" - }, - { - "npc_id": "668", - "loc_data": "{3284,3503,1,1,1}" - }, - { - "npc_id": "669", - "loc_data": "{2678,3086,1,1,1}" - }, - { - "npc_id": "670", - "loc_data": "{2465,3496,0,1,6}-{2466,9896,0,0,6}" - }, - { - "npc_id": "671", - "loc_data": "{2479,3463,1,1,6}" - }, - { - "npc_id": "672", - "loc_data": "{2390,3515,1,1,6}" - }, - { - "npc_id": "673", - "loc_data": "{2464,3495,3,0,6}" - }, - { - "npc_id": "674", - "loc_data": "{3001,3041,0,0,0}-{2954,3025,0,0,0}" - }, - { - "npc_id": "675", - "loc_data": "{2944,3041,0,0,0}" - }, - { - "npc_id": "677", - "loc_data": "{2708,9486,0,0,5}" - }, - { - "npc_id": "678", - "loc_data": "{2658,3436,0,1,3}-{2655,3431,0,1,0}-{2656,3424,0,1,1}-{2664,3426,0,1,4}-{2671,3438,0,1,1}-{2669,3441,0,1,7}-{2674,3438,0,1,5}-{2671,3418,0,1,7}-{2666,3417,0,1,3}-{2666,3415,0,1,2}-{2664,3416,0,1,3}-{2680,3431,0,1,6}-{2678,3431,0,1,1}-{2661,3430,0,1,4}" - }, - { - "npc_id": "679", - "loc_data": "{2658,3439,0,0,0}" - }, - { - "npc_id": "680", - "loc_data": "{2679,3432,0,1,6}" - }, - { - "npc_id": "682", - "loc_data": "{2671,3434,0,1,4}" - }, - { - "npc_id": "683", - "loc_data": "{2673,3434,0,1,3}" - }, - { - "npc_id": "684", - "loc_data": "{2667,3430,2,0,6}" - }, - { - "npc_id": "685", - "loc_data": "{2670,3429,2,0,3}" - }, - { - "npc_id": "686", - "loc_data": "{2669,3426,2,0,1}" - }, - { - "npc_id": "687", - "loc_data": "{2666,3427,2,0,4}" - }, - { - "npc_id": "688", - "loc_data": "{2668,3442,2,1,6}-{2667,3444,2,1,6}-{2669,3444,2,1,6}" - }, - { - "npc_id": "689", - "loc_data": "{2682,3428,2,1,3}-{2684,3427,2,1,3}-{2684,3429,2,1,3}" - }, - { - "npc_id": "690", - "loc_data": "{2654,3428,2,1,1}-{2652,3427,2,1,1}-{2652,3429,2,1,1}" - }, - { - "npc_id": "691", - "loc_data": "{2668,3414,2,1,4}-{2667,3412,2,1,4}-{2669,3412,2,1,4}" - }, - { - "npc_id": "692", - "loc_data": "{2661,3419,0,1,7}" - }, - { - "npc_id": "693", - "loc_data": "{2673,3416,0,0,0}" - }, - { - "npc_id": "694", - "loc_data": "{2659,3431,0,0,3}" - }, - { - "npc_id": "705", - "loc_data": "{3208,3252,0,0,6}" - }, - { - "npc_id": "706", - "loc_data": "{3103,3163,2,0,0}" - }, - { - "npc_id": "707", - "loc_data": "{3113,3160,2,0,0}" - }, - { - "npc_id": "708", - "loc_data": "{3215,3282,0,1,1}-{3242,3306,0,1,1}-{3208,3357,0,1,3}-{3240,3393,0,1,3}-{3219,3221,0,1,4}-{3162,3410,0,1,2}-{2962,3424,0,1,6}-{3011,3513,0,1,1}-{2980,3337,0,1,6}-{2997,3300,0,1,3}-{3031,3322,0,1,1}-{2957,3308,0,1,0}-{2935,3343,0,1,1}-{2942,3247,0,1,7}-{3239,3523,0,1,4}-{3262,3523,0,1,2}-{2865,3175,0,1,0}-{2855,3188,0,1,1}-{2838,3194,0,1,3}-{2806,3181,0,1,3}-{2827,3170,0,1,5}-{2788,3179,0,1,3}-{2753,3178,0,1,3}-{2759,3155,0,1,0}-{2603,3221,0,1,7}-{2604,3212,0,1,3}-{2612,3230,0,1,0}-{2611,3204,0,1,7}-{2544,3184,0,1,3}-{2627,3231,0,1,4}-{2595,3125,0,1,1}-{2598,3145,0,1,1}-{2588,3145,0,1,7}-{2601,3135,0,1,3}-{2598,3120,0,1,6}-{2615,3120,0,1,4}-{2598,3108,0,1,4}-{2733,3459,0,1,3}-{2732,3457,0,1,6}-{2715,3451,0,1,4}-{2715,3436,0,1,3}-{2977,3377,0,0,3}-{2941,3356,0,0,7}-{2932,3322,0,0,5}-{2934,3301,0,1,5}-{2934,3301,0,1,3}-{2973,3274,0,1,3}-{2994,3270,0,1,0}-{3010,3271,0,1,6}-{2977,3202,0,1,7}-{3068,3251,0,1,6}-{3189,3503,0,1,6}" - }, - { - "npc_id": "710", - "loc_data": "{2575,3333,0,1,0}" - }, - { - "npc_id": "711", - "loc_data": "{2536,3314,1,0,0}" - }, - { - "npc_id": "713", - "loc_data": "{2526,3319,0,0,6}" - }, - { - "npc_id": "714", - "loc_data": "{2569,3333,0,1,0}-{2517,9755,0,1,0}" - }, - { - "npc_id": "716", - "loc_data": "{2540,3286,0,0,1}" - }, - { - "npc_id": "717", - "loc_data": "{2513,3294,0,1,0}-{2518,3320,0,1,0}-{2539,3273,0,0,1}-{2535,3296,0,1,0}" - }, - { - "npc_id": "719", - "loc_data": "{2560,3288,0,0,0}-{2026,4619,0,1,6}-{2026,4615,0,0,6}-{2020,4614,0,1,1}-{2021,4614,0,1,6}-{2008,4612,0,1,3}-{2002,4613,0,1,4}" - }, - { - "npc_id": "720", - "loc_data": "{2542,3306,0,1,0}" - }, - { - "npc_id": "721", - "loc_data": "{2531,3331,0,1,3}" - }, - { - "npc_id": "722", - "loc_data": "{2532,3331,0,1,3}" - }, - { - "npc_id": "723", - "loc_data": "{2530,3331,0,1,3}" - }, - { - "npc_id": "724", - "loc_data": "{2531,3331,1,1,6}" - }, - { - "npc_id": "725", - "loc_data": "{2540,3305,0,1,0}" - }, - { - "npc_id": "728", - "loc_data": "{2540,3308,0,1,6}" - }, - { - "npc_id": "729", - "loc_data": "{2536,3308,0,1,4}" - }, - { - "npc_id": "731", - "loc_data": "{3278,3487,0,1,1}" - }, - { - "npc_id": "732", - "loc_data": "{3267,3392,0,0,0}" - }, - { - "npc_id": "733", - "loc_data": "{3226,3399,0,0,0}" - }, - { - "npc_id": "734", - "loc_data": "{3045,3256,0,1,3}" - }, - { - "npc_id": "735", - "loc_data": "{2795,3155,0,1,7}" - }, - { - "npc_id": "736", - "loc_data": "{2957,3373,0,1,7}" - }, - { - "npc_id": "737", - "loc_data": "{2572,3319,0,1,3}" - }, - { - "npc_id": "738", - "loc_data": "{2691,3492,0,0,0}" - }, - { - "npc_id": "739", - "loc_data": "{2554,3080,0,1,3}" - }, - { - "npc_id": "740", - "loc_data": "{2808,3086,0,1,0}" - }, - { - "npc_id": "741", - "loc_data": "{3212,3220,1,1,1}" - }, - { - "npc_id": "742", - "loc_data": "{2854,9636,0,1,4}" - }, - { - "npc_id": "743", - "loc_data": "{3098,3257,0,1,4}" - }, - { - "npc_id": "744", - "loc_data": "{3047,3204,0,0,0}" - }, - { - "npc_id": "745", - "loc_data": "{3013,3189,0,1,1}" - }, - { - "npc_id": "746", - "loc_data": "{3010,3502,0,1,3}" - }, - { - "npc_id": "747", - "loc_data": "{3069,3516,0,1,6}" - }, - { - "npc_id": "749", - "loc_data": "{1749,5323,0,1,4}" - }, - { - "npc_id": "750", - "loc_data": "{1765,5321,0,1,2}-{1744,5326,0,1,4}" - }, - { - "npc_id": "751", - "loc_data": "{1778,5323,0,1,6}" - }, - { - "npc_id": "753", - "loc_data": "{2929,9649,0,1,4}" - }, - { - "npc_id": "755", - "loc_data": "{3100,3268,0,0,0}" - }, - { - "npc_id": "756", - "loc_data": "{3222,3398,0,0,3}" - }, - { - "npc_id": "758", - "loc_data": "{3189,3273,0,0,4}" - }, - { - "npc_id": "767", - "loc_data": "{3303,3511,0,0,1}-{3302,3514,0,0,1}" - }, - { - "npc_id": "780", - "loc_data": "{3151,3410,0,1,6}" - }, - { - "npc_id": "781", - "loc_data": "{3221,3434,0,1,0}" - }, - { - "npc_id": "782", - "loc_data": "{3150,3406,0,0,0}" - }, - { - "npc_id": "783", - "loc_data": "{3221,3435,0,1,0}" - }, - { - "npc_id": "784", - "loc_data": "{3155,3406,0,0,0}" - }, - { - "npc_id": "785", - "loc_data": "{2470,3312,0,1,0}-{2479,3315,0,1,0}" - }, - { - "npc_id": "786", - "loc_data": "{2482,3313,0,1,0}-{2477,3311,0,1,0}" - }, - { - "npc_id": "787", - "loc_data": "{2486,3315,0,1,0}-{2479,3313,0,1,0}" - }, - { - "npc_id": "788", - "loc_data": "{2773,3187,0,0,6}" - }, - { - "npc_id": "789", - "loc_data": "{2811,3167,0,0,0}" - }, - { - "npc_id": "791", - "loc_data": "" - }, - { - "npc_id": "792", - "loc_data": "{2774,3197,0,0,0}" - }, - { - "npc_id": "793", - "loc_data": "{2790,3189,0,1,1}" - }, - { - "npc_id": "794", - "loc_data": "{2793,3191,0,1,3}" - }, - { - "npc_id": "796", - "loc_data": "{2903,3511,0,1,3}" - }, - { - "npc_id": "797", - "loc_data": "{2900,3511,1,1,6}" - }, - { - "npc_id": "798", - "loc_data": "{2930,9686,0,1,0}" - }, - { - "npc_id": "799", - "loc_data": "{2778,3197,0,1,4}-{2777,3194,0,1,4}-{2777,3199,0,1,3}-{2771,3193,0,1,3}-{2761,3192,0,1,5}-{2775,3190,0,1,4}-{2803,3195,0,1,4}" - }, - { - "npc_id": "801", - "loc_data": "{3058,3485,0,1,6}" - }, - { - "npc_id": "802", - "loc_data": "{3046,3486,1,0,0}" - }, - { - "npc_id": "804", - "loc_data": "{2934,3285,1,1,0}" - }, - { - "npc_id": "805", - "loc_data": "{2936,3288,0,1,4}" - }, - { - "npc_id": "812", - "loc_data": "{2738,3473,0,0,1}-{2733,3473,0,0,1}" - }, - { - "npc_id": "820", - "loc_data": "{2697,3496,0,0,0}" - }, - { - "npc_id": "822", - "loc_data": "{3302,9466,0,1,6}" - }, - { - "npc_id": "827", - "loc_data": "{3273,9417,0,1,6}-{3289,9443,0,1,4}" - }, - { - "npc_id": "830", - "loc_data": "{3271,3028,0,1,4}" - }, - { - "npc_id": "831", - "loc_data": "{3291,3032,1,0,1}" - }, - { - "npc_id": "832", - "loc_data": "{3171,3025,0,1,4}" - }, - { - "npc_id": "833", - "loc_data": "{3167,3033,0,1,2}-{3170,3027,0,1,5}-{3174,3028,0,1,6}" - }, - { - "npc_id": "834", - "loc_data": "{3170,3045,0,0,3}" - }, - { - "npc_id": "836", - "loc_data": "{3304,3124,0,1,6}" - }, - { - "npc_id": "837", - "loc_data": "{3305,3121,0,1,0}-{3301,3120,0,1,1}-{3302,3123,0,1,6}-{3307,3125,0,1,1}" - }, - { - "npc_id": "838", - "loc_data": "{3306,3117,0,1,4}" - }, - { - "npc_id": "839", - "loc_data": "{3153,3035,0,1,3}" - }, - { - "npc_id": "841", - "loc_data": "{3288,3022,0,1,1}" - }, - { - "npc_id": "843", - "loc_data": "{2644,3274,0,1,3}-{2644,3276,0,1,5}-{2647,3271,0,1,6}" - }, - { - "npc_id": "844", - "loc_data": "{2683,3323,0,0,0}" - }, - { - "npc_id": "845", - "loc_data": "{2635,3311,0,0,0}" - }, - { - "npc_id": "846", - "loc_data": "{2787,3184,0,1,4}" - }, - { - "npc_id": "847", - "loc_data": "{3143,3447,0,1,5}" - }, - { - "npc_id": "848", - "loc_data": "{2480,3489,1,1,3}" - }, - { - "npc_id": "849", - "loc_data": "{2482,3487,1,1,0}-{2482,3489,1,1,0}-{2417,3498,1,1,0}" - }, - { - "npc_id": "850", - "loc_data": "{2439,3502,1,0,4}" - }, - { - "npc_id": "851", - "loc_data": "{2449,3503,1,1,4}" - }, - { - "npc_id": "852", - "loc_data": "{2568,9432,0,1,0}-{2573,9447,0,1,0}-{2579,9441,0,1,0}-{2580,9431,0,1,0}-{2611,9454,0,1,0}-{2616,9447,0,1,0}-{2505,3032,0,1,0}-{2512,3022,0,1,0}-{2518,3041,0,1,0}-{2528,3044,0,1,0}-{2615,2987,0,1,0}" - }, - { - "npc_id": "853", - "loc_data": "{2506,3115,0,1,3}" - }, - { - "npc_id": "854", - "loc_data": "{2511,3085,0,1,4}" - }, - { - "npc_id": "855", - "loc_data": "{2576,3027,0,1,3}" - }, - { - "npc_id": "856", - "loc_data": "{2577,3021,0,1,1}" - }, - { - "npc_id": "858", - "loc_data": "{2549,3029,0,1,0}-{2551,3031,0,1,0}" - }, - { - "npc_id": "859", - "loc_data": "{2505,3062,0,1,0}-{2507,3062,0,1,0}" - }, - { - "npc_id": "860", - "loc_data": "{2501,3012,0,1,0}-{2503,3011,0,1,0}" - }, - { - "npc_id": "861", - "loc_data": "{2526,3018,0,1,0}-{2529,3019,0,1,0}" - }, - { - "npc_id": "862", - "loc_data": "{2541,3034,0,1,3}-{2541,3029,0,1,3}-{2543,3031,0,1,3}" - }, - { - "npc_id": "870", - "loc_data": "{2507,3040,0,1,0}-{2507,3036,0,1,0}" - }, - { - "npc_id": "872", - "loc_data": "{2547,3117,2,1,3}-{2547,3112,2,1,6}-{2544,3114,2,1,3}-{2547,3118,2,1,3}" - }, - { - "npc_id": "873", - "loc_data": "{2518,3035,0,1,1}" - }, - { - "npc_id": "874", - "loc_data": "{2528,3048,0,1,6}" - }, - { - "npc_id": "875", - "loc_data": "{2513,3034,0,1,1}" - }, - { - "npc_id": "876", - "loc_data": "{2525,3043,0,1,3}" - }, - { - "npc_id": "877", - "loc_data": "{2536,3092,0,1,1}-{2534,3091,0,1,1}-{2550,3118,0,1,4}-{2543,3117,0,1,3}-{2546,3113,0,1,4}" - }, - { - "npc_id": "878", - "loc_data": "{2534,3092,0,1,1}" - }, - { - "npc_id": "881", - "loc_data": "{3112,3162,1,0,0}" - }, - { - "npc_id": "882", - "loc_data": "{3203,3424,0,0,6}" - }, - { - "npc_id": "883", - "loc_data": "{3204,3470,0,1,3}" - }, - { - "npc_id": "884", - "loc_data": "{3204,3495,2,1,6}" - }, - { - "npc_id": "885", - "loc_data": "{2565,3271,0,0,0}-{2573,3268,1,0,0}" - }, - { - "npc_id": "887", - "loc_data": "{2570,3275,0,1,3}" - }, - { - "npc_id": "888", - "loc_data": "{2565,3273,0,0,0}" - }, - { - "npc_id": "889", - "loc_data": "{2571,3270,0,0,0}" - }, - { - "npc_id": "890", - "loc_data": "{2569,3272,0,0,0}-{2573,3269,1,0,0}" - }, - { - "npc_id": "895", - "loc_data": "{2929,3456,0,1,4}" - }, - { - "npc_id": "902", - "loc_data": "{2535,4715,0,1,6}" - }, - { - "npc_id": "903", - "loc_data": "{2541,4715,0,1,3}" - }, - { - "npc_id": "904", - "loc_data": "{2507,4693,0,1,3}" - }, - { - "npc_id": "905", - "loc_data": "{2540,4719,0,1,6}" - }, - { - "npc_id": "912", - "loc_data": "{3100,3927,0,1,0}-{3102,3938,0,1,0}-{3116,3934,0,1,0}" - }, - { - "npc_id": "913", - "loc_data": "{3098,3925,0,1,0}-{3100,3940,0,1,0}-{3110,3934,0,1,0}" - }, - { - "npc_id": "914", - "loc_data": "{3098,3942,0,1,0}-{3102,3929,0,1,0}-{3113,3934,0,1,0}" - }, - { - "npc_id": "915", - "loc_data": "{3108,3264,0,1,2}" - }, - { - "npc_id": "916", - "loc_data": "{3122,3245,0,0,0}" - }, - { - "npc_id": "917", - "loc_data": "{3125,3250,0,1,6}-{3122,3249,0,1,6}" - }, - { - "npc_id": "918", - "loc_data": "{3048,3209,1,1,3}" - }, - { - "npc_id": "919", - "loc_data": "{3128,3245,0,1,4}" - }, - { - "npc_id": "920", - "loc_data": "{3123,3241,0,1,1}" - }, - { - "npc_id": "922", - "loc_data": "{3086,3261,0,1,7}" - }, - { - "npc_id": "923", - "loc_data": "{3302,3163,0,0,0}" - }, - { - "npc_id": "924", - "loc_data": "{3286,3180,0,0,0}" - }, - { - "npc_id": "925", - "loc_data": "{3267,3226,0,0,3}-{3266,3229,0,0,3}-{3269,3229,0,0,4}-{3268,3226,0,0,4}" - }, - { - "npc_id": "931", - "loc_data": "{2792,2929,0,0,0}-{2815,2935,0,0,0}-{2842,2935,0,0,0}-{2842,2929,0,0,0}" - }, - { - "npc_id": "933", - "loc_data": "{2725,3367,0,0,2}-{2725,3380,2,0,6}" - }, - { - "npc_id": "941", - "loc_data": "{2982,3618,0,1,3}-{3340,3678,0,1,1}-{3339,3695,0,1,6}-{3344,3705,0,1,3}-{3307,5462,0,1,0}-{3118,3820,0,1,0}" - }, - { - "npc_id": "942", - "loc_data": "{3074,3086,0,0,0}" - }, - { - "npc_id": "943", - "loc_data": "{3103,3096,0,0,0}" - }, - { - "npc_id": "944", - "loc_data": "{3107,9511,0,1,5}" - }, - { - "npc_id": "945", - "loc_data": "{3093,3107,0,0,0}" - }, - { - "npc_id": "946", - "loc_data": "{3141,3090,0,0,6}" - }, - { - "npc_id": "947", - "loc_data": "{3127,3124,0,0,3}" - }, - { - "npc_id": "948", - "loc_data": "{3084,9506,0,0,0}" - }, - { - "npc_id": "949", - "loc_data": "{3086,3122,0,0,0}" - }, - { - "npc_id": "952", - "loc_data": "{3101,3092,0,0,4}" - }, - { - "npc_id": "954", - "loc_data": "{3123,3107,0,1,5}" - }, - { - "npc_id": "955", - "loc_data": "{3176,3317,0,1,3}-{3169,3319,0,1,1}" - }, - { - "npc_id": "957", - "loc_data": "{3314,3241,0,0,1}-{3314,3240,1,0,0}" - }, - { - "npc_id": "958", - "loc_data": "{3383,3272,0,1,3}" - }, - { - "npc_id": "959", - "loc_data": "{3359,3272,0,1,5}" - }, - { - "npc_id": "960", - "loc_data": "{3370,3276,0,1,3}" - }, - { - "npc_id": "961", - "loc_data": "{3377,3276,0,1,4}" - }, - { - "npc_id": "962", - "loc_data": "{3362,3276,0,1,3}" - }, - { - "npc_id": "963", - "loc_data": "{3375,3274,0,1,2}" - }, - { - "npc_id": "970", - "loc_data": "{3081,3247,0,0,1}" - }, - { - "npc_id": "1005", - "loc_data": "{3057,3905,0,1,3}" - }, - { - "npc_id": "1010", - "loc_data": "{2629,2979,0,1,0}" - }, - { - "npc_id": "1013", - "loc_data": "{2544,2988,0,1,0}-{2532,2983,0,1,0}-{2549,2983,0,1,0}-{2545,2983,0,1,0}-{2528,2986,0,1,0}-{2550,2979,0,1,0}-{2522,2978,0,1,0}-{2523,2976,0,1,0}-{2526,2974,0,1,0}-{2593,2971,0,1,0}-{2594,2971,0,1,0}-{2596,2961,0,1,0}-{2598,2971,0,1,0}-{2599,2960,0,1,0}-{2598,2960,0,1,0}-{2603,2966,0,1,0}-{2603,2970,0,1,0}-{2598,2960,0,1,0}-{2596,2963,0,1,0}-{2595,2964,0,1,0}-{2594,2964,0,1,0}-{2601,2964,0,1,0}-{2598,2969,0,1,0}-{2604,2971,0,1,0}-{2601,2972,0,1,0}-{2544,2904,0,1,0}-{2548,2990,0,1,0}-{2552,2988,0,1,0}-{2548,2979,0,1,0}-{2547,2991,0,1,0}-{2525,2975,0,1,0}-{2464,2912,0,1,0}-{2392,3057,0,1,0}-{2395,3056,0,1,0}-{2395,3052,0,1,0}-{2397,3048,0,1,0}-{2400,3050,0,1,0}-{2400,3053,0,1,0}-{2391,3052,0,1,0}-{2393,3047,0,1,0}-{2399,3046,0,1,0}-{2402,3044,0,1,0}-{2399,3040,0,1,0}-{2395,3042,0,1,0}-{2391,3045,0,1,0}" - }, - { - "npc_id": "1017", - "loc_data": "{3191,3277,0,1,0}-{3188,3279,0,1,0}-{3187,3277,0,1,0}-{3198,3359,0,1,0}-{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}-{2785,3069,0,1,4}-{2800,3072,0,1,6}-{3234,3297,0,1,3}-{3234,3292,0,1,4}-{3235,3289,0,1,1}-{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}-{2693,3271,0,1,3}-{2692,3272,0,1,3}-{3190,3278,0,1,0}-{3187,3277,0,1,0}--{3189,3278,0,1,3}" - }, - { - "npc_id": "1019", - "loc_data": "{3187,5555,0,1,4}-{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}-{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}-{3190,5563,0,1,1}-{3193,5555,0,1,3}" - }, - { - "npc_id": "1019", - "loc_data": "{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}" - }, - { - "npc_id": "1020", - "loc_data": "{2697,9913,0,1,0}-{2695,9907,0,1,0}-{2693,9901,0,1,0}-{2701,9902,0,1,0}-{2704,9908,0,1,0}-{2694,9889,0,1,0}-{2694,9879,0,1,0}-{2699,9875,0,1,0}-{2700,9869,0,1,0}-{2695,9870,0,1,0}" - }, - { - "npc_id": "1021", - "loc_data": "{2733,9882,0,1,0}-{2732,9888,0,1,0}-{2738,9889,0,1,0}-{2732,9892,0,1,0}-{2738,9894,0,1,0}-{2734,9896,0,1,0}" - }, - { - "npc_id": "1022", - "loc_data": "{2714,9904,0,1,0}-{2718,9906,0,1,0}-{2717,9902,0,1,0}-{2720,9904,0,1,0}-{2724,9903,0,1,0}-{2727,9905,0,1,0}-{2726,9907,0,1,0}" - }, - { - "npc_id": "1027", - "loc_data": "{3278,3155,0,0,0}-{2354,3608,0,0,0}" - }, - { - "npc_id": "1036", - "loc_data": "{3514,3478,0,0,0}-{3514,3479,0,0,0}-{3514,3480,0,0,0}-{3514,3481,0,0,0}-{3514,3482,0,0,0}-{3514,3483,0,0,0}" - }, - { - "npc_id": "1037", - "loc_data": "{2435,3412,0,1,3}" - }, - { - "npc_id": "1038", - "loc_data": "{3507,3496,1,1,1}-{3508,3494,0,1,1}" - }, - { - "npc_id": "1039", - "loc_data": "{3499,3506,1,1,5}-{3500,3505,0,1,5}" - }, - { - "npc_id": "1040", - "loc_data": "{3475,3494,0,1,6}" - }, - { - "npc_id": "1041", - "loc_data": "{3490,3503,1,0,0}-{3490,3500,0,1,4}" - }, - { - "npc_id": "1042", - "loc_data": "{3495,3469,0,1,3}" - }, - { - "npc_id": "1043", - "loc_data": "{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", - "loc_data": "{3408,3484,0,1,6}-{3408,3493,0,1,1}-{3410,3489,1,1,3}-{3412,3487,1,1,4}" - }, - { - "npc_id": "1045", - "loc_data": "{3413,3489,0,1,3}-{3409,3485,1,1,4}-{3413,3489,1,1,1}-{3213,3476,0,1,3}" - }, - { - "npc_id": "1046", - "loc_data": "{3409,3489,0,1,4}-{3413,3485,0,1,4}-{3414,3491,1,1,6}" - }, - { - "npc_id": "7690", - "loc_data": "{3415,3489,2,0,0}" - }, - { - "npc_id": "1048", - "loc_data": "{3437,3486,0,0,0}" - }, - { - "npc_id": "7707", - "loc_data": "{3440,9895,0,0,0}" - }, - { - "npc_id": "1054", - "loc_data": "{3444,3459,0,0,0}" - }, - { - "npc_id": "1055", - "loc_data": "{2811,3191,0,0,0}" - }, - { - "npc_id": "1060", - "loc_data": "{2898,3528,0,0,1}" - }, - { - "npc_id": "1061", - "loc_data": "{2898,3532,0,0,4}" - }, - { - "npc_id": "1062", - "loc_data": "{2893,3540,0,0,3}" - }, - { - "npc_id": "1063", - "loc_data": "{2900,3531,0,0,7}-{2900,3533,0,0,7}" - }, - { - "npc_id": "1064", - "loc_data": "{2895,3537,0,0,1}-{2894,3537,0,0,1}-{2893,3537,0,0,1}-{2892,3537,0,0,1}-{2891,3537,0,0,1}-{2891,3538,0,0,1}-{2892,3538,0,0,1}-{2893,3538,0,0,1}-{2894,3538,0,0,1}-{2895,3538,0,0,1}" - }, - { - "npc_id": "1065", - "loc_data": "{2889,3529,0,1,1}-{2904,3538,0,1,6}-{2882,3531,0,1,4}-{2903,3544,0,1,4}-{2911,3542,0,1,4}-{2906,3540,0,1,3}" - }, - { - "npc_id": "1066", - "loc_data": "{2892,3532,0,0,4}" - }, - { - "npc_id": "1067", - "loc_data": "{2893,3533,0,0,6}" - }, - { - "npc_id": "1068", - "loc_data": "{2894,3532,0,0,3}" - }, - { - "npc_id": "1069", - "loc_data": "{2840,3589,0,0,3}" - }, - { - "npc_id": "1070", - "loc_data": "{2269,4758,0,1,3}" - }, - { - "npc_id": "1071", - "loc_data": "{2820,3554,0,1,1}" - }, - { - "npc_id": "1072", - "loc_data": "{2893,3558,0,1,3}" - }, - { - "npc_id": "1073", - "loc_data": "{2900,3549,0,0,3}-{2897,3549,0,0,4}-{2902,3568,1,1,3}-{2903,3559,1,1,3}-{2903,3557,1,1,3}-{2903,3569,1,1,3}-{2894,3559,1,1,3}-{2892,3557,1,0,3}" - }, - { - "npc_id": "1074", - "loc_data": "{2893,3569,1,1,3}" - }, - { - "npc_id": "1076", - "loc_data": "{2897,3556,0,0,6}-{2896,3566,0,0,6}" - }, - { - "npc_id": "1077", - "loc_data": "{2900,3556,0,0,6}" - }, - { - "npc_id": "1079", - "loc_data": "{2906,3537,0,1,5}" - }, - { - "npc_id": "1081", - "loc_data": "{2897,3565,0,1,4}" - }, - { - "npc_id": "1082", - "loc_data": "{2919,3576,0,0,0}" - }, - { - "npc_id": "1083", - "loc_data": "{2931,3546,0,1,3}" - }, - { - "npc_id": "1084", - "loc_data": "{2915,3549,0,1,0}" - }, - { - "npc_id": "1085", - "loc_data": "{2927,3558,0,1,6}" - }, - { - "npc_id": "1086", - "loc_data": "{2965,3262,0,1,4}" - }, - { - "npc_id": "1087", - "loc_data": "{2911,3540,0,1,7}" - }, - { - "npc_id": "1088", - "loc_data": "{2918,3545,0,1,0}" - }, - { - "npc_id": "1089", - "loc_data": "{2919,3529,0,1,0}" - }, - { - "npc_id": "1090", - "loc_data": "{2931,3566,0,1,1}" - }, - { - "npc_id": "1093", - "loc_data": "{2817,3560,0,1,3}" - }, - { - "npc_id": "1094", - "loc_data": "{2802,3841,0,1,0}-{2798,3842,0,1,0}-{2804,3843,0,1,0}-{2807,3843,0,1,0}" - }, - { - "npc_id": "1096", - "loc_data": "{2852,3591,0,1,7}" - }, - { - "npc_id": "1097", - "loc_data": "{2858,3596,0,1,4}" - }, - { - "npc_id": "1098", - "loc_data": "{2868,3591,0,1,6}" - }, - { - "npc_id": "1099", - "loc_data": "{2857,3589,0,0,0}" - }, - { - "npc_id": "1100", - "loc_data": "{2859,3589,0,0,0}" - }, - { - "npc_id": "1101", - "loc_data": "{2854,3600,0,0,1}" - }, - { - "npc_id": "1102", - "loc_data": "{2859,3600,0,0,1}" - }, - { - "npc_id": "1103", - "loc_data": "{2863,3600,0,0,1}" - }, - { - "npc_id": "1104", - "loc_data": "{2867,3600,0,0,1}" - }, - { - "npc_id": "1105", - "loc_data": "{2870,3600,0,0,1}-{2851,3598,0,0,1}" - }, - { - "npc_id": "1106", - "loc_data": "{2864,3594,0,1,1}-{2881,3592,0,1,5}-{2857,3587,0,1,7}-{2855,3596,0,1,4}-{2827,10078,1,1,6}-{2916,3631,0,1,0}-{2843,3682,0,1,1}-{2769,10149,0,1,0}-{2777,10142,0,1,0}" - }, - { - "npc_id": "1107", - "loc_data": "{2874,3595,0,1,3}-{2875,3596,0,1,1}-{2873,3598,0,1,1}-{2836,10088,1,1,5}-{2845,3674,0,1,4}-{2781,10143,0,1,0}-{2917,3634,0,1,0}" - }, - { - "npc_id": "1108", - "loc_data": "{2878,3592,0,1,6}-{2861,3586,0,1,6}-{2855,10058,2,1,4}-{2852,3673,0,1,2}-{2783,10140,0,1,0}" - }, - { - "npc_id": "1109", - "loc_data": "{2875,3588,0,1,1}-{2883,3586,0,1,6}-{2825,10087,1,1,3}-{2836,10080,1,1,6}-{2784,10040,2,1,3}-{2909,3641,0,1,0}-{2923,10029,0,1,7}-{2851,3667,0,1,3}-{2784,10134,0,1,0}" - }, - { - "npc_id": "1110", - "loc_data": "{2883,3591,0,1,4}-{2869,3594,0,1,3}-{2861,3587,0,1,4}-{2872,3594,0,1,5}-{2829,10080,1,1,6}-{2836,10097,2,1,4}-{2852,10110,2,1,4}-{2771,10143,0,1,0}" - }, - { - "npc_id": "1111", - "loc_data": "{2863,3588,0,1,6}-{2865,3591,0,1,0}-{2866,3598,0,1,1}-{2840,10101,1,1,4}-{2857,10055,2,1,3}-{2836,10090,2,1,1}-{2925,10033,0,1,6}-{2855,3685,0,1,1}-{2788,10131,0,1,0}" - }, - { - "npc_id": "1112", - "loc_data": "{2867,3595,0,1,6}-{2871,3597,0,1,4}-{2832,10089,1,1,1}-{2836,10077,2,1,1}-{2925,10030,0,1,1}" - }, - { - "npc_id": "1113", - "loc_data": "{2829,10083,0,1,0}" - }, - { - "npc_id": "1114", - "loc_data": "{2827,10077,0,1,0}" - }, - { - "npc_id": "1115", - "loc_data": "{2831,10086,2,0,0}" - }, - { - "npc_id": "1116", - "loc_data": "{2822,10073,2,0,0}" - }, - { - "npc_id": "1117", - "loc_data": "{2829,10100,2,0,0}" - }, - { - "npc_id": "1118", - "loc_data": "{2906,3623,0,0,6}" - }, - { - "npc_id": "1119", - "loc_data": "{2920,3622,0,0,3}" - }, - { - "npc_id": "1120", - "loc_data": "{2923,3616,0,0,3}" - }, - { - "npc_id": "1121", - "loc_data": "{2923,3612,0,0,3}" - }, - { - "npc_id": "1122", - "loc_data": "{2916,3601,0,0,0}" - }, - { - "npc_id": "1123", - "loc_data": "{2902,3604,0,0,1}" - }, - { - "npc_id": "1124", - "loc_data": "{2922,3606,0,0,3}" - }, - { - "npc_id": "1125", - "loc_data": "{2911,3612,0,0,3}" - }, - { - "npc_id": "1128", - "loc_data": "{2832,10079,0,0,4}" - }, - { - "npc_id": "1129", - "loc_data": "{2832,10083,0,0,4}" - }, - { - "npc_id": "1130", - "loc_data": "{2902,3696,0,0,1}" - }, - { - "npc_id": "1131", - "loc_data": "{2898,3698,0,0,1}" - }, - { - "npc_id": "1132", - "loc_data": "{2893,3700,0,0,1}" - }, - { - "npc_id": "1133", - "loc_data": "{2889,3699,0,0,1}" - }, - { - "npc_id": "1134", - "loc_data": "{2886,3698,0,0,1}" - }, - { - "npc_id": "1135", - "loc_data": "{2843,10057,1,0,0}" - }, - { - "npc_id": "1136", - "loc_data": "{2840,10056,1,0,0}" - }, - { - "npc_id": "1137", - "loc_data": "{2847,10056,1,0,0}" - }, - { - "npc_id": "1138", - "loc_data": "{2826,10083,1,1,0}-{2837,10083,2,1,1}-{2904,3642,0,1,3}-{2923,10031,0,1,3}" - }, - { - "npc_id": "1139", - "loc_data": "{2854,3689,0,0,6}" - }, - { - "npc_id": "1140", - "loc_data": "{2856,10054,1,0,0}-{2860,10057,1,0,0}-{2819,3586,0,1,4}-{2892,3685,0,1,0}-{2900,3671,0,1,0}" - }, - { - "npc_id": "1141", - "loc_data": "{2857,10059,1,0,0}-{2857,10057,1,0,0}-{2893,3678,0,1,0}" - }, - { - "npc_id": "1142", - "loc_data": "{2851,10089,0,1,1}-{2854,10092,0,1,3}" - }, - { - "npc_id": "1143", - "loc_data": "{2850,10088,0,1,3}-{2854,10086,0,1,6}" - }, - { - "npc_id": "1144", - "loc_data": "{2856,10089,0,1,4}-{2858,10089,0,1,6}" - }, - { - "npc_id": "1146", - "loc_data": "{2864,10081,0,1,7}" - }, - { - "npc_id": "1147", - "loc_data": "{2853,10076,0,1,5}" - }, - { - "npc_id": "1148", - "loc_data": "{2858,10082,0,1,3}" - }, - { - "npc_id": "1149", - "loc_data": "{2861,10085,0,1,2}" - }, - { - "npc_id": "1150", - "loc_data": "{2857,10075,0,1,1}" - }, - { - "npc_id": "1151", - "loc_data": "{2845,10058,1,0,0}" - }, - { - "npc_id": "1153", - "loc_data": "{3489,9509,2,1,4}-{3499,9515,2,1,6}-{3505,9527,2,1,5}-{3510,9521,2,1,1}-{3490,9526,2,1,3}-{3475,9524,2,1,5}-{3465,9505,2,1,1}-{3479,9501,2,1,3}-{3487,9491,2,1,3}" - }, - { - "npc_id": "1154", - "loc_data": "{3472,9501,2,1,3}-{3495,9494,2,1,4}-{3479,9484,2,1,1}-{3500,9477,2,1,6}" - }, - { - "npc_id": "1155", - "loc_data": "{3468,9486,2,1,0}-{3467,9480,2,1,1}-{3505,9496,2,1,6}-{3513,9496,2,1,7}" - }, - { - "npc_id": "1157", - "loc_data": "{3480,9508,0,1,0}-{3495,9505,0,1,2}-{3492,9490,0,1,1}-{3472,9497,0,1,6}" - }, - { - "npc_id": "1158", - "loc_data": "{3484,9491,0,1,2}" - }, - { - "npc_id": "1161", - "loc_data": "{3487,9526,2,1,5}-{3474,9519,2,1,1}-{3465,9512,2,1,0}-{3489,9501,2,1,4}-{3494,9501,2,1,3}-{3480,9489,2,1,6}-{3483,9477,2,1,3}-{3494,9476,2,1,3}-{3473,9487,0,1,3}-{3476,9496,0,1,6}-{3473,9501,0,1,7}-{3481,9504,0,1,6}-{3488,9514,0,1,6}-{3497,9501,0,1,3}-{3486,9490,0,1,0}" - }, - { - "npc_id": "1162", - "loc_data": "{2781,3088,1,1,0}" - }, - { - "npc_id": "1171", - "loc_data": "{2766,3168,0,1,6}" - }, - { - "npc_id": "1172", - "loc_data": "{2909,3086,0,0,0}" - }, - { - "npc_id": "1174", - "loc_data": "{2792,3015,0,0,0}-{2792,3019,0,0,0}-{2801,3010,0,0,0}-{2801,3010,0,0,4}" - }, - { - "npc_id": "1176", - "loc_data": "{2928,3112,0,0,0}" - }, - { - "npc_id": "1177", - "loc_data": "{2912,3119,0,0,0}" - }, - { - "npc_id": "1178", - "loc_data": "{2768,3165,0,0,0}" - }, - { - "npc_id": "1179", - "loc_data": "{2941,3100,0,1,6}" - }, - { - "npc_id": "1180", - "loc_data": "{2916,3110,0,1,7}" - }, - { - "npc_id": "1181", - "loc_data": "{2902,3101,0,1,1}-{2922,3105,0,1,6}" - }, - { - "npc_id": "1192", - "loc_data": "{2924,3506,0,1,0}-{2927,3498,0,0,0}-{2297,3176,0,1,6}" - }, - { - "npc_id": "1193", - "loc_data": "{2297,3176,0,1,6}" - }, - { - "npc_id": "1194", - "loc_data": "{2298,3177,0,1,4}" - }, - { - "npc_id": "1195", - "loc_data": "{2200,3224,0,1,0}-{2215,3171,0,1,4}-{2293,3156,0,1,3}" - }, - { - "npc_id": "1196", - "loc_data": "{2217,3171,0,1,0}-{2199,3222,0,1,0}-{2201,3218,0,1,0}-{2291,3156,0,1,0}" - }, - { - "npc_id": "1197", - "loc_data": "{2197,3218,0,1,0}-{2292,3153,0,1,0}" - }, - { - "npc_id": "1198", - "loc_data": "{2296,3192,0,1,2}" - }, - { - "npc_id": "1199", - "loc_data": "{2262,3151,0,1,0}-{2194,3255,0,0,6}" - }, - { - "npc_id": "1201", - "loc_data": "{2264,3147,0,1,1}-{2266,3145,0,1,4}-{2198,3250,0,0,6}-{2203,3250,0,0,4}" - }, - { - "npc_id": "1202", - "loc_data": "{2352,3172,0,1,4}" - }, - { - "npc_id": "1203", - "loc_data": "{2189,3155,0,0,1}-{2183,3156,0,1,4}-{2194,3156,0,1,6}-{2182,3148,0,1,4}-{2179,3137,0,1,3}-{2189,3136,0,1,3}-{2182,3146,0,1,3}-{2187,3172,0,0,1}-{2180,3180,0,1,1}" - }, - { - "npc_id": "1205", - "loc_data": "{3054,3254,0,0,0}" - }, - { - "npc_id": "1206", - "loc_data": "{2185,3144,0,0,4}" - }, - { - "npc_id": "1207", - "loc_data": "{3055,3254,0,0,0}" - }, - { - "npc_id": "1208", - "loc_data": "{2194,3140,0,1,5}" - }, - { - "npc_id": "1213", - "loc_data": "{2914,3418,0,0,6}" - }, - { - "npc_id": "1214", - "loc_data": "{2891,3676,0,0,1}" - }, - { - "npc_id": "1215", - "loc_data": "{2612,3287,0,0,0}-{2613,3287,0,0,0}" - }, - { - "npc_id": "1216", - "loc_data": "{2611,3285,0,0,0}" - }, - { - "npc_id": "1217", - "loc_data": "{3000,3386,0,1,0}-{3019,3368,0,1,0}" - }, - { - "npc_id": "1218", - "loc_data": "{3412,3512,0,1,4}-{3412,3515,0,1,1}-{3413,3514,0,1,6}-{3414,3512,0,1,5}-{3415,3518,0,1,5}-{3416,3509,0,1,7}-{3416,3511,0,1,4}-{3417,3518,0,1,4}-{3418,3509,0,1,4}-{3419,3512,0,1,7}-{3420,3517,0,1,7}-{3420,3518,0,1,6}-{3423,3461,0,1,5}-{3426,3465,0,1,4}-{3427,3463,0,1,5}-{3428,3458,0,1,4}-{3428,3465,0,1,2}-{3430,3462,0,1,1}-{3430,3467,0,1,3}-{3433,3458,0,1,1}-{3433,3468,0,1,4}-{3434,3464,0,1,6}" - }, - { - "npc_id": "1219", - "loc_data": "{3537,3496,0,1,0}-{3545,3502,0,1,0}-{3546,3462,0,1,0}-{3548,3480,0,1,0}-{3551,3489,0,1,0}-{3554,3506,0,1,0}-{3560,3462,0,1,0}-{3566,3483,0,1,0}-{3567,3474,0,1,0}-{3572,3492,0,1,0}-{3574,3507,0,1,0}-{3588,3461,0,1,0}-{3589,3493,0,1,0}-{3590,3509,0,1,0}-{3596,3476,0,1,0}-{3599,3483,0,1,0}-{3607,3460,0,1,0}-{3613,3499,0,1,0}-{3614,3515,0,1,0}-{3620,3472,0,1,0}-{3623,3504,0,1,0}-{3626,3475,0,1,0}-{3626,3491,0,1,0}-{3629,3511,0,1,0}-{3633,3463,0,1,0}-{3644,3498,0,1,0}" - }, - { - "npc_id": "1221", - "loc_data": "{3566,3457,0,1,0}-{3567,3503,0,1,0}-{3597,3489,0,1,0}-{3611,3462,0,1,0}-{3620,3500,0,1,0}-{3636,3484,0,1,0}" - }, - { - "npc_id": "1223", - "loc_data": "{3585,3478,0,1,0}-{3586,3480,0,1,0}-{3586,3496,0,1,0}-{3592,3477,0,1,0}-{3593,3458,0,1,0}-{3593,3508,0,1,0}-{3595,3491,0,1,0}-{3597,3479,0,1,0}-{3607,3466,0,1,0}-{3617,3477,0,1,0}-{3618,3514,0,1,0}-{3631,3471,0,1,0}-{3631,3499,0,1,0}-{3632,3459,0,1,0}-{3632,3475,0,1,0}-{3638,3472,0,1,0}-{3646,3467,0,1,0}" - }, - { - "npc_id": "1227", - "loc_data": "{3424,3318,0,1,0}-{3423,3312,0,1,4}-{3507,3435,0,1,4}-{3484,3456,0,1,0}-{3486,3458,0,1,4}-{3511,3455,0,1,0}-{3508,3420,0,1,3}-{3508,3420,0,1,4}-{3506,3402,0,1,7}-{3494,3388,0,1,0}-{3502,3392,0,1,3}" - }, - { - "npc_id": "1228", - "loc_data": "{3469,3346,0,1,0}-{3494,3447,0,1,0}-{3530,3440,0,1,0}-{3556,3453,0,1,0}" - }, - { - "npc_id": "1229", - "loc_data": "{3421,3302,0,1,4}-{3417,3291,0,1,4}-{3423,3288,0,1,4}-{3418,3284,0,1,6}-{3427,3298,0,1,3}-{3431,3303,0,1,1}-{3432,3311,0,1,1}-{3492,3457,0,1,1}-{3502,3455,0,1,2}-{3516,3415,0,1,4}-{3511,3401,0,1,1}" - }, - { - "npc_id": "1230", - "loc_data": "{3419,3280,0,1,4}-{3421,3277,0,1,4}-{3431,3275,0,1,3}-{3428,3314,0,1,1}-{3436,3318,0,1,5}-{3434,3322,0,1,6}-{3496,3447,0,1,7}-{3520,3437,0,1,4}" - }, - { - "npc_id": "1231", - "loc_data": "{3504,3437,0,1,0}-{3509,3437,0,1,0}-{3498,3419,0,1,0}-{3493,3413,0,1,0}-{3524,3397,0,1,0}-{3438,3354,0,1,0}-{3429,3313,0,1,0}-{3429,3314,0,1,0}-{3437,3312,0,1,0}-{3437,3311,0,1,0}-{3451,3308,0,1,0}-{3447,3268,0,1,0}" - }, - { - "npc_id": "1232", - "loc_data": "{3441,3265,0,1,5}-{3438,3277,0,1,7}-{3438,3283,0,1,4}-{3439,3315,0,1,3}-{3439,3310,0,1,1}-{3435,3302,0,1,3}-{3508,3420,0,1,1}-{3514,3415,0,1,7}-{3525,3417,0,1,3}" - }, - { - "npc_id": "1233", - "loc_data": "{3485,3443,0,1,0}-{3516,3441,0,1,0}-{3537,3428,0,1,0}" - }, - { - "npc_id": "1234", - "loc_data": "{3446,3282,0,1,1}-{3446,3298,0,1,3}-{3449,3304,0,1,1}-{3493,3454,0,1,2}-{3493,3454,0,1,0}-{3518,3454,0,1,2}-{3519,3441,0,1,7}-{3506,3417,0,1,5}-{3519,3401,0,1,2}-{3499,3420,0,1,3}-{3520,3412,0,1,0}-{3516,3410,0,1,7}-{3513,3383,0,1,0}-{3491,3388,0,1,3}" - }, - { - "npc_id": "1235", - "loc_data": "{3425,3306,0,1,1}-{3442,3303,0,1,3}-{3437,3299,0,1,1}-{3422,3321,0,1,1}-{3486,3458,0,1,7}-{3503,3420,0,1,3}-{3514,3398,0,1,4}-{3524,3419,0,1,1}-{3509,3386,0,1,2}" - }, - { - "npc_id": "1239", - "loc_data": "{3166,3033,0,1,1}-{3174,3043,0,1,6}-{3177,3041,0,1,4}-{3176,3038,0,1,4}-{3177,3035,0,1,7}-{3183,3035,0,1,6}-{3182,3032,0,1,6}" - }, - { - "npc_id": "1240", - "loc_data": "{3502,3288,0,1,4}-{3505,3284,0,1,4}-{3490,3292,0,1,4}-{3502,3300,0,1,4}-{3492,3303,0,1,4}-{3516,3303,0,1,4}-{3478,3281,0,1,4}-{3473,3273,0,1,4}-{3469,3288,0,1,4}-{3469,3304,0,1,4}" - }, - { - "npc_id": "1251", - "loc_data": "{3474,3309,0,1,5}-{3486,3277,0,1,4}-{3496,3289,0,0,3}" - }, - { - "npc_id": "1253", - "loc_data": "{3488,3297,0,0,6}" - }, - { - "npc_id": "1257", - "loc_data": "{3490,3290,0,0,3}-{3487,3303,0,1,5}-{3496,3296,0,1,4}-{3479,3303,0,1,4}-{3498,3282,0,1,3}-{3473,3281,0,1,3}-{3478,3308,0,1,1}" - }, - { - "npc_id": "1258", - "loc_data": "{3488,3287,0,1,1}-{3501,3285,0,1,5}-{3478,3274,0,1,4}-{3497,3283,0,1,1}" - }, - { - "npc_id": "1261", - "loc_data": "{3492,3300,0,1,4}-{3498,3300,0,1,5}-{3487,3302,0,1,6}-{3497,3299,0,1,5}-{3488,3273,0,1,6}" - }, - { - "npc_id": "1262", - "loc_data": "{3487,3283,0,1,1}-{3477,3300,0,1,4}-{3497,3276,0,1,1}-{3502,3279,0,1,1}-{3481,3296,0,1,2}" - }, - { - "npc_id": "1263", - "loc_data": "{3108,3160,1,1,1}" - }, - { - "npc_id": "1266", - "loc_data": "{2680,3719,0,0,7}-{2684,3722,0,0,1}-{2684,3726,0,0,1}-{2678,3728,0,0,2}-{2673,3728,0,0,0}-{2670,3724,0,0,3}-{2671,3726,0,0,2}-{2679,3731,0,0,2}-{2684,3729,0,0,6}-{2688,3721,0,0,1}-{2693,3715,0,0,5}-{2699,3715,0,0,2}-{2706,3711,0,0,1}-{2712,3718,0,0,5}-{2716,3721,0,0,7}-{2722,3726,0,0,1}-{2524,3764,0,0,7}-{2508,3762,0,0,0}-{2510,3755,0,0,2}-{2523,3756,0,0,1}-{2532,3753,0,0,0}-{2529,3755,0,0,1}-{2528,3739,0,0,6}-{2529,3730,0,0,0}-{2518,3724,0,0,1}-{2508,3723,0,0,1}-{2525,3718,0,0,1}-{2549,3733,0,0,3}" - }, - { - "npc_id": "1268", - "loc_data": "{2681,3716,0,0,2}-{2678,3721,0,0,2}-{2684,3719,0,0,1}-{2699,3719,0,0,4}-{2701,3717,0,0,2}-{2707,3719,0,0,0}-{2700,3726,0,0,2}-{2695,3713,0,0,2}-{2716,3718,0,0,5}-{2706,3713,0,0,2}-{2539,3761,0,0,7}-{2539,3759,0,0,7}-{2528,3767,0,0,3}-{2526,3753,0,0,2}-{2513,3755,0,0,3}-{2505,3753,0,0,3}-{2530,3751,0,0,1}-{2526,3755,0,0,1}-{2532,3758,0,0,6}-{2532,3751,0,0,3}-{2530,3737,0,0,2}-{2531,3728,0,0,0}-{2523,3723,0,0,1}-{2520,3724,0,0,3}-{2515,3718,0,0,3}-{2505,3724,0,0,4}-{2505,3729,0,0,6}-{2535,3718,0,0,4}-{2536,3724,0,0,1}-{2543,3729,0,0,6}-{2550,3734,0,0,1}-{2552,3747,0,0,6}-{2552,3752,0,0,6}" - }, - { - "npc_id": "1269", - "loc_data": "{2672,3683,0,1,3}" - }, - { - "npc_id": "1270", - "loc_data": "{2770,3624,0,0,0}" - }, - { - "npc_id": "1274", - "loc_data": "{2660,3680,0,0,6}" - }, - { - "npc_id": "1275", - "loc_data": "{2657,3680,0,0,6}" - }, - { - "npc_id": "1276", - "loc_data": "{2658,3679,0,0,6}" - }, - { - "npc_id": "1277", - "loc_data": "{2659,3678,0,0,6}" - }, - { - "npc_id": "1278", - "loc_data": "{2659,3664,0,1,3}" - }, - { - "npc_id": "1281", - "loc_data": "{2661,3651,0,1,3}" - }, - { - "npc_id": "1282", - "loc_data": "{2640,3681,0,1,4}" - }, - { - "npc_id": "1283", - "loc_data": "{2647,3660,0,1,5}" - }, - { - "npc_id": "1284", - "loc_data": "{2658,3673,0,1,1}" - }, - { - "npc_id": "1285", - "loc_data": "{2657,3669,0,1,1}" - }, - { - "npc_id": "1286", - "loc_data": "{2656,3676,0,1,1}" - }, - { - "npc_id": "1287", - "loc_data": "{2652,3588,0,1,1}" - }, - { - "npc_id": "1288", - "loc_data": "{2634,3667,0,1,1}" - }, - { - "npc_id": "1289", - "loc_data": "{2667,3692,0,1,1}" - }, - { - "npc_id": "1294", - "loc_data": "{2658,3673,0,1,7}" - }, - { - "npc_id": "1296", - "loc_data": "{2660,3663,0,0,6}-{2657,3663,0,0,6}" - }, - { - "npc_id": "1298", - "loc_data": "{2663,3646,0,0,6}" - }, - { - "npc_id": "1299", - "loc_data": "{2660,3646,0,0,6}" - }, - { - "npc_id": "1300", - "loc_data": "{2662,3674,0,1,1}" - }, - { - "npc_id": "1301", - "loc_data": "{2623,3675,0,1,6}" - }, - { - "npc_id": "1302", - "loc_data": "{2641,3699,0,0,4}" - }, - { - "npc_id": "1303", - "loc_data": "{2664,3692,0,1,3}" - }, - { - "npc_id": "1304", - "loc_data": "{2582,3845,0,0,3}" - }, - { - "npc_id": "1305", - "loc_data": "{2643,3676,0,1,7}" - }, - { - "npc_id": "1306", - "loc_data": "{2674,3677,0,1,6}" - }, - { - "npc_id": "1307", - "loc_data": "{2658,3673,0,1,7}" - }, - { - "npc_id": "1308", - "loc_data": "{2672,3662,0,1,3}" - }, - { - "npc_id": "1309", - "loc_data": "{2667,3653,0,1,3}" - }, - { - "npc_id": "1310", - "loc_data": "{2667,3701,0,1,4}" - }, - { - "npc_id": "1311", - "loc_data": "{2655,3651,0,1,3}" - }, - { - "npc_id": "1312", - "loc_data": "{2639,3651,0,1,6}" - }, - { - "npc_id": "1313", - "loc_data": "{2626,3653,0,1,5}" - }, - { - "npc_id": "1314", - "loc_data": "{2675,3677,0,1,3}" - }, - { - "npc_id": "1315", - "loc_data": "{2646,3675,0,1,3}" - }, - { - "npc_id": "1317", - "loc_data": "{2632,3677,0,1,2}-{2641,3682,0,1,3}-{2643,3675,0,1,6}-{2643,3674,0,1,4}-{2646,3668,0,1,3}-{2651,3669,0,1,3}-{2648,3688,0,1,4}-{2656,3692,0,1,1}" - }, - { - "npc_id": "1318", - "loc_data": "{2671,3658,0,1,2}-{2638,3648,0,1,3}-{2626,3658,0,1,4}-{2626,3668,0,1,1}-{2613,3668,0,1,3}-{2630,3673,0,1,7}-{2635,3680,0,1,4}-{2644,3675,0,1,5}-{2642,3681,0,1,3}-{2631,3678,0,1,6}-{2661,3698,0,1,1}-{2663,3687,0,1,3}-{2673,3695,0,1,3}-{2674,3710,0,1,1}-{2676,3693,0,1,3}-{2675,3685,0,1,4}-{2669,3677,0,1,1}-{2667,3670,0,1,2}-{2675,3670,0,1,4}-{2679,3685,0,1,2}-{2667,3671,0,1,3}-{2666,3663,0,1,6}-{2688,3659,0,1,2}-{2658,3657,0,1,4}-{2684,3649,0,1,4}-{2679,3649,0,1,4}-{2646,3664,0,1,4}-{2646,3655,0,1,1}-{2637,3655,0,1,3}-{2667,3658,0,1,3}" - }, - { - "npc_id": "1320", - "loc_data": "{2738,3636,0,1,7}-{2739,3636,0,1,7}-{2738,3637,0,1,4}-{2735,3637,0,1,3}" - }, - { - "npc_id": "1321", - "loc_data": "{2736,3642,0,1,4}-{2744,3638,0,1,4}" - }, - { - "npc_id": "1329", - "loc_data": "{3085,3640,0,1,0}" - }, - { - "npc_id": "1334", - "loc_data": "{2509,3639,1,1,3}" - }, - { - "npc_id": "1335", - "loc_data": "{2518,4633,0,1,1}" - }, - { - "npc_id": "1336", - "loc_data": "{2508,3635,0,1,3}" - }, - { - "npc_id": "1337", - "loc_data": "{2445,4599,1,1,3}" - }, - { - "npc_id": "1338", - "loc_data": "{3151,5559,0,1,2}-{3157,5548,0,1,3}-{3155,5553,0,1,4}-{3145,5559,0,1,6}-{3152,5556,0,1,3}-{3149,5544,0,1,0}-{3145,5548,0,1,3}" - }, - { - "npc_id": "1339", - "loc_data": "{3153,5547,0,1,0}-{3150,5545,0,1,1}" - }, - { - "npc_id": "1340", - "loc_data": "{3147,5557,0,1,3}-{3154,5559,0,1,4}" - }, - { - "npc_id": "1343", - "loc_data": "{3145,5548,0,1,6}" - }, - { - "npc_id": "1357", - "loc_data": "{2201,4942,0,1,4}" - }, - { - "npc_id": "1358", - "loc_data": "{2208,4959,0,1,6}" - }, - { - "npc_id": "1359", - "loc_data": "{2612,3875,1,1,6}" - }, - { - "npc_id": "1360", - "loc_data": "{2620,3896,0,0,3}" - }, - { - "npc_id": "1369", - "loc_data": "{2527,5876,0,1,0}" - }, - { - "npc_id": "1373", - "loc_data": "{2500,3860,1,1,4}" - }, - { - "npc_id": "1374", - "loc_data": "{2505,3854,1,0,4}-{2509,3849,0,0,4}" - }, - { - "npc_id": "1375", - "loc_data": "{2501,3858,1,0,4}" - }, - { - "npc_id": "1376", - "loc_data": "{2548,3895,0,1,0}" - }, - { - "npc_id": "1377", - "loc_data": "{2544,3843,0,1,6}" - }, - { - "npc_id": "1378", - "loc_data": "{2516,3865,0,1,4}" - }, - { - "npc_id": "1385", - "loc_data": "{2629,3694,0,0,6}" - }, - { - "npc_id": "1389", - "loc_data": "{2603,3877,0,1,1}" - }, - { - "npc_id": "1390", - "loc_data": "{2604,3876,0,1,4}" - }, - { - "npc_id": "1391", - "loc_data": "{2604,3876,0,1,3}" - }, - { - "npc_id": "1393", - "loc_data": "{2519,3868,0,0,3}" - }, - { - "npc_id": "1394", - "loc_data": "{2519,3863,0,0,1}" - }, - { - "npc_id": "1395", - "loc_data": "{2547,3868,0,0,1}" - }, - { - "npc_id": "1396", - "loc_data": "{2525,3892,0,0,4}" - }, - { - "npc_id": "1397", - "loc_data": "{2573,3856,0,0,4}" - }, - { - "npc_id": "1398", - "loc_data": "{2526,3850,0,0,6}" - }, - { - "npc_id": "1399", - "loc_data": "{2577,3854,0,0,1}" - }, - { - "npc_id": "1400", - "loc_data": "{3037,4512,0,1,0}-{3049,4498,0,1,1}" - }, - { - "npc_id": "1401", - "loc_data": "{2605,3878,0,1,1}" - }, - { - "npc_id": "1402", - "loc_data": "{2603,3877,0,1,1}" - }, - { - "npc_id": "1404", - "loc_data": "{3037,4492,0,1,4}-{3036,4502,0,1,4}-{3029,4520,0,1,1}-{3019,4512,0,1,1}-{2239,3164,0,1,2}-{2241,3166,0,1,3}-{2242,3163,0,1,3}" - }, - { - "npc_id": "1405", - "loc_data": "{2576,3843,0,0,6}" - }, - { - "npc_id": "1427", - "loc_data": "{2957,3025,0,0,0}" - }, - { - "npc_id": "1434", - "loc_data": "{2753,2770,0,1,6}" - }, - { - "npc_id": "1447", - "loc_data": "{2761,2785,0,1,3}" - }, - { - "npc_id": "1452", - "loc_data": "{2743,2796,0,1,6}" - }, - { - "npc_id": "1454", - "loc_data": "{2742,2790,0,1,1}" - }, - { - "npc_id": "1457", - "loc_data": "{2747,2782,0,0,1}-{2754,2778,0,1,1}-{2761,2778,0,1,1}-{2765,2779,0,1,4}-{2767,2784,0,1,6}-{2772,2781,0,1,5}-{2774,2787,0,1,1}-{2777,2790,0,1,4}-{2762,2793,0,1,3}-{2756,2789,0,1,3}" - }, - { - "npc_id": "1458", - "loc_data": "{2728,2751,0,1,1}-{2728,2758,0,1,1}-{2727,2762,0,1,6}-{2728,2755,0,1,6}-{2714,2753,0,1,1}-{2714,2757,0,1,6}-{2714,2761,0,1,6}-{2715,2749,0,1,2}" - }, - { - "npc_id": "1459", - "loc_data": "{2790,2791,0,1,4}-{2803,2785,0,1,7}-{2797,2792,0,1,0}-{2800,2779,0,1,5}-{2788,2779,0,1,7}-{2795,2776,0,1,3}" - }, - { - "npc_id": "1460", - "loc_data": "{2787,2794,0,1,3}" - }, - { - "npc_id": "1463", - "loc_data": "{2600,3276,0,1,4}-{2601,3276,0,1,6}-{2601,3277,0,1,6}-{2601,3278,0,1,2}-{2602,3279,0,1,5}-{2602,3282,0,1,4}-{2604,3276,0,1,4}-{2605,3280,0,1,4}-{2605,3281,0,1,7}-{2606,3280,0,1,4}" - }, - { - "npc_id": "1469", - "loc_data": "{2595,3277,0,0,0}" - }, - { - "npc_id": "1471", - "loc_data": "{2707,9102,0,1,0}-{2704,9107,0,1,0}-{2705,9110,0,1,0}-{2708,9107,0,1,0}-{2743,9105,0,1,0}-{2741,9103,0,1,0}-{2744,9101,0,1,0}-{2743,9099,0,1,0}-{2737,9124,0,1,0}-{2741,9123,0,1,0}-{2740,9120,0,1,0}-{2736,9119,0,1,0}-{2734,9117,0,1,0}-{2712,9135,0,1,0}-{2714,9131,0,1,0}-{2709,9131,0,1,0}-{2708,9135,0,1,0}" - }, - { - "npc_id": "1514", - "loc_data": "{3432,3487,0,0,0}" - }, - { - "npc_id": "1515", - "loc_data": "{3438,3487,0,0,0}" - }, - { - "npc_id": "1516", - "loc_data": "{3437,3487,0,0,0}" - }, - { - "npc_id": "1517", - "loc_data": "{3434,3483,0,0,0}" - }, - { - "npc_id": "1518", - "loc_data": "{3435,3483,0,0,0}" - }, - { - "npc_id": "1519", - "loc_data": "{3433,3486,0,0,0}" - }, - { - "npc_id": "1520", - "loc_data": "{3434,3486,0,0,0}" - }, - { - "npc_id": "1526", - "loc_data": "{2443,3089,0,0,0}" - }, - { - "npc_id": "1553", - "loc_data": "{2828,10065,1,0,0}" - }, - { - "npc_id": "1554", - "loc_data": "{2829,10104,1,0,0}" - }, - { - "npc_id": "1555", - "loc_data": "{2829,10096,1,0,0}" - }, - { - "npc_id": "1558", - "loc_data": "{2800,3859,0,1,0}-{2803,3864,0,1,0}-{2805,3859,0,1,0}" - }, - { - "npc_id": "1560", - "loc_data": "{3150,5454,0,1,4}-{3144,5446,0,1,7}-{3166,5446,0,1,3}-{3158,5451,0,1,7}-{2775,10224,0,1,0}-{2783,10199,0,1,0}-{2784,10233,0,1,0}" - }, - { - "npc_id": "1561", - "loc_data": "{3144,5450,0,1,1}-{3153,5454,0,1,5}-{3160,5450,0,1,5}-{3167,5450,0,1,3}-{3161,5451,0,1,1}-{2777,10224,0,1,0}-{2784,10231,0,1,0}-{2792,10222,0,1,0}" - }, - { - "npc_id": "1562", - "loc_data": "{3147,5448,0,1,4}-{2772,10221,0,1,0}-{2785,10201,0,1,0}-{2787,10232,0,1,0}" - }, - { - "npc_id": "1563", - "loc_data": "{3152,5453,0,1,0}-{2773,10220,0,1,0}-{2788,10198,0,1,0}-{2790,10220,0,1,0}-{2800,10218,0,1,0}" - }, - { - "npc_id": "1564", - "loc_data": "{2770,10219,0,1,0}-{2788,10230,0,1,0}-{2798,10216,0,1,0}" - }, - { - "npc_id": "1565", - "loc_data": "{2788,10221,0,1,0}-{2801,10216,0,1,0}" - }, - { - "npc_id": "1566", - "loc_data": "{2786,10199,0,1,0}-{2790,10222,0,1,0}-{2799,10214,0,1,0}" - }, - { - "npc_id": "1582", - "loc_data": "{2657,9500,0,0,1}-{2632,9589,2,0,1}-{2628,9549,2,0,5}-{2629,9543,2,0,2}-{3249,5517,0,1,3}-{2568,9889,0,1,6}-{2578,9898,0,1,1}" - }, - { - "npc_id": "1583", - "loc_data": "{2655,9481,0,0,3}-{2631,9579,2,0,4}-{3211,5519,0,1,0}-{3254,5513,0,1,7}-{2563,9885,0,1,7}-{2575,9897,0,1,1}" - }, - { - "npc_id": "1584", - "loc_data": "{2661,9481,0,0,4}-{2635,9570,2,0,1}-{2627,9539,2,0,5}-{3209,5512,0,1,6}-{3231,5494,0,1,7}" - }, - { - "npc_id": "1585", - "loc_data": "{2666,9482,0,0,5}-{2641,9563,2,0,5}-{3257,5523,0,1,1}-{3204,5516,0,1,3}-{2575,9892,0,1,6}" - }, - { - "npc_id": "1586", - "loc_data": "{2668,9497,0,0,4}-{2635,9565,2,0,4}-{2633,9554,2,0,4}-{3247,5520,0,1,5}-{3205,5513,0,1,6}-{3213,5519,0,1,7}" - }, - { - "npc_id": "1587", - "loc_data": "{2692,3202,0,1,4}-{2695,3216,0,1,0}-{2830,3241,0,1,6}-{2681,9577,0,0,0}-{2673,9591,0,0,5}-{2655,9575,0,0,5}-{2640,9580,0,0,0}-{2646,9553,0,0,1}-{2650,9536,0,0,7}-{2645,9489,0,0,7}-{2670,9543,0,0,0}-{2663,9555,0,0,7}-{3145,3812,0,1,0}-{3141,3826,0,1,0}-{3237,5557,0,1,3}" - }, - { - "npc_id": "1588", - "loc_data": "{2691,3215,0,1,3}-{2826,3245,0,1,3}-{2679,9592,0,0,2}-{2654,9587,0,0,3}-{2650,9564,0,0,6}-{2647,9548,0,0,6}-{2637,9545,0,0,5}-{2637,9532,0,0,5}-{2634,9521,0,0,0}-{2642,9493,0,0,0}-{2645,9484,0,0,7}-{2678,9548,0,0,4}-{2658,9551,0,0,1}-{3246,5558,0,1,4}" - }, - { - "npc_id": "1589", - "loc_data": "{2698,9503,0,0,7}-{2713,9505,0,0,0}-{2717,9502,0,0,7}-{2699,9523,0,0,0}-{2706,9525,0,0,2}-{2719,9517,0,0,5}-{2724,9515,0,0,3}" - }, - { - "npc_id": "1590", - "loc_data": "{2730,9490,0,1,0}-{2731,9482,0,1,7}-{2742,9491,0,1,0}-{3184,5557,0,1,7}-{3148,5514,0,1,6}-{3144,5514,0,1,3}" - }, - { - "npc_id": "1591", - "loc_data": "{2703,9456,0,0,0}-{2717,9459,0,0,7}-{2731,9460,0,0,3}-{2737,9454,0,0,3}-{2737,9444,0,0,3}-{2736,9432,0,0,3}-{2738,9421,0,0,5}-{2727,9418,0,0,1}-{2724,9439,0,0,0}-{2715,9443,0,0,4}-{2702,9443,0,0,3}-{2700,9428,0,0,3}-{2705,9419,0,0,7}" - }, - { - "npc_id": "1592", - "loc_data": "{2728,9426,0,0,4}-{2714,9432,0,0,0}-{2707,9450,0,0,6}-{2724,9451,0,0,6}" - }, - { - "npc_id": "1593", - "loc_data": "{2744,9503,0,0,2}-{2681,9563,0,0,5}-{2677,9586,0,0,2}-{2644,9587,0,0,1}-{2652,9526,0,0,7}-{2656,9514,0,0,7}-{2666,9522,0,0,4}-{2665,9513,0,0,3}-{2666,9489,0,0,1}-{2702,9525,0,0,2}-{2704,9487,0,0,7}-{2711,9485,0,0,2}-{2722,9486,0,0,4}-{2717,9480,0,0,6}-{2733,9494,0,0,1}-{2742,9489,0,0,0}-{2638,9592,2,0,5}-{2633,9583,2,0,7}-{2627,9545,2,0,6}-{2626,9535,2,0,6}-{2628,9507,2,0,5}-{2635,9508,2,0,2}-{2634,9483,2,0,7}" - }, - { - "npc_id": "1594", - "loc_data": "{2672,9569,0,0,1}-{2657,9577,0,0,3}-{2643,9543,0,0,7}-{2646,9530,0,0,7}-{2643,9519,0,0,7}-{2660,9521,0,0,1}-{2665,9527,0,0,2}-{2671,9521,0,0,7}-{2670,9516,0,0,6}-{2660,9497,0,0,7}-{2643,9487,0,0,5}-{2653,9488,0,0,2}-{2677,9501,0,0,2}-{2699,9516,0,0,0}-{2709,9524,0,0,7}-{2683,9476,0,0,7}-{2700,9489,0,0,5}-{2703,9484,0,0,5}-{2710,9481,0,0,2}-{2716,9486,0,0,2}-{2726,9489,0,0,1}-{2735,9481,0,0,4}-{2740,9497,0,0,2}-{2739,9506,0,0,0}-{2658,9546,0,0,6}-{2636,9574,2,0,7}-{2643,9566,2,0,7}-{2635,9559,2,0,7}-{2630,9515,2,0,7}-{2642,9506,2,0,7}-{2629,9487,2,0,5}-{2648,9476,2,0,7}" - }, - { - "npc_id": "1595", - "loc_data": "{2744,3152,0,1,7}" - }, - { - "npc_id": "1597", - "loc_data": "{3147,9914,0,1,6}" - }, - { - "npc_id": "1598", - "loc_data": "{2447,4429,0,1,6}" - }, - { - "npc_id": "1600", - "loc_data": "{2791,9997,0,1,3}-{3190,9574,0,1,6}" - }, - { - "npc_id": "1601", - "loc_data": "{2797,9997,0,1,6}-{2781,9996,0,1,7}" - }, - { - "npc_id": "1602", - "loc_data": "{2793,9995,0,1,3}-{2783,9998,0,1,6}" - }, - { - "npc_id": "1603", - "loc_data": "{2789,9994,0,1,1}-{2786,9998,0,1,5}" - }, - { - "npc_id": "1604", - "loc_data": "{3434,3550,1,1,3}-{3439,3549,1,1,4}-{3425,3549,1,1,5}-{3423,3553,1,1,4}-{3426,3541,1,1,1}-{3425,3541,1,1,2}-{3426,3539,1,1,0}-{3420,3537,1,1,3}-{3417,3536,1,1,6}" - }, - { - "npc_id": "1608", - "loc_data": "{2698,9999,0,1,6}-{2695,9997,0,1,7}-{2697,9996,0,1,5}-{2695,9999,0,1,6}" - }, - { - "npc_id": "1609", - "loc_data": "{2700,9994,0,1,6}-{2698,10001,0,1,4}-{2703,9994,0,1,4}-{2701,10000,0,1,4}" - }, - { - "npc_id": "1610", - "loc_data": "{3443,3537,2,1,4}-{3434,3536,2,1,4}-{3443,3540,2,1,3}-{3446,3548,2,1,4}-{3439,3549,2,1,3}-{3434,3547,2,1,1}-{3444,3537,2,1,3}-{3174,5474,0,1,4}-{3175,5479,0,1,6}-{3183,5473,0,1,7}-{3178,5468,0,1,4}" - }, - { - "npc_id": "1612", - "loc_data": "{3438,3562,0,1,1}-{3430,3564,0,1,3}-{3434,3555,0,1,5}-{3443,3551,0,1,4}-{3442,3545,0,1,1}-{3449,3536,0,1,1}-{3440,3539,0,1,4}" - }, - { - "npc_id": "1613", - "loc_data": "{3439,3565,2,1,6}-{3443,3571,2,1,1}-{3435,3565,2,1,3}-{3443,3564,2,1,2}-{3444,3559,2,1,2}-{3427,3558,2,1,0}-{3425,3557,2,1,4}-{3430,3557,2,1,3}-{3170,5554,0,1,2}-{3166,5556,0,1,5}-{3171,5560,0,1,4}-{3173,5556,0,1,3}" - }, - { - "npc_id": "1615", - "loc_data": "{3422,3566,2,1,5}-{3426,3568,2,1,6}-{3418,3562,2,1,4}-{3414,3566,2,1,3}-{3412,3570,2,1,4}-{3421,3572,2,1,2}-{3422,3571,2,1,1}-{3033,4873,0,1,7}-{3039,4885,0,1,3}-{3050,4890,0,1,3}-{3062,4892,0,1,5}-{3064,4901,0,1,3}-{3056,4912,0,1,3}-{3048,4916,0,1,5}-{3038,4911,0,1,6}-{3022,4914,0,1,6}-{3018,4902,0,1,6}-{3025,4898,0,1,4}-{3033,4885,0,1,0}-{3021,4877,0,1,3}-{3048,4872,0,1,1}-{3061,4880,0,1,3}-{3061,4891,0,1,1}-{3054,4901,0,1,1}" - }, - { - "npc_id": "1616", - "loc_data": "{2741,10011,0,1,2}-{2742,10010,0,1,3}-{2739,10014,0,1,1}-{2740,10006,0,1,3}" - }, - { - "npc_id": "1617", - "loc_data": "{2743,10003,0,1,1}-{2746,10008,0,1,2}-{2743,10015,0,1,3}-{2745,10011,0,1,5}-{2744,10016,0,1,1}" - }, - { - "npc_id": "1618", - "loc_data": "{3424,3565,1,1,1}-{3416,3562,1,1,6}-{3423,3559,1,1,0}-{3424,3560,1,1,4}-{3412,3558,1,1,1}-{3412,3568,1,1,1}-{3417,3565,1,1,3}" - }, - { - "npc_id": "1620", - "loc_data": "{2794,10036,0,1,4}-{2788,10037,0,1,0}-{2795,10034,0,1,3}-{2785,10035,0,1,3}" - }, - { - "npc_id": "1621", - "loc_data": "{2797,10035,0,1,6}-{2787,10032,0,1,3}-{2802,10031,0,1,3}" - }, - { - "npc_id": "1623", - "loc_data": "{2725,9999,0,1,6}" - }, - { - "npc_id": "1624", - "loc_data": "{3179,5522,0,1,6}-{3223,9393,0,1,5}-{3225,9401,0,1,0}-{3222,9375,0,1,6}-{3220,9380,0,1,6}-{3215,9364,0,1,3}-{3213,9361,0,1,1}-{3212,9355,0,1,2}-{3230,9367,0,1,3}-{3240,9350,0,1,5}-{3248,9348,0,1,4}-{3255,9350,0,1,4}-{3256,9392,0,1,4}-{3168,5513,0,1,3}-{3174,5514,0,1,6}-{3178,5517,0,1,4}-{3178,5528,0,1,1}-{3169,5516,0,1,3}-{3179,5524,0,1,4}" - }, - { - "npc_id": "1626", - "loc_data": "{2723,10005,0,1,0}-{2726,9998,0,1,1}-{2723,10009,0,1,3}" - }, - { - "npc_id": "1627", - "loc_data": "{2724,10008,0,1,4}-{2721,10005,0,1,1}-{2724,10005,0,1,3}-{2729,10004,0,1,3}-{2724,10012,0,1,4}-{2718,10009,0,1,1}-{2721,10001,0,1,7}-{2720,9996,0,1,7}-{2721,10006,0,1,3}" - }, - { - "npc_id": "1628", - "loc_data": "{2723,10004,0,1,4}-{2722,9999,0,1,1}-{2729,10002,0,1,1}-{3155,5536,0,1,4}-{3156,5540,0,1,3}-{3157,5538,0,1,2}-{3166,5537,0,1,1}-{3165,5541,0,1,3}" - }, - { - "npc_id": "1629", - "loc_data": "{3162,5538,0,1,2}-{3157,5538,0,1,4}-{3155,5537,0,1,1}-{3164,5538,0,1,2}" - }, - { - "npc_id": "1631", - "loc_data": "{2800,10017,0,1,1}-{2796,10021,0,1,1}-{2805,10021,0,1,1}-{2803,10013,0,1,1}-{3160,9589,0,1,3}-{3167,9589,0,1,6}-{3179,9585,0,1,1}-{3188,9572,0,1,7}-{3190,9580,0,1,3}-{3206,9586,0,1,3}-{3221,9586,0,1,7}-{3226,9573,0,1,5}-{3223,9569,0,1,1}" - }, - { - "npc_id": "1632", - "loc_data": "{2798,10019,0,1,1}-{2800,10015,0,1,1}-{2797,10018,0,1,1}" - }, - { - "npc_id": "1633", - "loc_data": "{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}-{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}-{1858,3251,0,1,0}-{1853,3247,0,1,0}-{1848,3254,0,1,0}-{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}-{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}" - }, - { - "npc_id": "1634", - "loc_data": "{2757,10008,0,1,6}-{2760,10005,0,1,5}" - }, - { - "npc_id": "1635", - "loc_data": "{2758,10001,0,1,1}-{2763,10006,0,1,1}-{2766,10000,0,1,6}-{2759,9999,0,1,7}" - }, - { - "npc_id": "1636", - "loc_data": "{2760,10004,0,1,1}-{2760,10002,0,1,6}" - }, - { - "npc_id": "1637", - "loc_data": "{3276,5454,0,1,4}-{3279,5450,0,1,0}-{3271,5446,0,1,3}-{3277,5512,0,1,1}-{2700,10027,0,1,6}-{3278,5535,0,0,0}-{3277,5520,0,1,1}" - }, - { - "npc_id": "1638", - "loc_data": "{3284,5456,0,1,1}-{3278,5453,0,1,1}-{3271,5512,0,1,4}-{2704,10022,0,1,4}-{2707,10023,0,1,4}" - }, - { - "npc_id": "1639", - "loc_data": "{3283,5461,0,1,4}-{3285,5456,0,1,6}-{3275,5455,0,1,5}-{3272,5446,0,1,4}-{3277,5445,0,1,3}-{2711,10028,0,1,2}-{2710,10030,0,1,6}" - }, - { - "npc_id": "1640", - "loc_data": "{3272,5445,0,1,4}-{2705,10028,0,1,3}-{2704,10031,0,1,6}" - }, - { - "npc_id": "1641", - "loc_data": "{2708,10025,0,1,6}" - }, - { - "npc_id": "1642", - "loc_data": "{2703,10020,0,1,0}" - }, - { - "npc_id": "1643", - "loc_data": "{3246,5446,0,1,3}-{3434,3562,1,1,6}-{3439,3558,1,1,5}-{3444,3567,1,1,4}-{3439,3571,1,1,3}-{3435,3570,1,1,7}-{3447,3561,1,1,4}-{3442,3573,1,1,6}-{3237,5455,0,1,0}-{3246,5450,0,1,4}" - }, - { - "npc_id": "1644", - "loc_data": "{3240,5448,0,1,7}" - }, - { - "npc_id": "1648", - "loc_data": "{3426,3537,0,1,4}-{3413,3536,0,1,3}-{3419,3545,0,1,2}-{3426,3544,0,1,6}-{3410,3535,0,1,2}-{3425,3556,0,1,6}-{3418,3557,0,1,1}-{3428,3571,0,1,3}-{3432,3573,0,1,4}" - }, - { - "npc_id": "1650", - "loc_data": "{3411,3541,0,1,3}" - }, - { - "npc_id": "1655", - "loc_data": "{3424,3544,0,1,1}-{3423,3546,0,1,1}-{3409,3538,0,1,3}-{3421,3559,0,1,4}-{3413,3559,0,1,6}-{3433,3574,0,1,3}-{3428,3571,0,1,6}" - }, - { - "npc_id": "1658", - "loc_data": "{2595,3087,1,1,1}" - }, - { - "npc_id": "1665", - "loc_data": "{3544,3462,0,0,1}" - }, - { - "npc_id": "1668", - "loc_data": "{3551,3550,0,1,0}" - }, - { - "npc_id": "1669", - "loc_data": "{3549,3555,2,0,3}" - }, - { - "npc_id": "1671", - "loc_data": "{3547,3555,2,0,6}" - }, - { - "npc_id": "1672", - "loc_data": "{3552,3550,0,1,0}" - }, - { - "npc_id": "1675", - "loc_data": "{3551,3561,0,1,0}" - }, - { - "npc_id": "1679", - "loc_data": "{2291,3145,0,1,2}" - }, - { - "npc_id": "1680", - "loc_data": "{2290,3143,0,1,3}" - }, - { - "npc_id": "1681", - "loc_data": "{3202,5483,0,1,4}-{2544,9843,0,1,3}" - }, - { - "npc_id": "1683", - "loc_data": "{3678,3511,0,1,4}" - }, - { - "npc_id": "1684", - "loc_data": "{3660,3517,0,1,7}" - }, - { - "npc_id": "1685", - "loc_data": "{3659,3497,0,1,0}" - }, - { - "npc_id": "1686", - "loc_data": "{3657,3516,0,1,3}-{3663,3522,0,1,7}-{3654,3518,1,1,6}" - }, - { - "npc_id": "1688", - "loc_data": "{3709,3497,0,0,3}" - }, - { - "npc_id": "1691", - "loc_data": "{3624,3530,0,1,1}-{3623,3524,0,1,2}-{3618,3526,0,1,6}-{3612,3527,0,1,6}-{3609,3527,0,1,4}" - }, - { - "npc_id": "1692", - "loc_data": "{3632,3528,0,1,5}-{3633,3525,0,1,6}-{3631,3529,0,1,3}-{3631,3529,0,1,2}-{3628,3530,0,1,1}-{3631,3530,0,1,4}-{3629,3529,0,1,4}" - }, - { - "npc_id": "1694", - "loc_data": "{3673,3491,0,1,3}" - }, - { - "npc_id": "1695", - "loc_data": "{3461,3558,0,0,3}" - }, - { - "npc_id": "1697", - "loc_data": "{3662,3502,0,1,4}-{3664,3493,0,1,6}-{3672,3486,0,1,2}-{3685,3485,0,1,3}-{3684,3496,0,1,3}-{3675,3502,0,1,3}-{3667,3484,0,1,0}-{3666,3471,0,1,4}-{3676,3464,0,1,3}-{3683,3467,0,1,6}-{3685,3474,0,1,4}-{3690,3474,0,1,1}-{3695,3473,0,1,3}-{3693,3466,0,1,7}-{3698,3463,0,1,3}-{3688,3467,0,1,3}-{3675,3474,0,1,4}-{3665,3476,0,1,6}-{3662,3491,0,1,1}-{3662,3495,0,1,4}-{3661,3501,0,1,6}-{3682,3486,0,1,4}-{3678,3493,0,1,4}-{3673,3491,0,1,3}-{3658,3502,0,1,4}-{3660,3505,0,1,1}-{3665,3469,0,1,6}-{3659,3465,0,1,6}-{3667,3461,0,1,1}" - }, - { - "npc_id": "1698", - "loc_data": "{3665,3529,0,1,4}-{3669,3522,0,1,4}-{3672,3529,0,1,4}-{3668,3534,0,1,4}-{3657,3534,0,1,4}-{3654,3537,0,1,4}-{3649,3503,0,1,2}-{3647,3473,0,1,3}" - }, - { - "npc_id": "1699", - "loc_data": "{3659,3480,0,1,3}" - }, - { - "npc_id": "1700", - "loc_data": "{3681,3493,0,1,5}" - }, - { - "npc_id": "1702", - "loc_data": "{3690,3464,0,0,1}-{3691,3464,0,0,1}-{3688,3464,0,0,1}-{3687,3464,0,0,1}" - }, - { - "npc_id": "1703", - "loc_data": "{3697,3496,0,1,3}-{3699,3497,0,1,2}-{3689,3503,0,1,3}-{3690,3508,0,1,6}-{3690,3512,0,1,6}-{3689,3517,0,1,1}-{3690,3524,0,1,6}-{3689,3528,0,1,5}-{3690,3518,0,1,5}-{3689,3503,0,1,3}-{3689,3496,0,1,0}" - }, - { - "npc_id": "1704", - "loc_data": "{3703,3487,0,0,3}" - }, - { - "npc_id": "1706", - "loc_data": "{3661,3508,0,0,1}-{3658,3508,0,0,1}-{3652,3487,0,0,3}-{3652,3484,0,0,3}" - }, - { - "npc_id": "1710", - "loc_data": "{3165,9631,0,1,6}-{3165,9633,0,1,0}-{3160,9634,0,1,7}-{3156,9630,0,1,6}-{3152,9627,0,1,4}-{3148,9621,0,1,4}-{3175,9630,0,1,1}-{3174,9634,0,1,3}-{3172,9638,0,1,0}" - }, - { - "npc_id": "1711", - "loc_data": "{3160,9638,0,1,1}-{3159,9633,0,1,6}-{3161,9626,0,1,6}-{3169,9626,0,1,4}-{3171,9634,0,1,4}-{3164,9634,0,1,3}-{3165,9631,0,1,7}-{3180,9631,0,1,7}-{3183,9629,0,1,6}-{3182,9633,0,1,1}-{3172,9632,0,1,4}-{3158,9631,0,1,1}-{3160,9630,0,1,1}-{3159,9633,0,1,4}-{3150,9623,0,1,6}-{3152,9620,0,1,6}-{3159,9622,0,1,0}-{3169,9623,0,1,6}-{3169,9623,0,1,4}-{2566,5197,0,1,4}" - }, - { - "npc_id": "1712", - "loc_data": "{3153,9646,0,1,3}-{3151,9651,0,1,1}-{2573,5201,0,1,4}-{2577,5195,0,1,4}-{2577,5189,0,0,6}-{2571,5195,0,0,6}" - }, - { - "npc_id": "1714", - "loc_data": "{3172,9624,0,1,2}-{3161,9623,0,1,1}-{3151,9624,0,1,1}-{3150,9622,0,1,4}-{3153,9627,0,1,3}-{3157,9634,0,1,2}-{3171,9636,0,1,6}-{3174,9639,0,1,3}-{3174,9634,0,1,3}" - }, - { - "npc_id": "1715", - "loc_data": "{3171,9635,0,1,3}-{3159,9636,0,1,2}-{3169,9630,0,1,3}-{3172,9625,0,1,0}-{3159,9627,0,1,3}" - }, - { - "npc_id": "1752", - "loc_data": "{2510,3228,0,1,3}" - }, - { - "npc_id": "1754", - "loc_data": "{3259,3308,0,1,4}-{3100,3246,0,1,2}-{3131,3263,0,1,5}-{3122,3278,0,1,2}" - }, - { - "npc_id": "1755", - "loc_data": "{3258,3312,0,1,3}-{3106,3275,0,1,1}-{3106,3266,0,1,3}-{3114,3273,0,1,4}" - }, - { - "npc_id": "1756", - "loc_data": "{3262,3320,0,1,1}-{3096,3251,0,1,3}-{3117,3278,0,1,0}-{3102,3261,0,1,1}" - }, - { - "npc_id": "1757", - "loc_data": "{3233,3308,0,1,4}" - }, - { - "npc_id": "1758", - "loc_data": "{2895,3403,0,0,0}-{3262,3325,0,0,0}" - }, - { - "npc_id": "1759", - "loc_data": "{3250,3310,0,0,0}" - }, - { - "npc_id": "1761", - "loc_data": "{3121,3274,0,0,0}" - }, - { - "npc_id": "1765", - "loc_data": "{3206,3265,0,1,4}-{2923,3320,0,1,7}-{2921,3320,0,1,4}-{2923,3320,0,1,5}-{3201,3265,0,1,0}-{3198,3270,0,1,0}" - }, - { - "npc_id": "1766", - "loc_data": "{3259,3262,0,1,4}-{3262,3274,0,1,3}-{3259,3278,0,1,1}-{3263,3273,0,1,7}-{3171,3320,0,1,0}" - }, - { - "npc_id": "1767", - "loc_data": "{3262,3262,0,1,6}-{3025,3311,0,1,5}-{2924,3288,0,0,1}-{2921,3290,0,1,1}-{2926,3279,0,1,1}-{2931,3272,0,1,1}-{2936,3273,0,1,6}" - }, - { - "npc_id": "1769", - "loc_data": "{2979,3203,0,1,4}-{3264,3249,0,1,4}-{2596,9822,0,1,4}" - }, - { - "npc_id": "1770", - "loc_data": "{2999,3212,0,1,1}-{3262,3253,0,1,6}-{3259,3254,0,1,3}-{2595,9826,0,1,1}-{3189,3246,0,1,0}-{3143,3231,0,1,0}-{3140,3263,0,1,0}" - }, - { - "npc_id": "1771", - "loc_data": "{2993,3199,0,1,6}-{3248,3233,0,1,6}-{3247,3240,0,1,3}-{3187, 3286, 0, 1, 0}-{3209, 3277, 0, 1, 0}-{3174, 3294, 0, 1, 0}-{3178, 3294, 0, 1, 0}-{3144, 3302, 0, 1, 0}-{3142, 3303, 0, 1, 0}-{3151, 3300, 0, 1, 0}-{3139, 3299, 0, 1, 0}-{3192,3245,0,1,0}-{3184,3248,0,1,0}-{3140,3230,0,1,0}-{3141,3261,0,1,0}" - }, - { - "npc_id": "1772", - "loc_data": "{2993,3203,0,1,0}-{3251,3231,0,1,1}-{3260,3241,0,1,0}-{3259,3222,0,0,4}-{3194,3255,0,1,0}-{3143,3234,0,1,0}" - }, - { - "npc_id": "1773", - "loc_data": "{2991,3219,0,1,1}-{3252,3227,0,1,1}-{3263,3219,0,1,0}" - }, - { - "npc_id": "1774", - "loc_data": "{3012,3204,0,1,3}-{3255,3228,0,1,4}-{3265,3219,0,1,2}-{2592,9834,0,1,6}" - }, - { - "npc_id": "1775", - "loc_data": "{3010,3211,0,1,6}-{3254,3224,0,1,1}-{2585,9829,0,1,1}" - }, - { - "npc_id": "1776", - "loc_data": "{2999,3216,0,1,6}-{3264,3247,0,1,2}" - }, - { - "npc_id": "1778", - "loc_data": "{3260,3859,0,0,0}" - }, - { - "npc_id": "1779", - "loc_data": "{2978,3752,0,1,6}" - }, - { - "npc_id": "1780", - "loc_data": "{3272,3687,0,1,3}" - }, - { - "npc_id": "1781", - "loc_data": "{3008,3862,0,0,0}" - }, - { - "npc_id": "1782", - "loc_data": "{3363,3883,0,1,4}" - }, - { - "npc_id": "1783", - "loc_data": "{3079,3516,0,1,6}" - }, - { - "npc_id": "1784", - "loc_data": "{3025,3705,0,1,1}" - }, - { - "npc_id": "1785", - "loc_data": "{3144,3788,0,1,6}" - }, - { - "npc_id": "1786", - "loc_data": "{3234,3633,0,1,1}" - }, - { - "npc_id": "1787", - "loc_data": "{3074,3609,0,1,3}" - }, - { - "npc_id": "1795", - "loc_data": "{2967,9811,0,0,0}" - }, - { - "npc_id": "1796", - "loc_data": "{2968,9809,0,0,0}" - }, - { - "npc_id": "1798", - "loc_data": "{2701,3471,0,0,0}" - }, - { - "npc_id": "1805", - "loc_data": "{2760,3660,0,1,6}" - }, - { - "npc_id": "1806", - "loc_data": "{2798,3667,0,1,3}" - }, - { - "npc_id": "1807", - "loc_data": "{2811,3673,0,1,3}" - }, - { - "npc_id": "1808", - "loc_data": "{2765,3676,0,1,3}" - }, - { - "npc_id": "1810", - "loc_data": "{2811,3681,0,1,3}" - }, - { - "npc_id": "1814", - "loc_data": "{2794,3668,0,1,0}" - }, - { - "npc_id": "1815", - "loc_data": "{2801,3669,0,1,0}" - }, - { - "npc_id": "1816", - "loc_data": "{2803,3668,0,1,0}" - }, - { - "npc_id": "1817", - "loc_data": "{2797,3672,0,1,0}" - }, - { - "npc_id": "1818", - "loc_data": "{2802,3674,0,1,0}" - }, - { - "npc_id": "1819", - "loc_data": "{2813,3687,0,1,0}" - }, - { - "npc_id": "1820", - "loc_data": "{2812,3685,0,1,0}" - }, - { - "npc_id": "1821", - "loc_data": "{2757,3674,0,1,0}-{2762,3671,0,1,0}-{2785,3658,0,1,0}" - }, - { - "npc_id": "1828", - "loc_data": "{3231,9545,0,1,6}" - }, - { - "npc_id": "1829", - "loc_data": "{3153,9548,0,1,1}-{3154,9547,0,1,4}-{3176,9546,0,1,4}-{3221,9558,0,1,4}-{3226,9548,0,1,7}-{3224,9551,0,1,1}-{3219,9558,0,1,7}" - }, - { - "npc_id": "1831", - "loc_data": "{3208,9555,0,1,1}-{3165,9542,0,1,3}-{3211,9570,0,1,5}-{3211,9570,0,1,5}-{3208,9555,0,1,1}-{3208,9555,0,1,1}-{3164,9542,0,1,3}-{3207,9555,0,1,1}-{3166,9542,0,1,3}-{3213,9570,0,1,5}" - }, - { - "npc_id": "1832", - "loc_data": "{3150,9573,0,1,1}-{3188,9550,0,1,0}-{3148,9578,0,1,4}-{3152,9556,0,1,3}-{3150,9553,0,1,2}-{3167,9547,0,1,1}-{3168,9546,0,1,3}-{3184,9551,0,1,3}-{3220,9573,0,1,4}-{3224,9574,0,1,1}-{3208,9588,0,1,6}-{3210,9585,0,1,4}-{3191,9583,0,1,4}-{3187,9583,0,1,3}-{3272,5549,0,1,0}-{3275,5546,0,1,0}-{3281,5545,0,1,2}-{3286,5544,0,1,0}-{3285,5539,0,1,3}-{3268,5553,0,1,1}-{3274,5558,0,1,6}-{3281,5557,0,1,4}-{3282,5554,0,1,4}-{3269,5551,0,1,3}-{3287,5549,0,1,3}-{3279,5546,0,1,6}-{3283,5545,0,1,0}-{3269,5548,0,1,1}" - }, - { - "npc_id": "1833", - "loc_data": "{3151,9575,0,1,3}-{3150,9577,0,1,4}-{3148,9554,0,1,6}-{3165,9547,0,1,1}-{3165,9546,0,1,2}-{3184,9551,0,1,3}-{3222,9575,0,1,4}-{3226,9576,0,1,3}-{3210,9587,0,1,2}-{3189,9581,0,1,4}" - }, - { - "npc_id": "1834", - "loc_data": "{3169,3174,0,0,6}" - }, - { - "npc_id": "1841", - "loc_data": "{3020,3452,0,0,0}" - }, - { - "npc_id": "1843", - "loc_data": "{2839,10128,0,1,0}" - }, - { - "npc_id": "1844", - "loc_data": "{2855,10144,0,1,0}" - }, - { - "npc_id": "1846", - "loc_data": "{2873,10166,0,1,3}" - }, - { - "npc_id": "1860", - "loc_data": "{2957,3203,0,1,3}" - }, - { - "npc_id": "1861", - "loc_data": "{3208,3256,0,0,1}" - }, - { - "npc_id": "1862", - "loc_data": "{3304,3211,0,1,1}" - }, - { - "npc_id": "1863", - "loc_data": "{3358,2956,0,0,6}" - }, - { - "npc_id": "1864", - "loc_data": "{3363,2956,0,0,3}" - }, - { - "npc_id": "1865", - "loc_data": "{3352,2974,0,1,4}" - }, - { - "npc_id": "1866", - "loc_data": "{3360,2983,0,1,4}" - }, - { - "npc_id": "1867", - "loc_data": "{3347,2966,0,0,6}" - }, - { - "npc_id": "1870", - "loc_data": "{3373,2972,0,1,5}" - }, - { - "npc_id": "1871", - "loc_data": "{3344,2986,0,1,3}" - }, - { - "npc_id": "1872", - "loc_data": "{3354,2953,0,0,6}" - }, - { - "npc_id": "1873", - "loc_data": "{3343,2962,0,1,0}-{3343,2964,0,1,1}" - }, - { - "npc_id": "1875", - "loc_data": "{3354,2952,0,0,1}" - }, - { - "npc_id": "1884", - "loc_data": "{2811,3174,0,0,0}" - }, - { - "npc_id": "1902", - "loc_data": "{3333,2946,0,0,1}" - }, - { - "npc_id": "1905", - "loc_data": "{3335,2948,0,1,2}-{3331,2948,0,1,6}-{3333,2950,0,1,0}" - }, - { - "npc_id": "1912", - "loc_data": "{3376,3429,0,1,6}" - }, - { - "npc_id": "1917", - "loc_data": "{3177,2987,0,1,6}" - }, - { - "npc_id": "1918", - "loc_data": "{3180,3043,0,1,2}" - }, - { - "npc_id": "1920", - "loc_data": "{3497,3478,0,0,0}" - }, - { - "npc_id": "1921", - "loc_data": "{3159,2980,0,1,1}" - }, - { - "npc_id": "1923", - "loc_data": "{3183,2974,0,0,7}" - }, - { - "npc_id": "1926", - "loc_data": "{3179,2967,0,1,3}-{3172,2966,0,1,1}-{3171,2980,0,1,3}-{3166,2972,0,1,0}-{3181,2979,0,1,3}-{3187,2976,0,1,7}-{3174,2984,0,1,3}-{3183,2982,0,1,4}-{3187,2985,0,1,1}-{3182,2988,0,1,5}-{3188,2990,0,1,2}-{3172,2992,0,1,4}-{3171,2987,0,1,4}-{3161,2982,0,1,2}-{3161,2986,0,1,3}-{3159,2987,0,1,3}-{3163,2976,0,1,4}-{3161,2977,0,1,4}-{3161,2979,0,1,6}-{3162,2982,0,1,6}-{3164,2985,0,1,2}-{3158,2970,0,1,3}-{3155,2979,0,1,4}-{3167,2972,0,1,6}-{3172,2973,0,1,5}-{3175,2973,0,1,4}-{3185,2967,0,1,2}" - }, - { - "npc_id": "1927", - "loc_data": "{3160,2979,0,0,5}-{3162,2989,0,0,1}" - }, - { - "npc_id": "1928", - "loc_data": "{3161,2977,0,0,4}-{3161,2987,0,0,1}" - }, - { - "npc_id": "1929", - "loc_data": "{3158,2987,0,0,3}" - }, - { - "npc_id": "1930", - "loc_data": "{3161,2985,0,0,6}-{3161,2983,0,0,6}-{3161,2982,0,0,6}-{3161,2981,0,0,6}-{3161,2984,0,0,6}" - }, - { - "npc_id": "1935", - "loc_data": "{2841,3738,0,0,3}" - }, - { - "npc_id": "1936", - "loc_data": "{2852,3735,0,1,0}-{2855,3729,0,1,0}-{2861,3725,0,1,0}" - }, - { - "npc_id": "1937", - "loc_data": "{2850,3732,0,1,0}-{2857,3726,0,1,0}-{2865,3729,0,1,0}" - }, - { - "npc_id": "1938", - "loc_data": "{2849,3735,0,1,0}-{2853,3731,0,1,0}-{2859,3728,0,1,0}" - }, - { - "npc_id": "1939", - "loc_data": "{2856,3728,0,1,0}-{2862,3722,0,1,0}-{2864,3723,0,1,0}" - }, - { - "npc_id": "1940", - "loc_data": "{2853,3728,0,1,0}-{2856,3732,0,1,0}-{2863,3720,0,1,0}" - }, - { - "npc_id": "1941", - "loc_data": "{2854,3725,0,1,0}-{2862,3728,0,1,0}-{2862,3731,0,1,0}" - }, - { - "npc_id": "1942", - "loc_data": "{2855,3735,0,1,0}-{2858,3730,0,1,0}-{2865,3726,0,1,0}" - }, - { - "npc_id": "1951", - "loc_data": "{2886,3723,0,1,3}-{2887,3720,0,1,4}-{2890,3721,0,1,7}-{2890,3724,0,1,1}-{2909,3736,0,1,5}-{2910,3736,0,1,2}-{2891,3724,0,1,7}" - }, - { - "npc_id": "1952", - "loc_data": "{2892,3726,0,1,0}-{2894,3726,0,1,6}-{2895,3728,0,1,0}-{2895,3730,0,1,5}-{2922,3755,0,1,3}-{2893,3725,0,1,2}-{2894,3727,0,1,6}-{2895,3725,0,1,3}-{2898,3735,0,1,6}" - }, - { - "npc_id": "1953", - "loc_data": "{2897,3733,0,1,5}-{2897,3737,0,1,5}-{2913,3736,0,1,3}-{2897,3737,0,1,6}-{2915,3740,0,1,1}-{2894,3742,0,1,0}" - }, - { - "npc_id": "1954", - "loc_data": "{2895,3751,0,1,1}-{2895,3755,0,1,6}-{2898,3751,0,1,3}-{2898,3755,0,1,6}-{2930,3765,0,1,5}-{2893,3748,0,1,5}-{2896,3749,0,1,1}-{2929,3762,0,1,3}" - }, - { - "npc_id": "1955", - "loc_data": "{2895,3740,0,1,1}-{2895,3744,0,1,3}-{2898,3740,0,1,6}-{2898,3744,0,1,1}-{2908,3739,0,1,0}-{2928,3763,0,1,3}-{2899,3737,0,1,2}-{2897,3739,0,1,6}-{2912,3742,0,1,2}-{2898,3750,0,1,3}-{2895,3751,0,1,4}" - }, - { - "npc_id": "1956", - "loc_data": "{2886,3758,0,1,6}-{2886,3762,0,1,3}-{2890,3756,0,1,4}-{2890,3762,0,1,1}-{2928,3753,0,1,2}-{2926,3753,0,1,6}-{2895,3754,0,1,3}" - }, - { - "npc_id": "1958", - "loc_data": "{3240,9326,0,1,3}-{3209,9310,0,1,4}-{3245,9333,0,1,4}-{3213,9307,0,1,4}-{3221,9318,0,1,3}-{3229,9333,0,1,3}-{3216,9329,0,1,4}-{3208,9294,0,1,3}-{3201,9286,0,1,1}-{3212,9326,0,1,4}-{3217,9284,0,1,7}-{3226,9284,0,1,4}-{3232,9299,0,1,6}-{3242,9295,0,1,1}-{3220,9302,0,1,0}-{3238,9305,0,1,6}-{3220,9296,0,1,1}-{3246,9291,0,1,3}-{3240,9289,0,1,4}-{3237,9305,0,1,3}-{3257,9292,0,0,5}-{3246,9321,0,1,1}-{3261,9310,0,1,3}-{3258,9309,0,1,4}-{3251,9316,0,1,3}-{3247,9310,0,1,3}-{3147,5467,0,1,1}-{3144,5466,0,1,2}-{3147,5479,0,1,2}-{3144,5475,0,1,6}-{3161,5482,0,1,5}-{3147,5475,0,1,6}" - }, - { - "npc_id": "1961", - "loc_data": "{3250,9326,0,1,4}-{3258,9327,0,1,6}-{3256,9333,0,1,1}-{3220,9309,0,1,4}-{3252,9333,0,1,3}-{3208,9318,0,1,3}-{3241,9331,0,1,6}-{3204,9328,0,1,3}-{3209,9328,0,1,3}-{3228,9323,0,1,4}-{3210,9282,0,1,7}-{3225,9329,0,1,6}-{3207,9299,0,1,4}-{3215,9295,0,1,3}-{3213,9322,0,1,3}-{3214,9315,0,1,0}-{3221,9291,0,1,0}-{3213,9303,0,1,4}-{3227,9304,0,1,6}-{3247,9301,0,1,1}-{3228,9284,0,1,3}-{3254,9286,0,1,4}-{3262,9291,0,1,6}-{3224,9302,0,1,3}-{3250,9298,0,1,3}-{3252,9293,0,1,6}-{3262,9299,0,1,1}-{3256,9297,0,1,7}-{3262,9285,0,1,1}-{3254,9322,0,1,0}-{3261,9318,0,1,4}-{3242,9316,0,1,6}-{3250,9306,0,1,3}-{3250,9327,0,1,3}-{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}" - }, - { - "npc_id": "1962", - "loc_data": "{3168,5458,0,1,2}-{3242,9331,0,1,3}-{3238,9333,0,1,6}-{3261,9330,0,1,1}-{3232,9333,0,1,0}-{3222,9324,0,1,4}-{3205,9303,0,1,3}" - }, - { - "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}" - }, - { - "npc_id": "1972", - "loc_data": "{2536, 3426, 0, 1, 0}" - }, - { - "npc_id": "1976", - "loc_data": "{3315,5508,0,1,5}-{3308,5510,0,1,3}-{3304,5507,0,1,4}-{3299,5509,0,1,3}-{3317,5514,0,1,3}-{3319,5518,0,1,4}-{3320,5507,0,1,4}-{3324,5513,0,1,1}-{3283,5510,0,1,5}-{3320,5550,0,1,0}-{3313,5523,0,1,4}-{3310,5541,0,1,4}-{3308,5535,0,1,5}-{3301,5531,0,1,4}-{3315,5530,0,1,1}-{3322,5536,0,1,4}-{3320,5539,0,1,7}-{3311,5519,0,1,3}-{3289,5525,0,1,2}-{3300,5519,0,1,6}-{3289,5515,0,1,7}-{3287,5523,0,1,3}" - }, - { - "npc_id": "1990", - "loc_data": "{3300,2797,0,0,6}" - }, - { - "npc_id": "1993", - "loc_data": "{3360,2935,0,1,4}-{3359,2924,0,1,6}-{3352,2936,0,1,0}-{3349,2931,0,1,6}-{3341,2924,0,1,4}" - }, - { - "npc_id": "1994", - "loc_data": "{3344,2933,0,1,3}-{3350,2936,0,1,4}" - }, - { - "npc_id": "2014", - "loc_data": "{3293,2788,0,1,0}" - }, - { - "npc_id": "2020", - "loc_data": "{3503,3477,0,0,0}" - }, - { - "npc_id": "2021", - "loc_data": "{3222,9525,2,1,3}-{3216,9522,2,1,3}-{3219,9518,2,1,3}-{3224,9521,2,1,3}-{3218,9523,2,1,3}-{3215,9521,2,1,3}-{3213,9522,2,1,3}-{3227,9517,2,1,3}" - }, - { - "npc_id": "2031", - "loc_data": "{3290,5448,0,1,6}-{3291,5452,0,1,1}-{3293,5456,0,1,6}-{3551,9680,0,0,2}-{3553,9679,0,0,1}-{3551,9675,0,0,6}-{3554,9676,0,0,7}-{3548,9676,0,0,5}-{3552,9691,0,0,2}-{3569,9681,0,0,7}-{3577,9711,0,0,5}-{3567,9675,0,0,5}-{3567,9675,0,0,5}-{3570,9678,0,0,2}-{3568,9686,0,0,1}-{3545,9721,0,0,7}-{3560,9712,0,0,6}-{3552,9685,0,0,1}-{3525,9698,0,0,6}-{3538,9668,0,0,4}" - }, - { - "npc_id": "2032", - "loc_data": "{3549,9678,0,0,0}-{3569,9676,0,0,5}-{3566,9678,0,0,1}-{3560,9678,0,0,2}-{3526,9678,0,0,2}-{3532,9693,0,0,5}-{3534,9690,0,0,7}-{3536,9695,0,0,2}-{3542,9694,0,0,7}-{3548,9695,0,0,7}-{3556,9695,0,0,5}-{3554,9709,0,0,2}-{3533,9708,0,0,5}-{3534,9703,0,0,5}-{3538,9721,0,0,7}-{3567,9720,0,0,6}-{3561,9711,0,0,7}-{3552,9703,0,0,1}-{3551,9698,0,0,1}-{3532,9678,0,0,5}-{3531,9675,0,0,5}-{3537,9677,0,0,2}-{3187,5513,0,1,3}-{3186,5510,0,1,6}-{3186,5516,0,1,6}-{3194,5511,0,1,1}-{3185,5514,0,1,5}" - }, - { - "npc_id": "2033", - "loc_data": "{3300,5494,0,1,2}-{3285,5492,0,1,6}-{3285,5495,0,1,0}-{3288,5497,0,1,3}-{3268,5487,0,0,0}-{3533,9691,0,0,4}-{3537,9694,0,0,2}-{3533,9696,0,0,2}-{3533,9693,0,0,5}-{3188,5511,0,1,7}-{3190,5511,0,1,6}" - }, - { - "npc_id": "2034", - "loc_data": "{3288,5484,0,1,4}-{3288,5467,0,1,0}-{3293,5468,0,1,2}-{3296,5475,0,1,7}-{3296,5473,0,1,2}-{3297,5480,0,1,4}-{3565,9695,0,0,1}-{3566,9692,0,0,5}-{3572,9695,0,0,7}" - }, - { - "npc_id": "2035", - "loc_data": "{3569,9696,0,0,2}-{3569,9693,0,0,6}-{3566,9694,0,0,6}-{3567,9698,0,0,2}-{3572,9692,0,0,5}" - }, - { - "npc_id": "2036", - "loc_data": "{3569,9674,0,0,0}-{3561,9677,0,0,6}-{3535,9686,0,0,6}-{3535,9698,0,0,1}-{3576,9677,0,0,3}-{3577,9679,0,0,1}-{3568,9691,0,0,2}-{3560,9694,0,0,6}-{3570,9682,0,0,1}-{3569,9687,0,0,2}-{3550,9711,0,0,5}-{3551,9708,0,0,6}-{3548,9713,0,0,6}-{3537,9711,0,0,4}-{3535,9702,0,0,7}-{3553,9722,0,0,4}-{3567,9712,0,0,5}-{3569,9704,0,0,6}-{3579,9693,0,0,7}-{3534,9675,0,0,2}-{3526,9711,0,0,1}-{3566,9668,0,0,4}" - }, - { - "npc_id": "2037", - "loc_data": "{3543,9677,0,0,3}-{3572,9680,0,0,7}-{3559,9677,0,0,4}-{3535,9685,0,0,1}-{3537,9693,0,0,1}-{3570,9697,0,0,2}-{3552,9713,0,0,6}-{3553,9711,0,0,3}-{3567,9708,0,0,6}-{3535,9679,0,0,1}-{3525,9683,0,0,6}" - }, - { - "npc_id": "2038", - "loc_data": "{2443,3051,0,0,1}" - }, - { - "npc_id": "2039", - "loc_data": "{2442,3049,0,0,3}" - }, - { - "npc_id": "2040", - "loc_data": "{2444,3047,0,0,0}" - }, - { - "npc_id": "2041", - "loc_data": "{2447,3050,0,0,0}" - }, - { - "npc_id": "2042", - "loc_data": "{2454,3047,0,0,2}" - }, - { - "npc_id": "2043", - "loc_data": "{2443,3038,0,1,1}-{2452,3030,0,1,1}" - }, - { - "npc_id": "2044", - "loc_data": "{2486,3048,0,1,3}" - }, - { - "npc_id": "2045", - "loc_data": "{2482,3046,0,1,3}" - }, - { - "npc_id": "2047", - "loc_data": "{2480,3046,0,1,3}" - }, - { - "npc_id": "2050", - "loc_data": "{2461,3047,0,1,3}-{2466,3052,0,1,6}" - }, - { - "npc_id": "2051", - "loc_data": "{2464,3049,0,1,2}" - }, - { - "npc_id": "2052", - "loc_data": "{2464,3045,0,1,2}" - }, - { - "npc_id": "2053", - "loc_data": "{2467,3049,0,1,2}" - }, - { - "npc_id": "2054", - "loc_data": "{2468,3046,0,1,2}" - }, - { - "npc_id": "2055", - "loc_data": "{2470,3055,0,0,5}" - }, - { - "npc_id": "2056", - "loc_data": "{2470,3040,0,0,0}" - }, - { - "npc_id": "2059", - "loc_data": "{2588,3088,0,1,3}" - }, - { - "npc_id": "2067", - "loc_data": "{3154,9544,0,0,0}-{3245,9570,0,0,0}" - }, - { - "npc_id": "2082", - "loc_data": "{3210,3220,1,1,6}" - }, - { - "npc_id": "2130", - "loc_data": "{2889,10207,0,1,0}" - }, - { - "npc_id": "2131", - "loc_data": "{2822,10212,0,1,0}-{2891,10192,0,1,0}" - }, - { - "npc_id": "2132", - "loc_data": "{2828,10210,0,1,0}-{2891,10199,0,1,0}" - }, - { - "npc_id": "2133", - "loc_data": "{2847,10190,0,1,0}" - }, - { - "npc_id": "2134", - "loc_data": "{2823,10220,0,1,0}" - }, - { - "npc_id": "2135", - "loc_data": "{2825,10209,0,1,0}" - }, - { - "npc_id": "2136", - "loc_data": "{2827,10212,1,1,0}" - }, - { - "npc_id": "2138", - "loc_data": "{2836,10192,1,1,0}" - }, - { - "npc_id": "2139", - "loc_data": "{2835,10193,1,0,1}" - }, - { - "npc_id": "2154", - "loc_data": "{2869,10190,0,1,0}" - }, - { - "npc_id": "2156", - "loc_data": "{2892,10212,0,1,0}" - }, - { - "npc_id": "2157", - "loc_data": "{2887,10212,0,1,0}" - }, - { - "npc_id": "2158", - "loc_data": "{2885,10207,0,1,0}" - }, - { - "npc_id": "2159", - "loc_data": "{2885,10196,0,1,0}" - }, - { - "npc_id": "2160", - "loc_data": "{2925,10210,0,1,0}" - }, - { - "npc_id": "2171", - "loc_data": "{2931,10184,0,1,0}" - }, - { - "npc_id": "2172", - "loc_data": "{2929,10191,0,1,0}" - }, - { - "npc_id": "2173", - "loc_data": "{2931,10191,0,1,0}" - }, - { - "npc_id": "2174", - "loc_data": "{2930,10188,0,1,0}" - }, - { - "npc_id": "2175", - "loc_data": "{2931,10187,0,1,0}" - }, - { - "npc_id": "2178", - "loc_data": "{2916,10195,0,1,0}" - }, - { - "npc_id": "2180", - "loc_data": "{2997,9837,0,1,3}" - }, - { - "npc_id": "2187", - "loc_data": "{2906,10198,0,1,0}" - }, - { - "npc_id": "2190", - "loc_data": "{2923,10203,0,1,0}" - }, - { - "npc_id": "2191", - "loc_data": "{2924,10213,0,1,0}" - }, - { - "npc_id": "2192", - "loc_data": "{2933,10219,0,0,3}" - }, - { - "npc_id": "2193", - "loc_data": "{2899,10218,0,1,0}" - }, - { - "npc_id": "2195", - "loc_data": "{2900,10227,0,1,0}" - }, - { - "npc_id": "2198", - "loc_data": "{2906,10216,0,1,0}" - }, - { - "npc_id": "2203", - "loc_data": "{2912,10222,0,1,0}" - }, - { - "npc_id": "2205", - "loc_data": "{2842,10129,0,0,3}" - }, - { - "npc_id": "2206", - "loc_data": "{2888,10227,0,0,6}" - }, - { - "npc_id": "2234", - "loc_data": "{3081,3251,0,1,6}-{2636,3365,0,1,6}" - }, - { - "npc_id": "2235", - "loc_data": "{3241,3345,0,1,4}" - }, - { - "npc_id": "2236", - "loc_data": "{3078,3250,0,1,1}-{3081,3250,0,1,2}" - }, - { - "npc_id": "2237", - "loc_data": "{3249,3266,0,1,5}" - }, - { - "npc_id": "2240", - "loc_data": "{3077,3259,0,1,4}" - }, - { - "npc_id": "2241", - "loc_data": "{3078,3260,0,1,3}" - }, - { - "npc_id": "2242", - "loc_data": "{3076,3260,0,1,1}-{3078,3260,0,1,6}" - }, - { - "npc_id": "2243", - "loc_data": "{3078,3259,0,1,3}" - }, - { - "npc_id": "2244", - "loc_data": "{3233,3228,0,1,3}" - }, - { - "npc_id": "2245", - "loc_data": "{2505,3240,0,1,4}-{2512,3250,0,1,1}-{2506,3242,0,1,3}-{2510,3252,0,1,4}-{2516,3248,0,1,1}-{2518,3245,0,1,7}-{2518,3245,0,1,1}-{2517,3242,0,1,6}-{2523,3241,0,1,4}-{2525,3241,0,1,1}-{2515,3256,0,1,6}-{2519,3257,0,1,4}" - }, - { - "npc_id": "2246", - "loc_data": "{2521,3239,0,1,5}" - }, - { - "npc_id": "2247", - "loc_data": "{2543,3225,0,1,7}-{2553,3220,0,1,3}-{2545,3220,0,1,4}-{2525,3220,0,1,4}-{2518,3218,0,1,1}" - }, - { - "npc_id": "2248", - "loc_data": "{2540,3218,0,1,1}-{2515,3216,0,1,1}-{2512,3215,0,1,7}" - }, - { - "npc_id": "2249", - "loc_data": "{2530,3224,0,1,4}-{2524,3221,0,1,2}-{2543,3217,0,1,4}-{2530,3212,0,1,4}-{2557,3234,0,1,4}-{2540,3226,0,1,5}-{2532,3213,0,1,4}-{2527,3201,0,1,6}-{2544,3216,0,1,6}-{2519,3222,0,1,6}-{2526,3227,0,1,4}-{2550,3234,0,1,3}-{2553,3220,0,1,3}-{2521,3202,0,1,6}-{2525,3220,0,1,4}-{2544,3230,0,1,3}-{2547,3212,0,1,4}-{2545,3212,0,1,0}-{2540,3208,0,1,4}-{2533,3202,0,1,6}-{2536,3236,0,1,6}-{2525,3204,0,1,0}-{2539,3238,0,1,4}" - }, - { - "npc_id": "2250", - "loc_data": "{2525,3209,0,1,4}-{2558,3228,0,1,4}-{2543,3212,0,1,3}-{2538,3223,0,1,0}-{2527,3201,0,1,7}-{2517,3222,0,1,3}-{2547,3231,0,1,6}-{2550,3220,0,1,4}-{2538,3224,0,1,3}-{2545,3213,0,1,0}-{2542,3208,0,1,5}" - }, - { - "npc_id": "2251", - "loc_data": "{2520,3217,0,1,3}-{2524,3209,0,1,1}-{2529,3200,0,1,5}-{2530,3209,0,1,1}-{2519,3223,0,1,4}-{2518,3224,0,1,2}-{2527,3201,0,1,7}-{2521,3202,0,1,4}-{2542,3208,0,1,4}-{2539,3233,0,1,6}" - }, - { - "npc_id": "2252", - "loc_data": "{3199,3302,0,1,3}-{3079,3257,0,1,1}-{3091,3250,0,1,6}-{3137,3254,0,1,0}" - }, - { - "npc_id": "2256", - "loc_data": "{2571,3305,0,1,7}-{2572,3297,0,1,2}-{2572,3304,0,1,1}-{2577,3308,0,1,6}-{2582,3287,0,1,3}-{2582,3299,0,1,0}-{2583,3292,0,1,4}-{2572,3292,1,1,1}-{2577,3295,1,1,6}-{2576,3286,1,1,1}-{2585,3289,1,1,7}-{2585,3304,1,1,1}-{2582,3306,1,1,5}-{2582,3286,1,1,3}-{2653,3315,0,1,6}-{2658,3306,0,1,4}" - }, - { - "npc_id": "2257", - "loc_data": "{3105,3554,0,1,1}" - }, - { - "npc_id": "2260", - "loc_data": "{3259,3385,0,1,6}" - }, - { - "npc_id": "2262", - "loc_data": "{3039,4834,0,0,7}" - }, - { - "npc_id": "2263", - "loc_data": "{3037,4906,0,1,2}-{3021,4812,0,1,5}-{3057,4875,0,0,6}-{3014,4838,0,1,6}-{3016,4892,0,1,6}-{3058,4877,0,0,6}-{3051,4874,0,1,0}-{3061,4836,0,1,4}-{3029,4890,0,1,6}-{3032,4901,0,1,2}-{3024,4916,0,1,3}-{3020,4903,0,1,4}-{3061,4883,0,0,3}-{3058,4889,0,0,3}-{3053,4896,0,0,3}-{3038,4871,0,1,6}-{3053,4900,0,0,7}-{3017,4812,0,1,6}-{3061,4842,0,1,3}-{3048,4808,0,1,3}-{3021,4847,0,1,6}-{3054,4849,0,1,6}-{3055,4915,0,0,5}-{3015,4833,0,1,0}-{3035,4854,0,1,6}-{3016,4829,0,1,4}-{3037,4906,0,0,5}-{3040,4810,0,1,3}-{3047,4886,0,1,4}-{3059,4815,0,1,1}-{3033,4884,0,1,4}-{3027,4851,0,1,6}-{3020,4819,0,1,4}-{3020,4845,0,1,2}-{3061,4829,0,1,0}-{3030,4877,0,1,2}-{3026,4879,0,1,3}-{3024,4879,0,1,3}-{3018,4842,0,1,4}" - }, - { - "npc_id": "2264", - "loc_data": "{3020,4888,0,1,3}-{3039,4871,0,1,4}-{3049,4921,0,1,7}-{3019,4877,0,1,6}-{3017,4893,0,1,7}-{3017,4821,0,1,4}-{3019,4898,0,1,4}-{3059,4813,0,1,0}-{3020,4912,0,1,2}-{3032,4808,0,1,3}-{3059,4820,0,1,3}-{3062,4828,0,1,6}-{3036,4856,0,1,7}-{3062,4837,0,1,5}-{3025,4850,0,1,1}-{3052,4858,0,1,1}-{3024,4813,0,1,4}-{3054,4915,0,1,7}-{3048,4854,0,1,3}-{3034,4852,0,1,1}-{3052,4898,0,1,1}-{3028,4808,0,1,1}-{3034,4905,0,1,4}-{3018,4843,0,1,2}-{3028,4851,0,1,0}-{3016,4831,0,1,1}-{3052,4896,0,1,3}-{3046,4901,0,1,2}-{3043,4810,0,1,3}-{3062,4884,0,1,6}-{3049,4873,0,1,1}-{3041,4871,0,1,1}" - }, - { - "npc_id": "2265", - "loc_data": "{3020,4914,0,1,2}-{3038,4809,0,1,6}-{3059,4890,0,1,3}-{3024,4850,0,1,3}-{3031,4901,0,1,7}-{3044,4915,0,1,6}-{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}-{3044,4883,0,1,4}-{3017,4817,0,1,7}-{3025,4812,0,1,1}-{3039,4885,0,1,1}-{3048,4917,0,1,5}-{3060,4825,0,1,3}-{3059,4888,0,1,5}-{3047,4874,0,1,2}-{3062,4838,0,1,3}-{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}-{3054,4850,0,1,6}-{3026,4914,0,1,6}" - }, - { - "npc_id": "2266", - "loc_data": "{3053,4978,1,1,1}" - }, - { - "npc_id": "2270", - "loc_data": "{3048,4975,1,1,6}" - }, - { - "npc_id": "2271", - "loc_data": "{3045,4971,1,0,4}" - }, - { - "npc_id": "2274", - "loc_data": "{3243,3245,0,1,4}" - }, - { - "npc_id": "2275", - "loc_data": "{3244,3248,0,1,1}" - }, - { - "npc_id": "2276", - "loc_data": "{3243,3247,0,1,1}" - }, - { - "npc_id": "2277", - "loc_data": "{3248,3247,0,1,4}" - }, - { - "npc_id": "2278", - "loc_data": "{3247,3244,0,1,2}-{3190,3251,0,1,0}-{3179,3243,0,1,0}-{3179,3248,0,1,0}-{3146,3235,0,1,0}-{3146,3261,0,1,0}" - }, - { - "npc_id": "2279", - "loc_data": "{3244,3247,0,1,3}-{3181,3243,0,1,0}-{3176,3248,0,1,0}-{3182,3248,0,1,0}-{3140,3226,0,1,0}-{3140,3259,0,1,0}-{3143,3260,0,1,0}" - }, - { - "npc_id": "2280", - "loc_data": "{3245,3244,0,1,4}-{3144,3228,0,1,0}" - }, - { - "npc_id": "2281", - "loc_data": "{3246,3245,0,1,4}-{3187,3241,0,1,0}-{3177,3243,0,1,0}" - }, - { - "npc_id": "2290", - "loc_data": "{2997,3373,0,0,0}" - }, - { - "npc_id": "2291", - "loc_data": "{3311,3109,0,0,3}" - }, - { - "npc_id": "2292", - "loc_data": "{3182,3043,0,0,3}" - }, - { - "npc_id": "2293", - "loc_data": "{3469,3111,0,1,6}" - }, - { - "npc_id": "2294", - "loc_data": "{3350,3004,0,0,6}" - }, - { - "npc_id": "2296", - "loc_data": "{3399,2916,0,0,6}" - }, - { - "npc_id": "2298", - "loc_data": "{3287,2813,0,1,1}" - }, - { - "npc_id": "2301", - "loc_data": "{3310,3107,0,1,4}-{3181,3043,0,1,3}" - }, - { - "npc_id": "2304", - "loc_data": "{3039,3292,0,1,6}" - }, - { - "npc_id": "2305", - "loc_data": "{2821,3463,0,1,3}" - }, - { - "npc_id": "2306", - "loc_data": "{2643,3363,0,0,5}" - }, - { - "npc_id": "2307", - "loc_data": "{3627,3526,0,1,6}" - }, - { - "npc_id": "2310", - "loc_data": "{3025,3312,0,1,4}-{3043,3308,0,1,6}-{3023,3297,0,1,5}-{2928,3282,0,1,1}-{2927,3269,0,1,1}" - }, - { - "npc_id": "2311", - "loc_data": "{3035,3296,0,0,0}" - }, - { - "npc_id": "2312", - "loc_data": "{3014,3292,0,0,0}" - }, - { - "npc_id": "2313", - "loc_data": "{3020,3297,0,1,6}-{3040,3296,0,1,3}-{3031,3283,0,1,4}-{3020,3288,0,1,0}" - }, - { - "npc_id": "2314", - "loc_data": "{3018,3296,0,1,1}-{3016,3288,0,1,4}-{3030,3287,0,1,4}-{3017,3282,0,1,3}-{2549,3566,0,1,2}-{2553,3564,0,1,4}" - }, - { - "npc_id": "2315", - "loc_data": "{3018,3282,0,1,5}-{3037,3289,0,1,1}-{2551,3562,0,1,1}-{2554,3561,0,1,3}" - }, - { - "npc_id": "2316", - "loc_data": "{3015,3311,0,1,4}" - }, - { - "npc_id": "2317", - "loc_data": "{3016,3309,0,1,6}" - }, - { - "npc_id": "2318", - "loc_data": "{3017,3307,0,0,0}" - }, - { - "npc_id": "2319", - "loc_data": "{3017,3309,0,0,0}" - }, - { - "npc_id": "2321", - "loc_data": "{2915,10194,1,1,0}" - }, - { - "npc_id": "2323", - "loc_data": "{3056,3306,0,1,1}" - }, - { - "npc_id": "2324", - "loc_data": "{2807,3464,0,1,3}" - }, - { - "npc_id": "2325", - "loc_data": "{2664,3374,0,1,1}" - }, - { - "npc_id": "2326", - "loc_data": "{3600,3527,0,1,1}" - }, - { - "npc_id": "2327", - "loc_data": "{2814,3337,0,1,6}" - }, - { - "npc_id": "2330", - "loc_data": "{2766,3211,0,1,0}" - }, - { - "npc_id": "2331", - "loc_data": "{2860,3430,0,1,4}" - }, - { - "npc_id": "2332", - "loc_data": "{2573,3102,0,1,4}" - }, - { - "npc_id": "2333", - "loc_data": "{3226,3311,0,0,0}" - }, - { - "npc_id": "2334", - "loc_data": "{2662,3525,0,1,1}" - }, - { - "npc_id": "2335", - "loc_data": "{3181,3359,0,0,0}" - }, - { - "npc_id": "2336", - "loc_data": "{2938,3221,0,1,0}" - }, - { - "npc_id": "2337", - "loc_data": "{2589,3861,0,1,0}" - }, - { - "npc_id": "2338", - "loc_data": "{2615,3229,0,1,0}" - }, - { - "npc_id": "2339", - "loc_data": "{2934,3438,0,1,6}" - }, - { - "npc_id": "2340", - "loc_data": "{3007,3373,0,1,4}" - }, - { - "npc_id": "2341", - "loc_data": "{3229,3456,0,1,0}" - }, - { - "npc_id": "2342", - "loc_data": "{3196,3231,0,1,4}" - }, - { - "npc_id": "2343", - "loc_data": "{2473,3446,0,1,6}" - }, - { - "npc_id": "2344", - "loc_data": "{2490,3183,0,1,3}" - }, - { - "npc_id": "2353", - "loc_data": "{2324,3179,0,1,5}" - }, - { - "npc_id": "2355", - "loc_data": "{2353,3163,0,0,4}" - }, - { - "npc_id": "2359", - "loc_data": "{2335,3169,0,1,3}" - }, - { - "npc_id": "2360", - "loc_data": "{2335,3174,0,1,4}-{2322,3172,0,1,5}" - }, - { - "npc_id": "2361", - "loc_data": "{2345,3172,0,1,2}-{2340,3178,0,1,3}" - }, - { - "npc_id": "2362", - "loc_data": "{2330,3163,0,1,3}" - }, - { - "npc_id": "2372", - "loc_data": "{2044,4632,0,1,3}" - }, - { - "npc_id": "2374", - "loc_data": "{2041,4632,0,1,3}-{2037,4630,0,1,3}-{2041,4644,0,1,4}" - }, - { - "npc_id": "2385", - "loc_data": "{3112,3159,0,0,0}" - }, - { - "npc_id": "2386", - "loc_data": "{3051,3496,1,0,0}" - }, - { - "npc_id": "2389", - "loc_data": "{3219,3677,0,0,0}" - }, - { - "npc_id": "2392", - "loc_data": "{3043,3204,0,0,0}" - }, - { - "npc_id": "2414", - "loc_data": "{3263,3400,1,0,0}" - }, - { - "npc_id": "2435", - "loc_data": "{2621,3682,0,0,6}" - }, - { - "npc_id": "2438", - "loc_data": "{2544,3761,0,0,6}" - }, - { - "npc_id": "2439", - "loc_data": "{2659,3657,0,1,3}" - }, - { - "npc_id": "2440", - "loc_data": "{2545,10141,0,0,6}" - }, - { - "npc_id": "2443", - "loc_data": "{2543,10143,0,0,3}" - }, - { - "npc_id": "2446", - "loc_data": "{2545,10145,0,0,6}" - }, - { - "npc_id": "2449", - "loc_data": "{2546,10144,0,0,1}-{2546,10142,0,0,6}" - }, - { - "npc_id": "2452", - "loc_data": "{2526,10166,0,1,6}" - }, - { - "npc_id": "2453", - "loc_data": "{2521,10160,0,0,1}-{2535,10163,0,0,6}-{2520,10165,0,0,3}-{2533,10162,0,0,3}-{2545,10158,0,0,5}-{2547,10151,0,0,3}-{2543,10166,0,0,3}-{2538,10160,0,0,6}-{2506,10163,0,0,3}-{2516,10163,0,0,3}-{2498,10164,0,0,5}-{2544,10160,0,0,0}-{2525,10162,0,0,3}-{2530,10161,0,0,0}-{2537,10167,0,0,7}-{2519,10163,0,0,3}-{2511,10163,0,0,3}-{2530,10161,0,0,7}-{2524,10166,0,0,3}-{2508,10159,0,0,0}-{1846,4405,3,0,2}-{1839,4400,3,0,3}-{1835,4394,3,0,3}-{1834,4406,3,0,1}-{1843,4406,3,0,2}-{1899,4372,0,0,2}-{1888,4362,0,0,7}-{1896,4362,0,0,2}-{1897,4368,0,0,5}" - }, - { - "npc_id": "2455", - "loc_data": "{2457,10139,0,1,2}-{2465,10133,0,1,4}-{2449,10148,0,1,1}-{2471,10133,0,1,6}-{2446,10145,0,1,4}-{2443,10148,0,1,1}-{2452,10161,0,1,3}-{2444,10145,0,1,1}-{2462,10163,0,1,3}-{2446,10141,0,1,3}-{2454,10149,0,1,0}-{2476,10155,0,1,4}-{2456,10153,0,1,0}-{2485,10142,0,1,3}-{2457,10152,0,1,4}-{2485,10126,0,1,4}-{2475,10148,0,1,3}-{2471,10160,0,1,1}-{2453,10145,0,1,3}-{2463,10158,0,1,4}-{2457,10163,0,1,2}-{2471,10154,0,1,6}-{2476,10151,0,1,7}-{2479,10154,0,1,1}-{2486,10158,0,1,4}-{2485,10165,0,1,5}-{2485,10153,0,1,4}-{2474,10140,0,1,7}-{2466,10130,0,1,6}-{2464,10138,0,1,6}-{2486,10150,0,1,6}-{2473,10127,0,1,4}-{2483,10126,0,1,3}-{2456,10133,0,1,6}-{2467,10163,0,1,6}-{2477,10143,0,1,3}-{2489,10139,0,1,4}-{2486,10127,0,1,1}-{2480,10141,0,1,7}-{2490,10155,0,1,3}-{2459,10133,0,1,3}-{2455,10163,0,1,0}-{2496,10166,0,1,1}-{2494,10162,0,1,1}-{2502,10167,0,1,1}-{2493,10132,0,1,3}-{2499,10162,0,1,0}-{2502,10159,0,1,1}-{2502,10166,0,1,4}-{2494,10129,0,1,5}-{2496,10130,0,1,3}-{2498,10128,0,1,1}-{2497,10126,0,1,4}-{2499,10128,0,1,5}-{2501,10126,0,1,3}-{2506,10125,0,1,4}-{2506,10127,0,1,4}-{2507,10128,0,1,3}-{2507,10126,0,1,6}-{2511,10126,0,1,0}-{2513,10126,0,1,4}-{2517,10129,0,1,3}-{2513,10127,0,1,3}-{2516,10124,0,1,4}-{2520,10122,0,1,5}-{2522,10126,0,1,3}-{2524,10129,0,1,4}-{2525,10125,0,1,4}-{2529,10122,0,1,4}-{2529,10124,0,1,2}-{2526,10126,0,1,3}-{2529,10130,0,1,6}-{2530,10127,0,1,5}-{2532,10121,0,1,0}-{2532,10123,0,1,3}-{2538,10126,0,1,0}-{2535,10125,0,1,7}-{2536,10127,0,1,1}-{2539,10130,0,1,6}-{2543,10130,0,1,6}-{2548,10131,0,1,1}-{2542,10128,0,1,3}-{1815,4405,2,1,6}-{1818,4403,2,1,3}-{1813,4404,2,1,7}-{1814,4407,2,1,7}-{1821,4393,2,1,1}-{1823,4389,2,1,1}-{1828,4387,2,1,3}-{1821,4392,2,1,0}-{1826,4391,2,1,0}-{1865,4377,2,1,1}-{1865,4397,1,1,6}-{1844,4360,1,1,4}-{1844,4361,1,1,5}-{1851,4360,1,1,3}-{1857,4360,1,1,1}-{1862,4362,1,1,1}-{1862,4362,1,1,7}-{1864,4366,1,1,1}-{1865,4383,2,1,4}-{1864,4390,1,1,1}-{1865,4408,1,1,4}-{1877,4410,1,1,1}-{1883,4411,1,1,4}-{1889,4410,1,1,4}-{1887,4374,0,1,6}-{1890,4372,0,1,4}-{1890,4366,0,1,4}-{1896,4371,0,1,3}-{1890,4364,0,1,7}-{1831,4378,3,1,2}-{1838,4379,3,1,2}-{1824,4376,3,1,1}-{1832,4380,3,1,6}-{1844,4373,2,1,4}-{1849,4381,2,1,4}-{1846,4374,2,1,1}" - }, - { - "npc_id": "2456", - "loc_data": "{2452,10145,0,0,4}-{2445,10138,0,0,7}" - }, - { - "npc_id": "2457", - "loc_data": "{2500,10148,0,1,3}-{2505,10150,0,1,2}-{2510,10148,0,1,1}-{2511,10147,0,1,6}-{2517,10151,0,1,3}-{2519,10143,0,1,7}-{2529,10148,0,1,3}-{2528,10144,0,1,0}-{2532,10145,0,1,1}-{2532,10140,0,1,7}-{2520,10144,0,1,2}-{1804,4369,2,1,2}-{1812,4363,2,1,5}-{1817,4368,2,1,6}-{1801,4404,3,1,6}-{1805,4405,3,1,1}-{1803,4404,3,1,7}-{1811,4394,1,1,7}-{1804,4391,1,1,2}-{1798,4391,1,1,4}-{1802,4389,1,1,4}-{1799,4386,2,1,1}-{1799,4381,2,1,6}-{1798,4375,1,1,4}-{1803,4376,1,1,6}-{1797,4380,1,1,6}-{1902,4367,0,1,6}-{1894,4367,0,1,1}-{1893,4361,0,1,3}-{1886,4369,0,1,1}-{1848,4394,1,1,3}-{1850,4389,1,1,1}-{1850,4391,1,1,2}" - }, - { - "npc_id": "2458", - "loc_data": "{2599,4774,0,0,3}" - }, - { - "npc_id": "2459", - "loc_data": "{2604,4772,0,1,4}-{2606,4771,0,1,6}-{2604,4769,0,1,2}" - }, - { - "npc_id": "2460", - "loc_data": "{2600,4778,0,1,1}-{2607,4777,0,1,1}" - }, - { - "npc_id": "2461", - "loc_data": "{2597,4776,0,1,1}-{2606,4771,0,1,1}" - }, - { - "npc_id": "2462", - "loc_data": "{2596,4773,0,1,2}-{2602,4771,0,1,6}-{2596,4780,0,1,4}" - }, - { - "npc_id": "2479", - "loc_data": "{3420,4777,0,0,0}" - }, - { - "npc_id": "2481", - "loc_data": "{3423,4777,0,0,5}" - }, - { - "npc_id": "2483", - "loc_data": "{2914,3116,0,0,0}" - }, - { - "npc_id": "2484", - "loc_data": "{2782,3058,0,0,0}" - }, - { - "npc_id": "2486", - "loc_data": "{2789,3055,0,0,0}" - }, - { - "npc_id": "2487", - "loc_data": "{2848,3042,0,0,0}" - }, - { - "npc_id": "2488", - "loc_data": "{2799,3058,0,0,0}" - }, - { - "npc_id": "2489", - "loc_data": "{3781,3031,0,1,5}-{3775,3033,0,1,4}-{3780,3022,0,1,1}-{3788,3011,0,1,2}-{3793,3019,0,1,5}-{3766,3010,0,1,4}-{3764,3020,0,1,1}-{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}-{3749,3000,0,1,6}-{3738,3010,0,1,3}-{3723,2998,0,1,2}-{3731,2991,0,1,1}-{3703,3040,0,1,3}-{3706,3051,0,1,1}-{3664,3013,0,1,7}-{3720,3040,0,1,6}-{3719,3045,0,1,6}" - }, - { - "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}" - }, - { - "npc_id": "2504", - "loc_data": "{2796,3077,1,1,3}" - }, - { - "npc_id": "2507", - "loc_data": "{2794,3077,0,1,6}" - }, - { - "npc_id": "2510", - "loc_data": "{2786,3075,1,1,4}" - }, - { - "npc_id": "2513", - "loc_data": "{2796,3088,0,1,7}" - }, - { - "npc_id": "2516", - "loc_data": "{2791,3102,0,1,3}" - }, - { - "npc_id": "2519", - "loc_data": "{2794,3064,0,1,5}" - }, - { - "npc_id": "2522", - "loc_data": "{2758,3099,0,1,1}" - }, - { - "npc_id": "2525", - "loc_data": "{2800,3048,0,1,2}" - }, - { - "npc_id": "2528", - "loc_data": "{2816,3085,0,1,3}" - }, - { - "npc_id": "2531", - "loc_data": "{2784,3089,0,1,1}" - }, - { - "npc_id": "2547", - "loc_data": "{3109,9942,0,1,4}-{3111,9938,0,1,4}-{3116,9926,0,1,4}-{2615,9525,0,1,4}-{2615,9521,0,1,1}" - }, - { - "npc_id": "2548", - "loc_data": "{3349,2970,0,0,4}-{3358,2987,0,0,4}" - }, - { - "npc_id": "2549", - "loc_data": "{3364,3001,0,0,6}" - }, - { - "npc_id": "2553", - "loc_data": "{1944,4958,0,1,0}" - }, - { - "npc_id": "2564", - "loc_data": "{1936,4966,0,0,4}" - }, - { - "npc_id": "2565", - "loc_data": "{1939,4968,0,1,0}" - }, - { - "npc_id": "2568", - "loc_data": "{2729,3495,0,0,6}-{2724,3495,0,0,6}-{2728,3495,0,0,6}-{2721,3495,0,0,6}" - }, - { - "npc_id": "2569", - "loc_data": "{2727,3495,0,0,6}-{2722,3495,0,0,6}" - }, - { - "npc_id": "2572", - "loc_data": "{3079,3250,0,1,2}" - }, - { - "npc_id": "2574", - "loc_data": "{3087,3247,0,0,7}" - }, - { - "npc_id": "2580", - "loc_data": "{2918,3535,0,1,0}" - }, - { - "npc_id": "2591", - "loc_data": "{4786,5106,0,1,6}-{2474,5145,0,1,2}" - }, - { - "npc_id": "2592", - "loc_data": "{4680,5117,0,1,6}-{2461,5142,0,1,6}" - }, - { - "npc_id": "2594", - "loc_data": "{4703,5123,0,1,5}-{4680,5126,0,1,7}-{4730,5077,0,1,5}" - }, - { - "npc_id": "2595", - "loc_data": "{4699,5100,0,1,6}-{4647,5132,0,1,7}-{4633,5069,0,1,4}-{4757,5100,0,1,2}" - }, - { - "npc_id": "2596", - "loc_data": "{4628,5117,0,1,4}-{4787,5118,0,1,7}-{2483,5155,0,1,4}" - }, - { - "npc_id": "2597", - "loc_data": "{4621,5094,0,1,5}-{2446,5144,0,1,1}-{2470,5141,0,1,6}" - }, - { - "npc_id": "2598", - "loc_data": "{2485,5159,0,1,4}-{2489,5155,0,1,1}-{2471,5144,0,1,3}-{2476,5153,0,1,0}-{2477,5150,0,1,1}" - }, - { - "npc_id": "2600", - "loc_data": "{4656,5125,0,1,5}-{4711,5072,0,1,4}-{4751,5087,0,1,3}" - }, - { - "npc_id": "2601", - "loc_data": "{4676,5123,0,1,5}-{4693,5127,0,1,7}-{4724,5107,0,1,5}-{4788,5110,0,1,4}" - }, - { - "npc_id": "2602", - "loc_data": "{4668,5133,0,1,7}-{4627,5122,0,1,5}-{4714,5113,0,1,6}-{4752,5074,0,1,7}-{4789,5122,0,1,2}" - }, - { - "npc_id": "2603", - "loc_data": "{4711,5086,0,1,2}-{4760,5091,0,1,4}" - }, - { - "npc_id": "2604", - "loc_data": "{2439,5170,0,1,3}-{2451,5170,0,1,3}-{2453,5161,0,1,6}-{2460,5160,0,1,7}-{2457,5155,0,1,3}-{2480,5168,0,1,6}-{2469,5166,0,1,1}-{2490,5173,0,1,3}-{2488,5171,0,1,4}-{2483,5156,0,1,3}-{2470,5147,0,1,4}-{2468,5139,0,1,7}-{2458,5141,0,1,3}-{2454,5141,0,1,1}-{2452,5124,0,1,3}-{2454,5122,0,1,2}-{2453,5143,0,1,3}-{2443,5144,0,1,6}" - }, - { - "npc_id": "2605", - "loc_data": "{4633,5125,0,1,7}-{4631,5088,0,1,7}-{4760,5074,0,1,4}" - }, - { - "npc_id": "2606", - "loc_data": "{4670,5081,0,1,6}-{4642,5115,0,1,7}-{4756,5086,0,1,4}" - }, - { - "npc_id": "2607", - "loc_data": "{4638,5095,0,1,2}-{4647,5141,0,1,4}-{4699,5141,0,1,3}-{4774,5098,0,1,3}" - }, - { - "npc_id": "2608", - "loc_data": "{4656,5091,0,1,5}-{4637,5093,0,1,0}" - }, - { - "npc_id": "2609", - "loc_data": "{4691,5090,0,1,7}-{4617,5073,0,1,6}-{4711,5125,0,1,4}-{4727,5082,0,1,6}-{4748,5070,0,1,4}-{2440,5128,0,1,3}-{2436,5133,0,1,3}-{2450,5168,0,1,2}-{2443,5170,0,1,4}-{2445,5177,0,1,3}" - }, - { - "npc_id": "2610", - "loc_data": "{2446,5169,0,1,4}-{2458,5159,0,1,3}-{2466,5166,0,1,7}-{2471,5166,0,1,3}-{2478,5164,0,1,4}-{2490,5174,0,1,4}-{2485,5159,0,1,5}-{2442,5173,0,1,0}-{2440,5171,0,1,7}-{2440,5170,0,1,3}" - }, - { - "npc_id": "2611", - "loc_data": "{4646,5107,0,1,6}-{4683,5098,0,1,4}-{4690,5113,0,1,5}-{4720,5118,0,1,7}-{4716,5097,0,1,5}-{4762,5088,0,1,0}" - }, - { - "npc_id": "2614", - "loc_data": "{4666,5082,0,1,0}-{4669,5103,0,1,2}-{4664,5121,0,1,4}-{4639,5074,0,1,3}-{4704,5077,0,1,5}-{4688,5016,0,1,3}" - }, - { - "npc_id": "2615", - "loc_data": "{4695,5128,0,1,7}-{4625,5084,0,1,7}-{4628,5134,0,1,3}-{4725,5067,0,1,5}-{4748,5076,0,1,6}-{4790,5127,0,1,0}" - }, - { - "npc_id": "2616", - "loc_data": "{4673,5114,0,1,1}-{4631,5106,0,1,0}-{2441,5130,0,1,1}-{2438,5135,0,1,6}-{2443,5138,0,1,2}" - }, - { - "npc_id": "2617", - "loc_data": "{4611,5132,0,1,5}-{2440,5168,0,0,1}" - }, - { - "npc_id": "2618", - "loc_data": "{4603,5065,0,1,1}-{2399,5178,0,1,6}" - }, - { - "npc_id": "2619", - "loc_data": "{2446,5179,0,0,6}-{4644,5149,0,1,7}-{2418,5203,0,1,6}" - }, - { - "npc_id": "2620", - "loc_data": "{4701,5153,0,1,0}-{2478,5146,0,1,2}" - }, - { - "npc_id": "2621", - "loc_data": "{2724,3729,0,0,6}" - }, - { - "npc_id": "2622", - "loc_data": "{4689,5173,0,0,0}-{2464,5149,0,0,7}" - }, - { - "npc_id": "2623", - "loc_data": "{4655,5172,0,1,0}-{2462,5125,0,1,5}" - }, - { - "npc_id": "2624", - "loc_data": "{4718,5148,0,1,6}-{4708,5173,0,1,6}-{4716,5178,0,1,7}-{4726,5173,0,1,0}-{4730,5163,0,1,3}-{4726,5153,0,1,5}" - }, - { - "npc_id": "2625", - "loc_data": "{4668,5177,0,1,4}-{4676,5177,0,1,2}" - }, - { - "npc_id": "2634", - "loc_data": "{3095,3252,0,0,0}" - }, - { - "npc_id": "2652", - "loc_data": "{2868,3579,0,0,0}-{2851,3582,0,0,0}" - }, - { - "npc_id": "2655", - "loc_data": "{2921,3558,0,1,6}" - }, - { - "npc_id": "2660", - "loc_data": "{3208,3496,0,1,3}" - }, - { - "npc_id": "2674", - "loc_data": "{3017,3231,0,1,1}" - }, - { - "npc_id": "2676", - "loc_data": "{2914,3322,0,0,0}" - }, - { - "npc_id": "2677", - "loc_data": "{2945,3307,0,0,0}" - }, - { - "npc_id": "2678", - "loc_data": "{2988,3210,0,1,7}" - }, - { - "npc_id": "2679", - "loc_data": "{3000,3222,0,1,1}" - }, - { - "npc_id": "2680", - "loc_data": "{2985,3193,0,1,1}-{2582,9836,0,1,2}" - }, - { - "npc_id": "2681", - "loc_data": "{2991,3199,0,1,3}" - }, - { - "npc_id": "2682", - "loc_data": "{1866,4237,0,1,0}-{1866,4240,0,1,0}-{1867,4243,0,1,0}-{2950,3204,0,1,5}-{2971,3212,0,1,4}-{2960,3201,0,1,1}-{2959,3202,0,1,4}-{2904,3208,0,1,0}-{2956,3203,0,1,1}-{2969,3204,0,1,4}" - }, - { - "npc_id": "2683", - "loc_data": "{2967,3212,1,0,7}" - }, - { - "npc_id": "2684", - "loc_data": "{2965,3213,1,0,0}" - }, - { - "npc_id": "2685", - "loc_data": "{3124,9873,0,1,3}-{2911,3285,0,1,1}-{2696,9689,0,1,1}" - }, - { - "npc_id": "2686", - "loc_data": "{2907,3293,0,1,4}-{2700,9696,0,1,1}" - }, - { - "npc_id": "2687", - "loc_data": "{2907,3293,0,1,6}-{2908,3290,0,1,3}-{2703,9698,0,1,1}" - }, - { - "npc_id": "2688", - "loc_data": "{3127,9874,0,1,4}-{2906,3288,0,1,1}-{2910,3293,0,1,4}-{2699,9690,0,1,1}" - }, - { - "npc_id": "2689", - "loc_data": "{2918,3319,0,0,0}" - }, - { - "npc_id": "2690", - "loc_data": "{3048,3258,0,1,3}" - }, - { - "npc_id": "2691", - "loc_data": "{3048,3256,0,1,4}" - }, - { - "npc_id": "2692", - "loc_data": "{3049,3256,0,0,0}" - }, - { - "npc_id": "2693", - "loc_data": "{3214,3313,0,1,0}-{3214,3320,0,1,0}-{3215,3303,0,1,0}-{3219,3285,0,1,0}-{3219,3288,0,1,0}-{3234,3267,0,1,0}-{3234,3268,0,1,0}-{3238,3238,0,1,0}-{3238,3246,0,1,0}-{3239,3235,0,1,0}-{2976,3359,0,1,0}-{2977,3357,0,1,0}-{2988,3383,0,1,1}-{2992,3385,0,1,3}-{3015,3285,0,1,0}" - }, - { - "npc_id": "2695", - "loc_data": "{3013,3192,0,1,1}" - }, - { - "npc_id": "2696", - "loc_data": "{3013,3196,0,1,1}" - }, - { - "npc_id": "2697", - "loc_data": "{3018,3189,0,1,4}" - }, - { - "npc_id": "2698", - "loc_data": "{3018,3181,0,1,6}" - }, - { - "npc_id": "2699", - "loc_data": "{3037,2982,0,0,6}-{3036,2979,0,0,6}" - }, - { - "npc_id": "2700", - "loc_data": "{3037,2982,0,0,6}" - }, - { - "npc_id": "2704", - "loc_data": "{3019,3185,0,0,0}-{3034,2979,0,0,1}-{3033,2980,1,0,4}-{3032,2979,0,0,1}-{3034,2985,0,0,1}-{3035,2982,0,0,1}" - }, - { - "npc_id": "2705", - "loc_data": "{3034,2985,1,0,1}" - }, - { - "npc_id": "2706", - "loc_data": "{3725,3015,0,1,4}-{3745,3026,0,1,6}-{3741,3033,0,1,6}-{3734,3045,0,1,1}-{3019,3145,0,1,3}-{3015,3142,0,1,4}-{3022,3139,0,1,4}" - }, - { - "npc_id": "2709", - "loc_data": "{3002,3266,0,1,6}" - }, - { - "npc_id": "2710", - "loc_data": "{3001,3268,0,1,4}" - }, - { - "npc_id": "2711", - "loc_data": "{3002,3268,0,1,4}" - }, - { - "npc_id": "2712", - "loc_data": "{3001,3268,0,1,3}" - }, - { - "npc_id": "2713", - "loc_data": "{3002,3267,0,1,2}" - }, - { - "npc_id": "2725", - "loc_data": "{2500,3488,0,1,2}" - }, - { - "npc_id": "2726", - "loc_data": "{2994,3138,0,1,0}" - }, - { - "npc_id": "2727", - "loc_data": "{2969,3146,0,1,0}" - }, - { - "npc_id": "2728", - "loc_data": "{3043,3235,0,1,7}" - }, - { - "npc_id": "2729", - "loc_data": "{3047,3234,0,1,7}" - }, - { - "npc_id": "2730", - "loc_data": "{2835,3335,0,0,5}" - }, - { - "npc_id": "2731", - "loc_data": "{2833,3335,0,0,3}" - }, - { - "npc_id": "2732", - "loc_data": "{2933,3281,0,1,1}" - }, - { - "npc_id": "2733", - "loc_data": "{2932,3286,0,1,1}" - }, - { - "npc_id": "2759", - "loc_data": "{3254,3418,0,0,0}-{3255,3418,0,0,0}-{3256,3418,0,0,0}-{3096,3493,0,0,0}-{3097,3494,0,0,0}-{3104,3368,0,0,0}-{3180,3438,0,0,0}-{3180,3442,0,0,0}-{3191,3441,0,0,0}-{3191,3437,0,0,0}-{3191,3445,0,0,0}" - }, - { - "npc_id": "2782", - "loc_data": "{1925,4636,0,1,7}" - }, - { - "npc_id": "2783", - "loc_data": "{1995,4660,0,1,7}-{1989,4660,0,1,0}-{1989,4664,0,1,7}-{1995,4660,0,1,2}-{1997,4664,0,1,3}-{1989,4658,0,1,0}-{2001,4646,0,1,4}-{2000,4645,0,1,3}-{1997,4644,0,1,1}-{2006,4639,0,1,6}-{1999,4636,0,1,3}" - }, - { - "npc_id": "2792", - "loc_data": "{3228,3412,0,0,0}" - }, - { - "npc_id": "2794", - "loc_data": "{2606,3100,0,0,0}" - }, - { - "npc_id": "2796", - "loc_data": "{3096,3108,0,0,6}" - }, - { - "npc_id": "2800", - "loc_data": "{2981,3190,0,0,0}" - }, - { - "npc_id": "2803", - "loc_data": "{3391,3066,0,1,6}-{3397,3054,0,1,6}-{3398,3060,0,1,0}-{3412,3059,0,1,3}" - }, - { - "npc_id": "2804", - "loc_data": "{3388,3067,0,1,0}-{3396,3064,0,1,6}-{3402,3063,0,1,1}-{3423,3019,0,1,3}-{3420,3015,0,1,6}-{3430,3014,0,1,2}-{3426,3020,0,0,0}-{3416,3013,0,1,4}-{3410,3016,0,1,6}" - }, - { - "npc_id": "2805", - "loc_data": "{3391,3071,0,1,2}-{3397,3069,0,1,2}-{3398,3068,0,1,1}-{3385,3064,0,1,6}-{3406,3064,0,1,6}-{3413,3053,0,1,6}-{3424,3061,0,1,2}-{3423,3020,0,1,0}-{3429,3019,0,0,0}" - }, - { - "npc_id": "2806", - "loc_data": "{3426,3052,0,1,4}-{3423,3044,0,1,3}-{3415,3018,0,1,5}-{3425,3009,0,1,3}" - }, - { - "npc_id": "2807", - "loc_data": "{3414,3045,0,1,1}-{3403,3050,0,1,4}-{3407,3058,0,1,3}-{3420,3022,0,0,0}" - }, - { - "npc_id": "2808", - "loc_data": "{3384,3072,0,1,2}-{3394,3076,0,1,1}-{3403,3069,0,1,1}-{3402,3061,0,1,1}-{3419,3006,0,1,4}" - }, - { - "npc_id": "2809", - "loc_data": "{3274,3168,0,1,0}" - }, - { - "npc_id": "2810", - "loc_data": "{3311,3208,0,1,0}" - }, - { - "npc_id": "2811", - "loc_data": "{3289,3209,0,1,0}" - }, - { - "npc_id": "2812", - "loc_data": "{3296,3227,0,0,0}" - }, - { - "npc_id": "2813", - "loc_data": "{3285,3201,0,0,0}" - }, - { - "npc_id": "2816", - "loc_data": "{3301,3190,0,0,0}" - }, - { - "npc_id": "2817", - "loc_data": "{3306,3197,0,0,0}" - }, - { - "npc_id": "2818", - "loc_data": "{3303,3204,0,0,0}" - }, - { - "npc_id": "2819", - "loc_data": "{3295,3193,0,0,0}" - }, - { - "npc_id": "2820", - "loc_data": "{3302,3185,0,1,4}" - }, - { - "npc_id": "2821", - "loc_data": "{3306,3196,0,0,4}" - }, - { - "npc_id": "2823", - "loc_data": "{3302,3197,0,1,4}" - }, - { - "npc_id": "2824", - "loc_data": "{3275,3193,0,0,0}" - }, - { - "npc_id": "2860", - "loc_data": "{2345,3165,0,1,4}" - }, - { - "npc_id": "2881", - "loc_data": "{2907,4440,0,1,6}" - }, - { - "npc_id": "2882", - "loc_data": "{2910,4457,0,1,6}" - }, - { - "npc_id": "2883", - "loc_data": "{2923,4443,0,1,4}" - }, - { - "npc_id": "2890", - "loc_data": "{1889,4411,0,0,4}-{1895,4409,0,0,2}-{1903,4409,0,0,3}-{1906,4407,0,0,5}-{1903,4400,0,0,1}-{1899,4400,0,0,2}-{1893,4398,0,0,2}-{1888,4386,0,0,6}-{1884,4383,0,0,5}-{1879,4376,0,0,5}-{1883,4376,0,0,1}-{1921,4368,0,0,2}-{1927,4362,0,0,7}-{1935,4360,0,0,7}-{1942,4356,0,0,7}-{1948,4355,0,0,7}-{1951,4358,0,0,2}-{1957,4365,0,0,2}-{1958,4368,0,0,2}" - }, - { - "npc_id": "2894", - "loc_data": "{2908,4435,0,0,1}-{2924,4436,0,0,5}-{2924,4460,0,0,6}-{2908,4463,0,0,3}-{2901,4458,0,0,3}-{2928,4441,0,0,3}-{2929,4449,0,0,6}-{2902,4437,0,0,1}" - }, - { - "npc_id": "2935", - "loc_data": "{3671,3484,0,1,4}" - }, - { - "npc_id": "2939", - "loc_data": "{2657,3701,0,1,0}" - }, - { - "npc_id": "2940", - "loc_data": "{2675,3671,0,1,3}" - }, - { - "npc_id": "2942", - "loc_data": "{3017,3234,0,1,7}" - }, - { - "npc_id": "2943", - "loc_data": "{3265,3400,0,0,0}" - }, - { - "npc_id": "2945", - "loc_data": "{2562,3319,0,0,0}" - }, - { - "npc_id": "2946", - "loc_data": "{3246,9865,0,1,1}" - }, - { - "npc_id": "2947", - "loc_data": "{3245,9868,0,1,3}" - }, - { - "npc_id": "2948", - "loc_data": "{3265,3403,0,0,0}" - }, - { - "npc_id": "2949", - "loc_data": "{2562,3320,0,0,0}" - }, - { - "npc_id": "2950", - "loc_data": "{3020,3229,0,0,7}" - }, - { - "npc_id": "2997", - "loc_data": "{3308,3511,1,1,1}" - }, - { - "npc_id": "2998", - "loc_data": "{2657,9641,0,1,0}" - }, - { - "npc_id": "2999", - "loc_data": "{2658,9634,0,1,1}" - }, - { - "npc_id": "3001", - "loc_data": "{2646,9639,0,1,4}" - }, - { - "npc_id": "3002", - "loc_data": "{2666,9630,0,1,6}" - }, - { - "npc_id": "3003", - "loc_data": "{2660,9624,0,1,6}" - }, - { - "npc_id": "3020", - "loc_data": "{3348,2942,0,1,3}" - }, - { - "npc_id": "3021", - "loc_data": "{2796,3104,0,1,0}-{2815,3466,0,1,3}-{2856,3435,0,1,0}-{2933,3434,0,1,4}-{3182,3354,0,1,0}-{3088,3357,0,1,0}-{3060,3263,0,1,0}-{3189,3234,0,1,0}-{3232,3459,0,1,3}-{3449,3471,0,1,6}-{2812,3333,0,1,4}-{3007,3371,0,1,2}-{3053,3304,0,1,1}-{2663,3374,0,1,4}-{3603,3529,0,1,6}-{2474,3446,0,1,4}-{2611,3859,0,1,6}-{2589,3863,0,1,0}-{2434,3415,0,1,3}-{2764,3210,0,1,1}-{2940,3225,0,1,6}-{2614,3226,0,1,4}-{3315,3205,0,1,6}-{2666,3521,0,1,4}-{2491,3177,0,1,1}-{2572,3105,0,1,0}" - }, - { - "npc_id": "3029", - "loc_data": "{3422,2937,0,1,1}" - }, - { - "npc_id": "3030", - "loc_data": "{3421,2912,0,0,1}" - }, - { - "npc_id": "3031", - "loc_data": "{3439,2890,0,0,3}" - }, - { - "npc_id": "3032", - "loc_data": "{3439,2927,0,1,5}" - }, - { - "npc_id": "3034", - "loc_data": "{3409,2929,1,1,4}" - }, - { - "npc_id": "3035", - "loc_data": "{3450,2895,0,0,3}" - }, - { - "npc_id": "3036", - "loc_data": "{3424,2900,0,1,3}" - }, - { - "npc_id": "3037", - "loc_data": "{3424,2906,0,0,1}" - }, - { - "npc_id": "3038", - "loc_data": "{3406,2924,0,1,3}" - }, - { - "npc_id": "3039", - "loc_data": "{3414,2907,0,1,3}" - }, - { - "npc_id": "3040", - "loc_data": "{3443,2914,0,0,3}" - }, - { - "npc_id": "3041", - "loc_data": "{3445,2910,0,0,1}" - }, - { - "npc_id": "3042", - "loc_data": "{3441,2920,0,1,4}" - }, - { - "npc_id": "3043", - "loc_data": "{3445,2917,0,0,3}" - }, - { - "npc_id": "3044", - "loc_data": "{3428,2928,0,1,5}" - }, - { - "npc_id": "3045", - "loc_data": "{3432,2912,0,0,1}" - }, - { - "npc_id": "3046", - "loc_data": "{3425,2891,0,0,4}-{3425,2894,0,0,4}-{3425,2893,0,0,4}-{3425,2889,0,0,4}" - }, - { - "npc_id": "3050", - "loc_data": "{3187,9758,0,1,3}" - }, - { - "npc_id": "3068", - "loc_data": "{3062,9550,0,1,5}-{3050,9554,0,1,7}-{3048,9545,0,1,4}-{3061,9542,0,1,3}-{3065,9548,0,1,1}-{3034,9544,0,1,4}-{3033,9552,0,1,3}-{3029,9545,0,1,7}-{3027,9555,0,1,3}" - }, - { - "npc_id": "3072", - "loc_data": "{3210,5562,0,1,3}-{3207,5448,0,1,3}-{3223,5446,0,1,4}" - }, - { - "npc_id": "3073", - "loc_data": "{3217,5442,0,1,4}-{3231,5444,0,1,0}" - }, - { - "npc_id": "3074", - "loc_data": "{3404,3492,0,0,6}" - }, - { - "npc_id": "3076", - "loc_data": "{3403,3492,0,0,0}" - }, - { - "npc_id": "3090", - "loc_data": "{3082,3442,0,0,0}" - }, - { - "npc_id": "3094", - "loc_data": "{3361,3306,0,1,6}" - }, - { - "npc_id": "3095", - "loc_data": "{3364,3309,0,1,3}-{3363,3317,1,1,1}" - }, - { - "npc_id": "3097", - "loc_data": "{3364,3304,0,1,6}" - }, - { - "npc_id": "3099", - "loc_data": "{3364,9626,2,1,1}" - }, - { - "npc_id": "3100", - "loc_data": "{3363,9646,0,1,1}" - }, - { - "npc_id": "3101", - "loc_data": "{3364,9644,1,1,6}" - }, - { - "npc_id": "3103", - "loc_data": "{3362,3318,1,0,6}" - }, - { - "npc_id": "3104", - "loc_data": "{3364,3310,0,1,1}-{3379,9627,0,1,0}-{3367,9647,2,1,4}" - }, - { - "npc_id": "3105", - "loc_data": "{3367,3304,1,1,1}-{3376,9655,0,1,7}" - }, - { - "npc_id": "3106", - "loc_data": "{3360,3305,1,1,3}-{3348,9654,0,1,4}-{3362,9648,2,1,4}" - }, - { - "npc_id": "3107", - "loc_data": "{3361,3306,0,1,3}-{3348,9626,0,1,6}" - }, - { - "npc_id": "3108", - "loc_data": "{2549,3100,0,0,4}" - }, - { - "npc_id": "3109", - "loc_data": "{2551,3079,0,1,3}" - }, - { - "npc_id": "3110", - "loc_data": "{2789,3175,0,0,4}" - }, - { - "npc_id": "3111", - "loc_data": "{2788,3176,0,0,0}" - }, - { - "npc_id": "3114", - "loc_data": "{2815,3344,0,1,3}" - }, - { - "npc_id": "3115", - "loc_data": "{2439,4444,0,1,3}" - }, - { - "npc_id": "3123", - "loc_data": "{3344,2827,0,1,5}" - }, - { - "npc_id": "3124", - "loc_data": "{3372,2847,1,0,3}" - }, - { - "npc_id": "3125", - "loc_data": "{3366,2845,3,0,6}" - }, - { - "npc_id": "3127", - "loc_data": "{3331,2948,0,1,4}" - }, - { - "npc_id": "3153", - "loc_data": "{2871,3091,0,1,4}-{2862,3111,0,1,1}-{2861,3101,0,1,3}-{2870,3113,0,1,4}-{2870,3110,0,1,6}-{2865,3103,0,1,3}-{2873,3103,0,1,2}-{2863,3118,0,1,4}-{2886,3109,0,1,1}-{2892,3109,0,1,2}-{2883,3105,0,1,4}-{2889,3111,0,1,6}-{2883,3110,0,1,3}" - }, - { - "npc_id": "3155", - "loc_data": "{3679,3494,0,1,3}-{3687,2953,0,1,1}" - }, - { - "npc_id": "3162", - "loc_data": "{3691,2954,0,1,3}" - }, - { - "npc_id": "3164", - "loc_data": "{3667,2990,0,1,3}" - }, - { - "npc_id": "3166", - "loc_data": "{3693,2979,0,1,3}" - }, - { - "npc_id": "3167", - "loc_data": "{3662,2978,1,1,4}" - }, - { - "npc_id": "3168", - "loc_data": "{3663,2981,0,1,3}" - }, - { - "npc_id": "3170", - "loc_data": "{3674,2970,0,1,0}" - }, - { - "npc_id": "3188", - "loc_data": "{3664,2971,0,1,6}" - }, - { - "npc_id": "3189", - "loc_data": "{3690,2976,0,1,1}-{3671,3006,0,1,6}" - }, - { - "npc_id": "3191", - "loc_data": "{3659,2955,0,1,3}-{3683,2994,0,1,4}-{3662,3005,0,1,4}" - }, - { - "npc_id": "3192", - "loc_data": "{3700,2994,0,1,4}-{3683,3005,0,1,3}" - }, - { - "npc_id": "3193", - "loc_data": "{3696,2993,0,1,3}-{3693,3006,0,1,3}" - }, - { - "npc_id": "3194", - "loc_data": "{3654,2992,0,1,4}-{3682,2962,0,1,3}-{3704,3006,0,1,0}" - }, - { - "npc_id": "3195", - "loc_data": "{3696,2978,0,1,1}" - }, - { - "npc_id": "3196", - "loc_data": "{3649,3004,0,1,6}" - }, - { - "npc_id": "3198", - "loc_data": "{3682,2982,0,0,3}" - }, - { - "npc_id": "3199", - "loc_data": "{3682,2981,0,0,3}-{3682,2983,0,0,3}" - }, - { - "npc_id": "3200", - "loc_data": "{3263,3932,0,1,3}" - }, - { - "npc_id": "3205", - "loc_data": "{3139,3448,0,1,3}" - }, - { - "npc_id": "3215", - "loc_data": "{2541,9672,0,1,3}" - }, - { - "npc_id": "3216", - "loc_data": "{2534,3273,0,0,1}-{2581,3332,0,1,3}" - }, - { - "npc_id": "3217", - "loc_data": "{2956,3370,0,1,3}" - }, - { - "npc_id": "3219", - "loc_data": "{3027,3344,0,1,4}" - }, - { - "npc_id": "3220", - "loc_data": "{3013,3338,0,1,4}" - }, - { - "npc_id": "3221", - "loc_data": "{3016,3334,0,1,3}-{3016,3339,0,1,6}-{3019,3341,0,1,4}" - }, - { - "npc_id": "3223", - "loc_data": "{2994,3365,0,1,4}" - }, - { - "npc_id": "3224", - "loc_data": "{3047,3365,0,1,6}" - }, - { - "npc_id": "3226", - "loc_data": "{2996,3375,0,1,4}" - }, - { - "npc_id": "3227", - "loc_data": "{2756,3182,0,1,6}" - }, - { - "npc_id": "3228", - "loc_data": "{2969,3373,0,1,3}-{2948,3353,0,1,3}-{3036,3357,0,1,3}" - }, - { - "npc_id": "3229", - "loc_data": "{2912,3344,0,1,7}-{3040,3354,0,1,2}" - }, - { - "npc_id": "3230", - "loc_data": "{2986,3370,0,1,1}" - }, - { - "npc_id": "3234", - "loc_data": "{3011,3383,0,1,0}" - }, - { - "npc_id": "3235", - "loc_data": "{2970,3369,1,1,6}" - }, - { - "npc_id": "3236", - "loc_data": "{2973,3369,1,1,6}" - }, - { - "npc_id": "3237", - "loc_data": "{2984,3435,0,0,0}" - }, - { - "npc_id": "3238", - "loc_data": "{2981,3434,0,0,0}" - }, - { - "npc_id": "3239", - "loc_data": "{2975,3436,0,0,0}" - }, - { - "npc_id": "3240", - "loc_data": "{2977,3442,0,0,0}" - }, - { - "npc_id": "3241", - "loc_data": "{2969,3451,0,1,4}" - }, - { - "npc_id": "3242", - "loc_data": "{2910,3332,0,1,0}-{2910,3337,0,1,4}-{2904,3331,0,1,3}-{2979,3568,0,1,7}-{2983,3566,0,1,3}-{2979,3566,0,1,3}-{2981,3570,0,1,6}-{2984,3561,0,1,6}" - }, - { - "npc_id": "3243", - "loc_data": "{2906,3331,0,1,6}-{2909,3333,0,1,6}" - }, - { - "npc_id": "3244", - "loc_data": "{2909,3333,0,1,1}" - }, - { - "npc_id": "3245", - "loc_data": "{2907,3336,0,1,0}" - }, - { - "npc_id": "3246", - "loc_data": "{3071,3446,0,1,6}-{2548,3567,0,1,4}" - }, - { - "npc_id": "3247", - "loc_data": "{2548,3567,0,1,1}" - }, - { - "npc_id": "3248", - "loc_data": "{3073,3442,0,1,5}" - }, - { - "npc_id": "3249", - "loc_data": "{3082,3441,0,1,0}" - }, - { - "npc_id": "3250", - "loc_data": "{3088,3426,0,1,3}-{2547,3571,0,1,7}" - }, - { - "npc_id": "3251", - "loc_data": "{3073,3422,0,1,6}" - }, - { - "npc_id": "3252", - "loc_data": "{3077,3424,0,1,6}" - }, - { - "npc_id": "3253", - "loc_data": "{2542,3578,0,1,3}" - }, - { - "npc_id": "3255", - "loc_data": "{3073,3423,0,1,3}" - }, - { - "npc_id": "3256", - "loc_data": "{3080,3415,0,1,3}-{3084,3415,0,1,6}" - }, - { - "npc_id": "3257", - "loc_data": "{3096,3432,0,1,0}" - }, - { - "npc_id": "3258", - "loc_data": "{3080,3444,0,1,3}" - }, - { - "npc_id": "3259", - "loc_data": "{3075,3438,0,1,4}" - }, - { - "npc_id": "3260", - "loc_data": "{3083,3425,0,1,3}-{2554,3564,0,1,6}-{2547,3558,0,1,6}" - }, - { - "npc_id": "3261", - "loc_data": "{3086,3425,0,1,3}" - }, - { - "npc_id": "3262", - "loc_data": "{3080,3423,0,1,1}" - }, - { - "npc_id": "3263", - "loc_data": "{3084,3419,0,1,4}" - }, - { - "npc_id": "3264", - "loc_data": "{3259,3338,0,1,7}-{3126,3450,0,1,6}-{3121,3422,0,1,3}-{2565,9843,0,1,0}-{3311,3375,0,1,0}" - }, - { - "npc_id": "3265", - "loc_data": "{3121,3427,0,1,2}-{2582,9836,0,1,1}" - }, - { - "npc_id": "3266", - "loc_data": "{3094,3406,0,1,3}" - }, - { - "npc_id": "3267", - "loc_data": "{3134,3459,0,1,1}" - }, - { - "npc_id": "3268", - "loc_data": "{3012,3454,0,1,1}" - }, - { - "npc_id": "3269", - "loc_data": "{3022,3447,0,1,4}" - }, - { - "npc_id": "3270", - "loc_data": "{3010,3452,0,1,3}" - }, - { - "npc_id": "3272", - "loc_data": "{3042,3468,0,1,1}" - }, - { - "npc_id": "3273", - "loc_data": "{3030,3465,0,1,5}" - }, - { - "npc_id": "3276", - "loc_data": "{3039,3475,0,1,4}" - }, - { - "npc_id": "3277", - "loc_data": "{3035,3471,0,1,2}" - }, - { - "npc_id": "3278", - "loc_data": "{3045,3477,0,1,1}" - }, - { - "npc_id": "3279", - "loc_data": "{3039,3471,0,1,6}" - }, - { - "npc_id": "3280", - "loc_data": "{3015,3451,0,0,0}" - }, - { - "npc_id": "3281", - "loc_data": "{3018,3450,0,0,0}" - }, - { - "npc_id": "3282", - "loc_data": "{3008,3442,0,0,0}" - }, - { - "npc_id": "3283", - "loc_data": "{3039,3424,0,1,5}-{3043,3429,0,1,4}-{3047,3427,0,1,1}-{2964,3455,0,0,0}-{3048,3441,0,1,3}" - }, - { - "npc_id": "3284", - "loc_data": "{3058,3419,0,0,0}-{3048,3423,0,1,6}-{2969,3437,0,0,0}-{3054,3432,0,1,5}" - }, - { - "npc_id": "3285", - "loc_data": "{3036,3430,0,1,3}-{3025,3442,0,0,0}-{2952,3376,0,0,0}" - }, - { - "npc_id": "3286", - "loc_data": "{3034,3450,0,1,3}-{3057,3447,0,1,6}-{2968,3448,0,1,1}" - }, - { - "npc_id": "3287", - "loc_data": "{3073,3443,0,1,0}-{3046,3453,0,1,6}-{2987,3444,0,1,0}" - }, - { - "npc_id": "3288", - "loc_data": "{3034,3437,0,1,0}-{2987,3446,0,1,7}" - }, - { - "npc_id": "3294", - "loc_data": "{3014,3339,0,1,2}" - }, - { - "npc_id": "3295", - "loc_data": "{3020,3337,0,1,1}" - }, - { - "npc_id": "3296", - "loc_data": "{2957,3359,0,1,0}-{2958,3358,0,1,0}-{2967,3360,0,1,0}-{2970,3361,0,1,0}-{2973,3359,0,1,0}-{2977,3357,0,1,0}-{2989,3384,0,1,0}" - }, - { - "npc_id": "3298", - "loc_data": "{3366,9637,2,1,3}" - }, - { - "npc_id": "3299", - "loc_data": "{3077,3257,0,1,1}" - }, - { - "npc_id": "3302", - "loc_data": "{2453,4449,0,1,2}" - }, - { - "npc_id": "3307", - "loc_data": "{2467,4438,0,1,4}-{2471,4439,0,1,7}-{2465,4436,0,1,3}-{2464,4432,0,1,4}" - }, - { - "npc_id": "3308", - "loc_data": "{2905,3333,2,1,0}" - }, - { - "npc_id": "3310", - "loc_data": "{2415,4451,0,1,6}-{2416,4435,0,1,7}-{2423,4432,0,1,4}" - }, - { - "npc_id": "3318", - "loc_data": "{2401,4378,0,0,0}" - }, - { - "npc_id": "3324", - "loc_data": "{3159, 3433, 0, 1, 0}" - }, - { - "npc_id": "3325", - "loc_data": "{3161,3432,1,1,4}" - }, - { - "npc_id": "3328", - "loc_data": "{3203,3344,0,0,0}" - }, - { - "npc_id": "3329", - "loc_data": "{3112,3414,0,0,0}" - }, - { - "npc_id": "3330", - "loc_data": "{3132,3508,0,0,0}" - }, - { - "npc_id": "3331", - "loc_data": "{3242,3240,0,0,4}" - }, - { - "npc_id": "3340", - "loc_data": "{1759,5182,0,1,0}" - }, - { - "npc_id": "3341", - "loc_data": "{1759,5191,0,1,0}-{1754,5200,0,1,0}-{1748,5223,0,1,0}-{1784,5199,0,1,0}-{1739,5154,0,1,0}" - }, - { - "npc_id": "3342", - "loc_data": "{1745,5208,0,1,0}-{1737,5215,0,1,0}-{1758,5210,0,1,0}-{1768,5175,0,1,0}-{1760,5152,0,1,0}-{1745,5169,0,1,0}" - }, - { - "npc_id": "3343", - "loc_data": "{1764,5198,0,1,0}-{1774,5195,0,1,0}-{1773,5149,0,1,0}-{1742,5292,0,1,0}" - }, - { - "npc_id": "3344", - "loc_data": "{2412,4470,0,0,0}-{2414,4467,0,0,0}-{2415,4475,0,0,0}-{2419,4467,0,0,0}-{2419,4474,0,0,0}" - }, - { - "npc_id": "3345", - "loc_data": "{2412,4370,0,0,0}-{2416,4376,0,0,0}-{2419,4374,0,0,0}-{2420,4377,0,0,0}-{2421,4370,0,0,0}" - }, - { - "npc_id": "3348", - "loc_data": "{2978,3343,0,1,4}-{2978,3337,0,1,1}-{2966,3337,1,1,6}-{2970,3330,2,1,1}" - }, - { - "npc_id": "3349", - "loc_data": "{2961,3330,2,1,0}-{2966,3352,2,1,1}-{2974,3328,2,1,6}" - }, - { - "npc_id": "3350", - "loc_data": "{2959,3340,2,1,3}" - }, - { - "npc_id": "3376", - "loc_data": "{3313,5453,0,1,3}-{3312,5452,0,1,3}-{3310,5452,0,1,6}-{3311,5462,0,1,5}-{3313,5554,0,1,6}-{3297,5562,0,1,1}-{3305,5554,0,1,1}-{3300,5540,0,1,1}-{2438,4393,0,1,1}" - }, - { - "npc_id": "3384", - "loc_data": "{3211,3225,0,0,0}" - }, - { - "npc_id": "3402", - "loc_data": "{2560,2842,0,0,1}" - }, - { - "npc_id": "3407", - "loc_data": "{3114,3515,0,1,4}" - }, - { - "npc_id": "3408", - "loc_data": "{3113,3510,0,1,7}" - }, - { - "npc_id": "3419", - "loc_data": "{2496,3092,0,1,0}-{2497,3089,0,1,0}-{2500,3092,0,1,0}-{2500,3097,0,1,0}-{2503,3095,0,1,0}-{2504,3133,0,1,0}-{2585,9608,0,1,0}-{2586,9614,0,1,0}-{2588,9606,0,1,0}-{2561,3020,0,1,0}-{2567,3046,0,1,0}-{2387,9685,0,1,0}-{2389,9683,0,1,0}-{2513,3026,0,1,0}-{2522,3055,0,1,0}-{2523,3057,0,1,0}-{2529,3032,0,1,0}-{2539,3018,0,1,0}-{2541,3016,0,1,0}-{2550,3043,0,1,0}-{2550,3046,0,1,0}-{2553,3047,0,1,0}-{2719,9668,0,1,0}-{2719,9715,0,1,0}-{2720,9702,0,1,0}-{2722,9669,0,1,0}-{2723,9707,0,1,0}-{2724,9713,0,1,0}-{2504,2965,0,1,0}-{2504,2984,0,1,0}-{2520,2973,0,1,0}-{2525,2986,0,1,0}-{2533,2977,0,1,0}-{2536,2975,0,1,0}-{2539,2990,0,1,0}-{2549,2979,0,1,0}-{2551,2955,0,1,0}-{2552,2961,0,1,0}-{2581,9738,0,1,0}-{2581,9742,0,1,0}-{2584,9734,0,1,0}-{2585,9738,0,1,0}-{2587,9733,0,1,0}-{2587,9740,0,1,0}-{2590,9737,0,1,0}-{2568,2991,0,1,0}-{2574,2988,0,1,0}-{2584,2967,0,1,0}-{2585,2977,0,1,0}-{2592,2965,0,1,0}-{2608,2980,0,1,0}-{2609,2990,0,1,0}-{2614,2998,0,1,0}-{2554,3192,0,1,0}" - }, - { - "npc_id": "3450", - "loc_data": "{2901,2947,0,0,0}-{2925,2959,0,0,0}-{2920,3053,0,0,0}-{2924,3063,0,0,0}" - }, - { - "npc_id": "3469", - "loc_data": "{2657,2966,0,0,0}" - }, - { - "npc_id": "3470", - "loc_data": "{2755,3083,0,0,0}" - }, - { - "npc_id": "3479", - "loc_data": "{2906,3514,1,0,0}" - }, - { - "npc_id": "3541", - "loc_data": "{3517,3240,0,1,6}" - }, - { - "npc_id": "3587", - "loc_data": "" - }, - { - "npc_id": "3638", - "loc_data": "{3309,3453,0,0,1}" - }, - { - "npc_id": "3661", - "loc_data": "{3276,9836,0,1,0}-{3287,9840,0,1,0}" - }, - { - "npc_id": "3662", - "loc_data": "{3280,9849,0,1,0}-{3286,9830,0,1,0}" - }, - { - "npc_id": "3663", - "loc_data": "{3269,9849,0,1,0}-{3291,9843,0,1,0}" - }, - { - "npc_id": "3664", - "loc_data": "{3269,9843,0,1,0}-{3275,9833,0,1,0}" - }, - { - "npc_id": "3665", - "loc_data": "{3305,9832,0,1,0}-{3305,9843,0,1,0}-{3311,9850,0,1,0}-{3312,9845,0,1,0}-{3313,9827,0,1,0}-{3315,9837,0,1,0}-{3319,9832,0,1,0}-{3319,9849,0,1,0}-{3323,9842,0,1,0}" - }, - { - "npc_id": "3666", - "loc_data": "{3302,9806,0,1,0}-{3302,9816,0,1,0}-{3304,9801,0,1,0}-{3310,9796,0,1,0}-{3312,9811,0,1,0}-{3313,9805,0,1,0}-{3313,9817,0,1,0}-{3314,9797,0,1,0}-{3319,9800,0,1,0}-{3319,9806,0,1,0}-{3321,9811,0,1,0}" - }, - { - "npc_id": "3667", - "loc_data": "{3269,9798,0,1,0}-{3269,9806,0,1,0}-{3269,9814,0,1,0}-{3275,9817,0,1,0}-{3276,9809,0,1,0}-{3280,9803,0,1,0}-{3282,9797,0,1,0}-{3285,9812,0,1,0}-{3288,9819,0,1,0}-{3289,9809,0,1,0}-{3290,9797,0,1,0}" - }, - { - "npc_id": "3670", - "loc_data": "{3361,3505,0,1,0}" - }, - { - "npc_id": "3671", - "loc_data": "{3085,3251,0,0,3}" - }, - { - "npc_id": "3673", - "loc_data": "{2322,3189,0,1,0}-{2924,3318,0,1,0}-{2924,3325,0,1,4}-{3248,3351,0,1,0}-{3195,3265,0,1,0}-{2653,4716,0,1,0}-{2656,3359,0,1,0}-{2656,3375,0,1,0}" - }, - { - "npc_id": "3675", - "loc_data": "{3321,2857,0,1,3}-{3322,2861,0,0,4}-{3319,2855,0,1,1}-{3319,2853,0,1,6}-{3331,2791,0,1,1}-{3333,2803,0,1,2}-{3338,2801,0,1,4}-{3338,2776,0,1,3}" - }, - { - "npc_id": "3677", - "loc_data": "{2637,3440,0,0,0}" - }, - { - "npc_id": "3679", - "loc_data": "{2875,3482,0,0,4}" - }, - { - "npc_id": "3680", - "loc_data": "{3282,3327,0,1,7}" - }, - { - "npc_id": "3695", - "loc_data": "{2661,3701,0,1,0}" - }, - { - "npc_id": "3696", - "loc_data": "{2673,3658,0,1,3}" - }, - { - "npc_id": "3707", - "loc_data": "{2713,10133,0,1,2}-{2708,10133,0,1,4}-{2706,10135,0,0,3}-{2708,10133,0,1,3}-{2703,10135,0,1,6}-{2702,10131,0,1,4}-{2717,10133,0,1,0}-{2708,10130,0,1,1}" - }, - { - "npc_id": "3709", - "loc_data": "{2974,2906,0,0,0}-{2847,5075,0,0,0}" - }, - { - "npc_id": "3711", - "loc_data": "{2575,9746,0,1,0}-{2581,9747,0,1,0}-{2577,9743,0,1,1}-{2579,9740,0,1,0}-{2588,9733,0,1,4}" - }, - { - "npc_id": "3777", - "loc_data": "{3230,3231,0,1,5}" - }, - { - "npc_id": "3778", - "loc_data": "{1867,4233,0,0,0}" - }, - { - "npc_id": "3780", - "loc_data": "{1906,4281,0,0,0}" - }, - { - "npc_id": "3781", - "loc_data": "{2659,2675,0,1,1}" - }, - { - "npc_id": "3786", - "loc_data": "{2658,2666,0,1,1}" - }, - { - "npc_id": "3787", - "loc_data": "{2754,3509,0,0,0}" - }, - { - "npc_id": "3788", - "loc_data": "{2664,2653,0,1,3}" - }, - { - "npc_id": "3789", - "loc_data": "{2660,2649,0,1,3}" - }, - { - "npc_id": "3790", - "loc_data": "{3041,3203,0,0,6}" - }, - { - "npc_id": "3791", - "loc_data": "{2653,2655,0,1,1}" - }, - { - "npc_id": "3793", - "loc_data": "{2659,2660,0,1,6}" - }, - { - "npc_id": "3796", - "loc_data": "{2667,2661,0,0,3}" - }, - { - "npc_id": "3797", - "loc_data": "{2649,2659,0,0,2}" - }, - { - "npc_id": "3798", - "loc_data": "{2658,2652,0,1,6}" - }, - { - "npc_id": "3799", - "loc_data": "{2650,2663,0,0,4}" - }, - { - "npc_id": "3802", - "loc_data": "{2657,2637,0,0,6}" - }, - { - "npc_id": "3804", - "loc_data": "{3349,3793,0,0,0}" - }, - { - "npc_id": "3805", - "loc_data": "{2445,3097,0,1,5}" - }, - { - "npc_id": "3806", - "loc_data": "{3166,3304,0,1,0}" - }, - { - "npc_id": "3807", - "loc_data": "{3254,3274,0,0,0}" - }, - { - "npc_id": "3808", - "loc_data": "{2517,3205,0,1,3}-{2513,3204,0,1,3}-{2422,3526,0,1,1}" - }, - { - "npc_id": "3809", - "loc_data": "{3285,3213,0,1,1}" - }, - { - "npc_id": "3810", - "loc_data": "{2847,3499,0,1,7}-{2970,2973,0,0,0}" - }, - { - "npc_id": "3811", - "loc_data": "{2918,3057,0,0,0}-{2894,2730,0,1,2}-{2465,3501,3,1,3}" - }, - { - "npc_id": "3813", - "loc_data": "{2541,2969,0,1,4}" - }, - { - "npc_id": "3817", - "loc_data": "{2521,3208,0,1,1}" - }, - { - "npc_id": "3819", - "loc_data": "{2416,3525,0,1,3}-{2412,3527,0,1,6}" - }, - { - "npc_id": "3820", - "loc_data": "{3088,3255,0,0,6}" - }, - - { - "npc_id": "3822", - "loc_data": "{2353,3680,0,1,3}" - }, - { - "npc_id": "3823", - "loc_data": "{2343,3668,0,1,3}" - }, - { - "npc_id": "3824", - "loc_data": "{2329,3689,0,1,6}" - }, - { - "npc_id": "3831", - "loc_data": "{2357,3637,0,0,0}" - }, - { - "npc_id": "3832", - "loc_data": "{2345,3651,0,0,0}" - }, - { - "npc_id": "3848", - "loc_data": "{2337,3703,0,0,6}-{2340,3702,0,0,6}-{2343,3702,0,0,3}-{2349,3702,0,0,6}-{2353,3703,0,0,6}-{2310,3704,0,0,6}-{2311,3696,0,0,4}-{2343,3702,0,0,3}" - }, - { - "npc_id": "3851", - "loc_data": "{2332,3669,0,0,0}-{2325,3671,0,0,4}-{2331,3670,0,0,1}-{2333,3671,0,0,3}" - }, - { - "npc_id": "3915", - "loc_data": "{2945,3146,0,1,3}" - }, - { - "npc_id": "3916", - "loc_data": "{2595,3890,0,0,3}" - }, - { - "npc_id": "3917", - "loc_data": "{2581,3861,0,0,1}" - }, - { - "npc_id": "3920", - "loc_data": "{2527,10259,0,1,1}" - }, - { - "npc_id": "3921", - "loc_data": "{2506,10251,0,1,1}" - }, - { - "npc_id": "3922", - "loc_data": "{2508,10260,0,1,1}" - }, - { - "npc_id": "3923", - "loc_data": "{2521,10251,0,1,1}" - }, - { - "npc_id": "3937", - "loc_data": "{2508,3866,0,0,3}" - }, - { - "npc_id": "4235", - "loc_data": "{2669,3332,0,0,1}" - }, - { - "npc_id": "4237", - "loc_data": "{2669,3331,0,0,6}" - }, - { - "npc_id": "4239", - "loc_data": "{2669,3331,0,0,6}" - }, - { - "npc_id": "4241", - "loc_data": "{2669,3331,0,0,6}" - }, - { - "npc_id": "4243", - "loc_data": "{2669,3331,0,0,6}" - }, - { - "npc_id": "4246", - "loc_data": "{3480,3484,1,0,0}-{3479,3484,0,1,3}" - }, - { - "npc_id": "4247", - "loc_data": "{2639,3291,0,0,0}-{2981,3370,0,0,0}-{3240,3474,0,0,0}-{2737,3500,0,1,6}" - }, - { - "npc_id": "4249", - "loc_data": "{2981,3341,1,0,0}" - }, - { - "npc_id": "4250", - "loc_data": "{3302,3492,0,1,6}" - }, - { - "npc_id": "4251", - "loc_data": "{3009,3380,0,1,0}" - }, - { - "npc_id": "4261", - "loc_data": "{2569,9851,0,1,3}-{2579,9851,0,1,4}" - }, - { - "npc_id": "4263", - "loc_data": "{2584,9838,0,1,6}" - }, - { - "npc_id": "4264", - "loc_data": "{2588,9837,0,1,4}" - }, - { - "npc_id": "4269", - "loc_data": "{2608,9817,0,1,3}" - }, - { - "npc_id": "4270", - "loc_data": "{2593,9820,0,1,4}" - }, - { - "npc_id": "4271", - "loc_data": "{2596,9822,0,1,0}" - }, - { - "npc_id": "4273", - "loc_data": "{2564,9851,0,1,4}" - }, - { - "npc_id": "4274", - "loc_data": "{2586,9826,0,1,7}-{2569,9849,0,1,4}-{2581,9847,0,1,0}-{2609,9826,0,1,1}" - }, - { - "npc_id": "4275", - "loc_data": "{2594,9817,0,1,0}" - }, - { - "npc_id": "4276", - "loc_data": "{2581,9851,0,1,1}" - }, - { - "npc_id": "4285", - "loc_data": "{2880,3546,0,0,4}" - }, - { - "npc_id": "8267", - "loc_data": "{2866,3546,0,1,1}" - }, - { - "npc_id": "4287", - "loc_data": "{2840,3541,1,0,2}" - }, - { - "npc_id": "4288", - "loc_data": "{2851,3554,0,1,6}" - }, - { - "npc_id": "4289", - "loc_data": "{2792,3488,2,0,0}-{2845,3539,2,0,7}" - }, - { - "npc_id": "4290", - "loc_data": "{2854,3542,0,1,3}" - }, - { - "npc_id": "4291", - "loc_data": "{2863,3546,2,1,3}-{2870,3551,2,1,0}-{2850,3547,2,1,0}-{2844,3552,2,1,5}-{2843,3547,2,1,5}-{2872,3542,2,1,3}-{2861,3551,2,1,1}-{2865,3538,2,1,2}" - }, - { - "npc_id": "4292", - "loc_data": "{2847,3541,2,1,3}-{2855,3554,2,1,5}-{2860,3542,2,1,0}-{2869,3554,2,1,3}-{2872,3537,2,1,4}-{2850,3534,2,1,3}-{2857,3546,2,1,0}-{2838,3554,2,1,6}-{2869,3552,2,1,4}" - }, - { - "npc_id": "4293", - "loc_data": "{2840,3554,0,1,1}" - }, - { - "npc_id": "4294", - "loc_data": "{2847,3553,0,1,6}" - }, - { - "npc_id": "4295", - "loc_data": "{2856,3537,1,1,1}" - }, - { - "npc_id": "4296", - "loc_data": "{2841,3543,0,0,4}" - }, - { - "npc_id": "4297", - "loc_data": "{2856,3549,1,0,2}" - }, - { - "npc_id": "4298", - "loc_data": "{2864,3541,1,1,3}" - }, - { - "npc_id": "4299", - "loc_data": "{2866,3556,1,0,5}" - }, - { - "npc_id": "4300", - "loc_data": "{2865,3544,1,0,6}" - }, - { - "npc_id": "4312", - "loc_data": "{3323,9609,0,0,6}" - }, - { - "npc_id": "4315", - "loc_data": "{3224,3286,0,0,0}" - }, - { - "npc_id": "4317", - "loc_data": "{3230,3285,0,0,0}" - }, - { - "npc_id": "4344", - "loc_data": "{3747,3013,0,1,4}-{3766,3034,0,1,6}-{3733,2972,0,1,4}" - }, - { - "npc_id": "4346", - "loc_data": "{3786,9463,0,1,6}-{3714,2952,0,1,1}" - }, - { - "npc_id": "4347", - "loc_data": "{3728,3043,0,1,4}-{3725,3015,0,1,0}-{3722,3021,0,1,6}-{3717,3017,0,1,7}-{3723,3019,0,1,5}-{3731,3018,0,1,1}-{3734,3028,0,1,7}-{3743,3038,0,1,4}-{3749,2978,0,1,7}-{3756,2966,0,1,2}-{3759,2962,0,1,2}-{3749,2968,0,1,2}-{3757,2980,0,0,5}-{3742,3036,0,1,1}-{3747,3040,0,1,4}-{3748,3030,0,1,6}-{3748,3019,0,1,1}-{3745,3016,0,1,4}-{3741,3020,0,1,3}-{3727,3039,0,1,3}-{3734,3046,0,1,1}-{3736,3050,0,1,6}-{3735,3053,0,1,3}-{3741,3053,0,1,6}-{3756,3030,0,1,3}-{3754,3024,0,1,4}-{3769,3039,0,1,6}-{3766,3045,0,1,7}-{3764,3042,0,1,1}" - }, - { - "npc_id": "4348", - "loc_data": "{3676,3017,0,1,3}-{3687,3018,0,1,3}-{3707,3015,0,1,6}-{3729,3021,0,1,3}" - }, - { - "npc_id": "4349", - "loc_data": "{3664,3018,0,1,6}-{3658,3012,0,1,4}-{3718,3030,0,1,4}-{3757,3009,0,1,3}-{3729,2984,0,1,4}-{3720,3000,0,1,4}-{3727,3051,0,1,4}-{3809,3028,0,1,6}" - }, - { - "npc_id": "4350", - "loc_data": "{3680,3026,0,1,4}-{3675,3029,0,1,3}-{3711,3028,0,1,4}-{3733,3011,0,1,4}-{3793,3036,0,1,4}-{3698,3046,0,1,6}" - }, - { - "npc_id": "4351", - "loc_data": "{3694,3019,0,1,4}-{3760,3015,0,1,1}-{3758,3034,0,1,2}-{3728,2965,0,1,7}-{3787,3026,0,1,6}-{3714,3049,0,1,7}-{3770,3008,0,1,4}" - }, - { - "npc_id": "4352", - "loc_data": "{3683,3034,0,1,4}-{3719,3037,0,1,7}-{3770,3027,0,1,5}-{3747,3007,0,1,4}-{3735,2957,0,1,2}-{3719,3011,0,1,3}-{3727,3003,0,1,3}-{3787,3035,0,1,1}" - }, - { - "npc_id": "4353", - "loc_data": "{3719,9358,0,1,6}-{3738,9378,0,1,0}-{3765,9422,0,1,1}-{3741,9436,0,1,6}-{3727,9455,0,1,2}-{3780,9456,0,1,2}-{3827,9430,0,1,2}-{3737,9413,0,1,0}-{3735,9444,0,1,3}-{3819,9422,0,1,2}" - }, - { - "npc_id": "4354", - "loc_data": "{3739,9369,0,1,0}-{3737,9350,0,1,4}-{3721,9349,0,1,4}-{3733,9396,0,1,6}-{3813,9445,0,1,0}-{3749,9461,0,1,0}-{3786,9443,0,1,6}-{3783,9430,0,1,5}-{3821,9443,0,1,4}-{3746,9423,0,1,0}-{3764,9404,0,1,6}-{3790,9456,0,1,7}-{3760,9417,0,1,3}-{3760,9451,0,1,6}-{3732,9368,0,1,1}" - }, - { - "npc_id": "4355", - "loc_data": "{3728,9349,0,1,3}-{3749,9413,0,1,4}-{3810,9448,0,1,4}-{3752,9398,0,1,4}-{3721,9419,0,1,3}-{3786,9412,0,1,3}-{3797,9452,0,1,4}-{3754,9408,0,1,4}-{3723,9377,0,1,4}" - }, - { - "npc_id": "4356", - "loc_data": "{3755,9391,0,1,5}-{3723,9441,0,1,4}-{3766,9459,0,1,4}-{3800,9414,0,1,2}-{3821,9418,0,1,3}-{3810,9462,0,1,0}-{3809,9440,0,1,2}-{3748,9450,0,1,6}-{3765,9424,0,1,2}-{3787,9464,0,1,0}" - }, - { - "npc_id": "4357", - "loc_data": "{3771,9450,0,1,4}-{3730,9443,0,1,4}-{3808,9423,0,1,7}-{3797,9427,0,1,4}-{3833,9438,0,1,6}-{3834,9458,0,1,6}-{3797,9440,0,1,1}-{3793,9441,0,1,4}-{3829,9425,0,1,7}" - }, - { - "npc_id": "4358", - "loc_data": "{3751,2977,0,1,2}" - }, - { - "npc_id": "4359", - "loc_data": "{3669,2977,0,1,3}" - }, - { - "npc_id": "4375", - "loc_data": "{3012,3192,1,0,2}" - }, - { - "npc_id": "4376", - "loc_data": "{3081,3421,0,1,3}" - }, - { - "npc_id": "4381", - "loc_data": "{2361,5241,0,1,1}-{2358,5240,0,1,1}-{2362,5239,0,1,1}-{2364,5240,0,1,1}-{2364,5242,0,1,1}-{2357,5242,0,1,1}-{2356,5240,0,1,1}-{2356,5242,0,1,1}-{2360,5243,0,1,1}" - }, - { - "npc_id": "4382", - "loc_data": "{3139,3747,0,1,0}-{3141,3737,0,1,0}-{3157,3735,0,1,0}-{3165,3746,0,1,0}-{3164,3727,0,1,0}-{3172,3713,0,1,0}-{3177,3724,0,1,0}-{2324,5210,0,1,2}-{2322,5201,0,1,7}-{2324,5210,0,1,2}-{2322,5201,0,1,7}-{2327,5205,0,1,7}-{2328,5200,0,1,7}-{2326,5198,0,1,7}-{2331,5198,0,1,4}-{2333,5201,0,1,4}-{2333,5199,0,1,2}" - }, - { - "npc_id": "4383", - "loc_data": "{2321,5225,0,1,3}-{2321,5223,0,1,4}-{2317,5229,0,1,7}-{2322,5225,0,1,1}-{2325,5231,0,1,6}-{2321,5225,0,1,1}-{2314,5227,0,1,2}-{2312,5229,0,1,2}-{2330,5229,0,1,1}-{2324,5233,0,1,4}-{2320,5232,0,1,3}-{2324,5237,0,1,1}-{2329,5229,0,1,6}-{2315,5227,0,1,7}-{2318,5226,0,1,6}-{2322,5222,0,1,6}" - }, - { - "npc_id": "4384", - "loc_data": "{2355,5204,0,1,3}-{2347,5195,0,1,0}-{2336,5202,0,1,4}-{2332,5201,0,1,3}-{2327,5201,0,1,1}-{2328,5199,0,1,6}" - }, - { - "npc_id": "4385", - "loc_data": "{2350,5201,0,1,1}-{2353,5192,0,1,1}-{2324,5203,0,1,1}-{2333,5204,0,1,4}-{2264,5136,0,1,6}-{2326,5197,0,1,5}" - }, - { - "npc_id": "4386", - "loc_data": "{3097,3728,0,1,0}-{3130,3754,0,1,0}-{3128,3719,0,1,0}-{2311,5187,0,1,6}" - }, - { - "npc_id": "4387", - "loc_data": "{2350,5192,0,1,6}-{2350,5191,0,1,4}-{2347,5188,0,1,2}" - }, - { - "npc_id": "4388", - "loc_data": "{2305,5231,0,1,2}-{2312,5225,0,1,2}-{2314,5240,0,1,2}-{2310,5241,0,1,2}-{2308,5246,0,1,2}-{2309,5245,0,1,2}-{2315,5237,0,1,2}" - }, - { - "npc_id": "4389", - "loc_data": "{2013,5238,0,1,0}-{2043,5233,0,1,0}-{2041,5231,0,1,0}--{2029,5233,0,1,0}-{2020,5238,0,1,0}" - }, - { - "npc_id": "4390", - "loc_data": "{2040,5190,0,1,3}-{2044,5187,0,1,2}-{2042,5191,0,1,1}-{2045,5190,0,1,7}-{2041,5187,0,1,2}-{2045,5186,0,1,2}-{2044,5188,0,1,2}-{2042,5192,0,1,2}" - }, - { - "npc_id": "4391", - "loc_data": "{1952,5192,0,1,2}-{1993,5240,0,1,4}-{1991,5235,0,1,4}-{1990,5240,0,1,3}-{1989,5237,0,1,6}-{2009,5202,0,1,1}-{2005,5199,0,1,3}-{1994,5236,0,1,6}-{1991,5242,0,1,6}-{2007,5201,0,1,6}-{2009,5201,0,1,6}-{2004,5197,0,1,6}-{2004,5207,0,1,6}-{2006,5205,0,1,6}" - }, - { - "npc_id": "4392", - "loc_data": "{2033,5233,0,1,4}-{2033,5233,0,1,7}-{2026,5236,0,1,1}" - }, - { - "npc_id": "4393", - "loc_data": "{2045,5222,0,1,4}-{1993,5190,0,1,6}-{1993,5190,0,1,4}-{2044,5216,0,1,4}-{2008,5203,0,1,1}-{2006,5208,0,1,1}-{2037,5214,0,1,3}-{2037,5210,0,1,1}-{2043,5217,0,1,1}-{2040,5214,0,1,1}-{2042,5217,0,1,1}-{2038,5214,0,1,1}-{2041,5218,0,1,1}" - }, - { - "npc_id": "4394", - "loc_data": "{1997,5244,0,1,3}-{1992,5238,0,1,5}-{1990,5234,0,1,1}-{2010,5191,0,1,7}-{2018,5188,0,1,4}-{1989,5231,0,1,4}-{2018,5187,0,1,6}" - }, - { - "npc_id": "4395", - "loc_data": "{2016,5237,0,1,7}-{1988,5190,0,1,2}-{2039,5216,0,1,6}-{2017,5238,0,1,6}" - }, - { - "npc_id": "4396", - "loc_data": "{2034,5243,0,1,5}-{2034,5239,0,1,5}-{2032,5243,0,1,4}-{2034,5232,0,1,1}-{2033,5240,0,1,1}-{2029,5230,0,1,6}-{2030,5234,0,1,4}-{2029,5230,0,1,1}-{2029,5233,0,1,2}-{2031,5244,0,1,1}-{2039,5208,0,1,4}-{2029,5235,0,1,6}-{2028,5234,0,1,6}-{2030,5237,0,1,4}-{2033,5238,0,1,4}-{2030,5238,0,1,4}-{2032,5238,0,1,3}-{2027,5238,0,1,6}-{2034,5239,0,1,0}-{2016,5235,0,1,5}-{2015,5237,0,1,3}-{2004,5244,0,1,4}-{2005,5241,0,1,4}-{2035,5245,0,1,5}-{2004,5240,0,1,1}-{2027,5235,0,1,5}-{2027,5235,0,1,4}-{2027,5236,0,1,6}-{1999,5244,0,1,4}-{1995,5234,0,1,6}-{1993,5242,0,1,1}-{1990,5239,0,1,1}-{1990,5243,0,1,7}-{1989,5230,0,1,6}-{1990,5224,0,1,5}-{1993,5222,0,1,7}-{1986,5221,0,1,3}-{1990,5221,0,1,3}-{1986,5222,0,1,6}-{2002,5215,0,1,1}-{1996,5215,0,1,1}-{1994,5215,0,1,2}-{2002,5215,0,1,6}-{1995,5208,0,1,1}-{1993,5206,0,1,4}-{1986,5204,0,1,3}-{1986,5201,0,1,6}-{2001,5201,0,1,6}-{1987,5200,0,1,3}-{1986,5199,0,1,6}-{2009,5198,0,1,6}-{1995,5197,0,1,3}-{2009,5194,0,1,3}-{2005,5191,0,1,3}-{2004,5190,0,1,4}-{1991,5190,0,1,1}-{2003,5185,0,1,4}-{2004,5190,0,1,4}-{2004,5188,0,1,1}-{2005,5188,0,1,6}-{2008,5192,0,1,6}-{2008,5193,0,1,3}-{2009,5200,0,1,7}-{2010,5192,0,1,4}-{2010,5194,0,1,4}-{2013,5190,0,1,6}-{2018,5189,0,1,6}-{2018,5192,0,1,6}-{2018,5187,0,1,4}-{1993,5205,0,1,4}-{1994,5206,0,1,3}-{1989,5206,0,1,0}-{1993,5210,0,1,3}-{1989,5200,0,1,4}-{2002,5215,0,1,1}-{2022,5193,0,1,3}-{1995,5220,0,1,1}-{1992,5222,0,1,6}-{1992,5222,0,1,6}-{1988,5201,0,1,4}-{1988,5224,0,1,4}-{1989,5206,0,1,4}-{1987,5225,0,1,4}-{1988,5225,0,1,4}-{1986,5204,0,1,3}-{1987,5226,0,1,4}-{1990,5227,0,1,3}-{1993,5200,0,1,7}-{1990,5228,0,1,4}-{2004,5200,0,1,6}-{1990,5227,0,1,4}-{1990,5224,0,1,6}-{1994,5223,0,1,4}-{2036,5201,0,1,6}-{2038,5215,0,1,4}-{2042,5208,0,1,1}-{2045,5204,0,1,1}-{2046,5205,0,1,3}-{2028,5195,0,1,3}-{2026,5195,0,1,0}-{2039,5193,0,1,3}-{2025,5193,0,1,1}-{2022,5193,0,1,6}-{2043,5192,0,1,2}-{2018,5192,0,1,1}-{2045,5192,0,1,1}-{2032,5190,0,1,1}-{2030,5189,0,1,4}-{2024,5187,0,1,3}-{2038,5186,0,1,4}-{2038,5185,0,1,3}-{2033,5185,0,1,3}-{2016,5186,0,1,1}-{2017,5194,0,1,1}-{2024,5193,0,1,4}-{2008,5201,0,1,4}-{2005,5220,0,1,1}-{2005,5204,0,1,4}-{2037,5203,0,1,3}-{2037,5206,0,1,4}-{2041,5208,0,1,1}" - }, - { - "npc_id": "4397", - "loc_data": "{2072,5200,0,1,5}-{2145,5251,0,1,3}-{2153,5253,0,1,1}-{2149,5255,0,1,3}-{2146,5252,0,1,5}-{2146,5261,0,1,6}-{2147,5254,0,1,4}" - }, - { - "npc_id": "4398", - "loc_data": "{2160,5282,0,1,3}-{2162,5284,0,1,1}-{2164,5281,0,1,7}" - }, - { - "npc_id": "4399", - "loc_data": "{2122,5290,0,1,0}-{2131,5297,0,1,6}-{2119,5297,0,1,4}-{2120,5302,0,1,3}-{2080,5240,0,1,2}-{2129,5306,0,1,3}-{2123,5304,0,1,1}-{2130,5306,0,1,4}-{2163,5301,0,1,5}-{2166,5306,0,1,0}-{2169,5303,0,1,4}-{2122,5291,0,1,0}-{2119,5296,0,1,3}-{2131,5297,0,1,6}-{2125,5302,0,1,0}-{2127,5304,0,1,4}-{2165,5302,0,1,3}-{2163,5301,0,1,4}-{2168,5306,0,1,4}-{2132,5300,0,1,6}-{2132,5302,0,1,4}" - }, - { - "npc_id": "4400", - "loc_data": "{3018,10313,0,1,4}-{3016,10315,0,1,4}-{3018,10322,0,1,4}-{3024,10326,0,1,3}-{3030,10332,0,1,4}-{3029,10337,0,1,6}-{3034,10342,0,1,3}-{2072,5200,0,1,7}-{2130,5267,0,1,7}-{2140,5252,0,1,5}-{2141,5252,0,1,1}-{2142,5252,0,1,0}-{2148,5255,0,1,3}-{2125,5270,0,1,1}-{2148,5257,0,1,1}-{2117,5274,0,1,4}-{2146,5307,0,1,2}-{2145,5305,0,1,5}-{2152,5307,0,1,4}-{2148,5252,0,1,0}-{2142,5262,0,1,3}-{2124,5275,0,1,4}-{2121,5276,0,1,3}-{2148,5304,0,1,0}-{2146,5305,0,1,5}-{2148,5304,0,1,1}-{2151,5304,0,1,6}-{2145,5306,0,1,3}-{2134,5272,0,1,6}-{2151,5307,0,1,6}-{2151,5308,0,1,6}-{2149,5307,0,1,6}-{2150,5308,0,1,6}-{2150,5305,0,1,6}-{2153,5307,0,1,6}-{2152,5305,0,1,6}-{2128,5272,0,1,6}-{2129,5272,0,1,6}-{2124,5271,0,1,6}-{2124,5272,0,1,6}-{2122,5272,0,1,6}-{2121,5275,0,1,6}" - }, - { - "npc_id": "4401", - "loc_data": "{2124,5273,0,1,1}-{2072,5200,0,1,6}-{2128,5270,0,1,4}-{2117,5274,0,1,3}-{2154,5273,0,1,3}-{2167,5259,0,1,6}-{2170,5257,0,1,1}-{2170,5256,0,1,0}-{2171,5256,0,1,4}-{2167,5256,0,1,3}-{2153,5273,0,1,3}-{2153,5270,0,1,1}-{2171,5255,0,1,2}-{2168,5250,0,1,4}-{2168,5251,0,1,3}-{2131,5264,0,1,6}-{2096,5248,0,1,6}-{2157,5272,0,1,6}-{2150,5268,0,1,0}-{2133,5272,0,1,2}-{2157,5274,0,1,1}-{2156,5270,0,1,3}-{2154,5271,0,1,5}" - }, - { - "npc_id": "4402", - "loc_data": "{2080,5240,0,1,5}-{2171,5283,0,1,6}-{2170,5287,0,1,6}-{2170,5281,0,1,6}-{2172,5272,0,1,4}-{2172,5272,0,1,4}" - }, - { - "npc_id": "4403", - "loc_data": "{2096,5248,0,1,4}-{2172,5276,0,1,1}-{2172,5284,0,1,1}-{2172,5281,0,1,2}" - }, - { - "npc_id": "4404", - "loc_data": "{1872,5242,0,1,3}-{1873,5230,0,1,2}-{1870,5235,0,1,3}-{1872,5217,0,1,6}-{1873,5212,0,1,4}-{1877,5218,0,1,2}-{1877,5216,0,1,3}-{1875,5218,0,1,6}-{1871,5191,0,1,4}-{1868,5188,0,1,4}-{1861,5189,0,1,3}-{1882,5216,0,1,6}-{1874,5215,0,1,5}-{1873,5238,0,1,3}" - }, - { - "npc_id": "4406", - "loc_data": "{1898,5197,0,1,6}-{1898,5191,0,1,7}-{1890,5195,0,1,3}-{1893,5191,0,1,4}-{1903,5190,0,1,0}-{1889,5190,0,1,3}-{1889,5190,0,1,4}-{1885,5191,0,1,4}-{1900,5208,0,1,1}-{1900,5210,0,1,1}-{1903,5243,0,1,6}-{1848,5176,0,1,2}-{1901,5245,0,1,4}-{1894,5243,0,1,5}-{1890,5242,0,1,6}-{1889,5244,0,1,4}-{1897,5196,0,1,1}-{1902,5207,0,1,3}" - }, - { - "npc_id": "4407", - "loc_data": "{1860,5226,0,1,0}-{1859,5200,0,1,0}-{1860,5222,0,1,4}-{1865,5205,0,1,4}-{1862,5205,0,1,7}-{1862,5205,0,1,7}-{1864,5206,0,1,7}" - }, - { - "npc_id": "4408", - "loc_data": "{1860,5215,0,1,5}-{1864,5218,0,1,1}" - }, - { - "npc_id": "4409", - "loc_data": "{1864,5226,0,1,1}-{1859,5220,0,1,4}-{1861,5224,0,1,6}" - }, - { - "npc_id": "4410", - "loc_data": "{1890,5205,0,1,6}-{1897,5232,0,1,6}-{1906,5236,0,1,3}-{1848,5176,0,1,2}-{1904,5235,0,1,6}-{1895,5227,0,1,3}-{1863,5221,0,1,4}" - }, - { - "npc_id": "4411", - "loc_data": "{1882,5201,0,1,0}-{1884,5207,0,1,3}-{1912,5239,0,1,4}-{1912,5244,0,1,5}-{1890,5204,0,1,5}" - }, - { - "npc_id": "4412", - "loc_data": "{1880,5200,0,1,6}-{1911,5241,0,1,0}-{1893,5228,0,1,3}" - }, - { - "npc_id": "4413", - "loc_data": "{1871,5228,0,1,6}-{1872,5233,0,1,1}-{2645,3580,0,1,4}" - }, - { - "npc_id": "4414", - "loc_data": "{1890,5215,0,1,5}-{1892,5216,0,1,1}-{1889,5223,0,1,3}-{1892,5224,0,1,2}-{1893,5219,0,1,5}" - }, - { - "npc_id": "4415", - "loc_data": "{1874,5230,0,1,3}-{1870,5232,0,1,4}-{1858,5235,0,1,3}-{1871,5241,0,1,7}-{1860,5223,0,1,4}-{1862,5222,0,1,4}-{1874,5234,0,1,5}-{1874,5230,0,1,5}-{1875,5219,0,1,5}-{1876,5215,0,1,3}-{1876,5220,0,1,6}-{1859,5206,0,1,5}-{1877,5216,0,1,4}-{1862,5201,0,1,5}-{1858,5201,0,1,6}-{1874,5201,0,1,2}-{1858,5202,0,1,0}-{1860,5200,0,1,7}-{1875,5197,0,1,6}-{1875,5201,0,1,2}-{1861,5194,0,1,3}-{1861,5224,0,1,6}-{1860,5192,0,1,2}-{1874,5188,0,1,7}-{1867,5188,0,1,4}-{1866,5189,0,1,4}-{1876,5189,0,1,1}-{1876,5196,0,1,3}-{1876,5188,0,1,4}-{1876,5191,0,1,4}-{1877,5191,0,1,4}-{1877,5197,0,1,6}-{1881,5198,0,1,4}-{1882,5197,0,1,5}-{1883,5203,0,1,3}-{1882,5198,0,1,4}-{1863,5205,0,1,4}-{1883,5205,0,1,5}-{1861,5205,0,1,5}-{1887,5204,0,1,3}-{1883,5205,0,1,3}-{1884,5206,0,1,1}-{1876,5215,0,1,6}-{1879,5215,0,1,2}-{1870,5217,0,1,6}-{1881,5221,0,1,1}-{1871,5218,0,1,1}-{1872,5191,0,1,3}-{1874,5219,0,1,5}-{1884,5228,0,1,4}-{1882,5231,0,1,7}-{1882,5234,0,1,5}-{1879,5234,0,1,6}-{1897,5234,0,1,6}-{1884,5234,0,1,4}-{1884,5235,0,1,3}-{1885,5236,0,1,6}-{1879,5239,0,1,4}-{1893,5243,0,1,7}-{1880,5243,0,1,7}-{1882,5241,0,1,1}-{1902,5242,0,1,4}-{1848,5176,0,1,2}-{1901,5244,0,1,3}-{1910,5243,0,1,5}-{1911,5235,0,1,3}-{1881,5243,0,1,2}-{1879,5234,0,1,3}-{1880,5234,0,1,4}-{1879,5239,0,1,1}-{1874,5230,0,1,4}-{1874,5236,0,1,4}-{1911,5235,0,1,5}-{1912,5243,0,1,4}-{1912,5238,0,1,3}-{1913,5244,0,1,4}-{1890,5244,0,1,4}-{1907,5204,0,1,1}-{1909,5204,0,1,1}-{1908,5204,0,1,3}-{1889,5206,0,1,3}-{1890,5207,0,1,1}-{1890,5207,0,1,2}-{1912,5237,0,1,7}" - }, - { - "npc_id": "4422", - "loc_data": "{2377,4450,0,0,0}-{2381,4459,0,0,0}-{2388,4425,0,0,0}-{2394,4440,0,0,0}-{2418,4447,0,0,0}-{2427,4461,0,0,0}" - }, - { - "npc_id": "4423", - "loc_data": "{2387,4456,0,0,0}-{2423,4461,0,0,0}" - }, - { - "npc_id": "4424", - "loc_data": "{2396,4455,0,0,0}" - }, - { - "npc_id": "4425", - "loc_data": "{2398,4453,0,0,0}" - }, - { - "npc_id": "4431", - "loc_data": "{2388,4470,0,0,0}" - }, - { - "npc_id": "4437", - "loc_data": "{2447,4429,0,1,0}" - }, - { - "npc_id": "4438", - "loc_data": "{3028,4498,0,1,3}-{3046,4493,0,1,4}" - }, - { - "npc_id": "4439", - "loc_data": "{3035,4507,0,1,6}-{3041,4493,0,1,0}" - }, - { - "npc_id": "4443", - "loc_data": "{2451,4424,0,1,6}-{2452,4428,0,1,3}-{2443,4423,0,1,7}-{2446,4422,0,1,3}-{2443,4431,0,1,1}-{2441,4427,0,1,1}-{2463,4422,0,1,4}-{2478,4420,0,1,6}-{2489,4428,0,1,6}-{2478,4452,0,1,3}-{2474,4444,0,1,6}-{2493,4454,0,1,3}-{2486,4463,0,1,4}-{2486,4469,0,1,7}-{2479,4456,0,1,4}-{2477,4462,0,1,3}" - }, - { - "npc_id": "4455", - "loc_data": "{2410,4434,0,0,0}" - }, - { - "npc_id": "4472", - "loc_data": "{2524,3056,0,1,2}" - }, - { - "npc_id": "4474", - "loc_data": "{3214,3252,0,0,0}-{3214,3253,0,0,0}-{3214,3254,0,0,0}" - }, - { - "npc_id": "4478", - "loc_data": "{3291,2787,0,0,6}" - }, - { - "npc_id": "4479", - "loc_data": "{2952,3477,0,1,0}-{2959,3501,0,1,3}" - }, - { - "npc_id": "4481", - "loc_data": "{2943,3474,0,1,3}" - }, - { - "npc_id": "4482", - "loc_data": "{2952,3505,0,1,7}-{2950,3501,0,1,4}" - }, - { - "npc_id": "4483", - "loc_data": "{2944,3467,0,1,6}-{2961,3508,0,1,6}-{2953,3467,0,1,3}" - }, - { - "npc_id": "4484", - "loc_data": "{2956,3496,0,1,6}" - }, - { - "npc_id": "4485", - "loc_data": "{2951,3509,0,1,4}" - }, - { - "npc_id": "4487", - "loc_data": "{2943,3478,0,1,3}" - }, - { - "npc_id": "4488", - "loc_data": "{2946,3474,0,1,4}-{2956,3494,0,1,3}" - }, - { - "npc_id": "4489", - "loc_data": "{2946,3469,0,1,5}-{2943,3487,0,1,3}" - }, - { - "npc_id": "4491", - "loc_data": "{2963,3510,0,1,2}-{2949,3475,0,1,3}" - }, - { - "npc_id": "4492", - "loc_data": "{2953,3509,0,1,4}-{2987,3426,0,1,6}" - }, - { - "npc_id": "4493", - "loc_data": "{2957,3513,0,1,6}" - }, - { - "npc_id": "4494", - "loc_data": "{2957,3512,0,1,4}" - }, - { - "npc_id": "4495", - "loc_data": "{2957,3512,0,1,5}" - }, - { - "npc_id": "4511", - "loc_data": "{2149,3866,0,0,7}" - }, - { - "npc_id": "4512", - "loc_data": "{2092,3930,0,1,4}" - }, - { - "npc_id": "4513", - "loc_data": "{2450,4647,0,1,5}" - }, - { - "npc_id": "4514", - "loc_data": "{2079,3923,0,1,7}" - }, - { - "npc_id": "4515", - "loc_data": "{2081,3899,0,1,1}" - }, - { - "npc_id": "4516", - "loc_data": "{2096,3907,0,1,6}" - }, - { - "npc_id": "4517", - "loc_data": "{2086,3915,0,1,0}" - }, - { - "npc_id": "4518", - "loc_data": "{2106,3907,0,1,6}" - }, - { - "npc_id": "4519", - "loc_data": "{2098,3921,0,0,6}-{2099,3921,0,0,6}-{2097,3921,0,0,6}" - }, - { - "npc_id": "4524", - "loc_data": "{2083,3903,0,1,3}" - }, - { - "npc_id": "4525", - "loc_data": "{2098,3894,0,1,3}" - }, - { - "npc_id": "4526", - "loc_data": "{2103,3914,0,1,7}" - }, - { - "npc_id": "4527", - "loc_data": "{2071,3877,0,1,2}-{2084,3868,0,1,1}-{2089,3864,0,1,4}" - }, - { - "npc_id": "4528", - "loc_data": "{2115,3864,0,1,6}-{2105,3848,0,1,1}-{2122,3946,0,1,3}" - }, - { - "npc_id": "4529", - "loc_data": "{2070,3879,0,1,7}-{2097,3862,0,1,3}-{2118,3852,0,1,4}-{2144,3853,0,1,5}" - }, - { - "npc_id": "4530", - "loc_data": "{2076,3866,0,1,1}-{2133,3874,0,1,4}-{2107,3945,0,1,1}" - }, - { - "npc_id": "4531", - "loc_data": "{2103,3866,0,1,3}-{2134,3851,0,1,4}-{2116,3950,0,1,0}" - }, - { - "npc_id": "4534", - "loc_data": "{3206,5487,0,1,6}-{3200,5492,0,1,1}" - }, - { - "npc_id": "4536", - "loc_data": "{2213,3794,0,0,3}" - }, - { - "npc_id": "4537", - "loc_data": "{2621,3688,0,0,6}" - }, - { - "npc_id": "4539", - "loc_data": "{2079,3925,0,1,1}" - }, - { - "npc_id": "4540", - "loc_data": "{2221,3797,2,1,3}-{2137,3899,2,1,2}" - }, - { - "npc_id": "4560", - "loc_data": "{3062,3260,0,1,6}" - }, - { - "npc_id": "4561", - "loc_data": "{2615,3855,0,1,0}" - }, - { - "npc_id": "4562", - "loc_data": "{2799,3200,0,1,0}" - }, - { - "npc_id": "4563", - "loc_data": "{3003,9798,0,1,3}" - }, - { - "npc_id": "4564", - "loc_data": "{3354,3408,0,1,0}-{3365,3429,0,1,0}-{3373,3418,0,1,0}-{3356,3385,0,1,0}" - }, - { - "npc_id": "4565", - "loc_data": "{3350,3401,0,1,0}-{3362,3408,0,1,0}-{3366,3425,0,1,0}" - }, - { - "npc_id": "4566", - "loc_data": "{3360,3343,0,1,6}" - }, - { - "npc_id": "4567", - "loc_data": "{3364,3339,0,1,6}" - }, - { - "npc_id": "4568", - "loc_data": "{3365,3333,0,1,6}" - }, - { - "npc_id": "4569", - "loc_data": "{3381,3379,0,0,0}" - }, - { - "npc_id": "4570", - "loc_data": "{3201,3301,0,1,3}-{3115,3276,0,1,6}" - }, - { - "npc_id": "4571", - "loc_data": "{3205,3298,0,1,3}-{3095,3286,0,1,4}-{3098,3225,0,1,4}-{3056,3248,0,1,0}" - }, - { - "npc_id": "4572", - "loc_data": "{2448,3510,1,0,4}" - }, - { - "npc_id": "4573", - "loc_data": "{2441,3501,1,1,3}" - }, - { - "npc_id": "4575", - "loc_data": "{2441,3503,1,1,4}" - }, - { - "npc_id": "4576", - "loc_data": "{2786,3862,0,1,0}" - }, - { - "npc_id": "4578", - "loc_data": "{2933,2972,0,0,0}" - }, - { - "npc_id": "4580", - "loc_data": "{2572,3299,1,0,0}" - }, - { - "npc_id": "4585", - "loc_data": "{3115,3160,0,0,0}" - }, - { - "npc_id": "4586", - "loc_data": "{2593,3087,0,1,4}" - }, - { - "npc_id": "4587", - "loc_data": "{2449,3503,1,0,6}" - }, - { - "npc_id": "4588", - "loc_data": "{2448,3500,1,0,3}" - }, - { - "npc_id": "4597", - "loc_data": "{2375,3433,0,1,0}" - }, - { - "npc_id": "4599", - "loc_data": "{2417,3513,0,1,4}" - }, - { - "npc_id": "4603", - "loc_data": "{3008,3514,0,1,3}" - }, - { - "npc_id": "4604", - "loc_data": "{3017,3513,0,1,0}-{3018,3515,0,1,1}" - }, - { - "npc_id": "4605", - "loc_data": "{3018,3512,0,1,7}-{3019,3514,0,1,1}-{3019,3516,0,1,1}" - }, - { - "npc_id": "4606", - "loc_data": "{3017,3514,0,1,4}" - }, - { - "npc_id": "4607", - "loc_data": "{3030,3505,0,0,0}" - }, - { - "npc_id": "4611", - "loc_data": "{3076,3259,0,0,0}" - }, - { - "npc_id": "4648", - "loc_data": "{3376,3373,0,0,0}" - }, - { - "npc_id": "4649", - "loc_data": "{2142,3122,0,1,4}-{2147,3122,0,1,3}" - }, - { - "npc_id": "4650", - "loc_data": "{3041,3194,0,1,4}" - }, - { - "npc_id": "4651", - "loc_data": "{2760,3239,0,1,3}-{2143,3121,0,1,6}" - }, - { - "npc_id": "4652", - "loc_data": "{2674,3144,0,1,3}-{3701,3501,0,1,6}" - }, - { - "npc_id": "4653", - "loc_data": "{2794,3414,0,1,6}-{3040,3192,0,1,3}-{2954,3154,0,1,6}-{3671,2930,0,1,6}" - }, - { - "npc_id": "4654", - "loc_data": "{3001,3033,0,1,6}-{2674,3146,0,1,6}-{2145,3122,0,1,3}" - }, - { - "npc_id": "4655", - "loc_data": "{2954,3157,0,1,3}-{3701,3503,0,1,1}" - }, - { - "npc_id": "4656", - "loc_data": "{2795,3414,0,1,6}-{3038,3192,0,1,3}-{2760,3238,0,1,3}" - }, - { - "npc_id": "4659", - "loc_data": "{3095,3232,0,1,6}-{3095,3231,0,1,0}-{3225,3370,0,1,4}-{3230,3370,0,1,3}" - }, - { - "npc_id": "4660", - "loc_data": "{3225,3369,0,1,3}-{3226,3371,0,1,7}-{3230,3372,0,1,0}-{3230,3370,0,1,3}-{3229,3368,0,1,1}-{3227,3367,0,1,1}" - }, - { - "npc_id": "4665", - "loc_data": "{2894,9766,0,1,0}-{2904,9797,0,1,0}-{2918,9800,0,1,0}" - }, - { - "npc_id": "4666", - "loc_data": "{2893,9769,0,1,0}-{2912,9797,0,1,0}-{2909,9807,0,1,0}" - }, - { - "npc_id": "4670", - "loc_data": "{3181,3850,0,1,6}" - }, - { - "npc_id": "4672", - "loc_data": "{3190,3834,0,1,6}" - }, - { - "npc_id": "4673", - "loc_data": "{2829,9827,0,1,0}-{2454,4368,0,1,0}" - }, - { - "npc_id": "4674", - "loc_data": "{2464,4365,0,1,4}" - }, - { - "npc_id": "4675", - "loc_data": "{3048,10266,0,1,4}-{2460,4374,0,1,4}" - }, - { - "npc_id": "4676", - "loc_data": "{3054,10269,0,1,4}-{2451,4362,0,1,4}" - }, - { - "npc_id": "4677", - "loc_data": "{3335,3687,0,1,4}-{3337,3701,0,1,2}-{3316,5448,0,1,2}-{3299,5545,0,1,6}-{3107,3812,0,1,0}" - }, - { - "npc_id": "4678", - "loc_data": "{3333,3699,0,1,6}-{3350,3687,0,1,5}-{3305,5454,0,1,6}-{3098,3821,0,1,0}" - }, - { - "npc_id": "4679", - "loc_data": "{3332,3693,0,1,6}-{3341,3685,0,1,4}-{3312,5558,0,1,1}-{3092,3810,0,1,0}-{2973,3620,0,1,3}" - }, - { - "npc_id": "4680", - "loc_data": "{3331,3672,0,1,0}-{3347,3694,0,1,5}-{3078,3810,0,1,0}-{2977,3611,0,1,3}" - }, - { - "npc_id": "4681", - "loc_data": "{2904,9802,0,1,0}" - }, - { - "npc_id": "4682", - "loc_data": "{2896,9796,0,1,0}" - }, - { - "npc_id": "4685", - "loc_data": "{3209,5552,0,1,1}-{3223,5555,0,1,4}-{3220,5548,0,1,6}-{3220,5557,0,1,4}" - }, - { - "npc_id": "4686", - "loc_data": "{2827,3512,0,1,5}-{2954,3894,0,1,4}-{3213,5548,0,1,4}-{2950,3932,0,1,0}-{2955,3945,0,1,0}" - }, - { - "npc_id": "4687", - "loc_data": "{2957,3898,0,1,6}-{2946,3898,0,1,7}-{2978,3956,0,1,0}" - }, - { - "npc_id": "4688", - "loc_data": "{2700,3212,0,1,6}-{3139,3818,0,1,0}" - }, - { - "npc_id": "4689", - "loc_data": "{2902,9736,0,1,0}" - }, - { - "npc_id": "4690", - "loc_data": "{3256,3624,0,1,5}-{3104,3875,0,1,2}-{3308,3661,0,1,2}-{2912,9731,0,1,0}" - }, - { - "npc_id": "4691", - "loc_data": "{3110,3854,0,1,0}" - }, - { - "npc_id": "4692", - "loc_data": "{3116,3858,0,1,0}" - }, - { - "npc_id": "4693", - "loc_data": "{2912,9741,0,1,0}-{2906,9736,0,1,0}-{3094,3849,0,1,0}" - }, - { - "npc_id": "4694", - "loc_data": "{3103,3709,0,1,0}-{3094,3869,0,1,1}-{3017,3852,0,1,7}-{2843,9558,0,1,1}-{2840,9605,0,1,4}" - }, - { - "npc_id": "4695", - "loc_data": "{3098,3699,0,1,4}-{3015,3851,0,1,2}-{2839,9565,0,1,3}-{2837,9601,0,1,4}-{2840,3279,0,1,6}-{2631,9880,0,1,1}" - }, - { - "npc_id": "4696", - "loc_data": "{3096,3691,0,1,6}-{2931,9808,0,1,0}-{2930,9799,0,1,0}-{3014,3848,0,1,1}-{2839,9560,0,1,4}-{2846,9613,0,1,6}-{3091,3861,0,1,0}-{2632,9874,0,1,1}" - }, - { - "npc_id": "4697", - "loc_data": "{3105,3695,0,1,5}-{3324,3856,0,1,2}-{3016,3845,0,1,4}-{2845,9557,0,1,2}-{2839,9624,0,1,6}-{2825,3277,0,1,3}-{2630,9867,0,1,1}" - }, - { - "npc_id": "4698", - "loc_data": "{3139,3712,0,1,5}-{3304,3886,0,1,6}-{2861,9752,0,1,4}" - }, - { - "npc_id": "4699", - "loc_data": "{3035,10245,0,1,4}-{3159,3707,0,1,3}-{3296,3872,0,1,5}-{2629,9477,2,0,0}-{2640,9502,2,0,1}" - }, - { - "npc_id": "4700", - "loc_data": "{3144,3701,0,1,4}-{3282,3880,0,1,4}-{2633,9488,2,0,6}-{2862,9750,0,1,7}" - }, - { - "npc_id": "4701", - "loc_data": "{3028,10250,0,1,4}-{3149,3690,0,1,4}-{3287,3894,0,1,3}" - }, - { - "npc_id": "4702", - "loc_data": "{2869,9775,0,1,0}-{2853,9775,0,1,0}-{3084,9957,0,1,6}-{2712,9480,0,0,5}" - }, - { - "npc_id": "4703", - "loc_data": "{2857,9774,0,1,0}-{2860,9781,0,1,0}-{3092,9958,0,1,2}" - }, - { - "npc_id": "4704", - "loc_data": "{2863,9767,0,1,0}-{2871,9782,0,1,0}-{3160,5521,0,1,1}" - }, - { - "npc_id": "4705", - "loc_data": "{3088,9961,0,1,1}-{3169,5528,0,1,0}" - }, - { - "npc_id": "4706", - "loc_data": "{3237,5559,0,1,3}-{3244,5554,0,1,3}-{3239,5553,0,1,5}-{3249,5559,0,1,5}-{2195,3808,0,1,0}-{2196,3822,0,1,0}-{2197,3805,0,1,0}-{2197,3813,0,1,0}-{2201,3812,0,1,0}-{2202,3824,0,1,0}-{2203,3804,0,1,0}-{2207,3813,0,1,0}-{2211,3818,0,1,0}-{2214,3819,0,1,0}" - }, - { - "npc_id": "4707", - "loc_data": "{3213,3253,0,0,6}" - }, - { - "npc_id": "4710", - "loc_data": "{3222,3475,0,0,5}" - }, - { - "npc_id": "4732", - "loc_data": "{3429,3701,0,0,0}" - }, - { - "npc_id": "4898", - "loc_data": "{3124,9874,0,1,3}-{3128,9875,0,1,6}" - }, - { - "npc_id": "4899", - "loc_data": "{3231,3197,0,1,6}" - }, - { - "npc_id": "4900", - "loc_data": "{3210,3213,1,1,1}" - }, - { - "npc_id": "4901", - "loc_data": "{3245,3157,0,0,7}" - }, - { - "npc_id": "4902", - "loc_data": "{3227,3147,0,0,1}" - }, - { - "npc_id": "4903", - "loc_data": "{3244,3214,0,1,1}" - }, - { - "npc_id": "4904", - "loc_data": "{3228,3254,0,1,3}" - }, - { - "npc_id": "4906", - "loc_data": "{3230,3244,0,1,4}" - }, - { - "npc_id": "4907", - "loc_data": "{3208,3222,2,0,6}" - }, - { - "npc_id": "4909", - "loc_data": "{2831,3352,0,0,7}" - }, - { - "npc_id": "4911", - "loc_data": "{2703,9894,0,0,0}-{2705,9897,0,0,0}-{2706,9904,0,0,0}-{2707,9906,0,0,0}-{2704,9906,0,0,0}-{2706,9910,0,0,0}-{2703,9911,0,0,0}-{2699,9915,0,0,0}-{2697,9915,0,0,0}-{2694,9915,0,0,0}-{2692,9914,0,0,0}-{2696,9910,0,0,0}-{2694,9908,0,0,0}-{2693,9906,0,0,0}-{2696,9906,0,0,0}-{2698,9901,0,0,0}-{2698,9900,0,0,0}-{2696,9900,0,0,0}-{2690,9903,0,0,0}-{2692,9902,0,0,0}-{2693,9900,0,0,0}-{2692,9900,0,0,0}-{2691,9899,0,0,0}-{2692,9895,0,0,0}-{2693,9891,0,0,0}-{2695,9889,0,0,0}-{2692,9885,0,0,0}-{2699,9882,0,0,0}-{2695,9881,0,0,0}-{2693,9881,0,0,0}-{2690,9880,0,0,0}-{2700,9879,0,0,0}-{2696,9880,0,0,0}-{2694,9878,0,0,0}-{2695,9877,0,0,0}-{2699,9876,0,0,0}-{2692,9876,0,0,0}-{2692,9874,0,0,0}-{2699,9872,0,0,0}-{2698,9871,0,0,0}-{2701,9870,0,0,0}-{2695,9870,0,0,0}-{2695,9869,0,0,0}-{2702,9868,0,0,0}-{2701,9867,0,0,0}-{2697,9866,0,0,0}-{2699,9865,0,0,0}" - }, - { - "npc_id": "4920", - "loc_data": "{3289,5496,0,0,5}-{3272,5490,0,0,0}-{3188,5510,0,1,6}-{3194,5510,0,1,2}" - }, - { - "npc_id": "4921", - "loc_data": "{3304,5493,0,1,3}-{3303,5496,0,1,0}-{3271,5490,0,0,5}" - }, - { - "npc_id": "4926", - "loc_data": "{3227,3547,0,1,1}-{3251,3528,0,1,1}-{2969,3686,0,1,1}" - }, - { - "npc_id": "4927", - "loc_data": "{3227,3533,0,1,1}-{3236,3549,0,1,1}-{3049,3695,0,1,1}-{2962,3689,0,1,1}-{2971,3690,0,1,1}" - }, - { - "npc_id": "4930", - "loc_data": "{3412,2889,0,1,3}-{3414,2892,0,1,1}-{3414,2890,0,1,1}-{3416,2886,0,1,4}-{3416,2893,0,1,6}" - }, - { - "npc_id": "4932", - "loc_data": "{3414,2891,0,1,1}-{3410,2888,0,1,7}" - }, - { - "npc_id": "4942", - "loc_data": "{3191,3210,0,1,0}" - }, - { - "npc_id": "4944", - "loc_data": "{2042,5213,0,1,5}-{2020,5236,0,1,2}-{1993,5188,0,1,2}-{1994,5191,0,1,2}" - }, - { - "npc_id": "4945", - "loc_data": "{1994,5192,0,1,4}-{1997,5190,0,1,1}" - }, - { - "npc_id": "4946", - "loc_data": "{2721,3432,0,1,4}" - }, - { - "npc_id": "4947", - "loc_data": "{2825,3696,0,1,3}" - }, - { - "npc_id": "4950", - "loc_data": "{2855,10051,1,0,0}" - }, - { - "npc_id": "4953", - "loc_data": "{2684,3274,1,0,0}" - }, - { - "npc_id": "4955", - "loc_data": "{2772,3223,0,0,0}" - }, - { - "npc_id": "4956", - "loc_data": "{2783,3119,0,0,0}" - }, - { - "npc_id": "4961", - "loc_data": "{2683,3275,1,0,0}" - }, - { - "npc_id": "4975", - "loc_data": "{3303,3024,0,0,7}-{3299,3016,0,0,2}-{3280,9438,0,0,6}-{3275,9460,0,0,3}-{3267,9458,0,0,3}-{3270,9415,0,0,6}-{3272,9429,0,0,4}-{3272,9421,0,0,4}-{3271,9450,0,0,1}-{3288,9413,0,0,6}-{3297,9414,0,0,6}-{3300,9414,0,0,6}-{3304,9424,0,0,1}-{3292,9426,0,0,4}-{3295,9421,0,0,3}-{3297,9412,0,0,1}-{3292,9413,0,0,3}-{3324,9429,0,0,3}-{3311,9440,0,0,6}-{3294,9459,0,0,1}-{3291,9463,0,0,6}-{3296,9468,0,0,1}-{3288,9468,0,0,1}-{3313,9449,0,0,1}-{3034,2971,0,0,5}-{3030,2971,0,0,1}" - }, - { - "npc_id": "4976", - "loc_data": "{3301,3017,0,0,7}-{3281,9446,0,0,1}-{3270,9451,0,0,4}-{3269,9418,0,0,1}-{3267,9441,0,0,1}-{3280,9436,0,0,1}-{3291,9414,0,0,6}-{3305,9413,0,0,6}-{3308,9415,0,0,1}-{3309,9420,0,0,1}-{3296,9425,0,0,1}-{3294,9420,0,0,1}-{3295,9412,0,0,3}-{3323,9439,0,0,6}-{3295,9460,0,0,3}-{3294,9464,0,0,6}-{3294,9467,0,0,1}-{3311,9454,0,0,6}-{3036,2972,0,0,1}-{3039,2968,0,0,1}-{3046,2982,0,0,6}-{3045,2974,0,0,6}-{3042,2972,0,0,6}" - }, - { - "npc_id": "4977", - "loc_data": "{3294,3016,0,0,6}-{3287,3016,0,0,3}-{3277,9452,0,0,1}-{3267,9443,0,0,6}-{3267,9431,0,0,6}-{3269,9420,0,0,6}-{3273,9460,0,0,4}-{3277,9454,0,0,6}-{3281,9448,0,0,6}-{3294,9413,0,0,6}-{3306,9423,0,0,1}-{3298,9436,0,0,6}-{3292,9466,0,0,6}-{3296,9462,0,0,3}-{3290,9467,0,0,1}-{3315,9451,0,0,1}-{3316,9454,0,0,3}-{3038,2973,0,0,1}-{3027,2974,0,0,1}-{3023,2977,0,0,1}-{3035,2989,0,0,4}-{3039,2985,0,0,4}" - }, - { - "npc_id": "4978", - "loc_data": "{3291,3017,0,0,5}-{3275,9463,0,0,1}-{3273,9430,0,0,6}-{3273,9422,0,0,6}-{3267,9428,0,0,1}-{3294,9411,0,0,6}-{3300,9424,0,0,1}-{3289,9412,0,0,3}-{3042,2969,0,0,1}-{3038,2976,0,0,6}-{3041,2983,0,0,6}-{3043,2983,0,0,6}-{3044,2981,0,0,6}-{3043,2979,0,0,1}-{3038,2989,0,0,1}-{3025,2984,0,0,1}-{3026,2988,0,0,1}-{3030,2990,0,0,1}-{3024,2979,0,0,3}" - }, - { - "npc_id": "4985", - "loc_data": "{3304,3027,0,1,2}" - }, - { - "npc_id": "4986", - "loc_data": "{3308,3112,0,1,1}" - }, - { - "npc_id": "4989", - "loc_data": "{3272,3029,0,1,4}" - }, - { - "npc_id": "4990", - "loc_data": "{3269,3029,0,1,4}" - }, - { - "npc_id": "4991", - "loc_data": "{3270,3030,0,1,6}" - }, - { - "npc_id": "4992", - "loc_data": "{3267,3029,0,1,0}" - }, - { - "npc_id": "4993", - "loc_data": "{3277,3028,0,1,1}-{3279,9441,0,1,4}" - }, - { - "npc_id": "4994", - "loc_data": "{3285,3027,0,1,7}" - }, - { - "npc_id": "4995", - "loc_data": "{3279,3024,0,1,3}-{3286,3036,1,1,4}" - }, - { - "npc_id": "4996", - "loc_data": "{3284,3022,0,1,2}" - }, - { - "npc_id": "4997", - "loc_data": "{3289,3026,0,1,3}-{3317,9438,0,0,3}" - }, - { - "npc_id": "4998", - "loc_data": "{3293,3027,0,1,5}-{3286,9419,0,1,3}-{3296,9433,0,1,7}-{3310,9457,0,1,1}" - }, - { - "npc_id": "4999", - "loc_data": "{3280,3020,0,1,6}-{3319,9439,0,1,0}-{3299,9467,0,1,3}" - }, - { - "npc_id": "5000", - "loc_data": "{3285,3022,0,1,6}-{3295,9434,0,1,3}" - }, - { - "npc_id": "5001", - "loc_data": "{3297,3020,0,1,6}-{3280,9414,0,1,1}-{3279,9416,0,1,1}" - }, - { - "npc_id": "5002", - "loc_data": "{3295,3028,0,1,3}-{3291,9423,0,1,1}" - }, - { - "npc_id": "5020", - "loc_data": "{2905,3180,0,0,0}" - }, - { - "npc_id": "5021", - "loc_data": "{2906,3173,0,0,0}" - }, - { - "npc_id": "5022", - "loc_data": "{2907,3169,0,0,0}" - }, - { - "npc_id": "5029", - "loc_data": "{2534,3575,0,1,0}" - }, - { - "npc_id": "5030", - "loc_data": "{2533,3570,0,1,4}" - }, - { - "npc_id": "5031", - "loc_data": "{2525,3575,0,1,3}" - }, - { - "npc_id": "5032", - "loc_data": "{2525,3566,0,1,3}" - }, - { - "npc_id": "5033", - "loc_data": "{2520,3569,0,1,3}" - }, - { - "npc_id": "5049", - "loc_data": "{2808,3355,0,1,6}" - }, - { - "npc_id": "5062", - "loc_data": "{2927,3410,0,0,0}" - }, - { - "npc_id": "5063", - "loc_data": "{2460,3108,0,1,3}" - }, - { - "npc_id": "5065", - "loc_data": "{2925,3303,0,0,0}" - }, - { - "npc_id": "5070", - "loc_data": "{2912,3480,0,0,0}-{2911,3480,0,0,0}" - }, - { - "npc_id": "5071", - "loc_data": "{2609,2922,0,0,0}" - }, - { - "npc_id": "5072", - "loc_data": "{2526,2928,0,1,6}-{2510,2876,0,1,3}-{2517,2878,0,1,6}-{2527,2877,0,1,2}-{2532,2938,0,1,4}-{2527,2933,0,1,4}-{2521,2938,0,1,6}-{2507,2914,0,1,0}-{2512,2910,0,1,4}-{2517,2912,0,1,1}-{2517,2921,0,1,6}-{2511,2911,0,1,3}-{2521,2917,0,1,7}-{2522,2929,0,1,4}-{2510,2888,0,1,1}-{2502,2890,0,1,3}-{2520,2882,0,1,7}-{2507,2877,0,1,1}-{2464,2832,0,1,2}-{2500,2893,0,1,4}-{2548,2887,0,1,1}-{2544,2889,0,1,7}-{2536,2877,0,1,3}-{2530,2872,0,1,3}-{2520,2885,0,1,0}-{2517,2870,0,1,4}-{2498,2883,0,1,6}-{2501,2886,0,1,7}-{2508,2916,0,1,4}-{2502,2888,0,1,7}-{2514,2915,0,1,4}-{2520,2908,0,1,6}-{2515,2944,0,1,4}-{2526,2943,0,1,4}-{2525,2893,0,1,4}-{2521,2896,0,1,1}" - }, - { - "npc_id": "5073", - "loc_data": "{2603,2892,0,1,1}-{2604,2892,0,1,6}-{2611,2917,0,1,6}-{2611,2916,0,1,3}-{2613,2894,0,1,1}-{2614,2892,0,1,6}-{2608,2927,0,1,2}-{2612,2934,0,1,4}-{2609,2930,0,1,0}-{2594,2929,0,1,2}-{2616,2922,0,1,4}-{2593,2911,0,1,1}-{2617,2911,0,1,1}-{2602,2900,0,1,3}-{2601,2886,0,1,6}-{2587,2897,0,1,4}-{2612,2936,0,1,3}-{2606,2929,0,1,5}-{2615,2930,0,1,3}-{2576,2896,0,1,1}-{2588,2879,0,1,6}-{2593,2880,0,1,7}-{2583,2886,0,1,5}-{2609,2897,0,1,3}-{2608,2905,0,1,2}-{2606,2917,0,1,4}-{2604,2918,0,1,4}-{2613,2890,0,1,5}-{2608,2921,0,1,6}-{2604,2886,0,1,6}-{2602,2920,0,1,1}-{2607,2932,0,1,6}-{2578,2885,0,1,1}" - }, - { - "npc_id": "5074", - "loc_data": "{2727,3763,0,1,1}-{2724,3765,0,1,2}-{2725,3768,0,1,2}-{2730,3768,0,1,2}-{2731,3764,0,1,2}-{2731,3763,0,1,2}-{2731,3765,0,1,2}-{2733,3765,0,1,2}-{2709,3823,1,1,2}-{2713,3823,1,1,2}-{2709,3825,1,1,2}-{2710,3826,1,1,2}-{2709,3825,1,1,2}" - }, - { - "npc_id": "5075", - "loc_data": "{3402,3140,0,1,2}-{3402,3138,0,1,3}-{3404,3139,0,1,5}-{3400,3142,0,1,6}-{3405,3143,0,1,5}-{3409,3078,0,1,5}-{3410,3075,0,1,5}-{3411,3079,0,1,5}-{3408,3076,0,1,5}-{3414,3077,0,1,5}" - }, - { - "npc_id": "5076", - "loc_data": "{2310,3581,0,1,2}-{2314,3580,0,1,2}-{2311,3583,0,1,2}-{2304,3588,0,1,6}-{2305,3601,0,1,7}-{2341,3610,0,1,7}-{2344,3606,0,1,3}-{2340,3603,0,1,4}-{2348,3591,0,1,1}-{2356,3595,0,1,6}-{2361,3595,0,1,1}-{2359,3589,0,1,4}-{2346,3585,0,1,6}-{2360,3584,0,1,7}" - }, - { - "npc_id": "5077", - "loc_data": "{2558,2911,0,0,0}-{2558,2913,0,0,0}-{2556,2913,0,0,0}-{2557,2912,0,0,0}-{2560,2911,0,0,0}-{2559,2910,0,0,0}-{2559,2909,0,0,0}-{2559,2917,0,0,0}-{2558,2914,0,0,0}-{2559,2915,0,0,0}-{2558,2916,0,0,0}-{2559,2932,0,0,0}-{2559,2928,0,0,0}-{2558,2928,0,0,0}-{2558,2931,0,0,0}-{2558,2929,0,0,0}-{2556,2911,0,0,0}-{2557,2911,0,0,0}-{2556,2910,0,0,0}-{2556,2912,0,0,0}-{2557,2919,0,0,0}-{2556,2914,0,0,0}-{2556,2913,0,0,0}-{2556,2918,0,0,0}-{2556,2929,0,0,0}-{2557,2933,0,0,0}-{2556,2932,0,0,0}-{2557,2932,0,0,0}-{2557,2928,0,0,0}-{2559,2934,0,0,0}-{2554,2911,0,0,0}-{2555,2911,0,0,0}-{2554,2912,0,0,0}-{2555,2915,0,0,0}-{2555,2919,0,0,0}-{2554,2919,0,0,0}-{2554,2913,0,0,0}-{2556,2915,0,0,0}-{2555,2931,0,0,0}-{2554,2933,0,0,0}-{2555,2934,0,0,0}-{2555,2933,0,0,0}-{2555,2932,0,0,0}-{2554,2935,0,0,0}-{2554,2934,0,0,0}-{2556,2936,0,0,0}-{2558,2936,0,0,0}-{2554,2936,0,0,0}-{2553,2935,0,0,0}-{2556,2933,0,0,0}-{2558,2938,0,0,0}-{2556,2938,0,0,0}-{2557,2937,0,0,0}-{2557,2917,0,0,0}-{2555,2917,0,0,0}-{2555,2912,0,0,0}-{2555,2935,0,0,0}-{2547,2940,0,0,0}-{2546,2939,0,0,0}-{2548,2939,0,0,0}-{2557,2930,0,0,0}-{2556,2929,0,0,0}-{2546,2941,0,0,0}-{2548,2941,0,0,0}-{2557,2931,0,0,0}-{2556,2935,0,0,0}-{2555,2936,0,0,0}-{2553,2935,0,0,0}-{2559,2930,0,0,0}-{2547,2940,0,0,0}-{2509,2910,0,0,0}-{2507,2910,0,0,0}-{2509,2908,0,0,0}-{2508,2908,0,0,0}-{2508,2909,0,0,0}-{2510,2908,0,0,0}-{2506,2906,0,0,0}-{2508,2906,0,0,0}-{2507,2906,0,0,0}-{2507,2905,0,0,0}-{2509,2905,0,0,0}-{2505,2907,0,0,0}-{2504,2908,0,0,0}-{2467,2879,0,0,0}-{2484,2877,0,0,0}-{2485,2876,0,0,0}-{2511,2908,0,0,0}-{2504,2906,0,0,0}-{2508,2907,0,0,0}-{2509,2906,0,0,0}-{2508,2909,0,0,0}-{2506,2906,0,0,0}-{2510,2907,0,0,0}-{2503,2907,0,0,0}-{2509,2908,0,0,0}-{2507,2908,0,0,0}-{2508,2906,0,0,0}-{2505,2907,0,0,0}-{2507,2905,0,0,0}-{2506,2904,0,0,0}-{2505,2903,0,0,0}-{2508,2902,0,0,0}-{2504,2901,0,0,0}-{2506,2907,0,0,0}-{2507,2906,0,0,0}-{2524,2898,0,0,0}-{2524,2896,0,0,0}-{2525,2897,0,0,0}-{2521,2895,0,0,0}-{2525,2895,0,0,0}-{2507,2903,0,0,0}-{2526,2898,0,0,0}-{2507,2889,0,0,0}-{2506,2903,0,0,0}-{2503,2905,0,0,0}-{2507,2905,0,0,0}-{2511,2908,0,0,0}-{2507,2887,0,0,0}-{2554,2937,0,0,0}-{2558,2938,0,0,0}-{2557,2937,0,0,0}-{2556,2938,0,0,0}-{2553,2937,0,0,0}-{2556,2937,0,0,0}-{2553,2935,0,0,0}-{2556,2935,0,0,0}-{2558,2936,0,0,0}-{2555,2936,0,0,0}-{2552,2936,0,0,0}-{2554,2936,0,0,0}-{2552,2934,0,0,0}-{2553,2934,0,0,0}-{2557,2931,0,0,0}-{2559,2932,0,0,0}-{2558,2932,0,0,0}-{2556,2936,0,0,0}-{2553,2936,0,0,0}-{2554,2935,0,0,0}-{2559,2917,0,0,0}-{2556,2916,0,0,0}-{2555,2917,0,0,0}-{2559,2914,0,0,0}-{2557,2915,0,0,0}-{2561,2914,0,0,0}-{2557,2912,0,0,0}-{2559,2912,0,0,0}-{2558,2913,0,0,0}-{2555,2911,0,0,0}-{2554,2910,0,0,0}-{2559,2916,0,0,0}-{2557,2932,0,0,0}-{2557,2909,0,0,0}-{2559,2913,0,0,0}-{2552,2935,0,0,0}-{2512,2864,0,0,0}-{2560,2909,0,0,0}-{2561,2916,0,0,0}-{2559,2909,0,0,0}-{2557,2914,0,0,0}-{2555,2915,0,0,0}-{2527,2896,0,0,0}-{2524,2895,0,0,0}-{2524,2894,0,0,0}-{2525,2896,0,0,0}-{2524,2896,0,0,0}-{2523,2893,0,0,0}-{2523,2896,0,0,0}-{2523,2897,0,0,0}-{2521,2895,0,0,0}-{2521,2893,0,0,0}-{2526,2895,0,0,0}-{2507,2887,0,0,0}-{2507,2890,0,0,0}-{2506,2886,0,0,0}-{2505,2887,0,0,0}-{2505,2888,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2504,2903,0,0,0}-{2505,2907,0,0,0}-{2504,2906,0,0,0}-{2505,2905,0,0,0}-{2506,2907,0,0,0}-{2506,2905,0,0,0}-{2506,2906,0,0,0}-{2507,2903,0,0,0}-{2507,2906,0,0,0}-{2507,2907,0,0,0}-{2505,2893,0,0,0}-{2504,2892,0,0,0}-{2508,2909,0,0,0}-{2502,2891,0,0,0}-{2503,2890,0,0,0}-{2503,2891,0,0,0}-{2508,2891,0,0,0}-{2507,2891,0,0,0}-{2507,2890,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2505,2888,0,0,0}-{2506,2909,0,0,0}-{2507,2887,0,0,0}-{2505,2887,0,0,0}-{2506,2887,0,0,0}-{2508,2890,0,0,0}-{2504,2894,0,0,0}-{2502,2891,0,0,0}-{2524,2899,0,0,0}-{2524,2898,0,0,0}-{2526,2899,0,0,0}-{2526,2895,0,0,0}-{2526,2900,0,0,0}-{2524,2900,0,0,0}-{2525,2900,0,0,0}-{2525,2899,0,0,0}-{2524,2893,0,0,0}-{2522,2894,0,0,0}-{2521,2895,0,0,0}-{2523,2895,0,0,0}-{2524,2895,0,0,0}-{2506,2886,0,0,0}-{2521,2893,0,0,0}-{2526,2898,0,0,0}-{2524,2899,0,0,0}-{2456,2832,0,0,0}-{2523,2896,0,0,0}-{2522,2894,0,0,0}-{2524,2901,0,0,0}-{2524,2900,0,0,0}-{2523,2895,0,0,0}-{2526,2901,0,0,0}-{2525,2896,0,0,0}-{2526,2900,0,0,0}-{2523,2893,0,0,0}-{2525,2900,0,0,0}-{2524,2898,0,0,0}-{2525,2894,0,0,0}-{2527,2894,0,0,0}-{2524,2899,0,0,0}-{2508,2904,0,0,0}-{2507,2907,0,0,0}-{2506,2902,0,0,0}-{2506,2907,0,0,0}-{2506,2906,0,0,0}-{2503,2907,0,0,0}-{2503,2905,0,0,0}-{2505,2893,0,0,0}-{2503,2891,0,0,0}-{2506,2891,0,0,0}-{2508,2891,0,0,0}-{2506,2892,0,0,0}-{2506,2909,0,0,0}-{2503,2890,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2507,2890,0,0,0}-{2505,2887,0,0,0}-{2505,2888,0,0,0}-{2508,2890,0,0,0}-{2507,2905,0,0,0}-{2456,2832,0,0,0}-{2503,2890,0,0,0}-{2503,2891,0,0,0}-{2504,2892,0,0,0}-{2504,2894,0,0,0}-{2506,2887,0,0,0}-{2507,2887,0,0,0}-{2507,2891,0,0,0}-{2504,2889,0,0,0}-{2508,2891,0,0,0}-{2505,2888,0,0,0}-{2506,2894,0,0,0}-{2506,2902,0,0,0}-{2506,2904,0,0,0}-{2506,2886,0,0,0}-{2507,2903,0,0,0}-{2505,2893,0,0,0}-{2506,2891,0,0,0}-{2503,2889,0,0,0}-{2506,2887,0,0,0}-{2506,2892,0,0,0}-{2507,2891,0,0,0}-{2456,2832,0,0,0}-{2456,2832,0,0,0}-{2507,2887,0,0,0}-{2507,2890,0,0,0}-{2503,2905,0,0,0}-{2505,2906,0,0,0}-{2506,2905,0,0,0}-{2506,2906,0,0,0}-{2507,2905,0,0,0}-{2505,2905,0,0,0}-{2504,2906,0,0,0}-{2503,2907,0,0,0}-{2506,2894,0,0,0}-{2507,2908,0,0,0}-{2507,2907,0,0,0}-{2508,2909,0,0,0}-{2505,2888,0,0,0}-{2505,2907,0,0,0}-{2494,2940,0,0,0}-{2495,2940,0,0,0}-{2496,2939,0,0,0}-{2497,2938,0,0,0}-{2498,2942,0,0,0}-{2499,2943,0,0,0}-{2498,2944,0,0,0}-{2500,2942,0,0,0}-{2503,2940,0,0,0}-{2496,2939,0,0,0}-{2502,2939,0,0,0}-{2500,2944,0,0,0}" - }, - { - "npc_id": "5078", - "loc_data": "{2556,2911,0,0,0}-{2560,2909,0,0,0}-{2560,2933,0,0,0}-{2558,2910,0,0,0}-{2558,2909,0,0,0}-{2558,2918,0,0,0}-{2559,2930,0,0,0}-{2557,2915,0,0,0}-{2557,2931,0,0,0}-{2558,2935,0,0,0}-{2555,2936,0,0,0}-{2553,2936,0,0,0}-{2553,2933,0,0,0}-{2553,2934,0,0,0}-{2506,2908,0,0,0}-{2507,2908,0,0,0}-{2510,2906,0,0,0}-{2509,2907,0,0,0}-{2508,2907,0,0,0}-{2506,2904,0,0,0}-{2508,2904,0,0,0}-{2504,2906,0,0,0}-{2467,2878,0,0,0}-{2486,2877,0,0,0}-{2506,2909,0,0,0}-{2508,2904,0,0,0}-{2506,2902,0,0,0}-{2504,2903,0,0,0}-{2504,2902,0,0,0}-{2525,2899,0,0,0}-{2523,2897,0,0,0}-{2522,2897,0,0,0}-{2523,2895,0,0,0}-{2521,2893,0,0,0}-{2523,2893,0,0,0}-{2506,2888,0,0,0}-{2509,2889,0,0,0}-{2505,2889,0,0,0}-{2508,2888,0,0,0}-{2506,2887,0,0,0}-{2505,2887,0,0,0}-{2554,2935,0,0,0}-{2552,2935,0,0,0}-{2557,2934,0,0,0}-{2554,2934,0,0,0}-{2558,2933,0,0,0}-{2559,2931,0,0,0}-{2560,2918,0,0,0}-{2558,2916,0,0,0}-{2557,2917,0,0,0}-{2561,2916,0,0,0}-{2555,2915,0,0,0}-{2557,2914,0,0,0}-{2560,2915,0,0,0}-{2554,2912,0,0,0}-{2556,2912,0,0,0}-{2559,2909,0,0,0}-{2558,2909,0,0,0}-{2527,2894,0,0,0}-{2525,2894,0,0,0}-{2523,2895,0,0,0}-{2522,2894,0,0,0}-{2508,2890,0,0,0}-{2508,2891,0,0,0}-{2503,2905,0,0,0}-{2503,2907,0,0,0}-{2506,2902,0,0,0}-{2506,2904,0,0,0}-{2504,2894,0,0,0}-{2506,2894,0,0,0}-{2506,2892,0,0,0}-{2508,2904,0,0,0}-{2506,2891,0,0,0}-{2524,2901,0,0,0}-{2526,2901,0,0,0}-{2524,2894,0,0,0}-{2525,2894,0,0,0}-{2527,2894,0,0,0}-{2523,2896,0,0,0}-{2525,2896,0,0,0}-{2507,2903,0,0,0}-{2507,2905,0,0,0}-{2505,2902,0,0,0}-{2506,2904,0,0,0}-{2505,2907,0,0,0}-{2506,2905,0,0,0}-{2505,2905,0,0,0}-{2504,2906,0,0,0}-{2506,2894,0,0,0}-{2504,2894,0,0,0}-{2507,2891,0,0,0}-{2507,2887,0,0,0}-{2506,2891,0,0,0}-{2506,2907,0,0,0}-{2495,2938,0,0,0}-{2501,2938,0,0,0}-{2501,2940,0,0,0}-{2503,2938,0,0,0}" - }, - { - "npc_id": "5079", - "loc_data": "{2323,3538,0,1,6}-{2319,3539,0,1,6}-{2321,3545,0,1,5}-{2324,3556,0,1,1}-{2322,3594,0,1,5}-{2323,3594,0,1,1}-{2323,3602,0,1,1}-{2326,3602,0,1,4}-{2324,3622,0,1,1}-{2326,3630,0,1,6}-{2347,3617,0,1,7}-{2341,3615,0,1,3}-{2339,3596,0,1,3}-{2329,3584,0,1,6}-{2337,3589,0,1,5}-{2358,3573,0,1,3}-{2360,3571,0,1,4}-{2371,3560,0,1,3}-{2342,3549,0,1,0}-{2357,3539,0,1,1}-{2357,3537,0,1,4}-{2357,3549,0,1,6}" - }, - { - "npc_id": "5080", - "loc_data": "{2562,2927,0,1,4}-{2557,2915,0,1,3}-{2557,2921,0,1,1}-{2556,2938,0,1,7}-{2551,2930,0,1,6}-{2558,2912,0,1,4}-{2558,2909,0,1,3}-{2557,2932,0,1,6}-{2556,2935,0,1,1}-{2555,2915,0,1,2}-{2553,2935,0,1,4}-{2559,2918,0,1,4}-{2508,2909,0,1,3}-{2466,2878,0,1,6}-{2467,2881,0,1,7}-{2501,2912,0,1,6}-{2507,2907,0,1,1}-{2505,2902,0,1,2}-{2504,2896,0,1,2}-{2522,2895,0,1,3}-{2526,2897,0,1,1}-{2527,2904,0,1,0}-{2507,2889,0,1,4}-{2464,2832,0,1,2}-{2545,2948,0,1,2}-{2553,2935,0,1,1}-{2556,2931,0,1,5}-{2555,2915,0,1,6}-{2559,2911,0,1,4}-{2559,2918,0,1,3}-{2527,2896,0,1,4}-{2533,2896,0,1,5}-{2508,2890,0,1,4}-{2522,2895,0,1,2}-{2490,2877,0,1,1}-{2496,2897,0,1,1}-{2506,2906,0,1,0}-{2506,2894,0,1,3}-{2506,2902,0,1,6}-{2499,2942,0,1,7}-{2496,2936,0,1,4}-{2498,2939,0,1,3}" - }, - { - "npc_id": "5081", - "loc_data": "{2318,3501,0,1,5}-{2316,3502,0,1,6}-{2317,3512,0,1,6}-{2315,3512,0,1,5}-{2312,3504,0,1,1}-{2307,3513,0,1,6}-{2311,3517,0,1,0}-{2320,3517,0,1,1}-{2309,3520,0,1,6}" - }, - { - "npc_id": "5082", - "loc_data": "{2554,2894,0,1,4}-{2538,2897,0,1,6}-{2545,2899,0,1,3}-{2561,2913,0,1,1}-{2559,2908,0,1,6}-{2557,2920,0,1,4}-{2553,2913,0,1,4}-{2544,2910,0,1,2}-{2547,2909,0,1,4}-{2537,2906,0,1,5}-{2525,2914,0,1,3}-{2561,2918,0,1,7}-{2560,2915,0,1,1}-{2555,2920,0,1,6}-{2557,2908,0,1,6}-{2539,2920,0,1,1}-{2528,2915,0,1,4}-{2527,2904,0,1,0}-{2487,2878,0,1,4}-{2518,2901,0,1,5}-{2528,2902,0,1,1}-{2529,2918,0,1,6}-{2566,2881,0,1,7}-{2568,2917,0,1,6}-{2568,2918,0,1,4}-{2557,2915,0,1,7}-{2546,2912,0,1,3}-{2542,2911,0,1,5}-{2548,2910,0,1,6}-{2554,2907,0,1,1}-{2568,2885,0,1,4}-{2548,2896,0,1,5}-{2545,2895,0,1,6}-{2544,2910,0,1,7}-{2536,2895,0,1,3}-{2538,2902,0,1,6}-{2523,2910,0,1,3}-{2533,2910,0,1,2}" - }, - { - "npc_id": "5085", - "loc_data": "{2328,3526,0,0,0}-{2313,3528,0,0,0}-{2312,3542,0,0,0}-{2312,3544,0,0,0}-{2323,3543,0,0,0}-{2325,3544,0,0,0}-{2328,3539,0,0,0}-{2311,3564,0,0,0}-{2332,3548,0,0,0}-{2335,3545,0,0,0}-{2320,3580,0,0,0}-{2325,3582,0,0,0}-{2330,3583,0,0,0}-{2310,3585,0,0,0}-{2324,3584,0,0,0}-{2312,3587,0,0,0}-{2316,3599,0,0,0}-{2324,3599,0,0,0}-{2323,3600,0,0,0}-{2315,3602,0,0,0}-{2309,3605,0,0,0}-{2316,3608,0,0,0}-{2315,3608,0,0,0}-{2325,3614,0,0,0}-{2313,3620,0,0,0}-{2333,3612,0,0,0}-{2331,3608,0,0,0}-{2331,3604,0,0,0}-{2346,3591,0,0,0}-{2355,3573,0,0,0}-{2369,3573,0,0,0}-{2359,3571,0,0,0}-{2352,3567,0,0,0}-{2372,3561,0,0,0}-{2361,3554,0,0,0}-{2329,3561,0,0,0}-{2341,3560,0,0,0}-{2344,3543,0,0,0}-{2342,3539,0,0,0}-{2349,3547,0,0,0}-{2352,3545,0,0,0}-{2334,3522,0,0,0}" - }, - { - "npc_id": "5086", - "loc_data": "{2321,3593,0,1,1}-{2322,3597,0,1,0}-{2309,3603,0,1,5}-{2306,3607,0,1,0}-{2305,3619,0,1,1}-{2309,3618,0,1,6}-{2322,3627,0,1,3}-{2324,3614,0,1,1}-{2323,3614,0,1,1}-{2323,3629,0,1,6}-{2329,3633,0,1,4}-{2328,3634,0,1,3}-{2311,3642,0,1,1}-{2307,3644,0,1,6}-{2318,3645,0,1,1}-{2323,3645,0,1,3}-{2333,3629,0,1,3}-{2340,3641,0,1,7}-{2343,3640,0,1,6}-{2335,3633,0,1,6}" - }, - { - "npc_id": "5087", - "loc_data": "{2706,3776,0,1,4}-{2712,3774,0,1,3}-{2716,3778,0,1,4}-{2715,3772,0,1,3}-{2720,3767,0,1,4}-{2712,3766,0,1,0}-{2718,3767,0,1,7}-{2719,3771,0,1,3}" - }, - { - "npc_id": "5088", - "loc_data": "{2586,2883,0,1,6}-{2589,2909,0,1,1}-{2574,2915,0,1,3}-{2572,2928,0,1,3}-{2567,2932,0,1,6}-{2568,2933,0,1,0}-{2538,2889,0,1,3}-{2538,2902,0,1,4}-{2536,2872,0,1,4}-{2536,2872,0,1,6}-{2536,2872,0,1,4}-{2536,2872,0,1,6}-{2536,2872,0,1,3}-{2585,2884,0,1,5}-{2577,2890,0,1,0}-{2578,2918,0,1,1}-{2536,2864,0,1,7}-{2568,2934,0,1,1}-{2574,2930,0,1,1}-{2576,2928,0,1,4}-{2512,2864,0,1,6}-{2512,2864,0,1,1}-{2580,2889,0,1,1}-{2580,2886,0,1,1}-{2580,2886,0,1,0}-{2572,2900,0,1,4}-{2572,2896,0,1,3}-{2568,2905,0,1,4}" - }, - { - "npc_id": "5089", - "loc_data": "{2315,3523,0,1,1}-{2315,3530,0,1,6}-{2308,3536,0,1,4}-{2307,3538,0,1,1}-{2325,3551,0,1,3}-{2316,3559,0,1,1}-{2312,3559,0,1,1}-{2310,3572,0,1,3}-{2334,3552,0,1,4}-{2308,3582,0,1,2}-{2363,3579,0,1,1}-{2363,3577,0,1,1}-{2350,3568,0,1,1}-{2352,3561,0,1,1}-{2346,3547,0,1,7}-{2347,3546,0,1,3}-{2363,3541,0,1,1}-{2362,3536,0,1,2}-{2350,3526,0,1,7}-{2350,3521,0,1,4}-{2351,3562,0,1,0}-{2310,3574,0,1,6}" - }, - { - "npc_id": "5093", - "loc_data": "{2375,3607,0,0,0}" - }, - { - "npc_id": "5098", - "loc_data": "{2372,3592,0,1,5}-{2370,3587,0,1,3}-{2371,3586,0,1,6}-{2376,3582,0,1,6}-{2378,3582,0,1,1}-{2383,3582,0,1,5}-{2393,3590,0,1,6}" - }, - { - "npc_id": "5099", - "loc_data": "{2365,3597,0,1,5}-{2369,3588,0,1,4}-{2366,3577,0,1,4}-{2372,3573,0,1,1}-{2377,3584,0,1,1}-{2385,3590,0,1,3}-{2388,3598,0,1,3}" - }, - { - "npc_id": "5100", - "loc_data": "{2369,3579,0,1,5}-{2373,3575,0,1,1}-{2390,3588,0,1,4}" - }, - { - "npc_id": "5103", - "loc_data": "{2733,3777,0,1,6}-{2731,3784,0,1,4}-{2735,3790,0,1,6}-{2741,3783,0,1,2}-{2738,3790,0,1,1}-{2731,3783,0,1,1}-{2724,3791,0,1,4}" - }, - { - "npc_id": "5104", - "loc_data": "{2551,2890,0,0,1}-{2541,2899,0,0,0}-{2564,2896,0,0,4}-{2571,2882,0,1,1}-{2552,2910,0,0,0}-{2573,2900,0,1,3}-{2558,2881,0,0,0}-{2543,2899,0,0,0}-{2545,2925,0,0,0}-{2549,2905,0,1,3}" - }, - { - "npc_id": "5105", - "loc_data": "{2780,3017,0,1,0}-{2766,3013,0,1,0}-{2785,3006,0,1,0}-{2775,2996,0,1,0}" - }, - { - "npc_id": "5109", - "loc_data": "{3443,2902,0,1,3}" - }, - { - "npc_id": "5110", - "loc_data": "{2566,3084,0,1,6}" - }, - { - "npc_id": "5111", - "loc_data": "{2569,3083,0,1,1}" - }, - { - "npc_id": "5112", - "loc_data": "{2712,3833,1,1,1}" - }, - { - "npc_id": "5113", - "loc_data": "{2525,2916,0,1,5}" - }, - { - "npc_id": "5114", - "loc_data": "{3411,3076,0,1,6}-{3415,3075,0,1,1}-{3406,3093,0,1,3}-{3413,3078,0,1,1}-{3403,3093,0,1,1}-{3412,3081,0,1,3}-{3405,3099,0,1,3}-{3405,3102,0,1,0}-{3406,3098,0,1,4}-{3401,3099,0,1,7}-{3405,3093,0,1,4}-{3404,3085,0,1,5}-{3402,3094,0,1,6}-{3399,3096,0,1,1}-{3405,3088,0,1,3}-{3411,3088,0,1,2}" - }, - { - "npc_id": "5115", - "loc_data": "{2451,3219,0,1,3}-{2448,3221,0,1,3}-{2476,3236,0,1,3}-{2450,3226,0,1,6}-{2474,3239,0,1,0}-{2470,3240,0,1,2}-{2468,3243,0,1,0}-{2455,3219,0,1,6}-{2449,3225,0,1,7}-{2445,3226,0,1,2}-{2467,3241,0,1,1}-{2452,3221,0,1,3}-{2473,3235,0,1,7}-{2472,3241,0,1,4}-{2475,3237,0,1,3}" - }, - { - "npc_id": "5116", - "loc_data": "{3291,3675,0,1,6}-{3293,3678,0,1,1}-{3302,3671,0,1,0}-{3299,3666,0,1,5}-{3319,3662,0,1,2}-{3309,3661,0,1,5}-{3321,3661,0,1,1}-{3298,3672,0,1,0}-{3316,3657,0,1,4}-{3302,3667,0,1,1}-{3302,3666,0,1,3}-{3315,3663,0,1,1}" - }, - { - "npc_id": "5117", - "loc_data": "{3537,3449,0,1,2}-{3535,3447,0,1,6}-{3539,3447,0,1,2}-{3533,3446,0,1,1}-{3549,3439,0,1,4}-{3547,3439,0,1,7}-{3549,3439,0,1,4}" - }, - { - "npc_id": "5120", - "loc_data": "{2326,3488,0,1,2}-{2325,3483,0,1,2}-{2315,3482,0,1,2}-{2336,3491,0,1,2}" - }, - { - "npc_id": "5125", - "loc_data": "{2316,3501,0,0,0}" - }, - { - "npc_id": "5138", - "loc_data": "{2607,3264,0,0,0}" - }, - { - "npc_id": "5139", - "loc_data": "{2607,3260,0,0,0}" - }, - { - "npc_id": "5146", - "loc_data": "{3205,3263,0,0,0}-{2925,3324,0,1,0}" - }, - { - "npc_id": "5147", - "loc_data": "{3207, 3270, 0, 1, 0}" - }, - { - "npc_id": "5148", - "loc_data": "{3229,3347,0,1,1}-{3238,3347,0,1,1}" - }, - { - "npc_id": "5149", - "loc_data": "{3233,3350,0,1,0}" - }, - { - "npc_id": "5156", - "loc_data": "{3203,3262,0,1,5}-{3195,3265,0,1,0}-{3199,3267,0,1,0}" - }, - { - "npc_id": "5157", - "loc_data": "{3205,3262,0,1,1}-{3194,3265,0,1,0}-{3201,3274,0,1,0}" - }, - { - "npc_id": "5160", - "loc_data": "{3205,3270,0,1,4}-{3249,3345,0,1,5}-{3195,3272,0,1,0}-{3195,3268,0,1,0}" - }, - { - "npc_id": "5161", - "loc_data": "{3210,3273,0,1,6}-{3233,3340,0,1,5}-{3241,3344,0,1,3}-{2926,3326,0,1,6}-{2924,3324,0,1,1}" - }, - { - "npc_id": "5164", - "loc_data": "{2410,4456,0,1,1}-{2415,4429,0,1,2}" - }, - { - "npc_id": "5166", - "loc_data": "{2811,3687,0,1,0}" - }, - { - "npc_id": "5167", - "loc_data": "{2810,3684,0,1,0}" - }, - { - "npc_id": "5168", - "loc_data": "{3260,3349,0,1,0}-{3199,3270,0,1,1}-{2658,4713,0,1,0}-{3209,3259,0,1,3}" - }, - { - "npc_id": "5169", - "loc_data": "{3234,3345,0,1,0}-{3196,3275,0,1,0}-{2657,4705,0,1,0}" - }, - { - "npc_id": "5170", - "loc_data": "{3052,3516,0,1,0}" - }, - { - "npc_id": "5171", - "loc_data": "{2860,10054,1,0,0}" - }, - { - "npc_id": "5173", - "loc_data": "{2769,3611,0,1,7}-{2768,3607,0,1,6}-{2764,3612,0,1,2}-{2763,3608,0,1,6}-{2762,3611,0,1,4}-{2765,3606,0,1,0}" - }, - { - "npc_id": "5199", - "loc_data": "{3093,3357,0,0,0}" - }, - { - "npc_id": "5200", - "loc_data": "{3099,3369,0,1,1}" - }, - { - "npc_id": "5202", - "loc_data": "{3621,3528,0,1,4}" - }, - { - "npc_id": "5249", - "loc_data": "{3321,3431,0,0,1}" - }, - { - "npc_id": "5258", - "loc_data": "{2666,2651,0,0,1}-{2668,2651,0,0,1}" - }, - { - "npc_id": "5260", - "loc_data": "{2665,2651,0,0,1}-{2667,2651,0,0,1}" - }, - { - "npc_id": "5291", - "loc_data": "{3300,2792,0,0,6}-{3282,2807,0,0,6}-{3285,2811,0,0,1}-{3283,2772,0,1,5}-{3281,2771,0,1,7}-{3283,2776,0,1,6}-{3278,2770,0,1,6}" - }, - { - "npc_id": "5298", - "loc_data": "{2499,3286,0,1,6}" - }, - { - "npc_id": "5300", - "loc_data": "{2500,3283,0,1,0}" - }, - { - "npc_id": "5303", - "loc_data": "{2506,3284,0,1,6}" - }, - { - "npc_id": "5310", - "loc_data": "{3212,3682,0,1,0}-{3228,3680,0,1,6}-{3230,3683,0,1,1}-{3234,3692,0,1,5}" - }, - { - "npc_id": "5311", - "loc_data": "{3231,3673,0,1,0}-{3225,3677,0,1,3}-{3229,3689,0,1,4}-{3239,3681,0,1,4}-{3236,3694,0,1,6}" - }, - { - "npc_id": "5312", - "loc_data": "{3210,3667,0,1,4}-{3217,3688,0,1,4}-{3241,3694,0,1,3}" - }, - { - "npc_id": "5313", - "loc_data": "{3224,3686,0,1,5}-{3243,3688,0,1,1}-{3242,3687,0,1,0}" - }, - { - "npc_id": "5314", - "loc_data": "{2588,9491,0,1,3}-{2585,9492,0,1,3}-{2586,9491,0,1,5}-{2591,9492,0,1,3}" - }, - { - "npc_id": "5316", - "loc_data": "{2497,3292,0,1,3}" - }, - { - "npc_id": "5318", - "loc_data": "{2511,3287,0,1,3}" - }, - { - "npc_id": "5323", - "loc_data": "{3221,3693,0,1,6}" - }, - { - "npc_id": "5324", - "loc_data": "{3221,3682,0,1,4}" - }, - { - "npc_id": "5325", - "loc_data": "{3223,3675,0,1,6}-{3237,3692,0,1,7}" - }, - { - "npc_id": "5326", - "loc_data": "{3232,3685,0,1,0}" - }, - { - "npc_id": "5327", - "loc_data": "{3229,3678,0,1,6}" - }, - { - "npc_id": "5332", - "loc_data": "{2887,9822,0,1,0}-{2884,9824,0,1,0}-{3102,3571,0,1,3}-{2855,9571,0,1,0}-{3008,3595,0,1,0}-{3018,3584,0,1,0}-{3022,3595,0,1,0}-{3368,9748,0,1,0}-{3378,9757,0,1,0}-{3383,9746,0,1,0}-{3368,9812,0,1,0}-{3378,9821,0,1,0}-{3383,9810,0,1,0}" - }, - { - "npc_id": "5333", - "loc_data": "{2886,9816,0,1,0}-{3116,3524,0,1,4}-{3106,3548,0,1,2}-{2860,9571,0,1,7}-{3013,3586,0,1,0}-{3013,3602,0,1,0}-{3025,3597,0,1,0}-{3376,9751,0,1,0}-{3380,9756,0,1,0}-{3376,9815,0,1,0}-{3380,9820,0,1,0}" - }, - { - "npc_id": "5334", - "loc_data": "{2885,9819,0,1,0}-{3107,3528,0,1,1}-{2859,9572,0,1,6}-{3012,3597,0,1,0}-{3018,3592,0,1,0}-{3018,3599,0,1,0}-{3025,3590,0,1,0}-{3377,9743,0,1,0}-{3377,9754,0,1,0}-{3377,9807,0,1,0}-{3377,9818,0,1,0}" - }, - { - "npc_id": "5337", - "loc_data": "{2885,9825,0,1,0}-{3100,3563,0,1,1}-{2832,9656,0,1,6}-{2846,3250,0,1,7}-{2834,3233,0,1,5}-{3003,10347,0,1,7}-{3003,10350,0,1,6}-{3003,10357,0,1,4}-{2996,10343,0,1,3}-{2993,10346,0,1,4}-{3179,3786,0,1,0}" - }, - { - "npc_id": "5338", - "loc_data": "{3119,3565,0,1,1}-{2838,9648,0,1,5}-{2831,9654,0,1,1}-{2843,3248,0,1,1}-{2840,3240,0,1,3}-{2994,10349,0,1,5}-{2998,10350,0,1,0}-{3003,10350,0,1,4}-{2989,3944,0,1,4}" - }, - { - "npc_id": "5339", - "loc_data": "{3095,3554,0,1,4}-{2831,9655,0,1,7}-{2844,3239,0,1,0}" - }, - { - "npc_id": "5340", - "loc_data": "{3112,3573,0,1,6}-{2833,9655,0,1,6}-{2848,3246,0,1,3}-{2982,3947,0,1,0}" - }, - { - "npc_id": "5341", - "loc_data": "{2840,9633,0,1,1}-{2844,9634,0,1,6}-{2842,9637,0,1,3}-{2641,9889,0,1,0}-{2648,9890,0,1,0}-{2657,9894,0,1,0}-{2658,9888,0,1,0}" - }, - { - "npc_id": "5342", - "loc_data": "{3276,3659,0,1,0}" - }, - { - "npc_id": "5343", - "loc_data": "{3282,3662,0,1,0}" - }, - { - "npc_id": "5345", - "loc_data": "{2501,3294,0,1,3}" - }, - { - "npc_id": "5346", - "loc_data": "{3279,3654,0,1,0}" - }, - { - "npc_id": "5347", - "loc_data": "{3288,3650,0,1,0}" - }, - { - "npc_id": "5348", - "loc_data": "{3290,3653,0,1,0}" - }, - { - "npc_id": "5349", - "loc_data": "{3099,3354,1,1,3}" - }, - { - "npc_id": "5350", - "loc_data": "{3116,3367,1,1,3}" - }, - { - "npc_id": "5351", - "loc_data": "{3110,3358,1,1,1}" - }, - { - "npc_id": "5352", - "loc_data": "{3121,3359,1,1,4}" - }, - { - "npc_id": "5355", - "loc_data": "{2873,2950,0,0,0}-{2876,2951,0,0,0}" - }, - { - "npc_id": "5356", - "loc_data": "{2877,2958,0,0,0}" - }, - { - "npc_id": "5357", - "loc_data": "{2876,2958,0,0,0}" - }, - { - "npc_id": "5358", - "loc_data": "{2874,2954,0,0,6}" - }, - { - "npc_id": "5361", - "loc_data": "{3176,5543,0,1,3}-{3191,5542,0,1,4}-{1757,5358,0,1,5}-{1760,5357,0,1,5}-{1755,5352,0,1,5}-{1751,5342,0,1,3}-{1745,5342,0,1,6}-{1739,5342,0,1,3}-{1737,5344,0,1,7}-{1739,5347,0,1,6}-{1740,5350,0,1,5}-{1739,5355,0,1,3}-{1747,5363,0,1,4}-{1749,5363,0,1,4}-{1743,5360,0,1,3}-{1739,5356,0,1,0}-{3175,5543,0,1,2}-{3178,5541,0,1,2}-{3187,5542,0,1,3}-{3192,5545,0,1,2}-{3192,5542,0,1,3}-{3179,5549,0,1,6}" - }, - { - "npc_id": "5362", - "loc_data": "{1774,5361,0,1,3}-{1776,5350,0,1,3}-{1772,5342,0,1,1}-{1772,5336,0,1,4}-{1778,5328,0,1,7}-{1761,5334,0,1,4}-{1754,5337,0,1,7}-{1749,5331,0,0,1}" - }, - { - "npc_id": "5363", - "loc_data": "{1781,5340,1,1,5}-{1782,5356,1,1,1}-{1767,5343,1,1,6}-{1761,5338,1,1,3}-{1767,5332,1,1,6}-{1757,5326,1,1,7}-{1784,5329,1,1,1}" - }, - { - "npc_id": "5364", - "loc_data": "{1765,5344,0,1,3}" - }, - { - "npc_id": "5365", - "loc_data": "{2354,5191,0,1,4}-{2351,5191,0,1,5}" - }, - { - "npc_id": "5366", - "loc_data": "{2357,5196,0,1,2}-{2356,5188,0,1,4}-{2343,5195,0,1,6}-{2335,5202,0,1,3}-{2323,5211,0,1,1}" - }, - { - "npc_id": "5367", - "loc_data": "{3092,3717,0,1,0}-{3105,3721,0,1,0}-{3113,3740,0,1,0}-{2312,5189,0,1,7}" - }, - { - "npc_id": "5368", - "loc_data": "{3096,3738,0,1,0}-{3102,3716,0,1,0}-{3118,3733,0,1,0}-{3128,3741,0,1,0}" - }, - { - "npc_id": "5369", - "loc_data": "{2345,5195,0,1,3}-{2264,5136,0,1,1}-{2323,5227,0,1,5}-{2319,5223,0,1,6}" - }, - { - "npc_id": "5370", - "loc_data": "{2347,5195,0,1,3}-{2323,5225,0,1,3}-{2319,5227,0,1,0}-{2323,5227,0,1,3}-{2318,5227,0,1,0}-{2326,5227,0,1,6}" - }, - { - "npc_id": "5371", - "loc_data": "{2356,5204,0,1,1}-{2324,5223,0,1,1}" - }, - { - "npc_id": "5375", - "loc_data": "{2029,5236,0,1,6}-{2031,5235,0,1,1}-{2029,5230,0,1,3}" - }, - { - "npc_id": "5376", - "loc_data": "{2031,5233,0,1,1}" - }, - { - "npc_id": "5377", - "loc_data": "{2004,5189,0,1,3}-{2002,5207,0,1,3}-{2009,5199,0,1,5}-{2004,5203,0,1,6}-{1989,5192,0,1,6}-{1988,5187,0,1,6}" - }, - { - "npc_id": "5378", - "loc_data": "{1993,5192,0,1,5}-{1999,5190,0,1,6}-{1989,5187,0,1,4}-{2008,5205,0,1,4}" - }, - { - "npc_id": "5379", - "loc_data": "{1952,5192,0,1,5}-{1995,5240,0,1,5}-{2010,5186,0,1,4}-{2023,5190,0,1,1}" - }, - { - "npc_id": "5380", - "loc_data": "{1952,5192,0,1,2}-{2028,5192,0,1,0}-{2031,5186,0,1,0}-{2027,5194,0,1,0}--{2021,5188,0,1,0}" - }, - { - "npc_id": "5383", - "loc_data": "{3194,4569,0,0,0}" - }, - { - "npc_id": "5384", - "loc_data": "{3165,4598,0,0,0}-{3167,4577,0,0,0}" - }, - { - "npc_id": "5385", - "loc_data": "{3191,4595,1,1,1}-{3191,4601,1,1,2}" - }, - { - "npc_id": "5386", - "loc_data": "{3143,4595,0,1,1}-{3145,4598,0,1,3}-{3182,4577,0,1,1}-{3187,4578,0,1,0}-{3158,4589,1,1,7}-{3194,4599,1,1,4}" - }, - { - "npc_id": "5387", - "loc_data": "{3158,4584,0,1,4}-{3159,4577,0,1,6}-{3169,4587,0,1,1}-{3152,4569,1,1,1}" - }, - { - "npc_id": "5388", - "loc_data": "{3156,4567,2,1,0}" - }, - { - "npc_id": "5389", - "loc_data": "{3157,4568,0,1,6}-{3185,4562,0,1,3}" - }, - { - "npc_id": "5390", - "loc_data": "{3182,4562,0,1,3}-{3185,4560,0,1,3}" - }, - { - "npc_id": "5391", - "loc_data": "{3158,4552,0,1,4}-{3163,4569,0,1,1}-{3177,4549,0,1,3}-{3184,4550,0,1,3}-{3158,4560,1,1,4}-{3174,4566,1,1,1}-{3195,4577,1,1,6}" - }, - { - "npc_id": "5393", - "loc_data": "{3146,4596,0,1,1}-{3163,4598,0,1,3}-{3155,4591,1,1,3}" - }, - { - "npc_id": "5394", - "loc_data": "{3167,4589,0,1,6}-{3172,4598,0,1,1}-{2677,9688,0,1,1}-{2676,9679,0,1,4}-{2669,9681,0,1,1}" - }, - { - "npc_id": "5395", - "loc_data": "{3141,4598,0,1,3}-{3170,4592,0,1,6}-{3144,4589,1,1,3}" - }, - { - "npc_id": "5396", - "loc_data": "{3145,4583,1,1,0}" - }, - { - "npc_id": "5397", - "loc_data": "{3155,4583,0,1,3}-{3176,4576,1,1,3}" - }, - { - "npc_id": "5398", - "loc_data": "{3145,4574,1,1,3}-{3182,4586,1,1,3}" - }, - { - "npc_id": "5399", - "loc_data": "{3140,4557,2,1,4}-{3188,4578,2,1,1}" - }, - { - "npc_id": "5400", - "loc_data": "{3175,4579,1,1,6}-{3142,4560,2,1,3}-{3159,4569,2,1,1}" - }, - { - "npc_id": "5401", - "loc_data": "{3151,4573,1,1,4}" - }, - { - "npc_id": "5402", - "loc_data": "{3154,4559,0,1,6}-{3171,4571,1,1,6}-{3179,4586,1,1,1}" - }, - { - "npc_id": "5403", - "loc_data": "{3141,4557,0,1,4}-{3182,4577,2,1,6}" - }, - { - "npc_id": "5404", - "loc_data": "{3143,4555,0,1,4}-{3144,4549,1,1,4}-{3190,4587,1,1,1}-{3186,4576,2,1,3}" - }, - { - "npc_id": "5405", - "loc_data": "{3163,4559,0,1,4}-{3146,4565,1,1,3}-{3195,4582,1,1,6}" - }, - { - "npc_id": "5406", - "loc_data": "{3159,4603,1,1,1}" - }, - { - "npc_id": "5407", - "loc_data": "{3141,4547,1,1,3}" - }, - { - "npc_id": "5408", - "loc_data": "{3170,4559,1,1,4}" - }, - { - "npc_id": "5409", - "loc_data": "{3173,4550,0,1,6}-{3190,4551,0,1,2}" - }, - { - "npc_id": "5410", - "loc_data": "{3190,4561,0,1,5}" - }, - { - "npc_id": "5413", - "loc_data": "{3187,5450,0,1,6}-{3177,5453,0,1,6}" - }, - { - "npc_id": "5414", - "loc_data": "{3176,5454,0,1,3}-{3182,5447,0,1,3}" - }, - { - "npc_id": "5423", - "loc_data": "{3212,3263,0,0,0}" - }, - { - "npc_id": "5439", - "loc_data": "{3017,3277,0,0,0}" - }, - { - "npc_id": "5445", - "loc_data": "{2596,3269,0,1,4}-{2571,3907,0,1,7}-{2571,3907,0,1,3}-{2569,3908,0,1,3}-{2571,3908,0,1,3}-{2572,3906,0,1,6}" - }, - { - "npc_id": "5478", - "loc_data": "{2407,3803,0,1,3}" - }, - { - "npc_id": "5479", - "loc_data": "{2407,3803,0,1,3}" - }, - { - "npc_id": "5480", - "loc_data": "{2406,3803,0,1,3}" - }, - { - "npc_id": "5483", - "loc_data": "{2395,3796,0,1,2}" - }, - { - "npc_id": "5484", - "loc_data": "{2418,3816,0,1,3}" - }, - { - "npc_id": "5485", - "loc_data": "{2393,3796,0,1,2}" - }, - { - "npc_id": "5486", - "loc_data": "{2396,3805,0,1,3}" - }, - { - "npc_id": "5487", - "loc_data": "{2417,3815,0,1,3}" - }, - { - "npc_id": "5488", - "loc_data": "{2416,3799,0,0,1}" - }, - { - "npc_id": "5489", - "loc_data": "{2372,3801,2,0,3}" - }, - { - "npc_id": "5490", - "loc_data": "{2364,3800,2,0,4}" - }, - { - "npc_id": "5493", - "loc_data": "{2391,3800,0,1,5}" - }, - { - "npc_id": "5494", - "loc_data": "{2416,3808,0,1,5}" - }, - { - "npc_id": "5495", - "loc_data": "{2406,3813,0,1,3}" - }, - { - "npc_id": "5496", - "loc_data": "{2402,3800,0,1,4}" - }, - { - "npc_id": "5499", - "loc_data": "{2403,3807,0,1,4}" - }, - { - "npc_id": "5500", - "loc_data": "{2395,3812,0,2,2}" - }, - { - "npc_id": "5501", - "loc_data": "{2408,3816,0,1,3}" - }, - { - "npc_id": "5502", - "loc_data": "{2409,3814,0,1,3}" - }, - { - "npc_id": "5504", - "loc_data": "{2336,3799,0,0,4}" - }, - { - "npc_id": "5505", - "loc_data": "{2334,3800,0,0,4}" - }, - { - "npc_id": "5506", - "loc_data": "{2334,3798,0,0,4}" - }, - { - "npc_id": "5507", - "loc_data": "{2311,3781,0,0,1}" - }, - { - "npc_id": "5508", - "loc_data": "{2646,3710,0,0,4}" - }, - { - "npc_id": "5509", - "loc_data": "{2336,3808,0,0,6}" - }, - { - "npc_id": "5510", - "loc_data": "{2333,3804,0,1,1}" - }, - { - "npc_id": "5511", - "loc_data": "{2327,3794,0,1,6}" - }, - { - "npc_id": "5512", - "loc_data": "{2319,3795,0,1,3}" - }, - { - "npc_id": "5513", - "loc_data": "{2352,3797,0,1,2}" - }, - { - "npc_id": "5514", - "loc_data": "{2333,3795,1,1,4}" - }, - { - "npc_id": "5515", - "loc_data": "{2330,3799,1,1,3}" - }, - { - "npc_id": "5516", - "loc_data": "{2329,3808,1,1,4}" - }, - { - "npc_id": "5518", - "loc_data": "{2338,3798,0,0,1}" - }, - { - "npc_id": "5519", - "loc_data": "{2338,3800,0,0,6}" - }, - { - "npc_id": "5520", - "loc_data": "{2338,3810,0,0,3}" - }, - { - "npc_id": "5529", - "loc_data": "{2323,3790,0,1,1}-{2323,3795,0,1,0}-{2322,3795,0,1,0}-{2317,3794,0,1,6}-{2317,3790,0,1,3}-{2321,3792,0,1,6}" - }, - { - "npc_id": "5531", - "loc_data": "{3323,3138,1,1,6}" - }, - { - "npc_id": "5532", - "loc_data": "{3320,3138,0,1,3}" - }, - { - "npc_id": "5533", - "loc_data": "{2904,5460,0,1,4}" - }, - { - "npc_id": "5534", - "loc_data": "{2900,5455,0,1,1}" - }, - { - "npc_id": "5535", - "loc_data": "{2903,5449,0,1,4}" - }, - { - "npc_id": "5536", - "loc_data": "{2905,5451,0,1,1}" - }, - { - "npc_id": "5537", - "loc_data": "{2905,5457,0,1,4}" - }, - { - "npc_id": "5538", - "loc_data": "{2912,5455,0,1,3}" - }, - { - "npc_id": "5539", - "loc_data": "{2922,5464,0,1,1}" - }, - { - "npc_id": "5540", - "loc_data": "{2924,5462,0,1,4}" - }, - { - "npc_id": "5541", - "loc_data": "{2924,5458,0,1,4}" - }, - { - "npc_id": "5542", - "loc_data": "{2930,5458,0,1,1}" - }, - { - "npc_id": "5544", - "loc_data": "{2931,5469,0,1,3}" - }, - { - "npc_id": "5545", - "loc_data": "{2925,5471,0,1,1}" - }, - { - "npc_id": "5546", - "loc_data": "{2931,5475,0,1,1}" - }, - { - "npc_id": "5547", - "loc_data": "{2907,5486,0,1,6}" - }, - { - "npc_id": "5548", - "loc_data": "{2907,5492,0,1,6}" - }, - { - "npc_id": "5549", - "loc_data": "{2910,5491,0,1,6}" - }, - { - "npc_id": "5550", - "loc_data": "{2915,5483,0,1,1}" - }, - { - "npc_id": "5551", - "loc_data": "{2922,5486,0,1,4}" - }, - { - "npc_id": "5552", - "loc_data": "{2921,5493,0,1,4}" - }, - { - "npc_id": "5553", - "loc_data": "{2899,5468,0,1,4}" - }, - { - "npc_id": "5554", - "loc_data": "{2894,5470,0,1,3}" - }, - { - "npc_id": "5555", - "loc_data": "{2898,5478,0,1,4}" - }, - { - "npc_id": "5556", - "loc_data": "{2896,5482,0,1,3}" - }, - { - "npc_id": "5557", - "loc_data": "{2894,5483,0,1,4}" - }, - { - "npc_id": "5558", - "loc_data": "{2894,5485,0,1,3}" - }, - { - "npc_id": "5562", - "loc_data": "{2913,5462,0,0,0}-{2900,5458,0,0,0}-{2908,5455,0,0,0}-{2910,5455,0,0,0}-{2913,5452,0,0,0}-{2914,5453,0,0,0}" - }, - { - "npc_id": "5563", - "loc_data": "{2911,5474,0,0,0}" - }, - { - "npc_id": "5574", - "loc_data": "{2836,3066,0,0,0}" - }, - { - "npc_id": "5576", - "loc_data": "{3072,3336,0,0,0}-{3063,3348,0,0,0}" - }, - { - "npc_id": "5608", - "loc_data": "{3786,2827,2,0,0}" - }, - { - "npc_id": "5609", - "loc_data": "{3786,2824,1,0,0}" - }, - { - "npc_id": "5610", - "loc_data": "{3785,2827,0,0,0}" - }, - { - "npc_id": "5611", - "loc_data": "{3784,2824,0,0,0}" - }, - { - "npc_id": "5614", - "loc_data": "{3779,2826,0,0,0}-{3789,2817,0,0,0}-{3794,2821,0,0,0}-{3808,2823,0,0,0}-{3812,2861,0,0,0}-{3813,2822,0,0,0}-{3818,2862,0,0,0}-{3819,2870,0,0,0}-{3821,2824,0,0,0}-{3831,2828,0,0,0}" - }, - { - "npc_id": "5627", - "loc_data": "{3808,2845,0,0,0}-{3812,2846,0,0,0}-{3815,2844,0,0,3}-{3819,2839,0,0,0}-{3819,2851,0,0,0}-{3819,2838,1,0,0}-{3823,2836,1,0,0}-{3823,2846,1,0,0}" - }, - { - "npc_id": "5628", - "loc_data": "{3810,2843,0,0,0}-{3813,2844,0,0,0}-{3821,2837,0,0,0}-{3820,2840,1,0,0}-{3820,2851,1,0,0}-{3823,2839,1,0,0}" - }, - { - "npc_id": "5629", - "loc_data": "{3811,2826,0,0,0}" - }, - { - "npc_id": "5631", - "loc_data": "{3827,2833,0,0,0}" - }, - { - "npc_id": "5633", - "loc_data": "{3815,2859,0,0,0}" - }, - { - "npc_id": "5634", - "loc_data": "{3793,2851,0,0,0}" - }, - { - "npc_id": "5635", - "loc_data": "{3784,2843,0,0,0}" - }, - { - "npc_id": "5637", - "loc_data": "{3803,2843,0,0,0}" - }, - { - "npc_id": "5638", - "loc_data": "{3810,2833,0,0,0}" - }, - { - "npc_id": "5639", - "loc_data": "{3824,2857,0,0,0}" - }, - { - "npc_id": "5640", - "loc_data": "{3817,2824,0,0,0}" - }, - { - "npc_id": "5641", - "loc_data": "{3799,2855,0,0,0}" - }, - { - "npc_id": "5642", - "loc_data": "{3796,2858,0,0,0}" - }, - { - "npc_id": "5644", - "loc_data": "{3795,2845,0,0,3}" - }, - { - "npc_id": "5646", - "loc_data": "{3792,2830,0,0,0}" - }, - { - "npc_id": "5647", - "loc_data": "{3803,2872,2,0,0}" - }, - { - "npc_id": "5650", - "loc_data": "{3800,2835,0,0,0}" - }, - { - "npc_id": "5656", - "loc_data": "{3794,2873,1,0,0}" - }, - { - "npc_id": "5658", - "loc_data": "{3804,2873,1,0,0}" - }, - { - "npc_id": "5659", - "loc_data": "{3805,2874,2,0,0}" - }, - { - "npc_id": "5661", - "loc_data": "{3794,2874,3,0,0}" - }, - { - "npc_id": "5665", - "loc_data": "{3803,2848,0,0,0}" - }, - { - "npc_id": "5668", - "loc_data": "{3787,2824,0,0,0}" - }, - { - "npc_id": "5669", - "loc_data": "{3787,2827,0,0,0}" - }, - { - "npc_id": "5670", - "loc_data": "{3786,2827,1,0,0}" - }, - { - "npc_id": "5671", - "loc_data": "{3788,2826,1,0,0}" - }, - { - "npc_id": "5672", - "loc_data": "{3788,2825,2,0,0}" - }, - { - "npc_id": "5748", - "loc_data": "{3483,3449,0,0,0}-{3490,3444,0,0,0}-{3479,3434,0,0,0}-{3481,3473,0,0,0}-{3479,3430,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}-{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}" - }, - { - "npc_id": "5750", - "loc_data": "{3290,5544,0,1,4}-{3270,5552,0,1,1}-{3274,5547,0,1,7}" - }, - { - "npc_id": "5752", - "loc_data": "{2711,5331,0,0,0}" - }, - { - "npc_id": "5753", - "loc_data": "{2697,5332,0,0,0}" - }, - { - "npc_id": "5755", - "loc_data": "{2723,5336,0,0,0}" - }, - { - "npc_id": "5756", - "loc_data": "{2702,5352,1,0,0}" - }, - { - "npc_id": "5757", - "loc_data": "{2743,5358,0,0,0}" - }, - { - "npc_id": "5758", - "loc_data": "{2693,5332,0,0,0}-{2729,5329,0,0,0}" - }, - { - "npc_id": "5761", - "loc_data": "{2725,5349,0,0,0}" - }, - { - "npc_id": "5764", - "loc_data": "{2712,5342,0,0,0}" - }, - { - "npc_id": "5765", - "loc_data": "{2694,5333,0,0,0}" - }, - { - "npc_id": "5766", - "loc_data": "{2701,5352,1,0,0}" - }, - { - "npc_id": "5767", - "loc_data": "{2695,5331,0,0,0}-{2719,5331,0,0,0}" - }, - { - "npc_id": "5770", - "loc_data": "{2717,5359,0,0,0}" - }, - { - "npc_id": "5771", - "loc_data": "{2722,5333,0,0,0}" - }, - { - "npc_id": "5772", - "loc_data": "{2716,5359,0,0,0}" - }, - { - "npc_id": "5776", - "loc_data": "{2699,5349,0,0,4}" - }, - { - "npc_id": "5777", - "loc_data": "{2699,5348,0,0,4}" - }, - { - "npc_id": "5780", - "loc_data": "{2722,5313,0,0,0}" - }, - { - "npc_id": "5781", - "loc_data": "{2723,5321,0,0,0}" - }, - { - "npc_id": "5782", - "loc_data": "{2742,5339,1,0,0}" - }, - { - "npc_id": "5783", - "loc_data": "{2701,5348,0,0,0}" - }, - { - "npc_id": "5785", - "loc_data": "{2726,5372,1,0,0}" - }, - { - "npc_id": "5786", - "loc_data": "{2712,5369,1,0,0}" - }, - { - "npc_id": "5787", - "loc_data": "{2704,5366,0,0,0}" - }, - { - "npc_id": "5789", - "loc_data": "{2718,5313,0,0,0}" - }, - { - "npc_id": "5790", - "loc_data": "{2715,5313,0,0,0}" - }, - { - "npc_id": "5794", - "loc_data": "{2712,5321,0,0,0}" - }, - { - "npc_id": "5796", - "loc_data": "{2711,5314,0,0,0}" - }, - { - "npc_id": "5798", - "loc_data": "{2717,5315,0,1,2}" - }, - { - "npc_id": "5799", - "loc_data": "{2730,5365,1,0,0}" - }, - { - "npc_id": "5800", - "loc_data": "{2719,5335,0,1,1}-{2747,5373,0,1,3}" - }, - { - "npc_id": "5801", - "loc_data": "{2730,5333,0,1,2}" - }, - { - "npc_id": "5802", - "loc_data": "{2744,5353,1,0,0}" - }, - { - "npc_id": "5803", - "loc_data": "{2741,5344,1,0,0}" - }, - { - "npc_id": "5804", - "loc_data": "{2744,5333,1,0,0}" - }, - { - "npc_id": "5805", - "loc_data": "{2743,5345,1,0,0}" - }, - { - "npc_id": "5806", - "loc_data": "{2746,5333,1,0,0}" - }, - { - "npc_id": "5807", - "loc_data": "{2741,5334,1,0,0}" - }, - { - "npc_id": "5809", - "loc_data": "{2741,5341,1,0,0}" - }, - { - "npc_id": "5811", - "loc_data": "{2744,5342,1,0,0}" - }, - { - "npc_id": "5813", - "loc_data": "{2742,5338,1,0,0}" - }, - { - "npc_id": "5815", - "loc_data": "{2743,5341,1,0,0}" - }, - { - "npc_id": "5817", - "loc_data": "{2744,5338,1,0,0}" - }, - { - "npc_id": "5819", - "loc_data": "{2744,5336,1,0,0}" - }, - { - "npc_id": "5821", - "loc_data": "{2740,5334,1,0,0}" - }, - { - "npc_id": "5827", - "loc_data": "{2699,5329,0,0,0}-{2699,5335,0,0,0}-{2747,5342,0,0,0}" - }, - { - "npc_id": "5828", - "loc_data": "{2697,5316,0,0,0}-{3323,9606,0,0,0}" - }, - { - "npc_id": "5832", - "loc_data": "{2730,5366,1,0,0}" - }, - { - "npc_id": "5833", - "loc_data": "{3267,3334,0,1,3}" - }, - { - "npc_id": "5834", - "loc_data": "{3208,3496,0,1,6}" - }, - { - "npc_id": "5837", - "loc_data": "{3285,3468,0,0,4}" - }, - { - "npc_id": "5839", - "loc_data": "{3183,5205,0,0,0}" - }, - { - "npc_id": "5840", - "loc_data": "{3159,5211,0,0,0}" - }, - { - "npc_id": "5841", - "loc_data": "{3176,5215,0,0,0}" - }, - { - "npc_id": "5842", - "loc_data": "{3115,3475,0,1,1}" - }, - { - "npc_id": "5843", - "loc_data": "{3117,3474,0,1,4}" - }, - { - "npc_id": "5844", - "loc_data": "{3113,3473,0,1,7}" - }, - { - "npc_id": "5845", - "loc_data": "{3115,3475,0,1,2}" - }, - { - "npc_id": "5846", - "loc_data": "{3110,3471,0,0,2}" - }, - { - "npc_id": "5847", - "loc_data": "{3115,3476,0,1,6}" - }, - { - "npc_id": "5848", - "loc_data": "{3116,3475,0,1,1}" - }, - { - "npc_id": "5849", - "loc_data": "{3112,3473,0,1,3}" - }, - { - "npc_id": "5850", - "loc_data": "{3115,3476,0,1,4}" - }, - { - "npc_id": "5851", - "loc_data": "{3119,3474,0,1,5}" - }, - { - "npc_id": "5869", - "loc_data": "{2744,5344,0,0,0}" - }, - { - "npc_id": "5880", - "loc_data": "{2439,5544,0,1,0}-{2485,5535,0,1,0}" - }, - { - "npc_id": "5881", - "loc_data": "{2436,5540,0,1,0}" - }, - { - "npc_id": "5882", - "loc_data": "{2437,5549,0,1,0}-{2484,5531,0,1,0}" - }, - { - "npc_id": "5883", - "loc_data": "{2436,5526,0,1,0}-{2486,5525,0,1,0}" - }, - { - "npc_id": "5884", - "loc_data": "{2439,5520,0,1,0}" - }, - { - "npc_id": "5885", - "loc_data": "{2487,5538,0,1,0}" - }, - { - "npc_id": "5887", - "loc_data": "{2737,5351,1,0,0}" - }, - { - "npc_id": "5909", - "loc_data": "{3224,3402,0,1,4}" - }, - { - "npc_id": "5910", - "loc_data": "{3230,3401,0,0,0}-{3284,3491,0,1,1}" - }, - { - "npc_id": "5914", - "loc_data": "{3204,3419,0,0,6}" - }, - { - "npc_id": "5915", - "loc_data": "{3255,3488,1,0,6}" - }, - { - "npc_id": "5916", - "loc_data": "{3250,3429,0,1,5}" - }, - { - "npc_id": "5917", - "loc_data": "{3200,3399,0,1,4}-{3262,3410,0,1,3}-{3176,3431,0,1,5}-{3261,3424,0,1,2}" - }, - { - "npc_id": "5918", - "loc_data": "{3233,3394,0,0,7}-{3271,3399,0,1,3}" - }, - { - "npc_id": "5919", - "loc_data": "{3244,3498,0,1,1}-{3244,3503,0,1,6}-{3247,3500,0,1,3}-{3175,3432,0,1,3}-{3175,3427,0,1,5}-{3205,3379,0,1,6}-{3211,3381,0,1,3}-{3172,3430,0,1,2}-{3175,3416,1,1,6}-{3175,3404,1,1,7}" - }, - { - "npc_id": "5920", - "loc_data": "{3175,3421,0,1,0}-{3273,3430,0,1,0}-{3211,3378,0,1,0}-{3272,3429,0,1,1}-{3274,3427,0,1,3}-{3220,3461,0,1,3}-{3221,3464,0,1,4}-{3213,3466,0,1,1}-{3214,3461,0,1,5}-{3215,3464,0,1,0}-{3208,3467,0,1,6}-{3209,3460,0,1,4}-{3205,3463,0,1,4}" - }, - { - "npc_id": "5923", - "loc_data": "{3224,3394,1,1,6}" - }, - { - "npc_id": "5924", - "loc_data": "{3227,3396,0,1,1}" - }, - { - "npc_id": "5925", - "loc_data": "{3220,3433,0,1,5}" - }, - { - "npc_id": "5926", - "loc_data": "{3229,3392,0,1,6}-{3184,3386,0,1,4}-{3247,9775,0,1,3}-{3185,3391,0,1,5}-{3240,9770,0,1,4}-{3235,9766,0,1,3}" - }, - { - "npc_id": "5927", - "loc_data": "{3250,9776,0,1,3}" - }, - { - "npc_id": "5928", - "loc_data": "{3249,9772,0,1,5}" - }, - { - "npc_id": "5929", - "loc_data": "{3245,9772,0,1,7}-{3239,9766,0,1,2}" - }, - { - "npc_id": "5930", - "loc_data": "{3257,3454,2,0,0}" - }, - { - "npc_id": "5932", - "loc_data": "{3255,3442,0,0,0}" - }, - { - "npc_id": "5933", - "loc_data": "{3254,3443,0,0,0}" - }, - { - "npc_id": "5934", - "loc_data": "{3259,3443,0,0,0}" - }, - { - "npc_id": "5935", - "loc_data": "{3260,3442,0,0,0}" - }, - { - "npc_id": "5938", - "loc_data": "{3253,3454,0,0,0}" - }, - { - "npc_id": "5939", - "loc_data": "{3253,3453,1,1,0}" - }, - { - "npc_id": "5940", - "loc_data": "{3262,3442,1,1,0}" - }, - { - "npc_id": "5941", - "loc_data": "{3260,3447,0,0,0}" - }, - { - "npc_id": "5942", - "loc_data": "{3295,3427,0,0,1}" - }, - { - "npc_id": "5943", - "loc_data": "{3263,3441,0,0,0}" - }, - { - "npc_id": "5944", - "loc_data": "{3263,3450,0,0,0}-{1746,4961,0,1,4}-{1761, 4938, 0, 1, 4}-{1761, 4976, 0, 1, 5}" - }, - { - "npc_id": "5946", - "loc_data": "{3261,3455,0,1,0}-{1758,4945,0,1,4}-{1757, 4935, 0, 1, 4}-{1746, 4980, 0, 1, 4}" - }, - { - "npc_id": "5947", - "loc_data": "{3260,3452,0,1,0}-{3261,3451,2,1,0}-{1735,4950,0,1,4}-{1759, 4948, 0, 1, 4}-{1780, 4960, 0, 1, 4}-{1760, 4974, 0, 1, 5}" - }, - { - "npc_id": "5952", - "loc_data": "{3326,3443,0,1,5}" - }, - { - "npc_id": "5954", - "loc_data": "{3310,3425,0,1,5}" - }, - { - "npc_id": "5956", - "loc_data": "{2659,2664,0,1,3}" - }, - { - "npc_id": "5958", - "loc_data": "{3357,3415,0,0,3}" - }, - { - "npc_id": "5959", - "loc_data": "{3352,3449,0,0,0}" - }, - { - "npc_id": "5960", - "loc_data": "{3354,3449,0,0,0}" - }, - { - "npc_id": "5961", - "loc_data": "{3352,3446,0,0,0}" - }, - { - "npc_id": "5962", - "loc_data": "{3357,3446,0,0,0}" - }, - { - "npc_id": "5963", - "loc_data": "{3363,3445,0,0,0}" - }, - { - "npc_id": "5964", - "loc_data": "{3324,3445,0,1,7}" - }, - { - "npc_id": "5984", - "loc_data": "{3263,3452,0,1,0}-{1743,4950,0,1,4}-{1770, 4935, 0, 1, 4}-{1772, 4962, 0, 1, 4}-{1771, 4957, 0, 1, 4}-{1777, 4976, 0, 1, 6}" - }, - { - "npc_id": "5987", - "loc_data": "{2891,3455,0,1,0}" - }, - { - "npc_id": "5991", - "loc_data": "{2966,3466,0,0,0}" - }, - { - "npc_id": "5997", - "loc_data": "{2862,3511,0,0,0}" - }, - { - "npc_id": "5998", - "loc_data": "{2971,3473,0,0,0}" - }, - { - "npc_id": "6026", - "loc_data": "{3489,3490,0,1,3}" - }, - { - "npc_id": "6027", - "loc_data": "{3493,3472,0,1,4}" - }, - { - "npc_id": "6028", - "loc_data": "{3497,3497,0,1,2}" - }, - { - "npc_id": "6030", - "loc_data": "{3496,3476,0,1,2}" - }, - { - "npc_id": "6031", - "loc_data": "{3511,3482,1,0,0}" - }, - { - "npc_id": "6032", - "loc_data": "{3502,3487,0,1,1}" - }, - { - "npc_id": "6033", - "loc_data": "{3484,3478,0,1,4}" - }, - { - "npc_id": "6034", - "loc_data": "{3502,3493,0,1,3}" - }, - { - "npc_id": "6035", - "loc_data": "{3486,3488,0,1,3}" - }, - { - "npc_id": "6036", - "loc_data": "{3499,3474,1,0,0}" - }, - { - "npc_id": "6037", - "loc_data": "{3499,3477,0,1,3}" - }, - { - "npc_id": "6038", - "loc_data": "{3490,3472,1,0,0}" - }, - { - "npc_id": "6039", - "loc_data": "{3483,3495,0,1,4}" - }, - { - "npc_id": "6040", - "loc_data": "{3498,3472,1,0,0}" - }, - { - "npc_id": "6041", - "loc_data": "{3479,3492,0,1,4}" - }, - { - "npc_id": "6042", - "loc_data": "{3479,3498,0,1,4}" - }, - { - "npc_id": "6044", - "loc_data": "{3505,3491,1,0,0}" - }, - { - "npc_id": "6045", - "loc_data": "{3479,3498,1,0,0}" - }, - { - "npc_id": "6046", - "loc_data": "{2835,3509,0,0,1}-{3306,5510,0,1,5}-{3309,5543,0,1,6}" - }, - { - "npc_id": "6047", - "loc_data": "{2839,3497,0,1,6}-{2840,3504,0,1,4}" - }, - { - "npc_id": "6050", - "loc_data": "{3291,3081,0,1,7}-{3298,3075,0,1,1}-{3284,3076,0,1,3}-{3266,3072,0,1,1}-{3259,3080,0,1,2}-{3246,3080,0,1,3}-{3252,3069,0,1,1}-{3244,3082,0,1,1}-{3235,3072,0,1,4}-{3242,3059,0,1,7}-{3235,3051,0,1,5}-{3248,3051,0,1,4}-{3257,3055,0,1,3}-{3263,3063,0,1,3}-{3267,3048,0,1,0}-{3253,3040,0,1,6}-{3243,3039,0,1,6}-{3254,3031,0,1,1}-{3243,3035,0,1,5}-{3232,3039,0,1,2}-{3230,3046,0,1,6}-{3219,3033,0,1,3}-{3219,3022,0,1,1}-{3227,3017,0,1,5}-{3235,3012,0,1,4}-{3243,3019,0,1,2}-{3251,3028,0,1,3}" - }, - { - "npc_id": "6051", - "loc_data": "{3292,3069,0,1,1}-{3285,3071,0,1,1}-{3279,3078,0,1,4}-{3273,3065,0,1,3}-{3288,3066,0,1,6}-{3281,3057,0,1,5}-{3294,3056,0,1,5}-{3305,3064,0,1,3}-{3312,3075,0,1,6}-{3306,3086,0,1,4}-{3272,3082,0,1,4}" - }, - { - "npc_id": "6052", - "loc_data": "{2761,3863,0,1,0}-{2764,3858,0,1,0}-{2769,3854,0,1,0}-{2769,3861,0,1,0}" - }, - { - "npc_id": "6055", - "loc_data": "{2563,4348,0,1,0}-{2620,4348,0,1,0}-{2620,4291,0,1,0}-{2563,4291,0,1,0}-{2572,4339,0,1,0}-{2565,4327,0,1,0}-{2575,4315,0,1,0}-{2586,4306,0,1,0}-{2593,4304,0,1,0}-{2605,4312,0,1,0}-{2614,4330,0,1,0}-" - }, - { - "npc_id": "6056", - "loc_data": "{2565,4320,0,1,0}-{2592,4348,0,1,0}-{2596,4346,0,1,0}-{2576,4333,0,1,0}-{2576,4324,0,1,0}-{2612,4333,0,1,0}-{2617,4329,0,1,0}-{2608,4316,0,1,0}-{2593,4295,0,1,0}-{2587,4307,0,1,0}-" - }, - { - "npc_id": "6057", - "loc_data": "{2604,4348,0,1,0}-{2613,4346,0,1,0}-{2582,4347,0,1,0}-{2568,4333,0,1,0}-{2576,4321,0,1,0}-{2571,4302,0,1,0}-{2587,4296,0,1,0}-{2598,4301,0,1,0}-{2612,4303,0,1,0}-{2607,4321,0,1,0}-{2617,4325,0,1,0}-{2617,4346,0,1,0}-" - }, - { - "npc_id": "6058", - "loc_data": "{2585,4343,0,1,0}-{2599,4338,0,1,0}-{2612,4338,0,1,0}-{2573,4329,0,1,0}-{2572,4320,0,1,0}-{2575,4309,0,1,0}-{2587,4302,0,1,0}-{2606,4319,0,1,0}-{2608,4336,0,1,0}-" - }, - { - "npc_id": "6059", - "loc_data": "{2579,4340,0,1,0}-{2578,4323,0,1,0}-{2580,4302,0,1,0}-{2611,4324,0,1,0}-{2594,4338,0,1,0}-" - }, - { - "npc_id": "6060", - "loc_data": "{2589,4341,0,1,0}-{2569,4315,0,1,0}-{2591,4299,0,1,0}-{2611,4330,0,1,0}-" - }, - { - "npc_id": "6065", - "loc_data": "{2612,4348,0,1,0}-{2606,4340,0,1,0}-{2592,4333,0,1,0}-{2579,4336,0,1,0}-{2565,4340,0,1,0}-{2569,4329,0,1,0}-{2574,4311,0,1,0}-{2570,4304,0,1,0}-{2567,4294,0,1,0}-{2604,4305,0,1,0}-{2610,4293,0,1,0}-{2612,4320,0,1,0}-" - }, - { - "npc_id": "6066", - "loc_data": "{2601,4334,0,1,0}-{2586,4335,0,1,0}-{2572,4334,0,1,0}-{2574,4323,0,1,0}-{2579,4295,0,1,0}-{2588,4298,0,1,0}-{2600,4304,0,1,0}-{2612,4312,0,1,0}-{2607,4329,0,1,0}-{2616,4336,0,1,0}-" - }, - { - "npc_id": "6067", - "loc_data": "{2570,4341,0,0,0}-{2613,4298,0,0,0}-" - }, - { - "npc_id": "6070", - "loc_data": "{2589,4313,0,1,6}" - }, - { - "npc_id": "6072", - "loc_data": "{2426,4442,0,0,0}" - }, - { - "npc_id": "6073", - "loc_data": "{2424,4436,0,0,0}-{2594,4319,0,1,6}" - }, - { - "npc_id": "6074", - "loc_data": "{2603,4306,0,1,4}-{2607,4312,0,1,7}-{2610,4319,0,1,6}-{2613,4326,0,1,5}-{2616,4333,0,1,0}-{2619,4340,0,1,6}-{2614,4344,0,1,3}-{2609,4347,0,1,4}-{2605,4347,0,1,4}-{2599,4345,0,1,3}-{2598,4342,0,1,3}-{2597,4339,0,1,3}-{2596,4336,0,1,3}-{2595,4333,0,1,3}-{2594,4330,0,1,4}-{2587,4330,0,1,3}-{2583,4332,0,1,4}-{2579,4335,0,1,3}-{2576,4339,0,1,4}-{2571,4342,0,1,1}-{2569,4332,0,1,4}-{2572,4326,0,1,6}-{2572,4297,0,1,1}-{2585,4291,0,1,3}-{2599,4297,0,1,4}-{2607,4300,0,1,1}-{2614,4301,0,1,6}-{2619,4305,0,1,0}-{2616,4320,0,1,6}-{2613,4327,0,1,0}-{2611,4335,0,1,6}-{2607,4336,0,1,6}-{2581,4322,0,1,1}-{2578,4315,0,1,6}-{2575,4308,0,1,6}-{2572,4301,0,1,4}-{2567,4295,0,1,3}-{2577,4291,0,1,3}-{2587,4298,0,1,3}-{2595,4307,0,1,3}-{2603,4314,0,1,3}" - }, - { - "npc_id": "6085", - "loc_data": "{3046,3207,1,0,2}" - }, - { - "npc_id": "6088", - "loc_data": "{2932,3253,0,1,4}-{2936,3249,0,1,2}-{2933,3244,0,1,4}-{2927,3247,0,1,5}-{2925,3256,0,1,0}" - }, - { - "npc_id": "6089", - "loc_data": "{2932,3249,0,1,0}-{2929,3253,0,1,3}-{2938,3248,0,1,5}" - }, - { - "npc_id": "6090", - "loc_data": "{2932,3245,0,1,0}-{2934,3246,0,1,3}" - }, - { - "npc_id": "6101", - "loc_data": "{2932,9811,0,1,0}" - }, - { - "npc_id": "6102", - "loc_data": "{1762,5329,0,1,1}" - }, - { - "npc_id": "6104", - "loc_data": "{1751,5326,0,1,1}" - }, - { - "npc_id": "6105", - "loc_data": "{1767,5355,0,0,6}" - }, - { - "npc_id": "6108", - "loc_data": "{2854,3385,0,1,0}" - }, - { - "npc_id": "6109", - "loc_data": "{2526,3215,0,1,6}-{2545,3231,0,1,4}-{2537,3236,0,1,4}" - }, - { - "npc_id": "6110", - "loc_data": "{2532,3222,0,1,3}-{2533,3209,0,1,4}" - }, - { - "npc_id": "6112", - "loc_data": "{3238,3247,0,1,0}" - }, - { - "npc_id": "6113", - "loc_data": "{3232,3280,0,1,4}-{3222,3289,0,1,1}" - }, - { - "npc_id": "6114", - "loc_data": "{3232,3283,0,1,1}" - }, - { - "npc_id": "6115", - "loc_data": "{3033,3236,0,1,3}-{3041,3237,0,1,6}-{3035,3234,0,1,7}-{3028,3205,0,1,4}-{3019,3202,0,1,6}-{3026,3203,0,1,1}" - }, - { - "npc_id": "6116", - "loc_data": "{3030,3244,0,1,0}-{3025,3203,0,1,3}-{3034,3204,0,1,4}" - }, - { - "npc_id": "6127", - "loc_data": "{2467,3183,0,0,3}" - }, - { - "npc_id": "6128", - "loc_data": "{2465,3183,0,0,4}" - }, - { - "npc_id": "6129", - "loc_data": "{2470,3185,0,0,6}" - }, - { - "npc_id": "6135", - "loc_data": "{3254,3427,0,1,1}" - }, - { - "npc_id": "6136", - "loc_data": "{3077,3250,0,1,0}" - }, - { - "npc_id": "6137", - "loc_data": "{2952,3381,0,1,0}" - }, - { - "npc_id": "6138", - "loc_data": "{2665,3311,0,0,0}" - }, - { - "npc_id": "6139", - "loc_data": "{2734,3484,0,1,1}" - }, - { - "npc_id": "6140", - "loc_data": "{2644,2641,0,0,6}" - }, - { - "npc_id": "6141", - "loc_data": "{2638,2656,0,0,0}" - }, - { - "npc_id": "6166", - "loc_data": "{1904,4279,0,0,0}" - }, - { - "npc_id": "6167", - "loc_data": "{1906,4277,0,0,0}" - }, - { - "npc_id": "6168", - "loc_data": "{1904,4274,0,0,0}" - }, - { - "npc_id": "6188", - "loc_data": "{1901,4271,0,0,0}-{1909,4271,0,0,0}" - }, - { - "npc_id": "6189", - "loc_data": "{3016,3516,0,1,7}" - }, - { - "npc_id": "6190", - "loc_data": "{1906,4270,0,1,0}" - }, - { - "npc_id": "6200", - "loc_data": "{2947,3366,0,0,1}-{2949,3366,0,0,1}-{2946,3366,0,0,1}-{2948,3366,0,0,1}-{3010,3353,0,0,1}-{3011,3353,0,0,1}-{3012,3353,0,0,1}-{3013,3353,0,0,1}-{3014,3353,0,0,1}-{3015, 3353, 0, 0, 1}-{2945, 3366, 0, 0, 1}" - }, - { - "npc_id": "6202", - "loc_data": "{2912,3749,0,0,0}" - }, - { - "npc_id": "6203", - "loc_data": "{2924,5322,2,1,4}" - }, - { - "npc_id": "6205", - "loc_data": "{2933,5329,2,1,1}" - }, - { - "npc_id": "6210", - "loc_data": "{2927,5340,2,1,3}-{2884,5325,2,1,3}-{2837,5336,2,1,4}-{2876,5326,2,1,4}-{2898,5306,2,1,5}-{2886,5325,2,1,1}-{2838,5336,2,1,4}-{2919,5345,2,1,5}" - }, - { - "npc_id": "6211", - "loc_data": "{2920,5353,2,1,4}-{2902,5357,2,1,4}-{2885,5315,2,1,4}-{2898,5313,2,1,2}-{2908,5354,2,1,2}-{2924,5354,2,1,6}-{2925,5352,2,1,3}-{2928,5355,2,1,6}" - }, - { - "npc_id": "6212", - "loc_data": "{2920,5339,2,1,1}-{2836,5289,2,1,0}-{2869,5287,2,1,1}-{2867,5333,2,1,0}-{2921,5293,1,1,6}-{2877,5321,2,1,1}-{2888,5326,2,1,3}-{2872,5318,2,1,2}-{2894,5286,2,1,7}-{2896,5312,2,1,4}-{2894,5322,2,1,3}-{2836,5349,2,1,2}-{2858,5267,2,1,6}" - }, - { - "npc_id": "6213", - "loc_data": "{2894,5353,2,1,0}-{2838,5347,2,1,6}-{2872,5318,2,1,4}-{2875,5313,2,1,4}-{2865,5327,2,1,4}-{2886,5293,2,1,5}-{2909,5297,2,1,0}-{2919,5293,1,1,4}-{2843,5352,2,1,4}-{2908,5346,2,1,6}-{2913,5355,2,1,3}-{2916,5345,2,1,1}-{2919,5349,2,1,2}-{2922,5356,2,1,1}-{2927,5354,2,1,6}-{2928,5350,2,1,4}-{2931,5357,2,1,3}-{2911,5338,2,1,6}-{2922,5295,1,1,0}-{2862,5290,2,1,1}-{2919,5293,1,1,5}" - }, - { - "npc_id": "6214", - "loc_data": "{2919,5284,1,1,0}-{2862,5292,2,1,7}-{2864,5319,2,1,7}-{2828,5337,2,1,6}-{2878,5327,2,1,4}-{2884,5312,2,1,2}-{2883,5320,2,1,0}-{2895,5309,2,1,6}-{2853,5327,2,1,4}-{2854,5345,2,1,2}-{2861,5292,2,1,4}-{2881,5291,2,1,1}-{2892,5328,2,1,1}-{2828,5331,2,1,1}-{2924,5292,1,1,6}-{2918,5281,1,1,1}-{2918,5280,1,1,1}" - }, - { - "npc_id": "6215", - "loc_data": "{2926,5345,2,1,7}-{2888,5361,2,1,5}-{2856,5349,2,1,1}-{2839,5275,2,1,5}-{2880,5293,2,1,4}-{2886,5313,2,1,3}-{2862,5334,2,1,0}-{2881,5320,2,1,3}-{2886,5325,2,1,2}-{2862,5337,2,1,4}-{2845,5286,2,1,0}-{2889,5322,2,1,4}-{2919,5359,2,1,0}-{2855,5352,2,1,1}-{2923,5357,2,1,6}-{2891,5353,2,1,1}-{2832,5288,2,1,6}" - }, - { - "npc_id": "6216", - "loc_data": "{2888,5290,2,1,1}-{2865,5306,2,1,3}-{2871,5312,2,1,3}-{2867,5304,2,1,6}-{2871,5281,2,1,1}-{2864,5279,2,1,4}" - }, - { - "npc_id": "6217", - "loc_data": "{2884,5307,2,1,0}-{2899,5304,2,1,3}-{2915,5270,0,1,5}" - }, - { - "npc_id": "6218", - "loc_data": "{2906,5347,2,1,6}-{2913,5358,2,1,6}-{2856,5267,2,1,7}-{2911,5268,0,1,4}-{2887,5318,2,1,4}-{2888,5314,2,1,6}-{2859,5328,2,1,6}-{2861,5294,2,1,0}-{2931,5345,2,1,2}-{2903,5353,2,1,4}-{2836,5290,2,1,6}-{2907,5346,2,1,6}-{2921,5351,2,1,2}-{2858,5266,2,1,1}-{2916,5269,0,1,1}-{3044,5347,0,1,2}-{3041,5347,0,1,0}-{3031,5346,0,1,2}-{3023,5346,0,1,6}-{3038,5333,0,1,3}-{3048,5333,0,1,1}-{3054,5345,0,1,3}-{3043,5360,0,1,3}-{3030,5362,0,1,3}-{3040,5341,0,1,4}" - }, - { - "npc_id": "6219", - "loc_data": "{2915,5340,2,1,6}" - }, - { - "npc_id": "6220", - "loc_data": "{2931,5356,2,1,3}-{2846,5286,2,1,5}-{2937,5351,2,1,3}-{2848,5268,2,1,4}" - }, - { - "npc_id": "6221", - "loc_data": "{2906,5354,2,1,7}-{2886,5354,2,1,5}-{2885,5352,2,1,2}-{2901,5362,2,1,1}-{2914,5344,2,1,3}-{2918,5343,2,1,2}-{2918,5361,2,1,0}-{2923,5345,2,1,3}-{2924,5352,2,1,4}" - }, - { - "npc_id": "6222", - "loc_data": "{2831,5301,2,1,4}" - }, - { - "npc_id": "6229", - "loc_data": "{2841,5285,2,1,3}-{2841,5289,2,1,1}-{2856,5264,2,1,5}-{2842,5282,2,1,6}" - }, - { - "npc_id": "6230", - "loc_data": "{2843,5265,2,1,4}-{2851,5273,2,1,3}-{2846,5262,2,1,4}" - }, - { - "npc_id": "6231", - "loc_data": "{2870,5265,2,1,0}-{2869,5266,2,1,7}-{2855,5273,2,1,2}-{2840,5263,2,1,3}-{2831,5285,2,1,4}" - }, - { - "npc_id": "6232", - "loc_data": "{2872,5261,2,1,4}-{2863,5308,2,1,1}-{2872,5288,2,1,4}" - }, - { - "npc_id": "6233", - "loc_data": "{2861,5261,2,1,0}-{2852,5264,2,1,0}-{2865,5263,2,1,6}" - }, - { - "npc_id": "6234", - "loc_data": "{2862,5313,2,1,3}-{2839,5293,2,1,0}-{2842,5264,2,1,4}" - }, - { - "npc_id": "6235", - "loc_data": "{2864,5270,2,1,3}-{2869,5314,2,1,3}-{2859,5303,2,1,4}-{2863,5293,2,1,7}-{2862,5286,2,1,1}" - }, - { - "npc_id": "6236", - "loc_data": "{2854,5271,2,1,6}-{2872,5267,2,1,0}-{2858,5269,2,1,5}" - }, - { - "npc_id": "6237", - "loc_data": "{2840,5290,2,1,6}-{2841,5269,2,1,7}-{2833,5292,2,1,3}" - }, - { - "npc_id": "6238", - "loc_data": "{2874,5308,2,1,3}-{2851,5276,2,1,2}-{2864,5268,2,1,2}" - }, - { - "npc_id": "6239", - "loc_data": "{2875,5298,2,1,4}-{2846,5288,2,1,0}-{2845,5281,2,1,1}-{2824,5216,2,1,7}" - }, - { - "npc_id": "6240", - "loc_data": "{2837,5269,2,1,2}-{2860,5300,2,1,1}-{2852,5311,2,1,4}-{2879,5286,2,1,5}" - }, - { - "npc_id": "6241", - "loc_data": "{2863,5272,2,1,6}-{2850,5269,2,1,7}" - }, - { - "npc_id": "6242", - "loc_data": "{2836,5272,2,1,1}" - }, - { - "npc_id": "6243", - "loc_data": "{2848,5275,2,1,0}-{2869,5304,2,1,4}-{2850,5292,2,1,1}-{2857,5300,2,1,3}-{2874,5287,2,1,4}" - }, - { - "npc_id": "6244", - "loc_data": "{2847,5299,2,1,6}-{2844,5283,2,1,6}" - }, - { - "npc_id": "6245", - "loc_data": "{2861,5299,2,1,4}-{2833,5290,2,1,1}-{2851,5262,2,1,0}" - }, - { - "npc_id": "6246", - "loc_data": "{2855,5311,2,1,1}-{2872,5299,2,1,0}-{2872,5315,2,1,1}-{2856,5284,2,1,4}" - }, - { - "npc_id": "6247", - "loc_data": "{2894,5265,0,1,2}" - }, - { - "npc_id": "6254", - "loc_data": "{2917,5272,0,1,7}-{2898,5295,2,1,4}-{2881,5283,2,1,1}-{2883,5311,2,1,2}-{2894,5301,2,1,3}-{2882,5284,2,1,5}-{2890,5292,2,1,4}-{2896,5296,2,1,2}-{2905,5299,2,1,3}-{2905,5301,2,1,5}-{2921,5283,1,1,1}-{2923,5292,1,1,7}-{2906,5299,0,1,6}-{2910,5297,0,1,6}-{2915,5270,0,1,2}-{2910,5269,0,1,5}" - }, - { - "npc_id": "6255", - "loc_data": "{2922,5290,1,1,5}-{2880,5304,2,1,1}-{2884,5311,2,1,2}-{2885,5311,2,1,2}-{2887,5312,2,1,1}-{2886,5313,2,1,0}-{2895,5310,2,1,1}-{2884,5280,2,1,3}-{2885,5293,2,1,6}-{2886,5291,2,1,6}-{2894,5285,2,1,3}-{2893,5286,2,1,7}-{2905,5292,2,1,4}-{2908,5297,2,1,4}-{2910,5297,2,1,6}-{2897,5308,2,1,4}-{2919,5299,1,1,1}-{2921,5292,1,1,5}-{2924,5288,1,1,0}-{2923,5287,1,1,1}-{2908,5300,0,1,6}-{2907,5294,0,1,6}-{2923,5293,1,1,5}-{2919,5298,1,1,1}-{2922,5291,1,1,6}-{2917,5285,1,1,0}-{2905,5295,0,1,6}" - }, - { - "npc_id": "6256", - "loc_data": "{2922,5285,1,1,0}-{2886,5295,2,1,1}-{2891,5300,2,1,1}-{2889,5310,2,1,2}-{2887,5299,2,1,6}-{2891,5297,2,1,3}-{2884,5283,2,1,3}-{2885,5286,2,1,3}-{2886,5294,2,1,1}-{2896,5286,2,1,4}-{2908,5302,2,1,1}-{2919,5290,1,1,1}-{2919,5298,1,1,4}-{2918,5298,1,1,4}-{2911,5298,0,1,6}-{2919,5293,1,1,1}-{2917,5294,1,1,2}-{2923,5284,1,1,1}-{2916,5267,0,1,2}" - }, - { - "npc_id": "6257", - "loc_data": "{2913,5267,0,1,6}-{2892,5291,2,1,4}-{2895,5313,2,1,6}-{2879,5329,2,1,5}-{2886,5311,2,1,0}-{2888,5305,2,1,3}-{2877,5293,2,1,5}-{2894,5285,2,1,0}-{2900,5287,2,1,0}-{2905,5302,2,1,1}-{2898,5307,2,1,6}-{2918,5292,1,1,3}-{2924,5286,1,1,0}-{2924,5292,1,1,1}-{2920,5297,1,1,3}-{2924,5285,1,1,1}-{2921,5289,1,1,6}-{2923,5294,1,1,0}-{2922,5285,1,1,3}-{2915,5273,0,1,7}-{2915,5271,0,1,4}" - }, - { - "npc_id": "6258", - "loc_data": "{2918,5289,1,1,6}-{2884,5287,2,1,0}-{2895,5294,2,1,4}-{2897,5307,2,1,7}-{2918,5298,1,1,5}-{2917,5291,1,1,4}-{2916,5269,0,1,2}" - }, - { - "npc_id": "6259", - "loc_data": "{2890,5304,2,1,7}-{2886,5299,2,1,3}-{2891,5297,2,1,5}-{2890,5291,2,1,3}-{2900,5286,2,1,7}-{2905,5303,2,1,1}-{2918,5282,1,1,6}-{2922,5295,1,1,2}-{2905,5297,0,1,6}-{2920,5291,1,1,5}-{2920,5290,1,1,6}" - }, - { - "npc_id": "6260", - "loc_data": "{2870,5359,2,1,4}" - }, - { - "npc_id": "6262", - "loc_data": "{2873,5356,0,1,1}-{2866,5358,2,1,6}" - }, - { - "npc_id": "6264", - "loc_data": "{2870,5354,2,1,4}" - }, - { - "npc_id": "6266", - "loc_data": "{2868,5362,2,1,3}" - }, - { - "npc_id": "6268", - "loc_data": "{2856,5355,2,1,7}-{2838,5358,2,1,2}-{2841,5319,2,1,7}-{2856,5331,2,1,2}-{2854,5325,2,1,2}-{2873,5323,2,1,6}-{2860,5321,2,1,7}-{2868,5334,2,1,0}-{2856,5328,2,1,6}-{2855,5332,2,1,1}-{2845,5339,2,1,5}-{2831,5320,2,1,3}-{2847,5334,2,1,1}-{2858,5349,2,1,2}-{2829,5339,2,1,6}-{2856,5361,2,1,5}-{2839,5360,2,1,6}" - }, - { - "npc_id": "6269", - "loc_data": "{2846,5351,2,1,3}-{2846,5339,2,1,2}-{2840,5327,2,1,1}-{2836,5345,2,1,7}-{2834,5328,2,1,2}-{2857,5359,2,1,4}-{2846,5357,2,1,3}" - }, - { - "npc_id": "6270", - "loc_data": "{2845,5336,2,1,2}-{2848,5344,2,1,6}-{2842,5328,2,1,6}-{2844,5349,2,1,2}-{2836,5323,2,1,4}-{2832,5353,2,1,6}-{2848,5358,2,1,7}" - }, - { - "npc_id": "6271", - "loc_data": "{2840,5330,2,1,0}-{2859,5328,2,1,6}-{2873,5317,2,1,3}-{2862,5332,2,1,2}-{2869,5332,2,1,6}" - }, - { - "npc_id": "6272", - "loc_data": "{2838,5326,2,1,4}-{2870,5313,2,1,1}-{2865,5332,2,1,0}-{2838,5334,2,1,6}-{2837,5353,2,1,7}" - }, - { - "npc_id": "6273", - "loc_data": "{2832,5324,2,1,7}-{2864,5325,2,1,2}-{2873,5325,2,1,7}-{2873,5324,2,1,7}-{2865,5336,2,1,6}-{2840,5333,2,1,6}-{2836,5336,2,1,4}-{2838,5354,2,1,6}" - }, - { - "npc_id": "6274", - "loc_data": "{2858,5325,2,1,4}-{2873,5319,2,1,6}-{2872,5307,2,1,5}-{2867,5333,2,1,6}-{2866,5333,2,1,0}-{2839,5331,2,1,5}-{2837,5355,2,1,6}" - }, - { - "npc_id": "6275", - "loc_data": "{2865,5340,2,1,1}-{2869,5326,2,1,7}-{2875,5318,2,1,1}-{2875,5314,2,1,1}-{2878,5313,2,1,3}-{2879,5322,2,1,3}-{2859,5317,2,1,6}-{2858,5319,2,1,4}-{2878,5320,2,1,0}-{2863,5337,2,1,7}-{2862,5338,2,1,7}-{2867,5341,2,1,6}-{2852,5312,2,1,2}" - }, - { - "npc_id": "6276", - "loc_data": "{2856,5360,2,1,7}-{2854,5346,2,1,3}-{2845,5332,2,1,0}-{2845,5344,2,1,3}-{2848,5349,2,1,6}-{2837,5347,2,1,3}-{2835,5324,2,1,2}-{2835,5321,2,1,5}-{2858,5349,2,1,4}-{2830,5339,2,1,3}-{2828,5332,2,1,3}-{2846,5358,2,1,4}-{2855,5356,2,1,7}" - }, - { - "npc_id": "6277", - "loc_data": "{2843,5341,2,1,3}-{2843,5331,2,1,3}-{2847,5341,2,1,5}-{2852,5350,2,1,1}-{2844,5341,2,1,6}-{2843,5347,2,1,1}-{2836,5325,2,1,6}-{2826,5335,2,1,2}-{2837,5351,2,1,4}-{2845,5353,2,1,3}-{2840,5361,2,1,3}" - }, - { - "npc_id": "6278", - "loc_data": "{2845,5359,2,1,4}-{2830,5318,2,1,3}-{2848,5342,2,1,0}-{2846,5332,2,1,1}-{2843,5320,2,1,3}-{2838,5329,2,1,1}-{2855,5349,2,1,0}-{2837,5342,2,1,1}-{2792,5280,2,1,6}-{2827,5331,2,1,6}-{2830,5315,2,1,6}-{2845,5352,2,1,6}-{2839,5360,2,1,6}-{2851,5359,2,1,7}-{2860,5353,2,1,6}" - }, - { - "npc_id": "6279", - "loc_data": "{2857,5315,2,1,6}-{2859,5318,2,1,5}-{2858,5329,2,1,3}-{2879,5326,2,1,4}" - }, - { - "npc_id": "6280", - "loc_data": "{2869,5332,2,1,6}-{2871,5313,2,1,2}-{2879,5330,2,1,2}-{2877,5327,2,1,0}" - }, - { - "npc_id": "6281", - "loc_data": "{2865,5316,2,1,2}-{2861,5333,2,1,2}" - }, - { - "npc_id": "6282", - "loc_data": "{2874,5318,2,1,0}-{2859,5316,2,1,3}-{2878,5329,2,1,2}-{2858,5330,2,1,2}" - }, - { - "npc_id": "6283", - "loc_data": "{2877,5326,2,1,2}-{2857,5328,2,1,3}-{2861,5315,2,1,7}" - }, - { - "npc_id": "6339", - "loc_data": "{2547,3277,0,0,0}" - }, - { - "npc_id": "6345", - "loc_data": "{2518,3275,0,1,0}" - }, - { - "npc_id": "6346", - "loc_data": "{2799,3186,0,1,7}-{2797,3169,0,0,0}-{2793,3161,0,1,3}" - }, - { - "npc_id": "6347", - "loc_data": "{2799,3167,0,0,0}-{2787,3188,0,1,1}" - }, - { - "npc_id": "6348", - "loc_data": "{2793,3158,0,1,3}-{2798,3154,0,1,4}-{2796,3189,0,1,0}" - }, - { - "npc_id": "6349", - "loc_data": "{2797,3186,0,1,6}-{2793,3161,0,1,0}" - }, - { - "npc_id": "6350", - "loc_data": "{2798,3182,0,1,7}-{2793,3161,0,1,1}" - }, - { - "npc_id": "6351", - "loc_data": "{3041,3951,0,1,0}-{3043,3958,0,1,0}" - }, - { - "npc_id": "6352", - "loc_data": "{3039,3955,0,1,0}-{3043,3951,0,1,0}" - }, - { - "npc_id": "6353", - "loc_data": "{3040,3955,0,1,0}-{3039,3950,0,1,0}" - }, - { - "npc_id": "6354", - "loc_data": "{3041,3950,0,1,0}-{3041,3956,0,1,0}" - }, - { - "npc_id": "6355", - "loc_data": "{3043,3950,0,1,0}-{3043,3956,0,1,0}" - }, - { - "npc_id": "6356", - "loc_data": "{3041,3953,0,1,0}" - }, - { - "npc_id": "6357", - "loc_data": "{3359,2969,0,1,3}" - }, - { - "npc_id": "6362", - "loc_data": "{3270,4856,0,1,3}" - }, - { - "npc_id": "6363", - "loc_data": "{3251,5455,0,1,4}-{3258,5456,0,1,6}-{3257,5451,0,1,6}-{3257,5464,0,1,2}-{3256,5473,0,1,2}-{3244,5469,0,1,1}-{3269,4829,0,1,3}-{3271,4823,0,1,2}-{3283,4809,0,1,3}-{3190,5523,0,1,3}-{3225,5485,0,1,5}-{3219,5528,0,1,2}-{3223,5520,0,1,3}-{3225,5511,0,1,0}-{3236,5511,0,1,6}-{3232,5523,0,1,0}-{3239,5530,0,1,4}-{3233,5546,0,1,3}-{3222,5539,0,1,5}-{3218,5482,0,1,7}-{3219,5486,0,1,4}-{3207,5461,0,1,6}-{3208,5458,0,1,3}-{3194,5530,0,1,3}" - }, - { - "npc_id": "6364", - "loc_data": "{3271,4809,0,1,3}-{3188,5529,0,1,3}-{3194,5531,0,1,1}-{3222,5486,0,1,3}-{3221,5482,0,1,4}-{3212,5459,0,1,6}-{3210,5462,0,1,2}-{3188,5527,0,1,3}-{3234,5510,0,1,6}" - }, - { - "npc_id": "6365", - "loc_data": "{3267,4817,0,1,4}-{3290,4803,0,1,3}" - }, - { - "npc_id": "6367", - "loc_data": "{3276,4808,0,1,3}-{3291,4812,0,1,6}-{3207,5466,0,1,6}-{3223,5462,0,1,1}-{3237,5524,0,1,6}-{3229,5510,0,1,4}" - }, - { - "npc_id": "6368", - "loc_data": "{3226,5458,0,1,3}-{3222,5470,0,1,6}-{3238,5541,0,1,1}-{3226,5540,0,1,7}-{3222,5522,0,1,7}" - }, - { - "npc_id": "6369", - "loc_data": "{3268,4825,0,1,6}-{3268,4809,0,1,4}-{3288,4803,0,1,6}-{3298,4810,0,1,4}" - }, - { - "npc_id": "6376", - "loc_data": "{2925,2976,0,1,1}-{2909,2978,0,1,5}" - }, - { - "npc_id": "6377", - "loc_data": "{2920,2973,0,1,6}-{2919,2976,0,1,6}-{2910,2978,0,1,7}" - }, - { - "npc_id": "6379", - "loc_data": "{2894,2995,0,0,0}-{2909,2970,0,0,0}-{3303,5485,0,1,1}-{3320,5494,0,1,4}-{3313,5490,0,1,4}-{3315,5484,0,1,3}-{3323,5471,0,1,6}-{3319,5471,0,1,4}-{3323,5477,0,1,0}-{3310,5484,0,1,4}-{3313,5488,0,1,6}-{3309,5481,0,1,1}-{3320,5495,0,1,0}" - }, - { - "npc_id": "6380", - "loc_data": "{2883,2994,0,0,0}-{2882,2994,0,0,0}-{3319,5490,0,1,5}-{3298,5486,0,1,3}-{3312,5480,0,1,4}-{3315,5480,0,1,1}-{3308,5482,0,1,2}-{3321,5477,0,1,0}-{3317,5470,0,1,6}-{3324,5472,0,1,6}-{3311,5482,0,1,0}" - }, - { - "npc_id": "6383", - "loc_data": "{2888,3001,0,0,0}-{2891,3002,0,0,0}-{2890,2998,0,0,0}-{2887,2994,0,0,0}-{2901,2991,0,0,0}-{2905,2990,0,0,0}-{2886,2989,0,0,0}-{2927,2979,0,0,0}-{2926,2973,0,0,0}-{2919,2973,0,0,0}-{2919,2976,0,0,0}-{2920,2979,0,0,0}" - }, - { - "npc_id": "6386", - "loc_data": "{2683,3326,0,0,0}" - }, - { - "npc_id": "6387", - "loc_data": "{3275,4808,0,1,3}-{3276,4808,0,1,4}" - }, - { - "npc_id": "6388", - "loc_data": "{3369,2999,0,1,7}-{3368,2995,0,1,6}-{3362,2997,0,1,3}-{3361,2988,0,1,2}-{3367,2988,0,1,7}-{3379,2983,0,1,1}" - }, - { - "npc_id": "6390", - "loc_data": "{1707,4814,0,0,6}" - }, - { - "npc_id": "6394", - "loc_data": "{2703,5366,0,0,0}" - }, - { - "npc_id": "6396", - "loc_data": "{2791,4279,0,0,0}" - }, - { - "npc_id": "6405", - "loc_data": "{2799,4260,0,1,0}" - }, - { - "npc_id": "6406", - "loc_data": "{2765,4267,0,1,0}" - }, - { - "npc_id": "6407", - "loc_data": "{2802,4246,0,1,0}" - }, - { - "npc_id": "6408", - "loc_data": "{2770,4238,0,1,0}" - }, - { - "npc_id": "6409", - "loc_data": "{2787,4262,0,1,0}-{2792,4275,0,1,0}" - }, - { - "npc_id": "6410", - "loc_data": "{2766,4257,0,1,0}" - }, - { - "npc_id": "6416", - "loc_data": "{2801,4258,0,1,0}" - }, - { - "npc_id": "6417", - "loc_data": "{2765,4264,0,1,0}-{2777,4260,0,1,0}" - }, - { - "npc_id": "6418", - "loc_data": "{2799,4243,0,1,0}" - }, - { - "npc_id": "6419", - "loc_data": "{2771,4237,0,1,0}" - }, - { - "npc_id": "6420", - "loc_data": "{2796,4277,0,1,0}" - }, - { - "npc_id": "6421", - "loc_data": "{2768,4254,0,1,0}" - }, - { - "npc_id": "6427", - "loc_data": "{2802,4259,0,1,0}" - }, - { - "npc_id": "6428", - "loc_data": "{2768,4268,0,1,0}" - }, - { - "npc_id": "6429", - "loc_data": "{2776,4252,0,1,0}-{2805,4242,0,1,0}" - }, - { - "npc_id": "6430", - "loc_data": "{2769,4236,0,1,0}" - }, - { - "npc_id": "6431", - "loc_data": "{2792,4260,0,1,0}-{2796,4275,0,1,0}" - }, - { - "npc_id": "6432", - "loc_data": "{2766,4255,0,1,0}" - }, - { - "npc_id": "6438", - "loc_data": "{2790,4254,0,1,0}-{2800,4256,0,1,0}" - }, - { - "npc_id": "6439", - "loc_data": "{2766,4265,0,1,0}" - }, - { - "npc_id": "6440", - "loc_data": "{2789,4248,0,1,0}-{2802,4244,0,1,0}" - }, - { - "npc_id": "6441", - "loc_data": "{2771,4241,0,1,0}-{2777,4252,0,1,0}" - }, - { - "npc_id": "6442", - "loc_data": "{2795,4273,0,1,0}" - }, - { - "npc_id": "6443", - "loc_data": "{2767,4252,0,1,0}-{2776,4250,0,1,0}" - }, - { - "npc_id": "6449", - "loc_data": "{2792,4252,0,1,0}-{2800,4254,0,1,0}" - }, - { - "npc_id": "6450", - "loc_data": "{2764,4266,0,1,0}-{2781,4261,0,1,0}" - }, - { - "npc_id": "6451", - "loc_data": "{2802,4243,0,1,0}" - }, - { - "npc_id": "6452", - "loc_data": "{2772,4235,0,1,0}" - }, - { - "npc_id": "6453", - "loc_data": "{2797,4272,0,1,0}" - }, - { - "npc_id": "6454", - "loc_data": "{2765,4253,0,1,0}-{2779,4256,0,1,0}" - }, - { - "npc_id": "6458", - "loc_data": "{2784,4249,0,1,0}" - }, - { - "npc_id": "6459", - "loc_data": "{2778,4264,0,1,0}-{2784,4252,0,1,0}" - }, - { - "npc_id": "6460", - "loc_data": "{2783,4251,0,1,0}-{2789,4264,0,1,0}-{2801,4252,0,1,0}" - }, - { - "npc_id": "6461", - "loc_data": "{2787,4250,0,1,0}-{2791,4249,0,1,0}" - }, - { - "npc_id": "6462", - "loc_data": "{2785,4248,0,1,0}-{2786,4251,0,1,0}-{2801,4242,0,1,0}" - }, - { - "npc_id": "6463", - "loc_data": "{2773,4238,0,1,0}-{2779,4248,0,1,0}-{2785,4250,0,1,0}" - }, - { - "npc_id": "6464", - "loc_data": "{2796,4267,0,1,0}" - }, - { - "npc_id": "6465", - "loc_data": "{2764,4251,0,1,0}" - }, - { - "npc_id": "6482", - "loc_data": "{2801,4260,0,0,0}" - }, - { - "npc_id": "6483", - "loc_data": "{2804,4246,0,0,0}" - }, - { - "npc_id": "6484", - "loc_data": "{2795,4276,0,0,0}" - }, - { - "npc_id": "6485", - "loc_data": "{2766,4250,0,0,0}" - }, - { - "npc_id": "6486", - "loc_data": "{2774,4236,0,0,0}" - }, - { - "npc_id": "6487", - "loc_data": "{2765,4268,0,0,0}" - }, - { - "npc_id": "6488", - "loc_data": "{2784,4255,1,0,0}" - }, - { - "npc_id": "6489", - "loc_data": "{2787,4266,0,0,0}" - }, - { - "npc_id": "6498", - "loc_data": "{2796,4256,0,1,0}" - }, - { - "npc_id": "6499", - "loc_data": "{2793,4247,0,1,0}" - }, - { - "npc_id": "6500", - "loc_data": "{2793,4265,0,1,0}" - }, - { - "npc_id": "6501", - "loc_data": "{2772,4256,0,1,0}" - }, - { - "npc_id": "6502", - "loc_data": "{2775,4247,0,1,0}" - }, - { - "npc_id": "6503", - "loc_data": "{2775,4265,0,1,0}" - }, - { - "npc_id": "6521", - "loc_data": "{3161,3475,0,0,6}" - }, - { - "npc_id": "6522", - "loc_data": "{3168,3475,0,0,6}" - }, - { - "npc_id": "6523", - "loc_data": "{3173,3498,0,0,1}" - }, - { - "npc_id": "6524", - "loc_data": "{3156,3481,0,0,1}" - }, - { - "npc_id": "6525", - "loc_data": "{3175,3481,0,0,1}" - }, - { - "npc_id": "6526", - "loc_data": "{3152,3490,0,0,1}" - }, - { - "npc_id": "6527", - "loc_data": "{3154,3498,0,0,1}" - }, - { - "npc_id": "6528", - "loc_data": "{3165,3491,0,0,1}" - }, - { - "npc_id": "6529", - "loc_data": "{3164,3488,0,0,6}" - }, - { - "npc_id": "6530", - "loc_data": "{3164,3491,0,0,1}" - }, - { - "npc_id": "6531", - "loc_data": "{3165,3488,0,0,6}" - }, - { - "npc_id": "6532", - "loc_data": "{3163,3489,0,0,3}" - }, - { - "npc_id": "6533", - "loc_data": "{3166,3489,0,0,4}" - }, - { - "npc_id": "6534", - "loc_data": "{3166,3490,0,0,4}" - }, - { - "npc_id": "6535", - "loc_data": "{3163,3490,0,0,3}" - }, - { - "npc_id": "6538", - "loc_data": "{3189,3690,0,0,3}" - }, - { - "npc_id": "6731", - "loc_data": "{2655,5615,0,1,4}" - }, - { - "npc_id": "6750", - "loc_data": "{2577,3078,0,1,3}" - }, - { - "npc_id": "6753", - "loc_data": "{3381,2816,1,1,0}-{3061,4672,1,1,0}" - }, - { - "npc_id": "6754", - "loc_data": "{3386,2824,1,1,0}-{3066,4680,1,1,0}" - }, - { - "npc_id": "6755", - "loc_data": "{3382,2820,1,1,0}-{3062,4676,1,1,0}" - }, - { - "npc_id": "6756", - "loc_data": "{3384,2822,1,1,0}-{3064,4678,1,1,0}" - }, - { - "npc_id": "6761", - "loc_data": "{3424,2828,1,1,3}-{3430,2835,1,1,3}-{3436,2830,1,1,3}" - }, - { - "npc_id": "6762", - "loc_data": "{3420,2829,1,1,3}-{3429,2827,1,1,3}-{3432,2832,1,1,3}" - }, - { - "npc_id": "6763", - "loc_data": "{3427,2832,1,1,3}-{3432,2828,1,1,3}-{3435,2835,1,1,3}" - }, - { - "npc_id": "6764", - "loc_data": "{3415,2840,1,1,0}-{3426,2852,1,1,0}-{3439,2857,1,1,0}" - }, - { - "npc_id": "6765", - "loc_data": "{3418,2851,1,1,0}-{3432,2854,1,1,0}" - }, - { - "npc_id": "6766", - "loc_data": "{3406,2837,1,1,0}-{3434,2848,1,1,0}" - }, - { - "npc_id": "6767", - "loc_data": "{3409,2844,1,1,0}-{3421,2855,1,1,0}" - }, - { - "npc_id": "6768", - "loc_data": "{3414,2844,1,1,0}-{3439,2851,1,1,0}" - }, - { - "npc_id": "6773", - "loc_data": "{3358,9233,1,1,0}-{3363,9246,1,1,0}-{3369,9233,1,1,0}-{3373,9246,1,1,0}-{3377,9244,1,1,0}-{3381,9225,1,1,0}-{3389,9241,1,1,0}-{3391,9237,1,1,0}-{3391,9230,1,1,0}-{3439,2808,0,1,0}" - }, - { - "npc_id": "6774", - "loc_data": "{3361,9238,1,1,0}-{3363,9241,1,1,0}-{3373,9241,1,1,0}-{3374,9225,1,1,0}-{3379,9230,1,1,0}-{3383,9232,1,1,0}-{3387,9229,1,1,0}-{3389,9236,1,1,0}-{3435,2808,0,1,2}" - }, - { - "npc_id": "6776", - "loc_data": "{3441,2810,0,1,0}-{3445,2813,0,1,0}" - }, - { - "npc_id": "6777", - "loc_data": "{3432,2810,0,1,4}" - }, - { - "npc_id": "6778", - "loc_data": "{3440,2806,0,1,0}" - }, - { - "npc_id": "6779", - "loc_data": "{3392,2759,0,1,2}-{3408,2763,0,1,7}-{3410,2786,0,1,2}-{3420,2778,0,1,6}-{3427,2792,0,1,6}-{3429,2802,0,1,4}" - }, - { - "npc_id": "6780", - "loc_data": "{3366,9254,1,1,6}-{3398,9232,1,1,0}-{3409,9230,1,1,0}" - }, - { - "npc_id": "6781", - "loc_data": "{3363,9231,1,1,0}-{3369,9254,1,1,6}-{3370,9226,1,1,0}-{3397,9225,1,1,0}-{3408,9226,1,1,0}" - }, - { - "npc_id": "6785", - "loc_data": "{3442,9245,1,0,1}" - }, - { - "npc_id": "6786", - "loc_data": "{3409,2812,1,0,0}" - }, - { - "npc_id": "6787", - "loc_data": "{3408,2813,1,0,0}" - }, - { - "npc_id": "6788", - "loc_data": "{3368,9263,1,0,6}" - }, - { - "npc_id": "6891", - "loc_data": "{2598,3272,0,0,0}" - }, - { - "npc_id": "6893", - "loc_data": "{2926,3435,0,1,2}" - }, - { - "npc_id": "6894", - "loc_data": "{2928,3432,0,1,0}" - }, - { - "npc_id": "6895", - "loc_data": "{2927,3431,0,1,0}" - }, - { - "npc_id": "6896", - "loc_data": "{2573,3082,0,1,2}" - }, - { - "npc_id": "6897", - "loc_data": "{2574,3083,0,1,0}" - }, - { - "npc_id": "6917", - "loc_data": "{2916,3082,0,0,0}-{2917,3089,0,0,0}-{2912,3090,0,0,0}" - }, - { - "npc_id": "6918", - "loc_data": "{2917,3088,0,1,4}-{2917,3092,0,1,4}" - }, - { - "npc_id": "6921", - "loc_data": "{2974,3448,0,1,4}-{2971,3442,0,1,6}-{2968,3451,0,1,1}-{2967,3439,0,1,4}-{2965,3451,0,1,5}-{2954,3273,0,1,6}" - }, - { - "npc_id": "6942", - "loc_data": "{2890,3100,0,1,4}" - }, - { - "npc_id": "6944", - "loc_data": "{2855,3028,0,0,0}-{2841,3039,0,0,0}-{2838,3031,0,0,0}-{2836,3014,0,0,0}-{2839,3022,0,0,0}" - }, - { - "npc_id": "6962", - "loc_data": "{2842,5222,0,1,6}" - }, - { - "npc_id": "6971", - "loc_data": "{2926,3443,0,0,5}" - }, - { - "npc_id": "6972", - "loc_data": "{2922,3482,0,1,0}" - }, - { - "npc_id": "6973", - "loc_data": "{2893,3419,0,1,0}" - }, - { - "npc_id": "6974", - "loc_data": "{2897,3419,0,1,0}" - }, - { - "npc_id": "6975", - "loc_data": "{2928,3480,0,1,0}-{2932,3485,0,1,0}" - }, - { - "npc_id": "6976", - "loc_data": "{2897,3432,1,1,0}-{2884,3418,0,1,0}-{2884,3434,0,1,0}-{2902,3449,0,1,0}" - }, - { - "npc_id": "6977", - "loc_data": "{2890,3435,0,1,0}" - }, - { - "npc_id": "6978", - "loc_data": "{2896,3437,0,1,0}-{2917,3446,0,1,0}" - }, - { - "npc_id": "6979", - "loc_data": "{2888,3448,0,1,0}" - }, - { - "npc_id": "6981", - "loc_data": "{2883,3431,0,1,0}-{2900,3449,0,1,0}" - }, - { - "npc_id": "6982", - "loc_data": "{2918,3485,0,1,0}-{2896,3429,0,1,0}" - }, - { - "npc_id": "6983", - "loc_data": "{2926,3488,0,1,0}-{2908,3441,0,1,0}" - }, - { - "npc_id": "6985", - "loc_data": "{2894,3438,0,1,0}-{2913,3451,0,1,0}" - }, - { - "npc_id": "6986", - "loc_data": "{2887,3441,0,1,0}" - }, - { - "npc_id": "6987", - "loc_data": "{2894,3445,0,1,0}" - }, - { - "npc_id": "6996", - "loc_data": "{3259,3206,0,0,3}-{3259,3204,0,0,3}-{3169,3266,0,0,3}-{3173,3274,0,0,3}-{2689,3598,0,0,4}" - }, - { - "npc_id": "6997", - "loc_data": "{3002,3257,0,0,0}-{3058,3429,0,0,0}-{3059,3420,0,0,0}-{3059,3425,0,0,0}-{3049,3429,0,0,0}-{3007,3261,0,0,0}" - }, - { - "npc_id": "7003", - "loc_data": "{3253,5535,0,1,7}-{3230,5493,0,1,1}-{3224,5497,0,1,5}-{2582,9895,0,1,7}" - }, - { - "npc_id": "7004", - "loc_data": "{3256,5540,0,1,4}-{3247,5536,0,1,6}-{3225,5495,0,1,4}-{2581,9891,0,1,2}" - }, - { - "npc_id": "7009", - "loc_data": "{3282,3467,0,0,0}" - }, - { - "npc_id": "7021", - "loc_data": "{2510,2822,0,1,3}-{2511,2823,0,1,4}-{2509,2825,0,1,6}" - }, - { - "npc_id": "7022", - "loc_data": "{2503,2821,0,1,1}-{2500,2818,0,1,4}-{2503,2819,0,1,3}" - }, - { - "npc_id": "7023", - "loc_data": "{2552,2821,0,1,4}" - }, - { - "npc_id": "7024", - "loc_data": "{2523,2823,0,1,1}" - }, - { - "npc_id": "7025", - "loc_data": "{2537,2820,0,1,1}" - }, - { - "npc_id": "7026", - "loc_data": "{2543,2822,0,1,1}" - }, - { - "npc_id": "7027", - "loc_data": "{2609,2863,0,1,0}" - }, - { - "npc_id": "7028", - "loc_data": "{2610,2865,0,1,0}" - }, - { - "npc_id": "7029", - "loc_data": "{2611,2862,0,1,0}" - }, - { - "npc_id": "7030", - "loc_data": "{2611,2864,0,1,0}" - }, - { - "npc_id": "7031", - "loc_data": "{2499,2820,0,1,4}-{2511,2819,0,1,3}-{2525,2820,0,1,0}-{2527,2818,0,1,3}-{2542,2819,0,1,4}-{2551,2817,0,1,3}-{2554,2819,0,1,3}-{2559,2819,0,1,4}-{2566,2820,0,1,5}-{2575,2818,0,1,4}-{2578,2819,0,1,4}-{2587,2819,0,1,7}-{2595,2821,0,1,4}-{2595,2826,0,1,0}-{2503,2824,0,1,3}-{2509,2822,0,1,4}-{2518,2825,0,1,7}-{2526,2822,0,1,1}-{2547,2822,0,1,4}-{2588,2801,0,1,1}-{2557,2826,0,1,4}-{2562,2819,0,1,6}-{2565,2828,0,1,0}-{2573,2825,0,1,1}-{2578,2820,0,1,6}-{2584,2822,0,1,4}-{2590,2831,0,1,4}-{2594,2823,0,1,4}-{2502,2823,0,1,4}-{2513,2820,0,1,7}-{2527,2819,0,1,4}-{2529,2818,0,1,6}-{2541,2822,0,1,3}-{2549,2817,0,1,2}-{2557,2818,0,1,2}-{2488,2768,0,1,1}" - }, - { - "npc_id": "7032", - "loc_data": "{2539,2838,0,1,1}" - }, - { - "npc_id": "7033", - "loc_data": "{2541,2837,0,1,1}" - }, - { - "npc_id": "7034", - "loc_data": "{2536,2839,0,1,1}" - }, - { - "npc_id": "7035", - "loc_data": "{2542,2839,0,1,1}" - }, - { - "npc_id": "7036", - "loc_data": "{2538,2841,0,1,1}" - }, - { - "npc_id": "7039", - "loc_data": "{2503,2824,0,1,4}-{2518,2824,0,1,4}-{2534,2821,0,1,1}-{2548,2823,0,1,4}-{2560,2822,0,1,1}-{2570,2823,0,1,1}-{2582,2824,0,1,6}-{2588,2828,0,1,4}" - }, - { - "npc_id": "7040", - "loc_data": "{2562,2863,0,1,4}-{2617,5539,0,1,4}" - }, - { - "npc_id": "7041", - "loc_data": "{2562,2863,0,1,4}-{2616,5540,0,1,4}" - }, - { - "npc_id": "7042", - "loc_data": "{2551,2863,0,1,6}-{2605,5540,0,1,6}" - }, - { - "npc_id": "7043", - "loc_data": "{2553,2862,0,1,6}-{2607,5539,0,1,6}" - }, - { - "npc_id": "7044", - "loc_data": "{2467,2933,0,0,4}-{2470,2937,0,0,4}-{2445,2897,0,0,4}-{2444,2894,0,0,4}" - }, - { - "npc_id": "7045", - "loc_data": "{2460,2922,0,0,4}-{2463,2928,0,0,4}" - }, - { - "npc_id": "7046", - "loc_data": "{2460,2925,0,0,4}-{3364,3800,0,0,4}-{3368,3811,0,0,4}-{3347,3814,0,0,4}-{3363,3816,0,0,4}" - }, - { - "npc_id": "7047", - "loc_data": "{2557,2836,0,1,1}-{2611,5513,0,0,1}" - }, - { - "npc_id": "7048", - "loc_data": "{2545,2836,0,0,0}-{2599,5513,0,0,0}" - }, - { - "npc_id": "7049", - "loc_data": "{2553,2839,0,0,4}" - }, - { - "npc_id": "7049", - "loc_data": "{2553,2836,0,0,4}" - }, - { - "npc_id": "7051", - "loc_data": "{2547,2852,0,1,0}" - }, - { - "npc_id": "7052", - "loc_data": "{2571,2855,0,1,3}" - }, - { - "npc_id": "7054", - "loc_data": "{2560,2851,0,0,1}" - }, - { - "npc_id": "7055", - "loc_data": "{2559,2851,0,0,1}" - }, - { - "npc_id": "7056", - "loc_data": "{2560,2851,0,1,6}-{2613,5528,0,0,6}" - }, - { - "npc_id": "7057", - "loc_data": "{2596,2845,0,1,0}" - }, - { - "npc_id": "7062", - "loc_data": "{2571,2840,0,1,4}" - }, - { - "npc_id": "7065", - "loc_data": "{2621,2857,0,1,3}" - }, - { - "npc_id": "7066", - "loc_data": "{2619,2856,0,1,3}" - }, - { - "npc_id": "7067", - "loc_data": "{2518,2842,0,1,0}-{2572,5519,0,0,6}" - }, - { - "npc_id": "7068", - "loc_data": "{2531,2850,0,1,6}-{2585,5527,0,0,6}" - }, - { - "npc_id": "7069", - "loc_data": "{2559,2858,0,1,0}-{2613,5535,0,0,6}" - }, - { - "npc_id": "7070", - "loc_data": "{2581,2866,0,1,6}" - }, - { - "npc_id": "7071", - "loc_data": "{2598,2864,0,1,6}" - }, - { - "npc_id": "7072", - "loc_data": "{2517,2840,0,1,6}" - }, - { - "npc_id": "7073", - "loc_data": "{2533,2848,0,1,6}" - }, - { - "npc_id": "7074", - "loc_data": "{2556,2858,0,1,6}" - }, - { - "npc_id": "7075", - "loc_data": "{2583,2865,0,1,6}" - }, - { - "npc_id": "7076", - "loc_data": "{2596,2865,0,1,6}" - }, - { - "npc_id": "7077", - "loc_data": "{2617,2856,0,1,3}" - }, - { - "npc_id": "7078", - "loc_data": "{2451,2857,0,1,0}-{2453,2874,0,1,0}-{2475,2868,0,1,0}-{2475,2896,0,1,0}-{2476,2883,0,1,0}- {2477,2832,0,1,0}-{2502,2838,0,1,0}" - }, - { - "npc_id": "7079", - "loc_data": "{2451,2849,0,1,0}-{2463,2872,0,1,0}-{2470,2853,0,1,0}-{2477,2903,0,1,0}-{2478,2907,0,1,0}-{2487,2837,0,1,0}-{2501,2851,0,1,0}" - }, - { - "npc_id": "7080", - "loc_data": "{2456,2867,0,1,0}-{2457,2836,0,1,0}-{2480,2916,0,1,0}-{2484,2873,0,1,0}-{2487,2856,0,1,0}-{2492,2888,0,1,0}-{2508,2853,0,1,0}-{2562,5530,0,0,6}" - }, - { - "npc_id": "7081", - "loc_data": "{2446,2869,0,1,0}-{2451,2828,0,1,0}-{2467,2860,0,1,0}-{2480,2892,0,1,0}-{2484,2917,0,1,0}-{2487,2844,0,1,0}-{2499,2860,0,1,0}" - }, - { - "npc_id": "7082", - "loc_data": "{2440,2858,0,1,0}-{2459,2859,0,1,0}-{2471,2839,0,1,0}-{2482,2863,0,1,0}-{2483,2883,0,1,0}-{2491,2901,0,1,0}-{2516,2858,0,1,0}-{2569,5541,0,0,6}" - }, - { - "npc_id": "7091", - "loc_data": "{2609,2863,0,0,0}" - }, - { - "npc_id": "7092", - "loc_data": "{2610,2865,0,0,0}" - }, - { - "npc_id": "7093", - "loc_data": "{2611,2862,0,0,0}" - }, - { - "npc_id": "7094", - "loc_data": "{2611,2864,0,0,0}" - }, - { - "npc_id": "7095", - "loc_data": "{2609,2863,0,0,0}-{2610,2862,0,0,0}" - }, - { - "npc_id": "7096", - "loc_data": "{2610,2865,0,0,0}-{2605,2866,0,0,0}" - }, - { - "npc_id": "7097", - "loc_data": "{2611,2862,0,0,0}-{2607,2867,0,0,0}" - }, - { - "npc_id": "7098", - "loc_data": "{2611,2864,0,0,0}-{2608,2863,0,0,0}" - }, - { - "npc_id": "7099", - "loc_data": "{2585,2844,0,1,3}" - }, - { - "npc_id": "7100", - "loc_data": "{2586,2840,0,0,4}" - }, - { - "npc_id": "7101", - "loc_data": "{2585,2842,0,1,1}" - }, - { - "npc_id": "7102", - "loc_data": "{2576,2840,0,0,0}-{2576,2840,0,0,0}" - }, - { - "npc_id": "7103", - "loc_data": "{2579,2843,0,0,0}-{2582,2843,0,0,0}-{2579,2843,0,0,0}-{2582,2843,0,0,0}" - }, - { - "npc_id": "7104", - "loc_data": "{2549,2852,0,1,4}-{2603,5529,0,0,4}" - }, - { - "npc_id": "7105", - "loc_data": "{3111,9929,0,1,7}-{3106,9941,0,1,1}-{3111,9935,0,1,4}-{3114,9931,0,1,1}-{2612,9484,0,1,1}-{2611,9484,0,1,0}-{2614,9484,0,1,6}-{2611,9486,0,1,1}" - }, - { - "npc_id": "7106", - "loc_data": "{3131,9930,0,1,4}" - }, - { - "npc_id": "7107", - "loc_data": "{3130,9928,0,1,0}-{3131,9931,0,1,4}" - }, - { - "npc_id": "7109", - "loc_data": "{3127,9932,0,1,1}" - }, - { - "npc_id": "7110", - "loc_data": "{3121,9929,0,1,4}" - }, - { - "npc_id": "7112", - "loc_data": "{3130,9934,0,1,3}" - }, - { - "npc_id": "7113", - "loc_data": "{3127,9932,0,1,4}" - }, - { - "npc_id": "7114", - "loc_data": "{3132,9921,0,0,0}-{3132,9935,0,1,1}" - }, - { - "npc_id": "7115", - "loc_data": "{3050,3259,0,0,0}" - }, - { - "npc_id": "7121", - "loc_data": "{2189,3148,0,1,4}" - }, - { - "npc_id": "7138", - "loc_data": "{3245,5462,0,1,7}-{3257,5454,0,1,3}-{3255,5461,0,1,1}-{3251,5473,0,1,3}-{3224,5540,0,1,0}-{3240,5541,0,1,6}-{3239,5529,0,1,6}-{3233,5524,0,1,4}-{3237,5512,0,1,6}-{3225,5510,0,1,1}-{3222,5524,0,1,1}-{3213,5472,0,1,3}-{3210,5469,0,1,5}-{3221,5468,0,1,0}-{3223,5465,0,1,0}-{3190,5523,0,1,2}-{3225,5540,0,1,7}-{3218,5537,0,1,3}-{3195,5527,0,1,6}" - }, - { - "npc_id": "7139", - "loc_data": "{3220,5463,0,1,7}" - }, - { - "npc_id": "7142", - "loc_data": "{3082,4246,0,1,4}" - }, - { - "npc_id": "7143", - "loc_data": "{3082,3459,0,0,0}" - }, - { - "npc_id": "7144", - "loc_data": "{3085,4230,0,1,3}" - }, - { - "npc_id": "7145", - "loc_data": "{3081,4252,0,1,7}" - }, - { - "npc_id": "7146", - "loc_data": "{3078,4240,0,1,4}" - }, - { - "npc_id": "7147", - "loc_data": "{3079,4245,0,1,4}" - }, - { - "npc_id": "7148", - "loc_data": "{3077,4229,0,1,4}" - }, - { - "npc_id": "7149", - "loc_data": "{3085,4235,0,1,4}" - }, - { - "npc_id": "7150", - "loc_data": "{3085,4240,0,1,3}" - }, - { - "npc_id": "7151", - "loc_data": "{3081,3457,0,0,4}" - }, - { - "npc_id": "7152", - "loc_data": "{3081,3455,0,0,4}" - }, - { - "npc_id": "7153", - "loc_data": "{3081,3453,0,0,4}" - }, - { - "npc_id": "7154", - "loc_data": "{3079,3457,0,0,4}" - }, - { - "npc_id": "7155", - "loc_data": "{3079,3455,0,0,4}" - }, - { - "npc_id": "7156", - "loc_data": "{3077,3457,0,0,4}" - }, - { - "npc_id": "7157", - "loc_data": "{3077,3453,0,0,4}" - }, - { - "npc_id": "7158", - "loc_data": "{3080,3463,0,1,0}-{3079,3463,0,1,0}-{3144,4250,2,1,4}-{3143,4255,2,1,2}-{3140,4253,2,1,3}-{3145,4255,2,1,6}-{3149,4257,2,1,5}-{3148,4262,2,1,5}-{3142,4260,2,1,3}-{3144,4259,2,1,4}-{3145,4261,2,1,4}-{3146,4265,2,1,5}-{3143,4264,2,1,3}-{3144,4266,2,1,5}-{3141,4265,2,1,1}-{3144,4270,2,1,1}-{3142,4269,2,1,6}-{3157,4262,2,1,1}-{3164,4265,2,1,6}-{3162,4266,2,1,6}-{3165,4266,2,1,3}-{3164,4261,2,1,2}-{3162,4259,2,1,5}-{3162,4255,2,1,7}-{3165,4256,2,1,4}-{3163,4251,2,1,3}-{3165,4249,2,1,6}-{3162,4250,2,1,4}-{3163,4241,2,1,4}-{3165,4239,2,1,4}-{3165,4241,2,1,0}-{3160,4235,2,1,2}-{3165,4235,2,1,4}-{3165,4237,2,1,4}-{3162,4234,2,1,3}-{3164,4231,2,1,4}-{3166,4229,2,1,6}-{3162,4230,2,1,1}-{3167,4229,2,1,3}-{3184,4236,2,1,1}-{3153,4246,1,1,3}-{3159,4236,1,1,6}-{3163,4236,1,1,4}-{3163,4234,1,1,7}-{3146,4235,1,1,5}-{3144,4236,1,1,1}-{3143,4234,1,1,2}-{3145,4240,1,1,4}-{3148,4242,1,1,1}-{3145,4241,1,1,7}-{3142,4242,1,1,3}-{3146,4230,1,1,4}" - }, - { - "npc_id": "7159", - "loc_data": "{3174,4238,2,1,6}-{3175,4243,2,1,6}-{3179,4236,2,1,4}-{3145,4272,1,1,3}-{3145,4276,1,1,3}-{3152,4271,1,1,6}-{3144,4264,1,1,4}-{3153,4266,1,1,1}-{3152,4261,1,1,4}-{3145,4259,1,1,6}-{3161,4259,1,1,0}-{3160,4265,1,1,0}-{3153,4246,1,1,3}-{3157,4248,1,1,6}-{3161,4247,1,1,4}-{3146,4247,1,1,6}-{3153,4233,1,1,4}-{3153,4240,1,1,6}-{3155,4238,1,1,5}-{3169,4267,3,1,3}-{3160,4265,3,1,6}-{3168,4250,3,1,4}-{3168,4244,3,1,6}-{3160,4239,3,1,1}-{3160,4228,3,1,3}-{3147,4225,3,1,3}-{3145,4242,3,1,5}-{3139,4249,3,1,6}-{3144,4253,3,1,3}-{3145,4266,3,1,0}-{3138,4266,3,1,3}" - }, - { - "npc_id": "7160", - "loc_data": "{3175,4254,2,1,7}-{3176,4247,2,1,5}-{3174,4269,2,1,1}-{3190,4253,2,1,3}-{3192,4247,2,1,3}-{3189,4235,2,1,0}-{3152,4269,0,1,4}-{3160,4278,0,1,1}-{3160,4262,0,1,3}-{3149,4250,0,1,0}-{3158,4250,0,1,5}-{3161,4236,0,1,6}-{3172,4250,0,1,2}-{3171,4269,3,1,1}-{3157,4277,3,1,3}-{3150,4278,3,1,3}" - }, - { - "npc_id": "7161", - "loc_data": "{3142,4230,2,1,6}-{3143,4231,2,1,3}-{3141,4235,2,1,4}-{3144,4241,2,1,4}" - }, - { - "npc_id": "7162", - "loc_data": "{3144,4229,2,1,1}-{3143,4236,2,1,1}-{3145,4235,2,1,1}-{3148,4244,2,1,6}" - }, - { - "npc_id": "7168", - "loc_data": "{2714,3309,0,1,1}" - }, - { - "npc_id": "7228", - "loc_data": "{2862,3101,0,0,0}-{2879,3109,0,0,0}-{2882,3104,0,0,0}-{2874,3098,0,0,0}-{2882,3098,0,0,0}" - }, - { - "npc_id": "7229", - "loc_data": "{2833,3088,0,0,0}-{2830,3085,0,0,0}-{2844,3089,0,0,0}-{2837,3084,0,0,0}-{2840,3076,0,0,0}" - }, - { - "npc_id": "7230", - "loc_data": "{2887,3051,0,0,0}-{2890,3052,0,0,0}-{2894,3058,0,0,0}-{2840,3008,0,0,0}-{2897,3035,0,0,0}" - }, - { - "npc_id": "7231", - "loc_data": "{2930,2961,0,0,0}-{2942,2964,0,0,0}-{2933,2964,0,0,0}-{2932,2967,0,0,0}-{2936,2973,0,0,0}" - }, - { - "npc_id": "7232", - "loc_data": "{2802,3106,0,0,0}-{2807,3101,0,0,0}-{2806,3108,0,0,0}-{2808,3105,0,0,0}-{2814,3100,0,0,0}" - }, - { - "npc_id": "7233", - "loc_data": "{2922,3070,0,0,0}-{2929,3075,0,0,0}-{2919,3080,0,0,0}-{2924,3080,0,0,0}-{2923,3081,0,0,0}" - }, - { - "npc_id": "7234", - "loc_data": "{2900,3107,0,0,0}-{2903,3098,0,0,0}-{2908,3104,0,0,0}-{2905,3107,0,0,0}-{2904,3107,0,0,0}" - }, - { - "npc_id": "7235", - "loc_data": "{2820,3065,0,0,0}-{2830,3059,0,0,0}-{2825,3057,0,0,0}-{2827,3050,0,0,0}-{2822,3053,0,0,0}" - }, - { - "npc_id": "7236", - "loc_data": "{2799,3005,0,0,0}-{2801,2995,0,0,0}-{2804,3001,0,0,0}-{2803,2986,0,0,0}-{2811,2999,0,0,0}" - }, - { - "npc_id": "7275", - "loc_data": "{2995,3252,0,0,0}-{2996,3259,0,0,0}-{2993,3250,0,0,0}-{3000,3258,0,0,0}" - }, - { - "npc_id": "7276", - "loc_data": "{2994,3256,0,0,0}-{3002,3250,0,0,0}-{2992,3252,0,0,0}-{3000,3250,0,0,0}-{2728,3422,0,1,1}-{2718,3413,0,1,4}-{2729,3420,0,1,4}-{2727,3414,0,1,6}-{2714,3419,0,1,3}-{2719,3427,0,1,7}" - }, - { - "npc_id": "7285", - "loc_data": "{2835,3107,0,0,0}-{2836,3101,0,0,0}-{2836,3110,0,0,0}-{2837,3112,0,0,0}-{2841,3107,0,0,0}-{2885,3018,0,0,0}-{2885,3019,0,0,0}-{2891,3018,0,0,0}" - }, - { - "npc_id": "7286", - "loc_data": "{2931,3090,0,0,0}-{2925,3097,0,0,0}-{2936,3096,0,0,0}-{2924,3102,0,0,0}-{2933,3104,0,0,0}" - }, - { - "npc_id": "7288", - "loc_data": "{2834,3045,0,0,0}-{2826,3048,0,0,0}-{2824,3043,0,0,0}-{2822,3045,0,0,0}-{2818,3049,0,0,0}" - }, - { - "npc_id": "7289", - "loc_data": "{2882,3018,0,1,7}-{2887,3024,0,1,2}" - }, - { - "npc_id": "7290", - "loc_data": "{2845,3088,0,1,6}-{2854,3085,0,1,1}" - }, - { - "npc_id": "7292", - "loc_data": "{2863,3048,0,1,5}-{2868,3037,0,1,4}" - }, - { - "npc_id": "7309", - "loc_data": "{3023,3410,0,0,0}-{2946,3280,0,0,0}" - }, - { - "npc_id": "7310", - "loc_data": "{3015,3409,0,0,0}-{2946,3272,0,0,0}" - }, - { - "npc_id": "7311", - "loc_data": "{2956,3273,0,0,0}" - }, - { - "npc_id": "7312", - "loc_data": "{2950,3268,0,0,0}" - }, - { - "npc_id": "7420", - "loc_data": "{3168,3334,0,0,3}" - }, - { - "npc_id": "7421", - "loc_data": "{3163,3336,0,0,1}" - }, - { - "npc_id": "7451", - "loc_data": "{2328,3170,0,1,6}-{2341,3179,0,1,4}-{2338,3164,0,1,4}-{2189,3181,0,1,2}-{2178,3186,0,1,7}-{2199,3182,0,1,0}-{2288,3149,0,1,2}-{2282,3144,0,1,3}-{2285,3144,0,1,2}-{2281,3139,0,1,1}-{2289,3152,0,1,6}-{2287,3155,0,1,1}-{2286,3146,0,1,1}-{2289,3156,0,0,5}-{2285,3136,0,1,3}" - }, - { - "npc_id": "7600", - "loc_data": "{1714,5602,0,0,6}" - }, - { - "npc_id": "7602", - "loc_data": "{1693,5598,0,1,0}" - }, - { - "npc_id": "7603", - "loc_data": "{1696,5600,0,1,0}" - }, - { - "npc_id": "7604", - "loc_data": "{1699,5599,0,1,0}" - }, - { - "npc_id": "7605", - "loc_data": "{1705,5599,0,0,1}" - }, - { - "npc_id": "7636", - "loc_data": "{3618,9736,0,0,0}-{3620,9732,0,0,0}-{3624,9736,0,0,0}-{3625,9738,0,0,0}-{3618,9736,0,0,4}-{3620,9732,0,0,4}-{3624,9736,0,0,4}-{3625,9738,0,0,4}" - }, - { - "npc_id": "7637", - "loc_data": "{3588,9768,0,0,0}-{3600,9768,0,0,0}-{3606,9780,0,0,0}-{3612,9729,0,0,0}-{3613,9764,0,0,0}-{3614,9740,0,0,0}-{3614,9750,0,0,0}-{3621,9748,0,0,0}-{3629,9737,0,0,0}" - }, - { - "npc_id": "7639", - "loc_data": "{3592,9766,0,0,0}-{3603,9770,0,0,0}-{3605,9757,0,0,0}-{3611,9749,0,0,0}-{3611,9766,0,0,0}-{3616,9737,0,0,0}-{3616,9756,0,0,0}-{3616,9762,0,0,0}-{3617,9779,0,0,0}-{3621,9754,0,0,0}" - }, - { - "npc_id": "7640", - "loc_data": "{3608,9780,0,1,3}-{3610,9777,0,1,1}-{3613,9772,0,1,4}-{3617,9748,0,1,7}-{3617,9758,0,1,1}-{3621,9751,0,1,1}" - }, - { - "npc_id": "7641", - "loc_data": "{3607,9776,0,1,1}-{3610,9772,0,1,0}-{3614,9778,0,1,7}-{3615,9746,0,1,7}-{3618,9753,0,1,1}-{3619,9748,0,1,6}" - }, - { - "npc_id": "7642", - "loc_data": "{3595,9742,0,0,0}-{3597,9751,0,0,0}-{3598,9736,0,0,0}-{3604,9746,0,0,0}" - }, - { - "npc_id": "7643", - "loc_data": "{3587,9737,0,0,0}-{3592,9746,0,0,0}-{3600,9740,0,0,0}-{3605,9738,0,0,0}" - }, - { - "npc_id": "7711", - "loc_data": "{3405,9902,0,0,0}" - }, - { - "npc_id": "7716", - "loc_data": "{3005,3475,0,1,7}-{3003,3473,0,1,4}-{2997,3471,0,0,0}-{3004,3494,0,0,0}-{3005,3505,0,0,0}" - }, - { - "npc_id": "7726", - "loc_data": "{2996,3451,0,0,0}" - }, - { - "npc_id": "7727", - "loc_data": "{3045,3483,0,0,0}-{3053,3492,0,0,4}-{3055,3491,0,0,0}-{3045,3496,0,0,0}-{3046,3490,1,0,0}-{3057,3497,1,0,3}" - }, - { - "npc_id": "7728", - "loc_data": "{3009,3433,0,0,0}" - }, - { - "npc_id": "7731", - "loc_data": "{3008,3466,0,0,0}" - }, - { - "npc_id": "7732", - "loc_data": "{3009,3466,0,0,0}" - }, - { - "npc_id": "7733", - "loc_data": "{3008,3479,0,0,0}" - }, - { - "npc_id": "7734", - "loc_data": "{3009,3487,0,0,0}" - }, - { - "npc_id": "7735", - "loc_data": "{2999,3469,0,0,0}" - }, - { - "npc_id": "7744", - "loc_data": "{3149,3411,0,0,0}" - }, - { - "npc_id": "7746", - "loc_data": "{4755,5116,0,1,7}" - }, - { - "npc_id": "7747", - "loc_data": "{4751,5116,0,1,5}" - }, - { - "npc_id": "7748", - "loc_data": "{4752,5114,0,1,5}" - }, - { - "npc_id": "7749", - "loc_data": "{4739,5070,0,1,0}" - }, - { - "npc_id": "7750", - "loc_data": "{4765,5064,0,1,7}" - }, - { - "npc_id": "7751", - "loc_data": "{4778,5070,0,1,2}" - }, - { - "npc_id": "7752", - "loc_data": "{4432,5096,0,1,0}" - }, - { - "npc_id": "7753", - "loc_data": "{4438,5089,0,1,0}-{4428,5083,0,1,0}-{4437,5075,0,1,0}" - }, - { - "npc_id": "7763", - "loc_data": "{4777,5080,0,1,2}" - }, - { - "npc_id": "7765", - "loc_data": "{4776,5076,0,1,6}" - }, - { - "npc_id": "7767", - "loc_data": "{4762,5145,0,1,2}-{2457,5139,0,1,6}-{2460,5140,0,1,0}-{2443,5147,0,1,3}-{2454,5155,0,1,5}-{2461,5125,0,1,3}-{2456,5124,0,1,4}-{2458,5129,0,1,4}-{2443,5145,0,1,1}-{2451,5148,0,1,1}-{2447,5143,0,1,4}-{2443,5138,0,1,4}" - }, - { - "npc_id": "7768", - "loc_data": "{4762,5156,0,1,6}" - }, - { - "npc_id": "7823", - "loc_data": "{3215,9560,0,0,1}-{3161,9547,0,0,3}-{3164,9556,0,0,4}-{3162,9574,0,0,3}-{3198,9554,0,0,7}-{3216,9588,0,0,1}-{3198,9572,0,0,1}" - }, - { - "npc_id": "7891", - "loc_data": "{3207,3250,0,0,0}-{3208,3250,0,0,0}-{3209,3250,0,0,0}" - }, - { - "npc_id": "7959", - "loc_data": "{3188, 3425, 0, 1, 0}" - }, - { - "npc_id": "7969", - "loc_data": "{3205,3240,0,0,0}" - }, - { - "npc_id": "8041", - "loc_data": "{2794,3100,0,1,0}" - }, - { - "npc_id": "8082", - "loc_data": "{3552,5590,2,1,2}" - }, - { - "npc_id": "8083", - "loc_data": "{3560,5602,0,1,3}" - }, - { - "npc_id": "8084", - "loc_data": "{3541,5597,0,1,4}" - }, - { - "npc_id": "8085", - "loc_data": "{3550,5598,0,1,3}" - }, - { - "npc_id": "8111", - "loc_data": "{3273,3681,0,1,0}" - }, - { - "npc_id": "8149", - "loc_data": "{3244,9996,0,1,6}-{3229,10008,0,1,6}-{3222,10012,0,1,6}-{3205,10010,0,1,6}-{3212,10012,0,1,6}-{3205,10023,0,1,6}-{3210,10030,0,1,6}-{3212,10039,0,1,6}-{3213,10009,0,1,6}-{3243,9993,0,1,6}-{3218,10036,0,1,6}-{3218,10010,0,1,6}" - }, - { - "npc_id": "8150", - "loc_data": "{3241,9997,0,1,6}-{3230,10011,0,1,6}-{3221,10012,0,1,6}-{3207,10006,0,1,6}-{3207,10017,0,1,6-{3209,10031,0,1,6}-{3209,10019,0,1,6}-{3217,10012,0,1,6}-{3224,10012,0,1,6}" - }, - { - "npc_id": "8151", - "loc_data": "{3241,9998,0,1,6}-{3223,10010,0,1,6}-{3208,10012,0,1,6}-{3209,10026,0,1,6}-{3214,10038,0,1,6}-{3211,10035,0,1,6}-{3214,10037,0,1,6}-{3240,9991,0,1,6}-{3231,10010,0,1,6}-{3210,10007,0,1,6}-{3222,10010,0,1,6}" - }, - { - "npc_id": "8171", - "loc_data": "{3281,3403,0,1,6}" - }, - { - "npc_id": "8227", - "loc_data": "{3135,3631,0,1,6}" - }, - { - "npc_id": "8228", - "loc_data": "{3142,3635,0,1,0}" - }, - { - "npc_id": "8229", - "loc_data": "{3128,3632,0,1,0}-{3135,3616,0,1,6}" - }, - { - "npc_id": "8231", - "loc_data": "{3135,3627,0,1,0}" - }, - { - "npc_id": "8233", - "loc_data": "{3133,3628,0,1,0}" - }, - { - "npc_id": "8273", - "loc_data": "{2931,3536,0,1,5}" - }, - { - "npc_id": "8274", - "loc_data": "{3514,3513,0,1,6}" - }, - { - "npc_id": "8275", - "loc_data": "{2869,2982,1,1,5}" - }, - { - "npc_id": "8349", - "loc_data": "{2589,5735,0,1,0}-{2589,5713,0,1,0}-{2610,5709,0,1,0}-{2613,5732,0,1,0}" - }, - { - "npc_id": "8358", - "loc_data": "{2601,5710,0,1,0}-{2603,5737,0,1,0}" - }, - { - "npc_id": "8536", - "loc_data": "{2654,5600,0,1,3}-{2650,5600,0,0,3}-{2662,5593,0,0,3}-{2653,5590,0,0,3}-{2644,5592,0,0,3-{2644,5601,0,0,3}-{2654,5604,0,0,3}-{2663,5606,0,0,3}-{2670,5597,0,0,3}-{2657,5589,0,0,3}-" - }, - { - "npc_id": "8545", - "loc_data": "{3122,3626,0,1,0}" - }, - { - "npc_id": "8590", - "loc_data": "{2743,3444,0,1,0}" - }, - { - "npc_id": "7601", - "loc_data": "{1697,5605,0,0,6}" - }, - { - "npc_id": "3579", - "loc_data": "{3195,3263,0,1,0}" - }, - { - "npc_id": "5424", - "loc_data": "{2594,3269,0,0,0}" - }, - { - "npc_id": "7010", - "loc_data": "{2243,3200,0,1,3}-{2204,3229,0,1,3}-{2204,3229,0,1,3}-{2204,3229,0,1,3}-{2268,3239,0,1,3}-{2268,3239,0,1,3}-{2268,3239,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}" - }, - { - "npc_id": "7011", - "loc_data": "{2243,3200,0,1,3}-{2268,3239,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}" - }, - { - "npc_id": "7014", - "loc_data": "{2180,3201,0,1,3}-{2226,3143,0,1,3}-{2288,3147,0,1,3}-{2247,3187,0,1,3}" - }, - { - "npc_id": "7012", - "loc_data": "{2180,3201,0,1,3}-{2180,3201,0,1,3}-{2182,3204,0,1,3}-{2226,3143,0,1,3}-{2226,3143,0,1,3}-{2226,3143,0,1,3}-{2223,3146,0,1,3}-{2223,3146,0,1,3}-{2288,3147,0,1,3}-{2288,3147,0,1,3}-{2288,3147,0,1,3}-{2247,3187,0,1,3}-{2247,3187,0,1,3}" - }, - { - "npc_id": "1183", - "loc_data": "{2206,3258,0,1,3}-{2200,3261,0,1,3}-{2193,3253,0,1,3}-{2192,3243,0,1,3}" - }, - { - "npc_id": "1184", - "loc_data": "{2192,3249,0,1,3}-{2200,3249,0,1,3}-{2195,3247,0,1,3}-{2212,3258,0,1,3}" - }, - { - "npc_id": "4248", - "loc_data": "{2849,10183,0,1,0}" - }, - { - "npc_id": "2153", - "loc_data": "{2826,10197,0,1,0}" - }, - { - "npc_id": "2152", - "loc_data": "{2827,10231,0,1,0}" - }, - { - "npc_id": "2161", - "loc_data": "{2869,10211,0,1,0}" - }, - { - "npc_id": "2162", - "loc_data": "{2886,10187,0,1,0}" - }, - { - "npc_id": "2151", - "loc_data": "{2873,10210,0,1,0}" - }, - { - "npc_id": "4558", - "loc_data": "{2885,10202,0,1,0}" - }, - { - "npc_id": "2164", - "loc_data": "{2838,10205,0,0,1}" - }, - { - "npc_id": "2163", - "loc_data": "{2836,10205,0,1,0}" - }, - { - "npc_id": "1546", - "loc_data": "{2911,10175,0,0,4}-{2912,10175,0,0,4}-{2913,10175,0,0,4}-{2914,10175,0,0,4}-{2909,10173,0,0,4}-{2910,10173,0,0,4}-{2911,10173,0,0,4}-{2912,10173,0,0,4}-{2913,10173,0,0,4}-{2909,10171,0,0,4}-{2910,10171,0,0,4}-{2911,10169,0,0,4}-{2912,10169,0,0,4}-{2913,10169,0,0,4}" - }, - { - "npc_id": "2181", - "loc_data": "{2874,9871,0,1,0}" - }, - { - "npc_id": "2182", - "loc_data": "{2908,10176,0,1,0}" - }, - { - "npc_id": "2092", - "loc_data": "{2869,10196,1,1,0}" - }, - { - "npc_id": "2100", - "loc_data": "{2870,10196,1,1,0}" - }, - { - "npc_id": "2101", - "loc_data": "{2868,10202,1,1,0}" - }, - { - "npc_id": "2094", - "loc_data": "{2869,10205,1,0,6}" - }, - { - "npc_id": "2096", - "loc_data": "{2890,10207,1,0,6}" - }, - { - "npc_id": "2104", - "loc_data": "{2890,10204,1,1,0}" - }, - { - "npc_id": "2095", - "loc_data": "{2889,10211,1,0,6}" - }, - { - "npc_id": "2103", - "loc_data": "{2892,10209,1,1,0}" - }, - { - "npc_id": "2105", - "loc_data": "{2890,10196,1,1,0}" - }, - { - "npc_id": "2097", - "loc_data": "{2891,10196,1,0,6}" - }, - { - "npc_id": "2102", - "loc_data": "{2869,10208,1,1,0}" - }, - { - "npc_id": "2093", - "loc_data": "{2871,10207,1,1,0}" - }, - { - "npc_id": "2098", - "loc_data": "{2890,10192,1,0,6}" - }, - { - "npc_id": "2106", - "loc_data": "{2892,10190,1,1,0}" - }, - { - "npc_id": "2110", - "loc_data": "{2877,10198,1,1,0}-{2878,10196,1,1,0}" - }, - { - "npc_id": "2111", - "loc_data": "{2878,10201,1,1,0}-{2876,10201,1,1,0}" - }, - { - "npc_id": "2113", - "loc_data": "{2878,10201,1,1,0}-{2876,10201,1,1,0}" - }, - { - "npc_id": "2115", - "loc_data": "{2882,10201,1,1,0}-{2883,10200,1,1,0}" - }, - { - "npc_id": "2117", - "loc_data": "{2883,10198,1,1,0}-{2884,10199,1,1,0}" - }, - { - "npc_id": "2119", - "loc_data": "{2882,10197,1,1,0}-{2883,10196,1,1,0}" - }, - { - "npc_id": "2121", - "loc_data": "{2880,10195,1,1,0}-{2879,10193,1,1,0}" - }, - { - "npc_id": "2197", - "loc_data": "{2848,10198,0,1,0}" - }, - { - "npc_id": "2188", - "loc_data": "{2851,10223,0,1,0}" - }, - { - "npc_id": "2189", - "loc_data": "{2851,10223,0,1,0}" - }, - { - "npc_id": "1848", - "loc_data": "{2868,10168,0,1,0}" - }, - { - "npc_id": "1849", - "loc_data": "{2868,10168,0,1,0}" - }, - { - "npc_id": "1847", - "loc_data": "{2868,10168,0,1,0}" - }, - { - "npc_id": "1839", - "loc_data": "{2825,10167,0,1,0}" - }, - { - "npc_id": "2127", - "loc_data": "{2880,10199,1,1,0}" - }, - { - "npc_id": "2201", - "loc_data": "{2866,10208,0,1,0}" - }, - { - "npc_id": "2179", - "loc_data": "{2840,10199,0,0,6}" - }, - { - "npc_id": "2177", - "loc_data": "{2843,10191,1,0,1}" - }, - { - "npc_id": "2140", - "loc_data": "{2835,10226,0,0,6}" - }, - { - "npc_id": "2169", - "loc_data": "{2836,10222,0,1,0}" - }, - { - "npc_id": "2196", - "loc_data": "{2838,10196,0,1,0}" - }, - { - "npc_id": "2170", - "loc_data": "{2853,10196,0,1,0}" - }, - { - "npc_id": "2199", - "loc_data": "{2855,10199,0,1,0}" - }, - { - "npc_id": "2141", - "loc_data": "{2906,10204,0,1,0}" - }, - { - "npc_id": "2194", - "loc_data": "{2926,10225,0,1,0}" - }, - { - "npc_id": "2142", - "loc_data": "{2904,10206,0,1,0}" - }, - { - "npc_id": "2155", - "loc_data": "{2871,10199,0,1,0}" - }, - { - "npc_id": "8065", - "loc_data": "{3399,3464,0,0,1}" - }, - { - "npc_id": "8066", - "loc_data": "{3346,3513,0,0,6}" - }, - { - "npc_id": "8067", - "loc_data": "{3280,3524,0,0,0}" - }, - { - "npc_id": "8068", - "loc_data": "{3237,3525,0,0,0}" - }, - { - "npc_id": "8069", - "loc_data": "{3171,3535,0,0,0}" - }, - { - "npc_id": "8070", - "loc_data": "{3088,3515,0,0,0}" - }, - { - "npc_id": "8071", - "loc_data": "{3035,3517,0,0,0}" - }, - { - "npc_id": "8072", - "loc_data": "{2972,3515,0,0,0}" - }, - { - "npc_id": "8073", - "loc_data": "{2941,3564,0,0,0}" - }, - { - "npc_id": "8074", - "loc_data": "{2946,3621,0,0,0}" - }, - { - "npc_id": "8075", - "loc_data": "{2938,3679,0,0,0}" - }, - { - "npc_id": "8076", - "loc_data": "{2939,3772,0,0,0}" - }, - { - "npc_id": "2085", - "loc_data": "{3231,9610,0,1,0}" - }, - { - "npc_id": "2084", - "loc_data": "{3314,9612,0,1,0}" - }, - { - "npc_id": "2078", - "loc_data": "{3313,9622,0,0,3}" - }, - { - "npc_id": "2077", - "loc_data": "{3323,9616,0,0,6}" - }, - { - "npc_id": "2076", - "loc_data": "{3312,9628,0,0,3}" - }, - { - "npc_id": "2075", - "loc_data": "{3312,9638,0,0,3}" - }, - { - "npc_id": "2072", - "loc_data": "{3319,9632,0,1,0}" - }, - { - "npc_id": "2071", - "loc_data": "{3323,9643,0,1,0}" - }, - { - "npc_id": "2070", - "loc_data": "{3324,9621,0,1,0}" - }, - { - "npc_id": "2069", - "loc_data": "{3313,9652,0,1,0}" - }, - { - "npc_id": "2074", - "loc_data": "{3319,9605,0,0,1}" - }, - { - "npc_id": "7724", - "loc_data": "{3054,3503,0,1,0}" - }, - { - "npc_id": "15", - "loc_data": "{3203,3479,0,1,0}-{3202,3484,0,1,0}" - }, - { - "npc_id": "2910", - "loc_data": "{3082,9885,0,1,4}" - }, - { - "npc_id": "3382", - "loc_data": "{3080,9885,0,1,4}-{3076,9886,0,1,4}-{3081,9890,0,1,4}-{3081,9895,0,1,4}" - }, - { - "npc_id": "5965", - "loc_data": "{1759,4955,0,0,0}" - }, - { - "npc_id": "5966", - "loc_data": "{1754,4960,0,1,4}" - }, - { - "npc_id": "5967", - "loc_data": "{1735,4950,0,1,4}-{1758, 4937, 0, 1, 4}-{1756, 4947, 0, 1, 4}-{1771, 4960, 0, 1, 4}-{1779, 4950, 0, 1, 4}-{1759, 4971, 0, 1, 4}-{1773, 4983, 0, 1, 4}" - }, - { - "npc_id": "5945", - "loc_data": "{1743,4959,0,1,4}-{1739,4969,0,1,4}-{1748, 4941, 0, 1, 4}-{1759, 4979, 0, 1, 6}" - }, - { - "npc_id": "10", - "loc_data": "{1740,4970,0,1,4}-{1783, 4950, 0, 1, 4}-{1755,4951,0,1,3}-{1754, 4986, 0, 1, 6}-{1763, 4972, 0, 1, 6}" - }, - { - "npc_id": "5971", - "loc_data": "{1742,4964,0,0,0}" - }, - { - "npc_id": "5977", - "loc_data": "{1735,4964,0,0,0}" - }, - { - "npc_id": "5982", - "loc_data": "{1735,4954,0,0,0}" - }, - { - "npc_id": "5976", - "loc_data": "{1742,4954,0,0,0}" - }, - { - "npc_id": "5978", - "loc_data": "{1752,4938,0,0,0}" - }, - { - "npc_id": "5972", - "loc_data": "{1781,4954,0,0,0}" - }, - { - "npc_id": "5973", - "loc_data": "{1774,4964,0,0,0}" - }, - { - "npc_id": "5974", - "loc_data": "{1774,4954,0,0,0}" - }, - { - "npc_id": "5975", - "loc_data": "{1740,4979,0,0,0}" - }, - { - "npc_id": "5981", - "loc_data": "{1750,4979,0,0,0}" - }, - { - "npc_id": "5979", - "loc_data": "{1765,4979,0,0,0}" - }, - { - "npc_id": "5980", - "loc_data": "{1775,4979,0,0,0}" - }, - { - "npc_id": "8000", - "loc_data": "{2987,3691,0,1,3}" - }, - { - "npc_id": "2861", - "loc_data": "{2347,3165,0,1,3}" - }, - { - "npc_id": "4965", - "loc_data": "{2826,3685,0,0,1}" - }, - { - "npc_id": "2790", - "loc_data": "{3163,4822,0,0,6}" - }, - { - "npc_id": "6117", - "loc_data": "{1884,5020,0,0,6}" - }, - { - "npc_id": "6134", - "loc_data": "{1890,5018,0,0,0}" - }, - { - "npc_id": "6132", - "loc_data": "{1883,5029,0,0,6}" - }, - { - "npc_id": "6133", - "loc_data": "{1883,5026,0,0,6}" - }, - { - "npc_id": "6131", - "loc_data": "{1885,5026,0,0,6}" - }, - { - "npc_id": "490", - "loc_data": "{1887,5026,0,0,6}" - }, - { - "npc_id": "487", - "loc_data": "{1885,5029,0,0,6}" - }, - { - "npc_id": "555", - "loc_data": "{2657,3153,0,1,0}" - }, - { - "npc_id": "2357", - "loc_data": "{2340,3157,1,1,0}" - }, - { - "npc_id": "2356", - "loc_data": "{2323,3163,0,1,0}" - }, - { - "npc_id": "4874", - "loc_data": "{2709,3291,0,0,0}" - }, - { - "npc_id": "4856", - "loc_data": "{2732,3292,0,1,1}" - }, - { - "npc_id": "4872", - "loc_data": "{2740,3310,0,0,0}" - }, - { - "npc_id": "4878", - "loc_data": "{2726,3283,0,1,1}" - }, - { - "npc_id": "4883", - "loc_data": "{2713,3278,0,1,1}" - }, - { - "npc_id": "4885", - "loc_data": "{2705,3287,0,1,1}-{2716,3287,0,1,1}-{2732,3282,0,1,1}" - }, - { - "npc_id": "7780", - "loc_data": "{3358,2993,0,1,0}" - }, - { - "npc_id": "4887", - "loc_data": "{2738,3303,0,1,1}-{2723,3274,0,1,1}" - }, - { - "npc_id": "4895", - "loc_data": "{2718,3302,0,1,1}" - }, - { - "npc_id": "696", - "loc_data": "{2716,3303,0,1,1}" - }, - { - "npc_id": "698", - "loc_data": "{2716,3303,0,1,1}" - }, - { - "npc_id": "2352", - "loc_data": "{2334,3183,0,1,0}" - }, - { - "npc_id": "5497", - "loc_data": "{2376,10198,0,1,0}-{2384,10202,0,1,0}-{2390,10207,0,1,0}-{2385,10193,0,1,0}-{2419,10192,0,1,0}-{2412,10200,0,1,0}" - }, - { - "npc_id": "5498", - "loc_data": "{2412,3818,0,1,0}-{2412,3801,0,1,0}-{2392,3802,0,1,0}" - }, - { - "npc_id": "5474", - "loc_data": "{2390,10216,0,1,0}-{2417,10228,0,1,0}-{2385,10227,0,1,0}" - }, - { - "npc_id": "5475", - "loc_data": "{2396,10232,0,1,0}-{2402,10225,0,1,0}-{2420,10221,0,1,0}" - }, - { - "npc_id": "5473", - "loc_data": "{2377,10212,0,1,0}-{2392,10226,0,1,0}-{2407,10228,0,1,0}-{2406,10214,0,1,0}-{2420,10214,0,1,0}" - }, - { - "npc_id": "5491", - "loc_data": "{2387,3800,0,0,3}-{2411,3795,0,0,6}-{2417,3825,0,0,1}" - }, - { - "npc_id": "5492", - "loc_data": "{2387,3797,0,0,3}-{2414,3825,0,0,1}-{2414,3795,0,0,6}" - }, - { - "npc_id": "5481", - "loc_data": "{2644,3709,0,1,0}" - }, - { - "npc_id": "5482", - "loc_data": "{2422,3781,0,1,0}" - }, - { - "npc_id": "5463", - "loc_data": "{2324,3809,0,1,0}-{2317,3802,0,1,0}" - }, - { - "npc_id": "5521", - "loc_data": "{2324,3832,0,1,0}-{2350,3859,0,1,0}-{2375,3892,0,1,0}-{2388,3867,0,1,0}-{2384,3863,0,1,0}-{2387,3859,0,1,0}-{2350,3862,0,1,0}-{2352,3857,0,1,0}" - }, - { - "npc_id": "5523", - "loc_data": "{2367,3832,0,1,0}-{2319,3854,0,1,0}-{2324,3891,0,1,0}-{2406,3854,0,1,0}-{2405,3863,0,1,0}-{2400,3856,0,1,0}" - }, - { - "npc_id": "5522", - "loc_data": "{2362,3893,0,1,0}-{2390,3861,0,1,0}-{2395,3859,0,1,0}-{2323,3857,0,1,0}-{2331,3860,0,1,0}" - }, - { - "npc_id": "5524", - "loc_data": "{2340,3830,0,1,0}-{2338,3860,0,1,0}-{2339,3895,0,1,0}-{2411,3886,0,1,0}-{2396,3854,0,1,0}" - }, - { - "npc_id": "5514", - "loc_data": "{2337,3831,0,1,0}-{2399,3852,0,1,0}-{2321,3833,0,1,0}" - }, - { - "npc_id": "5515", - "loc_data": "{2327,3858,0,1,0}-{2321,3860,0,1,0}-{2343,3862,0,1,0}" - }, - { - "npc_id": "5516", - "loc_data": "{2393,3861,0,1,0}-{2347,3890,0,1,0}-{2347,3890,0,1,0}" - }, - { - "npc_id": "5517", - "loc_data": "{2403,3853,0,1,0}-{2328,3830,0,1,0}-{2405,3860,0,1,0}-{2335,3894,0,1,0}" - }, - { - "npc_id": "313", - "loc_data":"{2401,3781,0,0,0}" - }, - { - "npc_id": "312", - "loc_data": "{2421,3789,0,0,0}-{2410,3780,0,0,0}-{2414,3780,0,0,0}" - }, - { - "npc_id": "1164", - "loc_data": "{2780,3058,0,1,0}" - }, - { - "npc_id": "1051", - "loc_data": "{3440,9738,1,1,0}" - }, - { - "npc_id": "1052", - "loc_data": "{3420,3442,0,1,0}-{3429,3436,0,1,0}-{3439,3437,0,1,0}-{3448,3444,0,1,0}-{3458,3433,0,1,0}-{3459,3419,0,1,0}-{3449,3420,0,1,0}-{3441,3421,0,1,0}-{3427,3421,0,1,0}-{3414,3422,0,1,0}-{3411,3423,0,1,0}-{3415,3414,0,1,0}-{3410,3409,0,1,0}-{3422,3405,0,1,0}-{3431,3402,0,1,0}-{3444,3405,0,1,0}-{3453,3397,0,1,0}-{3467,3402,0,1,0}-{3473,3395,0,1,0}-{3470,3385,0,1,0}-{3463,3379,0,1,0}-{3451,3380,0,1,0}-{3445,3374,0,1,0}-{3438,3365,0,1,0}-{3429,3367,0,1,0}-{3417,3367,0,1,0}-{3413,3362,0,1,0}-{3417,3358,0,1,0}-{3426,3352,0,1,0}-{3435,3349,0,1,0}-{3442,3353,0,1,0}-{3448,3358,0,1,0}-{3457,3355,0,1,0}-{3461,3348,0,1,0}-{3457,3343,0,1,0}-{3471,3344,0,1,0}-{3426,3338,0,1,0}-{3423,3335,0,1,0}" - }, - { - "npc_id": "7132", - "loc_data": "{1630,4705,0,0,0}" - }, - { - "npc_id": "7131", - "loc_data": "{1634,4699,0,1,0}" - }, - { - "npc_id": "7125", - "loc_data": "{1605,4702,0,1,0}-{1649,4730,0,1,0}" - }, - { - "npc_id": "7126", - "loc_data": "{1605,4720,0,1,0}-{1658,4705,0,1,0}-{1633,4678,0,1,0}" - }, - { - "npc_id": "7127", - "loc_data": "{1631,4730,0,1,0}-{1658,4687,0,1,0}-{1615,4678,0,1,0}" - }, - { - "npc_id": "7128", - "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}" - }, - { - "npc_id": "7129", - "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}" - }, - { - "npc_id": "7130", - "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}" - }, - { - "npc_id": "800", - "loc_data": "{2884,9765,0,0,0}-{2890,9766,0,0,0}-{2894,9764,0,0,0}" - }, - { - "npc_id": "3322", - "loc_data": "{2384,4439,0,1,0}" - }, - { - "npc_id": "1080", - "loc_data": "{2900,3567,1,1,0}" - }, - { - "npc_id": "1078", - "loc_data": "{2905,3540,1,1,0}" - }, - { - "npc_id": "1011", - "loc_data": "{2642,9394,0,1,0}" - }, - { - "npc_id": "1012", - "loc_data": "{2650,9393,0,1,0}" - }, - { - "npc_id": "1907", - "loc_data": "{3488,3091,0,1,0}" - }, - { - "npc_id": "1911", - "loc_data": "{3416,3155,0,1,0}" - }, - { - "npc_id": "1678", - "loc_data": "{3473,9943,0,1,0}-{3488,9944,0,1,0}-{3498,9937,0,1,0}-{3534,9926,0,1,0}-{3553,9941,0,1,0}-{3548,9956,0,1,0}-{3564,9949,0,1,0}" - }, - { - "npc_id": "1677", - "loc_data": "{3474,9933,0,1,0}-{3488,9934,0,1,0}-{3547,9932,0,1,0}-{3540,9958,0,1,0}-{3564,9959,0,1,0}" - }, - { - "npc_id": "1676", - "loc_data": "{3481,9942,0,1,0}-{3493,9925,0,1,0}-{3525,9932,0,1,0}-{3555,9927,0,1,0}-{3554,9947,0,1,0}-{3529,9968,0,1,0}" - }, - { - "npc_id": "5083", - "loc_data": "{2699,3780,0,1,4}-{2694,3785,0,1,4}-{2708,3772,0,1,0}-{2714,3765,0,1,0}-{2717,3776,0,1,0}-{2718,3784,0,1,0}-{2621,3835,1,1,4}-{2723,3786,0,1,0}-{2725,3833,1,1,0}-{2728,3776,0,1,0}" - }, - { - "npc_id": "5084", - "loc_data": "{2703,3829,1,1,0}-{2708,3824,1,1,0}-{2714,3826,1,1,0}-{2722,3833,1,1,0}-{2724,3771,0,1,0}-{2738,3769,0,1,0}-{2737,3776,0,1,0}" - }, - { - "npc_id": "8133", - "loc_data": "{2993,4380,2,1,0}" - } -] +[ + { + "npc_id": "0", + "loc_data": "{3221,3218,0,1,0}-" + }, + { + "npc_id": "1", + "loc_data": "{3093,3509,0,1,3}-{3098,3508,0,1,7}-{3096,3510,0,1,3}-{3222,3221,0,1,3}-{3210,3223,1,1,4}-{3230,3208,0,1,1}-{3237,3404,0,1,6}-{3237,3408,0,1,0}-{3247,3396,0,1,3}-{3263,3403,0,1,3}-{2712,3484,0,1,4}-{2693,3495,0,1,1}-{3285,3208,0,1,4}-{3017,3239,0,1,2}-{2804,3427,0,1,2}-" + }, + { + "npc_id": "2", + "loc_data": "{3101,3511,0,1,4}-{3236,3203,0,1,4}-{3217,3209,0,1,4}-{3237,3217,0,1,6}-{2690,3489,0,1,3}-{3298,3185,0,1,3}-{3282,3503,0,1,6}-" + }, + { + "npc_id": "3", + "loc_data": "{2818,3443,0,1,3}-{3095,3511,0,1,3}-{3094,3513,0,1,4}-{3097,3510,0,1,4}-{3231,3239,0,1,4}-{2699,3491,0,1,1}-{3306,3200,0,1,6}-{3300,3208,0,1,3}-{3278,3502,0,1,6}-" + }, + { + "npc_id": "4", + "loc_data": "{3230,3204,0,1,3}-{2693,3493,0,1,7}-{2693,3492,0,1,0}-{2695,3497,0,1,1}-{2700,3496,0,1,5}-{3010,3236,0,1,6}-{3281,3499,0,1,4}-" + }, + { + "npc_id": "5", + "loc_data": "{2817,3447,0,1,5}-{3236,3205,0,1,2}-{3230,3238,0,1,1}-{2697,3493,0,1,4}-{3279,3497,0,1,3}-" + }, + { + "npc_id": "6", + "loc_data": "{3097,3259,0,1,4}-{3246,3207,0,1,4}-{3280,3491,0,1,0}-" + }, + { + "npc_id": "7", + "loc_data": "{3158,3301,0,1,0}-{2629,3360,0,1,4}-{2920,3434,0,1,0}-{3226,3290,0,1,3}-" + }, + { + "npc_id": "8", + "loc_data": "{2804,3186,0,1,4}-{3019,3230,0,1,4}-" + }, + { + "npc_id": "9", + "loc_data": "{3007,3321,0,1,6}-{3005,3321,0,1,4}-{2965,3389,0,1,6}-{2964,3392,0,1,6}-{2967,3393,0,1,1}-{2966,3392,0,1,1}-{2964,3392,0,1,1}-{3008,3324,0,1,2}-" + }, + { + "npc_id": "10", + "loc_data": "{1740,4970,0,1,4}-{1783,4950,0,1,4}-{1755,4951,0,1,3}-{1754,4986,0,1,6}-{1763,4972,0,1,6}-" + }, + { + "npc_id": "11", + "loc_data": "{3235,3399,0,1,5}-" + }, + { + "npc_id": "13", + "loc_data": "{2588,3087,0,1,4}-{2592,3086,0,1,6}-{2590,3083,1,1,4}-{2587,3086,1,1,4}-{2594,3084,1,1,6}-{2593,3090,2,1,5}-{2594,3085,2,1,1}-{2588,3089,2,1,4}-{3108,3157,1,1,3}-{3113,3157,1,1,4}-{3107,3170,0,1,0}-{3112,3167,0,1,0}-{3106,3157,0,1,3}-{3110,3155,0,1,6}-" + }, + { + "npc_id": "14", + "loc_data": "{2914,3444,0,1,3}-{2905,3450,1,1,0}-" + }, + { + "npc_id": "15", + "loc_data": "{2584,3304,0,1,3}-{2588,3291,0,1,7}-{2584,3288,0,1,0}-{2563,3382,0,1,6}-{2569,3383,0,1,6}-{2571,3381,0,1,1}-{2575,3384,0,1,4}-{2572,3385,0,1,4}-{2631,3294,0,1,6}-{2629,3296,0,1,4}-{3203,3479,0,1,0}-{3202,3484,0,1,0}-" + }, + { + "npc_id": "16", + "loc_data": "{3279,3191,0,1,4}-{3296,3202,1,1,3}-" + }, + { + "npc_id": "18", + "loc_data": "{3282,3176,0,1,0}-{3284,3170,0,1,7}-{3284,3174,0,1,0}-{3288,3168,0,1,5}-{3292,3169,0,1,3}-{3295,3168,0,1,3}-{3301,3170,0,1,6}-{3301,3174,0,1,6}-{3301,3177,0,1,0}-" + }, + { + "npc_id": "19", + "loc_data": "{2971,3349,0,1,4}-{2979,3350,0,1,2}-{2973,3337,0,1,3}-{2959,3335,0,1,6}-{2982,3339,0,1,6}-{2983,3346,0,1,4}-{2980,3329,0,1,4}-{2987,3330,0,1,6}-{2958,3340,1,1,1}-{2970,3333,1,1,3}-{2966,3329,1,1,4}-{2961,3353,1,1,4}-{2973,3337,1,1,6}-{2998,3341,0,1,6}-{2983,3342,2,1,6}-" + }, + { + "npc_id": "20", + "loc_data": "{2588,3301,1,0,0}-" + }, + { + "npc_id": "21", + "loc_data": "{2630,3288,0,1,1}-{2647,3306,0,1,1}-{2667,3315,0,1,4}-" + }, + { + "npc_id": "23", + "loc_data": "{2582,3297,0,1,6}-{2652,3318,0,1,0}-{2653,3300,0,1,5}-{2669,3298,0,1,4}-{2671,3313,0,1,6}-" + }, + { + "npc_id": "24", + "loc_data": "{2614,3318,0,1,6}-{2621,3293,0,1,3}-" + }, + { + "npc_id": "25", + "loc_data": "{2612,3316,0,1,1}-{2574,3321,1,1,4}-{2621,3293,1,1,3}-" + }, + { + "npc_id": "27", + "loc_data": "{2574,3285,2,0,0}-{2574,3308,2,0,0}-{2582,3284,2,0,0}-{2582,3309,2,0,0}-{2588,3290,2,0,0}-{2588,3303,2,0,0}-" + }, + { + "npc_id": "28", + "loc_data": "{2611,3269,0,0,0}-" + }, + { + "npc_id": "31", + "loc_data": "{2616,3308,0,1,1}-" + }, + { + "npc_id": "32", + "loc_data": "{2651,3307,0,1,1}-{2659,3309,0,1,6}-{2660,3309,0,1,3}-{2661,3309,0,1,1}-{2661,3317,0,1,5}-{2663,3301,0,1,3}-{2665,3300,0,1,3}-{2636,3342,0,1,4}-{2634,3342,0,1,3}-{2635,3339,0,1,3}-" + }, + { + "npc_id": "34", + "loc_data": "{2548,3111,1,1,3}-{2545,3116,1,1,7}-{2549,3115,1,1,1}-{2549,3112,1,1,4}-" + }, + { + "npc_id": "35", + "loc_data": "{2545,3094,0,1,3}-{2549,3093,0,1,2}-{2543,3089,0,1,3}-{2540,3098,0,1,3}-" + }, + { + "npc_id": "36", + "loc_data": "{3020,3373,0,1,0}-" + }, + { + "npc_id": "38", + "loc_data": "{2952,3063,0,0,0}-{2955,3057,0,0,0}-{2956,3035,0,0,0}-{2969,3034,0,0,0}-{2984,3048,0,0,0}-" + }, + { + "npc_id": "39", + "loc_data": "{2958,3055,0,0,0}-{2954,3029,0,0,0}-{2968,3039,0,0,0}-{2984,3048,0,0,0}-{2989,3058,0,0,0}-{2982,3060,0,0,0}-{2972,3060,0,0,0}-{2998,3061,0,0,0}-{2999,3049,0,0,0}-" + }, + { + "npc_id": "40", + "loc_data": "{3005,3060,0,0,0}-{3008,3067,0,0,0}-" + }, + { + "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}-" + }, + { + "npc_id": "42", + "loc_data": "{3185,3220,0,1,0}-{3183,3222,0,1,0}-{3237,3346,0,1,1}-" + }, + { + "npc_id": "43", + "loc_data": "{2926,3323,0,1,0}-{2922,3325,0,1,3}-{3200,3263,0,1,0}-{3212,3262,0,1,1}-{3204,3268,0,1,0}-{3241,3354,0,1,3}-" + }, + { + "npc_id": "44", + "loc_data": "{3096,3489,0,0,3}-{3096,3493,0,0,3}-{3097,3494,0,0,1}-{3147,3448,0,0,1}-{3180,3436,0,0,4}-{3180,3440,0,0,4}-{3180,3444,0,0,4}-{3191,3437,0,0,3}-{3191,3441,0,0,3}-{3191,3445,0,0,3}-{3251,3418,0,0,1}-{3252,3418,0,0,1}-{3253,3418,0,0,1}-{3254,3418,0,0,1}-{3255,3418,0,0,1}-{3256,3418,0,0,1}-" + }, + { + "npc_id": "45", + "loc_data": "{3096,3491,0,0,3}-{2843,5214,0,0,4}-{3148,3448,0,0,0}-{3191,3443,0,0,3}-{3191,3439,0,0,3}-{3191,3435,0,0,3}-{3180,3442,0,0,4}-{3180,3434,0,0,4}-{3180,3438,0,0,4}-" + }, + { + "npc_id": "46", + "loc_data": "{3179,3348,0,1,0}-{3178,3353,0,1,0}-{3235,3259,0,1,0}-{3238,3244,0,1,0}-{3241,3230,0,1,0}-{3241,3234,0,1,0}-{3243,3228,0,1,0}-{3247,3221,0,1,0}-{3248,3220,0,1,0}-{3259,3212,0,1,0}-{3211,3322,0,1,0}-{3214,3314,0,1,0}-{3214,3318,0,1,0}-{3216,3314,0,1,0}-{3218,3287,0,1,0}-{3223,3281,0,1,0}-{3231,3271,0,1,0}-{3233,3267,0,1,0}-{3233,3270,0,1,0}-{3235,3265,0,1,0}-{2979,3359,0,1,0}-{2979,3357,0,1,0}-{2993,3384,0,1,6}-{3015,3285,0,1,0}-" + }, + { + "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}-{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", + "loc_data": "{2817,2921,0,1,2}-{2775,2929,0,1,2}-{2789,2926,0,1,5}-{2795,2932,0,1,6}-{2775,2933,0,1,0}-{2769,2928,0,1,3}-" + }, + { + "npc_id": "49", + "loc_data": "{3133,3678,0,1,0}-{3127,3677,0,1,0}-{3127,3667,0,1,0}-{3117,3674,0,1,0}-{3121,3679,0,1,0}-{3104,3678,0,1,0}-{3110,3687,0,1,0}-{3116,3688,0,1,0}-{3103,3704,0,1,0}-{2871,9819,0,1,0}-{2871,9822,0,1,0}-{2870,9826,0,1,0}-{2869,9829,0,1,0}-{2867,9834,0,1,0}-{2867,9841,0,1,0}-{2864,9853,0,1,0}-{2859,9852,0,1,0}-{2851,9850,0,1,0}-{2856,9846,0,1,0}-{2857,9841,0,1,0}-{2861,9838,0,1,0}-{2854,9838,0,1,0}-{3178,3929,0,1,0}-{3193,3926,0,1,0}-{3195,3915,0,1,0}-{3186,3913,0,1,0}-{3175,3906,0,1,0}-{2735,9692,0,1,1}-{2732,9687,0,1,1}-{2733,9688,0,1,1}-{2734,9690,0,1,1}-{2733,9686,0,1,1}-{2732,9692,0,1,1}-{2736,9685,0,1,1}-{2730,9691,0,1,1}-" + }, + { + "npc_id": "50", + "loc_data": "{2273,4698,0,0,0}-" + }, + { + "npc_id": "52", + "loc_data": "{2898,9764,0,1,0}-{2892,9800,0,1,0}-{2912,9808,0,1,0}-" + }, + { + "npc_id": "53", + "loc_data": "{3212,3820,0,1,0}-{3222,3829,0,1,0}-{2694,9508,0,0,3}-{2703,9505,0,0,6}-{2710,9499,0,0,1}-{2704,9520,0,0,5}-{2714,9523,0,0,3}-{2722,9520,0,0,7}-" + }, + { + "npc_id": "54", + "loc_data": "{2834,9824,0,1,0}-" + }, + { + "npc_id": "55", + "loc_data": "{2897,9802,0,1,0}-{2909,9906,0,1,4}-" + }, + { + "npc_id": "58", + "loc_data": "{2574,9874,0,1,0}-{2577,9877,0,1,3}-{2574,9878,0,1,0}-{2575,9871,0,1,4}-{3289,5482,0,1,1}-{3296,5472,0,1,0}-{3291,5466,0,1,6}-{3294,5481,0,1,0}-{3300,5479,0,1,7}-{3288,5480,0,1,3}-{3293,5475,0,1,3}-{3293,5481,0,1,2}-{3286,5465,0,1,3}-{3279,5478,0,1,4}-{3270,5464,0,1,2}-{3270,5468,0,1,1}-" + }, + { + "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}-{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", + "loc_data": "{3189,3883,0,1,5}-{3182,3882,0,1,5}-{3177,3871,0,1,0}-{3176,3878,0,1,0}-{3173,3892,0,1,6}-{3171,3884,0,1,4}-{3166,3874,0,1,1}-{3169,3900,0,1,1}-{3165,3881,0,1,3}-{3164,3888,0,1,6}-{3163,3894,0,1,7}-{3162,3894,0,1,4}-{2448,2784,0,1,7}-{2477,2866,0,1,5}-{2455,2857,0,1,6}-{2434,2845,0,1,4}-{2460,2869,0,1,3}-{2484,2897,0,1,3}-{2471,2890,0,1,6}-{2491,2906,0,1,0}-{2544,2904,0,1,2}-{2547,2982,0,1,6}-{2548,2981,0,1,1}-{2523,2975,0,1,1}-" + }, + { + "npc_id": "61", + "loc_data": "{3111,9747,0,1,5}-{3108,9747,0,1,5}-{3156,3248,0,1,4}-{3143,3241,0,1,4}-{3156,3249,0,1,4}-{3175,3248,0,1,7}-{3169,3260,0,1,4}-{3171,3254,0,1,5}-{3162,3246,0,1,6}-{3179,3262,0,1,1}-{3162,3226,0,1,1}-{3166,3222,0,1,1}-{3157,3229,0,1,5}-{3168,3222,0,1,4}-{3182,3878,0,1,6}-{3182,3886,0,1,7}-{3182,3888,0,1,5}-{3175,3888,0,1,4}-{3175,3879,0,1,7}-{3174,3879,0,1,3}-{3174,3882,0,1,0}-{3175,3885,0,1,3}-{3172,3876,0,1,2}-{3172,3894,0,1,1}-{3172,3885,0,1,3}-{3165,3886,0,1,3}-{3165,3896,0,1,1}-{3166,3886,0,1,1}-{3164,3893,0,1,4}-{3163,3881,0,1,4}-{3159,3889,0,1,0}-{2893,9835,0,1,0}-{2895,9828,0,1,0}-{2891,9831,0,1,0}-{3211,9622,0,1,2}-{3215,9620,0,1,4}-{3212,9618,0,1,3}-{3217,9617,0,1,3}-{3214,9620,0,1,7}-{3213,9890,0,1,5}-{3210,9886,0,1,4}-{3213,9887,0,1,1}-{3207,9880,0,1,0}-{3255,9870,0,1,4}-{3258,9872,0,1,3}-{3261,9870,0,1,4}-{3327,3131,0,1,5}-{3321,3145,0,1,3}-{3322,3143,0,1,3}-{2541,3189,0,1,3}-{2542,3187,0,1,6}-{2543,3186,0,1,4}-{2545,3183,0,1,3}-{2545,3183,0,1,3}-{2545,3182,0,1,3}-{2543,3183,0,1,0}-{2543,3182,0,1,3}-{2539,3182,0,1,0}-" + }, + { + "npc_id": "62", + "loc_data": "{2850,3060,0,1,6}-{2857,3044,0,1,7}-{2856,3066,0,1,4}-{2860,3094,0,1,6}-{2854,3086,0,1,6}-{2863,3083,0,1,3}-{2853,3081,0,1,4}-{2859,3079,0,1,4}-{2859,3075,0,1,6}-{2871,3092,0,1,1}-{2867,3094,0,1,1}-{2861,3082,0,1,3}-{2861,3094,0,1,3}-{2863,3081,0,1,2}-{2864,3089,0,1,3}-{2859,3084,0,1,0}-{2885,2950,0,1,4}-{2891,2951,0,1,6}-{2894,2946,0,1,4}-{2928,3022,0,1,3}-{2930,3029,0,1,4}-{2927,3039,0,1,6}-{2928,3043,0,1,0}-{2929,3036,0,1,7}-{2919,3040,0,1,4}-{2934,3045,0,1,2}-{2929,3035,0,1,4}-{2914,3055,0,1,4}-{2922,3059,0,1,3}-{2930,3049,0,1,4}-{2914,3047,0,1,4}-{2935,3049,0,1,1}-{2927,3058,0,1,3}-{2811,2975,0,1,7}-{2815,2987,0,1,5}-{2808,3007,0,1,6}-{2812,3007,0,1,1}-{2784,3030,0,1,4}-{2783,3017,0,1,3}-{2776,3107,0,1,1}-{2794,3121,0,1,3}-{2795,3118,0,1,1}-{2796,3109,0,1,1}-{2808,3112,0,1,6}-{2791,3124,0,1,1}-{2769,3198,0,1,7}-{2768,3206,0,1,1}-" + }, + { + "npc_id": "63", + "loc_data": "{3074,3892,0,1,3}-{2841,9583,0,1,4}-{2841,9582,0,1,4}-{2839,9582,0,1,1}-{2837,9572,0,1,6}-{2832,9580,0,1,1}-{3128,9954,0,1,0}-{3127,9956,0,1,0}-{3125,9958,0,1,0}-{3117,9956,0,1,0}-{3122,9956,0,1,0}-{3120,9952,0,1,0}-{3123,9951,0,1,0}-{3125,9948,0,1,0}-{3118,9950,0,1,0}-{3165,9891,0,1,6}-{3177,9886,0,1,4}-{3175,9889,0,1,4}-{3177,9879,0,1,4}-{3177,9888,0,1,1}-{3178,9895,0,1,4}-{3182,9884,0,1,3}-{3164,9888,0,1,3}-{3169,9892,0,1,6}-{3213,3728,0,1,0}-{3211,3734,0,1,0}-{3214,3738,0,1,0}-{3215,3748,0,1,0}-{3218,3746,0,1,0}-{3226,3750,0,1,0}-{3239,3749,0,1,0}-{3247,3748,0,1,0}-{3248,3746,0,1,0}-{3244,3743,0,1,0}-{3245,3737,0,1,0}-{3258,5560,0,1,2}-{3258,5555,0,1,3}-{3260,5557,0,1,3}-{3257,5552,0,1,1}-{3260,5558,0,1,5}-{3258,5556,0,1,6}-{3257,5555,0,1,6}-{3260,5559,0,1,7}-{3257,5561,0,1,1}-{3071,3894,0,1,7}-{3062,3897,0,1,3}-{3058,3889,0,1,4}-{3054,3880,0,1,2}-{3054,3895,0,1,7}-{3051,3879,0,1,6}-{3051,3886,0,1,6}-{3047,3893,0,1,1}-{3047,3881,0,1,4}-{3044,3893,0,1,5}-{3041,3894,0,1,1}-{3038,3890,0,1,1}-{3046,3872,0,1,4}-" + }, + { + "npc_id": "64", + "loc_data": "{2869,9912,0,1,0}-{2819,9950,0,1,0}-{2824,9934,0,1,0}-{2827,9925,0,1,0}-{2828,9956,0,1,0}-{2857,9969,0,1,0}-{2872,9972,0,1,0}-{2684,9798,0,1,2}-{2885,9962,0,1,0}-{2887,9936,0,1,0}-{2959,3917,0,1,0}-{2960,3925,0,1,0}-{2961,3920,0,1,0}-{2962,3915,0,1,0}-{2962,3933,0,1,0}-{2963,3923,0,1,0}-{2963,3929,0,1,0}-{2965,3927,0,1,0}-{2965,3931,0,1,0}-{2691,9814,0,1,5}-{2694,9819,0,1,6}-{2696,9831,0,1,3}-{2702,9837,0,1,6}-{2709,9844,0,1,3}-{2720,9847,0,1,4}-{2724,9843,0,1,6}-{2733,9846,0,1,4}-{2740,9841,0,1,3}-{2745,9836,0,1,4}-{2745,9828,0,1,5}-{2747,9826,0,1,1}-" + }, + { + "npc_id": "66", + "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": "{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": "{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", + "loc_data": "{3123,9657,0,1,2}-{3087,9672,0,1,1}-{3103,9672,0,1,1}-{3119,9670,0,1,1}-{3251,9896,0,1,6}-{3268,9893,0,1,3}-" + }, + { + "npc_id": "74", + "loc_data": "{3124,9660,0,1,3}-{3122,9656,0,1,4}-{3117,9646,0,1,1}-{3124,9651,0,1,2}-{3117,9645,0,1,3}-{3082,9671,0,1,3}-{3105,9672,0,1,1}-{3113,9673,0,1,3}-{3094,9671,0,1,3}-{2843,9767,0,1,3}-{2845,9762,0,1,7}-{2842,9759,0,1,6}-{3126,9865,0,1,7}-{3129,9859,0,1,3}-{3121,9863,0,1,3}-{3126,9862,0,1,5}-{3148,9899,0,1,3}-{3147,9905,0,1,6}-{3141,9901,0,1,5}-{3142,9891,0,1,6}-{3139,9886,0,1,2}-{3148,9883,0,1,1}-{3151,9889,0,1,3}-" + }, + { + "npc_id": "75", + "loc_data": "{2588,9493,0,1,3}-{2584,9491,0,1,3}-{2589,9492,0,1,4}-{2591,9492,0,1,6}-{2593,9491,0,1,6}-{2588,9491,0,1,3}-{2839,9774,0,1,4}-{2931,9641,0,1,6}-{2931,9643,0,1,6}-{3226,9909,0,1,4}-{3225,9905,0,1,0}-{3234,9907,0,1,4}-" + }, + { + "npc_id": "76", + "loc_data": "{2843,9774,0,1,6}-{2842,9769,0,1,4}-{2842,9764,0,1,7}-{2845,9754,0,1,4}-{2849,9751,0,1,1}-{2850,9747,0,1,3}-{2844,9758,0,1,7}-{2540,9814,0,1,6}-{2547,9817,0,1,6}-" + }, + { + "npc_id": "78", + "loc_data": "{2585,3479,0,1,1}-{2589,3480,0,1,2}-{2582,3476,0,1,3}-{2588,3485,0,1,4}-{2580,3490,0,1,5}-{2577,3482,0,1,6}-{2583,3484,0,1,7}-{3073,3961,0,1,0}-{3074,3952,0,1,0}-{3075,3955,0,1,0}-{3076,3961,0,1,0}-{3077,3949,0,1,0}-{3079,3957,0,1,0}-{3080,3961,0,1,0}-{2568,9528,0,1,5}-{2573,9530,0,1,3}-{2575,9525,0,1,6}-{2571,9528,0,1,7}-{2862,9569,0,1,4}-{2857,9571,0,1,7}-{2837,9561,0,1,1}-{2608,9818,0,1,7}-{2609,9825,0,1,5}-{2607,9830,0,1,1}-{2605,9837,0,1,1}-{2607,9833,0,1,6}-{2905,9832,0,1,0}-{2907,9831,0,1,0}-{2913,9828,0,1,0}-{2921,9826,0,1,0}-{2924,9831,0,1,0}-{2921,9834,0,1,0}-{2918,9831,0,1,0}-{2908,9838,0,1,0}-{2913,9841,0,1,0}-{2672,9805,0,1,3}-{2663,9804,0,1,4}-{2658,9809,0,1,5}-{3725,9369,0,1,1}-{3737,9390,0,1,4}-{3738,9399,0,1,0}-{3740,9407,0,1,1}-{3765,9407,0,1,1}-{3762,9395,0,1,3}-{3725,9390,0,1,3}-{3753,9387,0,1,0}-{3728,9382,0,1,3}-{3729,9360,0,1,6}-{3755,9424,0,1,0}-{3760,9440,0,1,4}-{3737,9425,0,1,7}-{3720,9430,0,1,4}-{3736,9451,0,1,3}-{3728,9463,0,1,2}-{3740,9462,0,1,4}-{3770,9463,0,1,1}-{3752,9450,0,1,6}-{3762,9432,0,1,1}-{3733,9421,0,1,7}-{2759,3402,0,1,2}-{2756,3400,0,1,0}-{3787,9459,0,1,1}-{3803,9447,0,1,4}-{3778,9438,0,1,1}-{3786,9422,0,1,4}-{3800,9419,0,1,3}-{3797,9427,0,1,4}-{3820,9432,0,1,5}-{3825,9414,0,1,3}-{3833,9415,0,1,0}-{3825,9449,0,1,3}-{3827,9459,0,1,1}-{3818,9466,0,1,4}-{3799,9460,0,1,1}-{3794,9451,0,1,1}-{2543,9564,0,1,1}-{2543,9562,0,1,1}-{2542,9557,0,1,1}-{2542,9569,0,1,4}-{2540,9566,0,1,0}-{2549,9568,0,1,6}-{2538,9568,0,1,3}-" + }, + { + "npc_id": "80", + "loc_data": "{2619,3265,0,1,0}-{2619,3265,0,1,3}-{2615,3267,0,1,4}-" + }, + { + "npc_id": "81", + "loc_data": "{2589,3120,0,1,4}-{2605,3116,0,1,5}-{2923,3277,0,1,1}-{2921,3289,0,1,1}-{2923,3280,0,1,6}-{3255,3255,0,1,3}-{3255,3256,0,1,4}-{3259,3261,0,1,1}-{3263,3283,0,1,6}-{3257,3268,0,1,4}-{2436,4443,0,1,3}-{2441,4449,0,1,6}-{3038,3310,0,1,5}-{3028,3312,0,1,6}-{3028,3299,0,1,6}-{3042,3300,0,1,6}-" + }, + { + "npc_id": "82", + "loc_data": "{3110,3157,2,1,5}-{3096,3703,0,1,0}-{2841,9557,0,1,3}-{2831,9562,0,1,3}-{2838,9605,0,1,3}-{2936,9652,0,1,3}-{2926,9803,0,1,0}-{2935,9794,0,1,0}-{3052,3117,2,1,1}-{3311,3845,0,1,0}-" + }, + { + "npc_id": "83", + "loc_data": "{3169,3711,0,1,7}-{2639,9477,2,0,3}-{2635,9497,2,0,2}-" + }, + { + "npc_id": "84", + "loc_data": "{2875,9776,0,1,0}-{2862,9776,0,1,0}-{2860,9762,0,1,0}-{2854,9780,0,1,0}-{2866,9781,0,1,0}-{2700,9481,0,0,5}-{2729,9483,0,0,5}-" + }, + { + "npc_id": "86", + "loc_data": "{3105,9517,0,1,7}-{3101,9517,0,1,6}-{3102,9521,0,1,4}-{3106,9519,0,1,1}-{3104,9514,0,1,4}-{3104,9518,0,1,1}-{3107,9519,0,1,1}-{3109,9516,0,1,1}-{3107,9515,0,1,7}-{3105,9512,0,1,6}-{3102,9513,0,1,3}-{3100,9514,0,1,1}-{3100,9516,0,1,7}-{3099,9521,0,1,0}-{3103,9523,0,1,1}-{3098,9880,0,1,5}-{3094,9883,0,1,1}-{3119,9891,0,1,4}-{3188,3181,0,1,7}-{3181,3191,0,1,7}-{3185,3199,0,1,4}-{3176,3198,0,1,6}-{3168,3188,0,1,4}-{3172,3178,0,1,1}-{3179,3174,0,1,1}-{3189,3168,0,1,5}-{3196,3208,0,1,1}-{3196,3205,0,1,0}-{3195,3212,0,1,0}-{2632,9696,0,1,1}-{2639,9697,0,1,3}-{2646,9696,0,1,3}-{2650,9697,0,1,4}-{2646,9702,0,1,6}-{2647,9713,0,1,3}-{2641,9719,0,1,1}-{2650,9716,0,1,1}-{3212,3191,0,1,6}-{3207,3192,0,1,2}-{3216,3184,0,1,2}-{3224,3174,0,1,4}-{3229,3183,0,1,5}-{3222,3185,0,1,3}-{3216,3177,0,1,4}-{3200,3182,0,1,5}-{3202,3189,0,1,6}-{2977,3700,0,1,0}-{2967,3694,0,1,0}-{2973,3694,0,1,0}-{3495,9812,0,1,1}-{3502,9807,0,0,4}-{3249,9868,0,1,4}-{3237,9868,0,1,3}-{3221,9873,0,1,6}-{3225,9862,0,1,2}-{3237,9866,0,1,6}-{3232,9867,0,1,7}-{3219,9862,0,1,7}-{3227,9875,0,1,4}-{2518,3187,0,1,3}-{3291,3388,0,1,6}-{3049,3691,0,1,0}-{3276,9870,0,1,3}-" + }, + { + "npc_id": "87", + "loc_data": "{3103,9881,0,1,3}-{3122,9889,0,1,2}-{3117,9890,0,1,2}-{3242,3530,0,1,7}-{3256,3541,0,1,7}-{3248,3553,0,1,7}-{2520,3188,0,1,3}-{3293,3382,0,1,4}-" + }, + { + "npc_id": "88", + "loc_data": "{2573,9630,0,1,0}-{2601,9802,0,1,6}-{2594,9805,0,1,1}-{2587,9806,0,1,2}-{2585,9803,0,1,1}-{2584,9801,0,1,5}-" + }, + { + "npc_id": "89", + "loc_data": "{2575,3058,0,0,6}-{3080,3451,0,1,1}-{3096,3460,0,1,3}-{3142,3211,0,1,6}-{2629,3266,0,1,2}-{2634,3265,0,1,1}-{2557,3065,0,0,1}-{3294,3344,0,1,3}-{3289,3349,0,1,6}-{3286,3345,0,1,1}-{2783,3463,0,1,7}-" + }, + { + "npc_id": "90", + "loc_data": "{3110,3367,0,0,3}-{3116,3534,0,0,4}-{3072,3531,0,1,0}-{3112,3538,0,1,0}-{2852,9577,0,1,1}-{2865,9567,0,1,3}-{3373,9748,0,1,0}-{3378,9748,0,1,0}-{3382,9753,0,1,0}-{3373,9812,0,1,0}-{3378,9812,0,1,0}-{3382,9817,0,1,0}-{3110,9909,0,1,4}-{3121,9910,0,1,4}-{3133,9909,0,1,5}-{3133,9904,0,1,6}-{3129,9916,0,1,3}-{3133,9916,0,1,0}-{2925,3254,2,0,3}-{2924,3252,2,1,3}-{2926,3255,2,1,4}-{2932,3255,2,1,3}-{2931,3251,2,1,4}-{3194,5448,0,1,3}-{3183,5447,0,1,3}-{3177,5457,0,1,4}-{3187,5456,0,1,1}-{2885,9812,0,1,0}-{2883,9836,0,1,0}-{3141,9875,0,1,6}-{3208,9905,0,1,4}-{3011,3590,0,1,0}-{3016,3596,0,1,0}-{3022,3586,0,1,0}-" + }, + { + "npc_id": "91", + "loc_data": "{3099,9910,0,1,3}-{3100,9906,0,1,6}-{3194,5454,0,1,4}-{3212,9906,0,1,6}-{3252,9916,0,1,3}-{3259,9915,0,1,3}-{3272,9913,0,1,6}-{3274,9914,0,1,1}-{3278,9908,0,1,1}-{3275,9911,0,1,5}-" + }, + { + "npc_id": "92", + "loc_data": "{2848,3248,0,1,3}-{2850,3235,0,1,3}-{2819,3288,0,1,1}-{3111,3561,0,1,3}-{2840,9652,0,1,1}-{2829,9657,0,1,4}-{3122,9661,0,1,6}-{3123,9648,0,1,6}-{3117,9671,0,1,1}-{2593,9883,0,1,1}-{2590,9885,0,1,4}-{2586,9883,0,1,1}-{2585,9878,0,1,3}-{3132,9915,0,1,5}-{3095,9909,0,1,3}-{3143,3802,0,1,0}-{3145,3779,0,1,0}-{3149,9869,0,1,6}-{3138,9878,0,1,5}-{3137,9869,0,1,3}-{2664,9877,0,1,0}-{2980,3763,0,1,0}-{2977,3753,0,1,0}-{2992,3942,0,1,4}-{3005,10362,0,1,1}-{3003,10358,0,1,1}-{3005,10351,0,1,1}-{3003,10345,0,1,5}-{2537,9845,0,1,1}-{2540,9846,0,1,4}-{2552,9844,0,1,6}-{2556,9846,0,1,4}-{2535,9844,0,1,3}-{2532,9843,0,1,4}-{2530,9845,0,1,3}-{2540,9820,0,1,4}-{2544,9824,0,1,4}-" + }, + { + "npc_id": "93", + "loc_data": "{2566,9507,0,1,3}-{2562,9505,0,1,1}-{2568,9509,0,1,3}-{2571,9509,0,1,1}-{2564,9504,0,1,1}-{2566,9505,0,1,1}-{2841,9625,0,1,4}-{2845,9628,0,1,3}-{2841,9638,0,1,6}-{2845,9645,0,1,1}-{3101,9956,0,1,3}-{3104,9948,0,1,0}-{3104,9955,0,1,4}-{3108,9951,0,1,4}-{3108,9954,0,1,1}-{3111,9954,0,1,6}-{3112,9958,0,1,4}-{3194,5460,0,1,6}-{3192,5466,0,1,7}-{3185,5493,0,1,4}-{3182,5494,0,1,6}-{3185,5486,0,1,7}-{3175,5492,0,1,3}-{3169,5496,0,1,4}-{3170,5492,0,1,3}-{3186,5487,0,1,4}-{3169,5491,0,1,2}-{2655,9824,0,1,1}-{2669,9824,0,1,4}-{2667,9823,0,1,5}-{2658,9830,0,1,6}-{2666,9829,0,1,6}-{2637,9891,0,1,0}-{2645,9891,0,1,0}-{2653,9892,0,1,0}-{2653,9892,0,1,0}-{2544,9841,0,1,1}-{2536,9844,0,1,6}-{2527,9844,0,1,3}-{2530,9842,0,1,4}-{2543,9815,0,1,3}-" + }, + { + "npc_id": "94", + "loc_data": "{2589,9881,0,1,6}-{2591,9882,0,1,4}-" + }, + { + "npc_id": "95", + "loc_data": "{2602,2955,0,1,0}-{2605,2963,0,1,0}-{2607,2967,0,1,0}-{2610,2958,0,1,0}-{2610,2961,0,1,0}-{2610,2965,0,1,0}-{2616,3283,0,1,0}-{2620,3283,0,1,0}-{2379,3344,0,1,0}-{2395,3340,0,1,0}-{2398,3330,0,1,0}-{2406,3329,0,1,0}-{2647,3584,0,1,0}-{2680,3585,0,1,0}-{2682,3592,0,1,0}-{2482,2923,0,1,0}-{2462,3274,0,1,0}-{2465,3272,0,1,0}-{2465,3275,0,1,0}-{2470,3272,0,1,0}-{2472,3274,0,1,0}-{2477,3275,0,1,0}-{2437,3336,0,1,0}-{2439,3333,0,1,0}-{2439,3335,0,1,0}-{2440,3333,0,1,0}-{2440,3335,0,1,0}-{2706,3580,0,1,0}-{2717,3569,0,1,0}-{3207,3919,0,1,0}-{3218,3924,0,1,0}-{3229,3917,0,1,0}-{3241,3921,0,1,0}-{2496,2959,0,1,0}-{2498,2968,0,1,0}-{2500,2959,0,1,0}-{2502,2969,0,1,0}-{2509,2964,0,1,0}-{2516,3197,0,1,0}-{2548,3179,0,1,0}-" + }, + { + "npc_id": "96", + "loc_data": "{2842,3450,0,1,0}-{2864,3453,0,1,0}-{2870,3438,0,1,0}-{2872,3445,0,1,0}-{2846,3477,0,1,0}-{2847,3474,0,1,0}-{2860,3492,0,1,0}-{2866,3498,0,1,0}-" + }, + { + "npc_id": "97", + "loc_data": "{2831,3515,0,1,0}-{2833,3513,0,1,0}-{2848,3487,0,1,0}-{2850,3484,0,1,0}-{2854,3509,0,1,0}-{2856,3508,0,1,0}-{2988,3921,0,1,0}-{2991,3924,0,1,0}-{2992,3917,0,1,0}-{2994,3921,0,1,0}-{3002,3924,0,1,0}-{3004,3917,0,1,0}-{3005,3926,0,1,0}-{3006,3921,0,1,0}-" + }, + { + "npc_id": "98", + "loc_data": "{3014,3294,0,1,1}-" + }, + { + "npc_id": "99", + "loc_data": "{2624,3319,0,1,1}-{2624,3327,0,1,6}-{2642,3314,0,1,4}-{2646,3325,0,1,1}-{2646,3504,0,1,4}-{2634,3476,0,1,5}-{2651,3477,0,1,5}-{2665,3472,0,1,5}-{2662,3488,0,1,5}-{2672,3505,0,1,5}-{2790,3199,0,1,2}-{2784,3188,0,1,1}-{2779,3207,0,1,1}-" + }, + { + "npc_id": "100", + "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}-{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", + "loc_data": "{2622,3390,0,1,7}-{2622,3390,0,1,7}-{2562,9657,0,1,0}-{2571,9653,0,1,0}-" + }, + { + "npc_id": "103", + "loc_data": "{2928,3251,1,1,3}-{2928,3244,1,1,4}-{2924,3253,1,1,3}-{2927,3252,1,1,4}-{2927,3254,1,1,3}-{2888,9850,0,1,0}-{2901,9848,0,1,0}-{2909,9848,0,1,0}-{2919,9848,0,1,0}-{2937,9837,0,1,0}-{2906,9818,0,1,0}-{2920,9818,0,1,0}-{2968,3761,0,1,0}-{2985,3761,0,1,0}-{2962,3755,0,1,0}-{2990,3755,0,1,0}-{2968,3749,0,1,0}-{2985,3749,0,1,0}-{2713,4892,0,1,3}-{2711,4899,0,1,3}-{2732,4901,0,1,3}-{2729,4893,0,1,3}-{2713,4907,0,1,3}-{3241,9907,0,1,4}-{3243,9914,0,1,1}-{3242,9913,0,1,6}-{3242,9916,0,1,5}-{3239,9914,0,1,1}-{2501,3289,0,1,1}-{3272,3664,0,1,1}-" + }, + { + "npc_id": "104", + "loc_data": "{3093,3357,1,1,6}-{2894,9849,0,1,0}-{2905,9851,0,1,0}-{2915,9852,0,1,0}-{2934,9837,0,1,0}-{2935,9828,0,1,0}-{2912,9820,0,1,0}-{2900,9819,0,1,0}-" + }, + { + "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}-{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", + "loc_data": "{2959,3445,0,1,3}-{2968,3480,0,1,6}-{2968,3490,0,1,3}-{2978,3507,0,1,1}-{2807,3377,0,1,6}-{3289,3353,0,1,3}-" + }, + { + "npc_id": "107", + "loc_data": "{2870,3166,0,1,3}-{2868,3159,0,1,4}-{2867,3172,0,1,5}-{2858,3167,0,1,3}-{2846,3153,0,1,3}-{2851,3149,0,1,1}-{2602,3267,0,1,6}-{2603,3270,0,1,4}-{2607,3268,0,1,5}-{2645,9823,0,1,1}-{2641,9819,0,1,7}-{2641,9817,0,1,2}-{2642,9813,0,1,5}-{2639,9809,0,1,2}-{3256,3954,0,1,1}-{3249,3949,0,1,1}-{3246,3945,0,1,1}-{3239,3943,0,1,1}-{3231,3940,0,1,1}-{3225,3943,0,1,1}-{3234,3945,0,1,1}-{3253,9909,0,1,4}-{2787,2943,0,1,3}-{2789,2949,0,1,3}-{2781,2949,0,1,3}-{2813,3112,0,1,4}-{3275,3154,0,1,6}-{3300,3312,0,1,1}-{3300,3305,0,1,3}-{3298,3300,0,1,5}-{3298,3294,0,1,0}-{3299,3290,0,1,6}-{3025,3568,0,1,0}-{3028,3577,0,1,0}-{3032,3570,0,1,0}-{3040,3581,0,1,0}-{3041,3572,0,1,0}-{3047,3567,0,1,0}-{3055,3574,0,1,0}-{3056,3566,0,1,0}-{3062,3555,0,1,0}-{3062,3570,0,1,0}-{3055,9776,0,1,4}-{3039,9769,0,1,6}-{3049,9764,0,1,3}-{3050,9770,0,1,1}-{3039,9765,0,1,1}-{3048,9762,0,1,4}-{3044,9760,0,1,6}-{3048,9779,0,1,1}-{3038,9802,0,1,5}-{3041,9804,0,1,6}-{3040,9795,0,1,7}-" + }, + { + "npc_id": "108", + "loc_data": "{2941,9779,0,1,0}-{2934,9776,0,1,0}-{2934,9768,0,1,0}-{2934,9757,0,1,0}-{2930,9752,0,1,0}-{2732,3225,0,1,0}-{2729,3224,0,1,0}-{2719,3223,0,1,0}-{2722,3220,0,1,0}-{2724,3216,0,1,0}-{2721,3213,0,1,0}-{2713,3217,0,1,0}-" + }, + { + "npc_id": "109", + "loc_data": "{2601,3269,0,0,0}-{2604,3272,0,0,0}-{2605,3268,0,0,0}-{2606,3271,0,0,0}-" + }, + { + "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}-{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", + "loc_data": "{2920,3800,0,1,3}-{2883,9932,0,1,0}-{2883,9965,0,1,0}-{2952,3902,0,1,6}-{2953,3889,0,1,6}-{2947,3921,0,1,6}-{2811,3506,0,1,0}-{3043,9581,0,1,1}-{3055,9577,0,1,6}-{3061,9576,0,1,4}-{3055,9571,0,1,7}-{3052,9566,0,1,6}-" + }, + { + "npc_id": "112", + "loc_data": "{2825,3249,0,1,6}-{2560,3406,0,1,3}-{3144,3822,0,1,0}-{3165,9880,0,1,3}-{3168,9880,0,1,1}-{3159,9903,0,1,4}-{3156,9907,0,1,0}-{2699,3206,0,1,1}-{3202,5490,0,1,4}-{3237,5555,0,1,5}-{2554,3411,0,1,3}-{2554,3400,0,1,3}-{2551,3401,0,1,4}-{2543,9823,0,1,3}-{2540,9819,0,1,5}-{2543,9817,0,1,4}-" + }, + { + "npc_id": "113", + "loc_data": "{2622,3272,0,0,0}-{2835,9526,0,1,4}-{2832,9514,0,1,4}-{2825,9521,0,1,5}-{2846,9520,0,1,3}-{2838,9508,0,1,0}-{2832,9495,0,1,6}-{2838,9492,0,1,6}-{2851,9485,0,1,3}-{2847,9481,0,1,3}-{2865,9494,0,1,3}-{2855,9502,0,1,2}-{2870,9496,0,1,1}-{2851,9511,0,1,2}-{2853,9519,0,1,3}-{2857,9520,0,1,6}-{2863,9525,0,1,1}-{2869,9530,0,1,5}-{2865,9515,0,1,4}-{2868,9507,0,1,3}-{2874,9512,0,1,6}-{2896,2946,0,0,0}-{2918,2961,0,0,0}-{2926,2986,0,0,0}-{2930,3044,0,0,0}-{2926,3056,0,0,0}-{2916,3065,0,0,0}-" + }, + { + "npc_id": "115", + "loc_data": "{2571,3027,0,1,0}-{2572,3031,0,1,0}-{2575,3024,0,1,0}-{2578,3031,0,1,0}-{2510,3044,0,1,0}-{2520,3046,0,1,0}-{2526,3039,0,1,0}-{2502,3114,0,1,0}-{2505,3111,0,1,0}-{2507,3108,0,1,0}-{2508,3119,0,1,0}-{2509,3087,0,1,0}-{2510,3084,0,1,0}-{2513,3087,0,1,0}-{2514,3081,0,1,0}-" + }, + { + "npc_id": "116", + "loc_data": "{2622,3277,0,1,5}-" + }, + { + "npc_id": "117", + "loc_data": "{3118,9845,0,1,4}-{3111,9844,0,1,6}-{3123,9845,0,1,3}-{3114,9833,0,1,3}-{3110,9841,0,1,1}-{3119,9839,0,1,1}-{3097,9832,0,1,0}-{3101,9832,0,1,1}-{3107,9829,0,1,3}-{3115,9831,0,1,4}-{3109,9835,0,1,4}-{2369,3404,0,1,0}-{2904,9734,0,1,0}-{2548,3146,0,1,1}-{2542,3145,0,1,4}-{2503,3150,0,1,4}-{3300,3649,0,1,0}-{3044,10321,0,1,2}-{3044,10316,0,1,4}-{3045,10308,0,1,4}-{3048,10317,0,1,2}-" + }, + { + "npc_id": "118", + "loc_data": "{2603,3056,0,1,0}-{2598,3064,0,1,3}-{2589,3068,0,1,5}-{2930,9703,0,1,0}-{2933,9702,0,1,0}-{3000,9842,0,1,2}-{2996,9844,0,1,4}-{3003,9828,0,1,1}-{2995,9811,0,1,3}-{3002,9811,0,1,3}-{2994,9809,0,1,5}-{3004,9813,0,1,6}-{2998,9828,0,1,4}-{3004,9799,0,1,4}-{2997,9809,0,1,0}-{2983,9807,0,1,6}-{2964,9811,0,1,1}-{3035,3443,0,1,6}-{3008,3449,0,1,4}-{3042,3465,0,1,1}-{3042,3468,0,1,3}-{3019,9813,0,1,4}-{3012,9812,0,1,3}-{3010,9811,0,1,3}-{3025,9801,0,1,4}-{3026,9824,0,1,1}-{3018,9819,0,1,6}-{3028,9815,0,1,3}-{3024,9811,0,1,4}-{3021,9826,0,1,5}-{3022,9832,0,1,1}-{3023,9833,0,1,6}-{3027,9833,0,1,3}-{3028,9828,0,1,6}-{3023,9814,0,1,6}-{3042,9831,0,1,4}-{3024,9824,0,1,6}-{3019,9847,0,1,0}-{3034,9847,0,1,7}-{3021,9851,0,1,5}-{3043,9824,0,1,3}-{3038,9821,0,1,1}-{3044,9818,0,1,4}-{3047,9814,0,1,4}-{3037,9814,0,1,6}-" + }, + { + "npc_id": "119", + "loc_data": "{2921,9757,0,1,0}-{2915,9759,0,1,0}-{2927,9761,0,1,0}-{2925,9769,0,1,0}-{2931,9784,0,1,0}-{2938,9788,0,1,0}-{3233,3783,0,1,0}-{3241,3796,0,1,0}-{3253,3782,0,1,0}-{3019,3760,0,1,0}-{3019,3766,0,1,0}-{3021,3755,0,1,0}-{3025,3763,0,1,0}-{3029,10313,0,1,4}-{3028,10309,0,1,4}-{3033,10311,0,1,6}-{3035,10309,0,1,1}-{3035,10313,0,1,1}-" + }, + { + "npc_id": "120", + "loc_data": "{2874,9880,0,1,0}-{2861,9874,0,1,0}-{2863,9874,0,1,0}-{2866,9874,0,1,0}-{2865,9877,0,1,0}-{2864,9877,0,1,0}-{2861,9877,0,1,0}-{2867,9877,0,1,0}-{2867,9879,0,1,0}-" + }, + { + "npc_id": "122", + "loc_data": "{2566,9632,0,1,0}-{2573,9626,0,1,0}-{2996,9574,0,1,6}-{3007,9579,0,1,7}-{3001,9580,0,1,4}-{2507,3726,0,1,2}-{2523,3724,0,1,3}-{2535,9556,0,1,4}-{3018,9580,0,1,3}-{3021,9585,0,1,4}-{3024,9582,0,1,4}-" + }, + { + "npc_id": "123", + "loc_data": "{2675,3731,0,1,6}-{2669,3726,0,1,6}-{2655,3727,0,1,6}-{2661,3732,0,1,6}-{2656,3720,0,1,6}-{3014,3787,0,1,0}-{3015,3813,0,1,0}-{3017,3781,0,1,0}-{3017,3793,0,1,0}-{3017,3803,0,1,0}-{3019,3789,0,1,0}-{3019,3798,0,1,0}-{3020,3809,0,1,0}-{3022,3805,0,1,0}-{3024,3781,0,1,0}-{3024,3796,0,1,0}-{3024,3812,0,1,0}-{3026,3791,0,1,0}-{3027,3797,0,1,0}-{3027,3804,0,1,0}-{3028,3809,0,1,0}-{3031,3795,0,1,0}-{3031,3802,0,1,0}-{3033,3785,0,1,0}-{3033,3811,0,1,0}-{3035,3806,0,1,0}-{3036,3795,0,1,0}-{3036,3800,0,1,0}-{3038,3809,0,1,0}-{3044,3802,0,1,0}-{3046,3797,0,1,0}-{2533,9554,0,1,4}-{2531,9556,0,1,5}-{2522,9562,0,1,4}-{2524,9558,0,1,4}-{2518,9562,0,1,6}-{2515,9566,0,1,3}-{2517,9573,0,1,1}-" + }, + { + "npc_id": "124", + "loc_data": "{3124,9974,0,1,0}-{3119,9976,0,1,0}-{3116,9974,0,1,0}-{3118,9971,0,1,0}-{3121,9997,0,1,0}-{3124,9996,0,1,0}-{3125,9993,0,1,0}-{3121,9993,0,1,0}-{3115,9992,0,1,0}-{3118,9990,0,1,0}-{3122,9989,0,1,0}-{3142,5532,0,1,6}-{3141,5531,0,1,6}-{3139,5526,0,1,3}-{3143,5524,0,1,3}-{3148,5523,0,1,4}-{3148,5531,0,1,4}-" + }, + { + "npc_id": "125", + "loc_data": "{2845,3517,0,1,4}-{2848,3515,0,1,4}-{2822,9901,0,1,0}-{2836,9905,0,1,0}-{2838,9917,0,1,0}-{2847,9919,0,1,0}-{2848,9912,0,1,0}-{2834,9940,0,1,0}-{2836,9953,0,1,0}-{2844,9944,0,1,0}-{2958,3867,0,1,3}-{2956,3857,0,1,6}-{2952,3862,0,1,3}-{2949,3858,0,1,4}-{2955,3875,0,1,7}-{2954,3874,0,1,5}-{2962,3876,0,1,1}-{2961,3877,0,1,3}-{2947,3878,0,1,6}-{2959,3884,0,1,4}-{2956,3885,0,1,3}-{2956,3886,0,1,3}-{2948,3886,0,1,1}-{2947,3934,0,1,0}-{2948,3917,0,1,0}-{2949,3926,0,1,0}-{2952,3913,0,1,0}-{2952,3936,0,1,0}-{2954,3921,0,1,0}-{2956,3930,0,1,0}-{2964,3944,0,1,0}-{2970,3947,0,1,0}-{2971,3938,0,1,0}-{2977,3953,0,1,0}-{2978,3942,0,1,0}-{2984,3933,0,1,0}-{3227,5443,0,1,5}-{3220,5448,0,1,5}-{3208,5443,0,1,6}-{3207,5448,0,1,4}-{3050,9570,0,1,3}-{3043,9579,0,1,2}-{3056,9585,0,1,0}-{3058,9575,0,1,1}-{3052,9582,0,1,0}-{3049,9577,0,1,4}-{3042,9586,0,1,6}-{3052,9588,0,1,1}-{3049,9590,0,1,5}-{3060,9578,0,1,3}-{3054,9566,0,1,7}-" + }, + { + "npc_id": "126", + "loc_data": "{2381,4422,0,1,5}-{2381,4425,0,1,4}-{2382,4429,0,1,0}-{2385,4419,0,1,0}-{2388,4421,0,1,1}-{2388,4428,0,1,4}-" + }, + { + "npc_id": "127", + "loc_data": "{3193,3959,0,1,0}-{3188,3960,0,1,0}-{3190,3962,0,1,0}-{3187,3959,0,1,0}-{3192,3961,0,1,0}-{3187,3960,0,1,0}-{3193,3959,0,1,0}-{3187,3961,0,1,0}-{3193,3958,0,1,0}-{2956,9791,0,1,0}-{2966,9788,0,1,0}-{2959,9783,0,1,0}-{2964,9775,0,1,0}-{2954,9776,0,1,0}-{2955,9795,0,1,0}-{2962,9792,0,1,0}-" + }, + { + "npc_id": "128", + "loc_data": "{2844,3040,0,1,0}-{2840,3049,0,1,0}-{2837,3043,0,1,0}-{2834,3042,0,1,0}-{2827,3026,0,1,0}-{2824,3018,0,1,0}-{2830,3021,0,1,0}-{2832,3018,0,1,0}-{2824,3084,0,1,0}-{2821,3078,0,1,0}-{2817,3078,0,1,0}-{2837,3076,0,1,0}-{2877,3154,0,1,0}-{2878,3153,0,1,0}-{2870,3161,0,1,0}-{2822,3192,0,1,5}-{2610,3274,0,1,0}-{2610,3275,0,1,0}-{2615,3276,0,1,0}-{2616,3275,0,1,0}-{2616,3276,0,1,0}-{2925,2969,0,1,0}-{2939,2977,0,1,0}-{2936,2980,0,1,0}-{2933,2976,0,1,0}-{2930,2971,0,1,0}-{2928,2984,0,1,0}-{2942,2983,0,1,0}-{2936,2989,0,1,0}-{2887,3145,0,1,0}-{2741,3173,0,0,0}-{2746,3167,0,0,0}-{2746,3147,0,1,0}-{2733,3162,0,1,0}-{2730,3146,0,1,0}-{2782,3104,0,1,0}-{2782,3103,0,1,0}-{2777,3090,0,1,0}-{2773,3089,0,1,0}-{2803,3115,0,1,0}-{2808,3112,0,1,0}-{2759,3179,0,0,0}-{2757,3170,0,0,0}-{2781,3173,0,1,0}-{2770,3213,0,0,0}-" + }, + { + "npc_id": "131", + "loc_data": "{2594,3271,0,1,4}-{2593,3269,0,1,1}-{2592,3272,0,1,6}-" + }, + { + "npc_id": "132", + "loc_data": "{2875,2936,0,1,2}-{2837,3038,0,1,0}-{2837,3041,0,1,3}-{2831,3031,0,1,3}-{2818,3054,0,1,4}-{2832,3050,0,1,6}-{2832,3011,0,1,4}-{2876,3011,0,1,3}-{2863,3021,0,1,3}-{2867,3016,0,1,1}-{2871,3022,0,1,4}-{2877,3028,0,1,3}-{2868,3012,0,1,4}-{2867,3163,0,1,3}-{2856,3144,0,1,7}-{2600,3275,0,1,6}-{2600,3279,0,1,4}-{2603,3279,0,1,3}-{2603,3281,0,1,3}-{2604,3277,0,1,6}-{2897,3064,0,1,0}-{2896,3071,0,1,1}-{2910,3026,0,1,5}-{2902,3010,0,1,1}-{2928,3029,0,1,4}-{2920,3027,0,1,6}-{2932,3023,0,1,3}-{2933,3024,0,1,3}-{2929,3009,0,1,0}-{2927,3022,0,1,1}-{2926,3023,0,1,6}-{2927,3020,0,1,7}-{2891,3079,0,1,1}-{2908,3078,0,1,4}-{2920,3081,0,1,4}-{2890,3082,0,1,1}-{2916,3082,0,1,1}-{2883,3147,0,1,6}-{2945,2979,0,1,3}-{2953,2965,0,1,4}-{2952,2982,0,1,4}-{2955,3003,0,1,3}-{2944,2999,0,1,5}-{2749,3173,0,1,3}-{2784,3005,0,1,1}-{2791,2999,0,1,2}-{2791,2988,0,1,1}-{2795,2987,0,1,6}-{2797,2985,0,1,3}-{2782,3008,0,1,1}-{2812,3180,0,1,1}-{2791,3181,0,1,3}-{2758,3170,0,1,4}-{2754,3167,0,1,3}-{2754,3175,0,1,1}-" + }, + { + "npc_id": "133", + "loc_data": "{3072,3625,0,1,7}-{3079,3627,0,1,3}-{3083,3632,0,1,6}-{3082,3643,0,1,0}-{3079,3637,0,1,5}-{3086,3636,0,1,3}-{3096,3641,0,1,2}-{3115,3644,1,0,5}-{3112,3614,0,1,5}-{3124,3600,0,1,1}-{3132,3597,0,1,0}-{2730,3659,0,1,7}-{2708,3672,0,1,7}-" + }, + { + "npc_id": "134", + "loc_data": "{2598,9558,0,1,6}-{2599,9557,0,1,4}-{2598,9556,0,1,4}-{2600,9555,0,1,3}-{2601,9553,0,1,1}-{2572,9569,0,1,3}-{2599,9552,0,1,6}-{2601,9560,0,1,1}-{2599,9564,0,1,6}-{2600,9563,0,1,4}-{2597,9569,0,1,1}-{2594,9571,0,1,2}-{2589,9571,0,1,4}-{2589,9574,0,1,3}-{2584,9572,0,1,6}-{2581,9575,0,1,6}-{2583,9577,0,1,6}-{2582,9572,0,1,3}-{2572,9568,0,1,1}-{2570,9567,0,1,1}-{2572,9571,0,1,3}-{2575,9572,0,1,0}-{2577,9574,0,1,3}-{2580,9578,0,1,3}-{2597,9572,0,1,6}-{2871,9793,0,1,0}-{2869,9799,0,1,0}-{2876,9806,0,1,0}-{2864,9819,0,1,0}-{2857,9822,0,1,0}-{2858,9814,0,1,0}-{2859,9802,0,1,0}-{2849,9809,0,1,0}-{3088,9945,0,1,3}-{3089,9943,0,1,0}-{3090,9944,0,1,7}-{3278,5477,0,1,1}-{3278,5476,0,1,2}-{3277,5451,0,1,5}-{3283,5449,0,1,4}-{3280,5476,0,1,3}-{3274,5463,0,1,1}-{3272,5477,0,1,1}-{3271,5473,0,1,4}-{3270,5459,0,1,1}-{3271,5454,0,1,3}-{3278,5525,0,1,3}-{3272,5529,0,1,3}-{3270,5536,0,1,6}-{3277,5536,0,1,4}-{3277,5512,0,1,3}-{3279,5516,0,1,6}-{3280,5531,0,1,6}-{3043,10262,0,1,1}-{3044,10252,0,1,4}-{3051,10257,0,1,3}-{3055,10253,0,1,3}-{3067,10255,0,1,3}-{3067,10257,0,1,3}-{3068,10253,0,1,2}-{3069,10257,0,1,4}-{3069,10259,0,1,0}-" + }, + { + "npc_id": "138", + "loc_data": "{2375,3434,0,1,4}-{2379,3438,0,1,6}-" + }, + { + "npc_id": "139", + "loc_data": "{2378,3432,0,1,2}-{2375,3438,0,1,7}-{2376,3431,0,1,1}-{2380,3438,0,1,4}-" + }, + { + "npc_id": "141", + "loc_data": "{3324,5510,0,1,3}-{3286,5512,0,1,1}-{3310,5524,0,1,2}-{3289,5527,0,1,7}-{3296,5519,0,1,4}-" + }, + { + "npc_id": "142", + "loc_data": "{2514,3194,0,1,3}-{2547,3178,0,1,1}-" + }, + { + "npc_id": "143", + "loc_data": "{2835,2934,0,1,4}-{2889,2936,0,1,2}-{2921,2937,0,1,1}-{2787,2929,0,1,6}-" + }, + { + "npc_id": "144", + "loc_data": "{2842,3298,0,1,6}-{2844,3291,0,1,6}-{2851,3298,0,1,4}-{2855,3303,0,1,4}-{2859,3283,0,1,1}-{2861,3295,0,1,5}-{3075,3848,0,1,0}-{3090,3841,0,1,0}-{3070,3829,0,1,0}-{3070,3836,0,1,0}-{3040,9775,0,1,4}-{3048,9770,0,1,1}-" + }, + { + "npc_id": "145", + "loc_data": "{2864,9950,0,1,0}-{2865,9951,0,1,0}-{2866,9948,0,1,0}-{2867,9948,0,1,0}-{2867,9951,0,1,0}-{2868,9950,0,1,0}-" + }, + { + "npc_id": "146", + "loc_data": "{2827,3139,0,1,0}-{2896,2992,0,1,7}-{2976,3053,0,1,0}-{2985,3038,0,1,0}-{2994,3048,0,1,5}-{2992,3030,0,1,0}-" + }, + { + "npc_id": "151", + "loc_data": "{2756,3156,0,0,4}-{2760,3152,0,0,6}-" + }, + { + "npc_id": "152", + "loc_data": "{3103,3347,0,0,0}-{3107,3337,0,0,0}-{3107,3342,0,0,0}-{3107,3344,0,0,0}-{3108,3346,0,0,0}-{3111,3339,0,0,0}-{3111,3348,0,0,0}-{3115,3344,0,0,0}-{3120,3344,0,0,0}-{3122,3335,0,0,0}-" + }, + { + "npc_id": "153", + "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": "{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}-{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}-{2444,3491,0,1,0}-{2725,6127,0,1,0}-{2277,3183,0,1,0}-" + }, + { + "npc_id": "157", + "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", + "loc_data": "{2693,9776,0,1,0}-{2697,9773,0,1,0}-{2697,9779,0,1,0}-{2700,9770,0,1,0}-{2700,9774,0,1,0}-{2700,9777,0,1,0}-{2701,9781,0,1,0}-{2703,9770,0,1,0}-{2703,9774,0,1,0}-{2703,9779,0,1,0}-{2705,9757,0,1,0}-{2705,9760,0,1,0}-{2705,9766,0,1,0}-{2706,9753,0,1,0}-{2706,9763,0,1,0}-{2706,9772,0,1,0}-{2707,9756,0,1,0}-{2707,9761,0,1,0}-{2708,9758,0,1,0}-" + }, + { + "npc_id": "159", + "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", + "loc_data": "{2424,3430,1,1,0}-{2474,3500,1,1,0}-" + }, + { + "npc_id": "161", + "loc_data": "{2409,3436,1,1,0}-{2484,3501,1,1,0}-" + }, + { + "npc_id": "162", + "loc_data": "{2480,3426,0,1,6}-{2489,3429,0,1,1}-{2480,3431,0,1,0}-{2475,3418,0,1,4}-{2470,3423,0,1,1}-" + }, + { + "npc_id": "163", + "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": "{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", + "loc_data": "{2448,3427,1,0,6}-{2448,3424,1,0,6}-{2443,3424,1,0,3}-{2443,3425,1,0,3}-{2450,3480,1,0,1}-{2449,3480,1,0,1}-{2448,3480,1,0,1}-{2440,3488,1,0,4}-{2440,3487,1,0,4}-" + }, + { + "npc_id": "168", + "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": "{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", + "loc_data": "{2890,3175,0,1,4}-" + }, + { + "npc_id": "171", + "loc_data": "{2409,9817,0,1,0}-" + }, + { + "npc_id": "175", + "loc_data": "{3084,3495,0,1,4}-{3179,3215,0,1,0}-{3182,3365,0,1,6}-{2994,9549,0,1,6}-{2993,9551,0,1,6}-{2995,9555,0,1,6}-{2999,9548,0,1,0}-" + }, + { + "npc_id": "178", + "loc_data": "{3096,3220,0,1,1}-{2909,9690,0,1,0}-{2911,9689,0,1,0}-{2905,9687,0,1,0}-{2912,9682,0,1,0}-{2917,9681,0,1,0}-{2918,9679,0,1,0}-{2916,9677,0,1,0}-{2911,9675,0,1,0}-{2906,9675,0,1,0}-{2905,9673,0,1,0}-{2897,9675,0,1,0}-{2899,9678,0,1,0}-{2898,9682,0,1,0}-{2897,9687,0,1,0}-{2903,9690,0,1,0}-{2904,9692,0,1,0}-{2902,9693,0,1,0}-{2915,9691,0,1,0}-{2916,9694,0,1,0}-{2913,9694,0,1,0}-{2908,9694,0,1,0}-{2908,9702,0,1,0}-{2906,9706,0,1,0}-{2915,9703,0,1,0}-{2915,9711,0,1,0}-{2899,9700,0,1,0}-{2898,9703,0,1,0}-{2898,9708,0,1,0}-{2900,9710,0,1,0}-{2907,9711,0,1,0}-{2923,9710,0,1,0}-{2920,9708,0,1,0}-{2919,9703,0,1,0}-{2891,9679,0,1,0}-{2891,9682,0,1,0}-{2890,9692,0,1,0}-{2891,9695,0,1,0}-{2892,9701,0,1,0}-{2893,9701,0,1,0}-{2892,9709,0,1,0}-{2895,9710,0,1,0}-{2942,9801,0,1,0}-{2938,9812,0,1,0}-{3273,3507,0,1,6}-" + }, + { + "npc_id": "179", + "loc_data": "{3025,3511,1,1,3}-{3038,3505,0,1,4}-{3026,3494,0,1,6}-{3026,3505,0,1,4}-{3026,3512,0,1,6}-{3025,3512,0,1,4}-{3027,3512,0,1,6}-{3030,3511,0,1,6}-{3028,3511,0,1,1}-{3023,3514,1,1,3}-{3014,3516,2,1,1}-{3038,3854,0,1,4}-{3029,3852,0,1,1}-{3032,3851,0,1,3}-{3023,3849,0,1,6}-{3042,3856,0,1,0}-{3050,3849,0,1,0}-{3054,3852,0,1,0}-{3057,3842,0,1,0}-{3058,3854,0,1,0}-" + }, + { + "npc_id": "180", + "loc_data": "{3110,3295,0,1,4}-{2957,3238,0,1,4}-{2953,3240,0,1,3}-{2988,3423,0,1,7}-{2744,3519,0,0,0}-{3011,3277,0,1,1}-{3009,3280,0,1,1}-" + }, + { + "npc_id": "181", + "loc_data": "{2563,3355,0,1,4}-{2561,3355,0,1,3}-{2561,3357,0,1,1}-{2563,3358,0,1,4}-{3105,9942,0,1,3}-{3106,9934,0,1,3}-{3109,9930,0,1,1}-{2937,9847,0,1,0}-{2934,9846,0,1,0}-{2929,9848,0,1,0}-{2931,9846,0,1,0}-{2937,9849,0,1,0}-{2936,9850,0,1,0}-" + }, + { + "npc_id": "182", + "loc_data": "{3690,2966,0,1,4}-" + }, + { + "npc_id": "183", + "loc_data": "{3670,2977,0,1,1}-{3654,2960,0,1,0}-{3698,2969,0,1,2}-{3676,2991,0,1,3}-{3666,2994,0,1,7}-{3666,2981,1,1,4}-{2805,3160,0,0,0}-{2803,3190,0,1,4}-{2795,3167,0,0,0}-{2793,3184,0,1,3}-" + }, + { + "npc_id": "184", + "loc_data": "{3659,2952,0,1,6}-{3669,2961,0,1,4}-{3683,2977,0,1,6}-{3668,2981,0,1,3}-{3655,2973,0,1,1}-{3675,2967,0,1,3}-{2994,9584,0,1,6}-{2992,9573,0,1,1}-{2990,9583,0,1,4}-{2998,9572,0,1,2}-{2998,9573,0,1,4}-{2989,9578,0,1,0}-{2993,9566,0,1,4}-{2994,9570,0,1,3}-{3039,3958,0,1,0}-" + }, + { + "npc_id": "186", + "loc_data": "{3160,3568,0,1,0}-{3167,3576,0,1,0}-{3171,3579,0,1,0}-{3172,3561,0,1,0}-{3175,3572,0,1,0}-{3179,3564,0,1,0}-{3184,3580,0,1,0}-{3189,3563,0,1,0}-{3190,3570,0,1,0}-" + }, + { + "npc_id": "187", + "loc_data": "{3076,3916,0,1,0}-{3079,3909,0,1,0}-{3291,3924,0,1,4}-{3294,3930,0,1,4}-{3286,3933,0,1,6}-{3285,3928,0,1,6}-{3287,3935,1,1,6}-{3286,3927,1,1,6}-{3283,3936,2,1,6}-{3292,3932,1,1,4}-{3291,3940,0,1,6}-{3280,3943,0,1,6}-{3278,3926,0,1,4}-" + }, + { + "npc_id": "188", + "loc_data": "{2940,3517,0,1,3}-{2938,3517,0,1,3}-{3158,5497,0,1,6}-{3157,5494,0,1,1}-{3155,5498,0,1,6}-{3140,5494,0,1,6}-{3143,5493,0,1,5}-{3142,5497,0,1,4}-{3156,5494,0,1,5}-{3148,5496,0,1,4}-" + }, + { + "npc_id": "189", + "loc_data": "{2936,3514,0,1,3}-{2931,3514,0,1,3}-{2933,3517,0,1,3}-{2936,3517,0,1,3}-{3151,5489,0,1,7}-{3151,5489,0,1,7}-{3145,5495,0,1,3}-{3142,5493,0,1,3}-{3149,5489,0,1,6}-{3155,5491,0,1,2}-{3142,5497,0,1,3}-" + }, + { + "npc_id": "190", + "loc_data": "{2833,9814,0,1,0}-{2829,9812,0,1,0}-{2825,9811,0,1,0}-{2829,9808,0,1,0}-{2833,9808,0,1,0}-{2827,9805,0,1,0}-" + }, + { + "npc_id": "191", + "loc_data": "{2868,3053,0,1,0}-{2840,3018,0,1,0}-{2919,2983,0,1,0}-{2937,2993,0,1,0}-{2940,2994,0,1,0}-{2940,2997,0,1,0}-{2942,2997,0,1,0}-{2730,3161,0,1,0}-{2729,3159,0,1,0}-{2727,3163,0,1,0}-{2725,3157,0,1,0}-{2726,3161,0,1,0}-{2725,3161,0,1,0}-{2723,3155,0,1,0}-{2723,3156,0,1,0}-{2725,3162,0,1,0}-{2775,3066,0,1,0}-{2802,3071,0,1,0}-{2770,3018,0,1,0}-{2770,3014,0,1,0}-" + }, + { + "npc_id": "192", + "loc_data": "{3022,3624,0,1,0}-{3022,3639,0,1,0}-{3029,3638,0,1,0}-{3030,3626,0,1,0}-{3035,3632,0,1,0}-{3036,3624,0,1,0}-{3036,3639,0,1,0}-{3022,3624,1,1,0}-{3022,3639,1,1,0}-{3023,3632,1,1,0}-{3028,3626,1,1,0}-{3029,3637,1,1,0}-{3035,3631,1,1,0}-{3036,3624,1,1,0}-{3036,3639,1,1,0}-" + }, + { + "npc_id": "193", + "loc_data": "{2581,9505,0,1,4}-{2594,9497,0,1,1}-{2593,9499,0,1,7}-{2587,9498,0,1,3}-{2573,9500,0,1,5}-{2579,9503,0,1,5}-{2580,9500,0,1,4}-{2578,9497,0,1,7}-" + }, + { + "npc_id": "195", + "loc_data": "{3029,3701,0,1,6}-{3031,3685,0,1,6}-{3032,3695,0,1,6}-{3033,3705,0,1,6}-{3037,3683,0,1,6}-{3040,3703,0,1,6}-" + }, + { + "npc_id": "196", + "loc_data": "{3038,3670,0,1,1}-{3036,3672,0,1,2}-{3037,3661,0,1,6}-{3039,3660,0,1,6}-" + }, + { + "npc_id": "198", + "loc_data": "{3190,3358,0,1,1}-" + }, + { + "npc_id": "199", + "loc_data": "{3079,3443,0,1,3}-" + }, + { + "npc_id": "201", + "loc_data": "{2931,9692,0,0,0}-" + }, + { + "npc_id": "202", + "loc_data": "{3039,3700,0,1,0}-" + }, + { + "npc_id": "203", + "loc_data": "{3038,3695,0,1,0}-" + }, + { + "npc_id": "204", + "loc_data": "{3035,3696,0,1,0}-" + }, + { + "npc_id": "205", + "loc_data": "{2613,9523,0,1,0}-" + }, + { + "npc_id": "206", + "loc_data": "{3026,3455,0,1,6}-{3015,3458,0,1,5}-" + }, + { + "npc_id": "208", + "loc_data": "{2568,3459,0,1,4}-" + }, + { + "npc_id": "209", + "loc_data": "{3013,3452,0,1,6}-" + }, + { + "npc_id": "216", + "loc_data": "{2851,3348,0,0,7}-" + }, + { + "npc_id": "217", + "loc_data": "{2852,3343,0,1,2}-" + }, + { + "npc_id": "218", + "loc_data": "{2613,3475,0,1,2}-" + }, + { + "npc_id": "222", + "loc_data": "{2851,3351,0,0,7}-{2849,3348,0,0,7}-" + }, + { + "npc_id": "223", + "loc_data": "{2569,3250,0,1,0}-" + }, + { + "npc_id": "224", + "loc_data": "{2579,9655,0,1,0}-{2581,9656,0,1,0}-{2581,9659,0,1,0}-{2583,9659,0,1,0}-{2584,9656,0,1,0}-{2586,9659,0,1,0}-{2587,9656,0,1,0}-{2589,9654,0,1,0}-{2589,9658,0,1,0}-{2591,9656,0,1,0}-{2591,9659,0,1,0}-" + }, + { + "npc_id": "225", + "loc_data": "{2641,3437,0,0,0}-" + }, + { + "npc_id": "227", + "loc_data": "{2643,3440,0,0,0}-" + }, + { + "npc_id": "228", + "loc_data": "{2634,3427,0,0,0}-" + }, + { + "npc_id": "229", + "loc_data": "{2628,3413,0,1,4}-" + }, + { + "npc_id": "230", + "loc_data": "{2649,3454,0,0,0}-" + }, + { + "npc_id": "231", + "loc_data": "{2650,3468,0,0,0}-" + }, + { + "npc_id": "232", + "loc_data": "{2819,3487,0,0,4}-" + }, + { + "npc_id": "237", + "loc_data": "{2769,3403,0,1,0}-{2766,3402,0,1,3}-{2770,3402,0,1,0}-{2772,3404,0,1,1}-{2767,3405,1,1,0}-{2771,3403,1,1,6}-{2767,3401,1,1,7}-" + }, + { + "npc_id": "239", + "loc_data": "{2758,3516,1,0,0}-" + }, + { + "npc_id": "240", + "loc_data": "{2760,3501,0,1,1}-" + }, + { + "npc_id": "241", + "loc_data": "{2762,3498,0,0,0}-" + }, + { + "npc_id": "242", + "loc_data": "{2764,3509,1,0,0}-" + }, + { + "npc_id": "243", + "loc_data": "{2756,3513,1,0,0}-" + }, + { + "npc_id": "244", + "loc_data": "{2765,3504,1,0,0}-" + }, + { + "npc_id": "245", + "loc_data": "{2755,3517,0,0,0}-" + }, + { + "npc_id": "247", + "loc_data": "{2769,3402,2,1,5}-" + }, + { + "npc_id": "250", + "loc_data": "{2924,3405,0,0,1}-" + }, + { + "npc_id": "251", + "loc_data": "{2761,3512,0,0,0}-" + }, + { + "npc_id": "253", + "loc_data": "{2580,3163,0,1,1}-{2590,3184,0,1,1}-{2589,3140,0,1,1}-{2601,3177,0,1,1}-{2607,3182,0,1,1}-{2612,3187,0,1,1}-" + }, + { + "npc_id": "254", + "loc_data": "{2569,3149,0,0,1}-{2614,3169,0,1,1}-{2564,3153,0,1,1}-{2568,3147,0,1,1}-{2579,3176,0,1,3}-{2580,3174,0,1,1}-{2584,3149,0,1,3}-{2590,3190,0,1,1}-{2594,3181,0,1,1}-{2595,3186,0,1,1}-{2597,3175,0,1,1}-{2597,3181,0,1,1}-{2607,3185,0,1,1}-{2614,3150,0,1,1}-{2619,3168,0,1,1}-" + }, + { + "npc_id": "255", + "loc_data": "{2571,3142,0,1,1}-{2617,3150,0,1,1}-{2609,3171,0,0,1}-{2617,3157,0,1,1}-{2577,3167,0,1,1}-{2597,3176,0,1,1}-{2607,3181,0,1,1}-{2608,3148,0,1,1}-{2600,3190,0,1,1}-{2610,3150,0,1,1}-{2609,3171,0,1,1}-" + }, + { + "npc_id": "256", + "loc_data": "{2609,3194,0,1,2}-" + }, + { + "npc_id": "259", + "loc_data": "{2566,3140,0,0,1}-" + }, + { + "npc_id": "260", + "loc_data": "{2589,3142,0,1,4}-" + }, + { + "npc_id": "261", + "loc_data": "{2587,3142,0,1,3}-" + }, + { + "npc_id": "262", + "loc_data": "{2615,3160,0,1,4}-{2616,3164,0,1,1}-{2594,3143,0,0,2}-" + }, + { + "npc_id": "263", + "loc_data": "{2597,3143,0,0,4}-" + }, + { + "npc_id": "264", + "loc_data": "{2568,3200,0,1,0}-" + }, + { + "npc_id": "266", + "loc_data": "{2601,3154,0,1,0}-" + }, + { + "npc_id": "267", + "loc_data": "{2601,3168,0,1,3}-" + }, + { + "npc_id": "268", + "loc_data": "{2582,3172,0,1,1}-" + }, + { + "npc_id": "269", + "loc_data": "{2608,3163,0,1,3}-" + }, + { + "npc_id": "270", + "loc_data": "{2608,3165,0,1,3}-" + }, + { + "npc_id": "271", + "loc_data": "{2608,3158,0,1,3}-" + }, + { + "npc_id": "272", + "loc_data": "{3127,3485,0,1,1}-" + }, + { + "npc_id": "273", + "loc_data": "{2573,3321,0,1,3}-" + }, + { + "npc_id": "274", + "loc_data": "{2638,9899,0,1,6}-{2639,9911,0,1,6}-{2648,9901,0,1,6}-{2649,9912,0,1,6}-{2649,9905,0,1,6}-{2650,9897,0,1,6}-" + }, + { + "npc_id": "275", + "loc_data": "{2636,9910,0,1,4}-{2637,9897,0,1,4}-{2643,9913,0,1,6}-{2646,9906,0,1,6}-" + }, + { + "npc_id": "276", + "loc_data": "{2656,9875,0,1,3}-" + }, + { + "npc_id": "278", + "loc_data": "{3209,3215,0,0,0}-" + }, + { + "npc_id": "279", + "loc_data": "{2604,3209,0,0,0}-" + }, + { + "npc_id": "280", + "loc_data": "{2615,3258,0,1,6}-" + }, + { + "npc_id": "281", + "loc_data": "{2604,3219,0,1,6}-{2609,3207,0,1,6}-{2618,3211,0,1,6}-" + }, + { + "npc_id": "282", + "loc_data": "{2562,9608,0,1,6}-{2564,9604,0,1,6}-{3280,3494,0,1,6}-" + }, + { + "npc_id": "283", + "loc_data": "{2569,9602,0,1,6}-" + }, + { + "npc_id": "284", + "loc_data": "{2953,3450,0,0,0}-" + }, + { + "npc_id": "285", + "loc_data": "{3110,3330,0,0,0}-" + }, + { + "npc_id": "286", + "loc_data": "{3111,3367,2,0,4}-" + }, + { + "npc_id": "287", + "loc_data": "{2770,3403,2,1,3}-" + }, + { + "npc_id": "288", + "loc_data": "{3110,3366,2,0,2}-{2677,3655,0,1,3}-{2681,3653,0,1,6}-" + }, + { + "npc_id": "289", + "loc_data": "{2616,3302,0,1,3}-" + }, + { + "npc_id": "290", + "loc_data": "{2614,3306,0,1,4}-" + }, + { + "npc_id": "296", + "loc_data": "{3084,3518,0,1,1}-" + }, + { + "npc_id": "297", + "loc_data": "{3093,3518,0,1,5}-" + }, + { + "npc_id": "298", + "loc_data": "{3113,3513,0,1,3}-" + }, + { + "npc_id": "299", + "loc_data": "{3113,3511,0,1,3}-" + }, + { + "npc_id": "300", + "loc_data": "{3105,9569,0,0,0}-" + }, + { + "npc_id": "302", + "loc_data": "{2519,3429,0,1,1}-" + }, + { + "npc_id": "304", + "loc_data": "{2520,3496,0,1,3}-" + }, + { + "npc_id": "305", + "loc_data": "{2511,3484,0,0,4}-" + }, + { + "npc_id": "306", + "loc_data": "{2514,9580,0,1,1}-" + }, + { + "npc_id": "307", + "loc_data": "{2968,3206,0,0,0}-" + }, + { + "npc_id": "308", + "loc_data": "{2611,3393,0,1,4}-" + }, + { + "npc_id": "309", + "loc_data": "{2860,2976,0,0,4}-{2856,2977,0,0,4}-{2852,2973,0,0,4}-{2561,3374,0,0,4}-{2562,3374,0,0,4}-{2566,3370,0,0,0}-{3104,3424,0,0,0}-{3104,3425,0,0,0}-{2637,3444,0,0,0}-{2678,3595,0,0,4}-{2633,3693,0,0,4}-{2633,3694,0,0,4}-{2471,3146,0,0,4}-{2465,3156,0,0,4}-{2461,3150,0,0,4}-{2215,3248,0,0,4}-{2217,3245,0,0,4}-{2213,3241,0,0,4}-{2716,3530,0,0,0}-{2267,3253,0,0,4}-{2266,3253,0,0,4}-{2262,3249,0,0,4}-" + }, + { + "npc_id": "310", + "loc_data": "{2860,2972,0,0,6}-{2865,2972,0,0,4}-{2382,3414,0,0,4}-{2382,3415,0,0,0}-{2392,3420,0,0,0}-{3239,3243,0,0,6}-{3239,3241,0,0,6}-{3238,3252,0,0,6}-{2537,3406,0,0,4}-{2527,3412,0,0,4}-{2507,3421,0,0,4}-{2508,3421,0,0,4}-" + }, + { + "npc_id": "312", + "loc_data": "{2860,3426,0,0,1}-{2421,3789,0,0,0}-{2410,3780,0,0,0}-{2414,3780,0,0,0}-" + }, + { + "npc_id": "313", + "loc_data": "{2840,3356,0,0,1}-{2843,3359,0,0,1}-{2848,3361,0,0,1}-{2879,3339,0,0,1}-{2879,3338,0,0,1}-{2879,3335,0,0,1}-{2879,3334,0,0,1}-{2876,3331,0,0,1}-{2853,3423,0,0,1}-{2401,3781,0,0,0}-" + }, + { + "npc_id": "316", + "loc_data": "{3086,3227,0,0,1}-{3085,3230,0,0,0}-{2986,3176,0,0,4}-{2996,3157,0,0,1}-{2790,3273,0,0,1}-{2790,3276,0,0,6}-{2794,3283,0,0,6}-{2795,3279,0,0,3}-" + }, + { + "npc_id": "322", + "loc_data": "{2641,3700,0,0,4}-{2640,3700,0,0,4}-{2777,2740,0,0,4}-{2780,2741,0,0,4}-{2788,2741,0,0,4}-{2771,2733,0,0,4}-{2771,2734,0,0,4}-" + }, + { + "npc_id": "323", + "loc_data": "{2926,3177,0,0,6}-{3248,3161,0,0,4}-{2516,3575,0,0,7}-{2511,3562,0,0,7}-{2498,3547,0,0,7}-" + }, + { + "npc_id": "324", + "loc_data": "{2632,3427,0,0,0}-{2645,3708,0,0,1}-{2648,3708,0,0,1}-{2647,3711,0,0,1}-" + }, + { + "npc_id": "325", + "loc_data": "{2671,3160,0,0,4}-{2672,3160,0,0,4}-{2678,3160,0,0,4}-{2682,3160,0,0,4}-{2683,3160,0,0,4}-" + }, + { + "npc_id": "326", + "loc_data": "{2840,3431,0,0,0}-{2855,3423,0,0,1}-{2607,3416,0,0,1}-{2604,3417,0,0,1}-{2602,3419,0,0,1}-{3246,3156,0,0,0}-{3245,3152,0,0,0}-{3242,3148,0,0,0}-{3267,3148,0,0,6}-{3276,3140,0,0,6}-{3275,3140,0,0,6}-" + }, + { + "npc_id": "333", + "loc_data": "{2845,3429,0,0,6}-{2602,3411,0,0,3}-{2605,3413,0,0,3}-{2609,3416,0,0,3}-{2610,3413,0,0,3}-{2605,3421,0,0,1}-{2602,3423,0,0,1}-{2603,3426,0,0,1}-{2923,3178,0,0,6}-{2923,3180,0,0,6}-{2926,3180,0,0,6}-" + }, + { + "npc_id": "334", + "loc_data": "{2612,3411,0,0,0}-{2165,3268,0,0,4}-{2162,3274,0,0,4}-{2627,3415,0,0,0}-{2639,3698,0,0,4}-{2642,3698,0,0,4}-{2699,2702,0,0,4}-{2694,2706,0,0,4}-{2700,2702,0,0,4}-{2707,2698,0,0,4}-{2700,2702,0,0,0}-{2700,2702,0,0,0}-{2700,2702,0,0,0}-{2707,2698,0,0,0}-{2707,2698,0,0,0}-{2707,2698,0,0,0}-" + }, + { + "npc_id": "335", + "loc_data": "{2592,3336,0,1,0}-" + }, + { + "npc_id": "336", + "loc_data": "{2928,3218,0,1,4}-" + }, + { + "npc_id": "338", + "loc_data": "{2929,3222,0,0,6}-" + }, + { + "npc_id": "340", + "loc_data": "{2929,3217,0,1,6}-" + }, + { + "npc_id": "342", + "loc_data": "{3281,3382,0,0,0}-" + }, + { + "npc_id": "343", + "loc_data": "{3284,3382,0,0,0}-" + }, + { + "npc_id": "344", + "loc_data": "{2510,3378,0,1,0}-{2511,3383,0,1,0}-{2515,3356,0,1,0}-{2519,3356,0,1,0}-{2521,3383,0,1,0}-{2524,3362,0,1,0}-{2525,3383,0,1,0}-" + }, + { + "npc_id": "345", + "loc_data": "{2519,3366,0,1,0}-{2525,3369,0,1,0}-{2531,3368,0,1,0}-" + }, + { + "npc_id": "346", + "loc_data": "{2508,3370,0,1,0}-{2512,3374,0,1,0}-{2518,3371,0,1,0}-{2525,3380,0,1,0}-" + }, + { + "npc_id": "347", + "loc_data": "{2524,3292,0,1,3}-{2536,3294,0,1,3}-{2538,3321,0,1,3}-" + }, + { + "npc_id": "348", + "loc_data": "{2501,3315,0,1,3}-{2513,3325,0,1,3}-{2526,3279,0,1,3}-{2528,3297,0,1,3}-{2535,3288,0,1,3}-{2548,3287,0,1,3}-" + }, + { + "npc_id": "349", + "loc_data": "{2556,3266,0,0,0}-" + }, + { + "npc_id": "350", + "loc_data": "{2559,3266,0,0,0}-" + }, + { + "npc_id": "351", + "loc_data": "{2465,3307,0,1,0}-{2482,3293,0,1,0}-{2509,3325,0,1,0}-" + }, + { + "npc_id": "352", + "loc_data": "{2453,3307,0,1,0}-{2480,3300,0,1,0}-{2511,3322,0,1,0}-{2540,3279,0,1,0}-" + }, + { + "npc_id": "353", + "loc_data": "{2473,3288,0,1,0}-{2504,3326,0,1,0}-{2509,3314,0,1,0}-{2546,3277,0,1,0}-" + }, + { + "npc_id": "354", + "loc_data": "{2470,3290,0,1,0}-{2472,3296,0,1,0}-{2483,3318,0,1,0}-{2483,3323,0,1,0}-{2490,3293,0,1,0}-{2510,3318,0,1,0}-{2524,3271,0,1,0}-" + }, + { + "npc_id": "355", + "loc_data": "{2464,3302,0,1,0}-{2466,3300,0,1,0}-{2466,3307,0,1,0}-{2467,3306,0,1,0}-{2468,3315,0,1,0}-{2469,3319,0,1,0}-{2471,3323,0,1,0}-{2473,3305,0,1,0}-{2474,3296,0,1,0}-{2476,3307,0,1,0}-{2478,3323,0,1,0}-{2480,3296,0,1,0}-{2481,3313,0,1,0}-{2485,3312,0,1,0}-{2504,3318,0,1,0}-{2547,3277,0,1,0}-" + }, + { + "npc_id": "356", + "loc_data": "{2462,3306,0,1,0}-{2465,3305,0,1,0}-{2469,3295,0,1,0}-{2477,3320,0,1,0}-{2482,3311,0,1,0}-{2518,3275,0,1,0}-{2523,3307,0,1,0}-" + }, + { + "npc_id": "357", + "loc_data": "{2518,3309,0,1,3}-{2526,3303,0,1,3}-{2543,3309,0,1,3}-{2552,3320,0,1,3}-" + }, + { + "npc_id": "358", + "loc_data": "{2529,3285,0,1,0}-" + }, + { + "npc_id": "360", + "loc_data": "{2472,3307,0,1,0}-{2492,3314,0,1,0}-{2553,3275,0,1,0}-" + }, + { + "npc_id": "361", + "loc_data": "{2463,3317,0,1,0}-{2482,3296,0,1,0}-{2485,3315,0,1,0}-{2519,3277,0,1,0}-" + }, + { + "npc_id": "362", + "loc_data": "{2482,3286,0,1,0}-{2489,3295,0,1,0}-{2537,3324,0,1,0}-" + }, + { + "npc_id": "363", + "loc_data": "{2468,3290,0,1,0}-{2475,3325,0,1,0}-{2479,3312,0,1,0}-{2513,3315,0,1,0}-" + }, + { + "npc_id": "364", + "loc_data": "{2577,3298,1,0,0}-" + }, + { + "npc_id": "366", + "loc_data": "{2612,3324,0,0,0}-" + }, + { + "npc_id": "367", + "loc_data": "{2935,3210,0,1,2}-" + }, + { + "npc_id": "368", + "loc_data": "{3263,3407,0,1,2}-" + }, + { + "npc_id": "369", + "loc_data": "{2548,3324,0,1,3}-{2550,3326,0,1,3}-{2553,3325,0,1,3}-" + }, + { + "npc_id": "370", + "loc_data": "{2549,3325,1,1,0}-{2550,3327,1,1,0}-" + }, + { + "npc_id": "371", + "loc_data": "{2545,3326,0,1,3}-{2551,3322,0,1,3}-{2555,3324,0,1,3}-" + }, + { + "npc_id": "372", + "loc_data": "{2561,3305,0,1,2}-{2561,3303,0,1,7}-{2559,3304,0,1,3}-" + }, + { + "npc_id": "373", + "loc_data": "{2516,3274,0,1,0}-" + }, + { + "npc_id": "375", + "loc_data": "{3053,3249,0,0,0}-" + }, + { + "npc_id": "376", + "loc_data": "{3027,3217,0,1,4}-" + }, + { + "npc_id": "377", + "loc_data": "{3028,3223,0,1,2}-" + }, + { + "npc_id": "378", + "loc_data": "{3028,3220,0,1,4}-" + }, + { + "npc_id": "379", + "loc_data": "{2939,3156,0,1,4}-" + }, + { + "npc_id": "380", + "loc_data": "{2953,3149,0,0,0}-{2770,3226,0,0,0}-" + }, + { + "npc_id": "381", + "loc_data": "{2684,3272,0,1,4}-" + }, + { + "npc_id": "382", + "loc_data": "{3044,9758,0,1,3}-" + }, + { + "npc_id": "383", + "loc_data": "{2582,3485,0,1,7}-" + }, + { + "npc_id": "384", + "loc_data": "{2545,3568,0,1,7}-{2545,3571,0,1,6}-" + }, + { + "npc_id": "385", + "loc_data": "{2552,3570,0,1,6}-" + }, + { + "npc_id": "386", + "loc_data": "{2877,9797,0,1,0}-" + }, + { + "npc_id": "387", + "loc_data": "{3058,3488,1,0,0}-" + }, + { + "npc_id": "388", + "loc_data": "{2709,3485,0,1,1}-{2716,3475,0,1,3}-{2697,3472,0,1,6}-{2702,3469,0,1,3}-" + }, + { + "npc_id": "389", + "loc_data": "{2704,3405,3,1,3}-" + }, + { + "npc_id": "397", + "loc_data": "{3254,3258,0,1,1}-{3261,3260,0,1,1}-{3259,3279,0,1,6}-{3247,3292,0,1,3}-{3260,3291,0,1,2}-{3247,3282,0,1,5}-{3252,3283,0,1,6}-{3264,3258,0,1,2}-{3026,3303,0,1,4}-" + }, + { + "npc_id": "398", + "loc_data": "{2849,5091,0,0,0}-{2726,3349,0,1,6}-" + }, + { + "npc_id": "399", + "loc_data": "{2846,5081,0,0,0}-{2730,3349,0,1,1}-" + }, + { + "npc_id": "400", + "loc_data": "{2728,3377,0,1,1}-" + }, + { + "npc_id": "401", + "loc_data": "{2823,2941,0,0,0}-{2861,2941,0,0,0}-{2800,2943,0,0,0}-{2762,2944,0,0,0}-" + }, + { + "npc_id": "402", + "loc_data": "{2818,2939,0,0,0}-{2870,2943,0,0,0}-{2933,2944,0,0,0}-{2762,2943,0,0,0}-{2791,2944,0,0,0}-" + }, + { + "npc_id": "407", + "loc_data": "{3059,4507,0,0,3}-" + }, + { + "npc_id": "411", + "loc_data": "{2878,3161,0,0,0}-{2887,3164,0,0,0}-" + }, + { + "npc_id": "412", + "loc_data": "{3341,3478,0,1,0}-{3343,3484,0,1,0}-{3345,3484,0,1,0}-{3350,3493,0,1,0}-{3353,3494,0,1,0}-{3361,3484,0,1,0}-{3364,3489,0,1,0}-{3370,3497,0,1,0}-{3379,3478,0,1,0}-{3379,3484,0,1,0}-{3387,3483,0,1,0}-{3388,3494,0,1,0}-{3389,3483,0,1,0}-{3588,3472,0,1,0}-{3598,3509,0,1,0}-{3599,3486,0,1,0}-{3607,3462,0,1,0}-{3611,3499,0,1,0}-{3613,3482,0,1,0}-{3627,3511,0,1,0}-{3628,3473,0,1,0}-{3636,3466,0,1,0}-{3639,3510,0,1,0}-{3411,3536,0,1,0}-{3418,3522,0,1,0}-{3425,3525,0,1,0}-{3429,3522,0,1,0}-{3432,3527,0,1,0}-{3434,3521,0,1,0}-{3445,3526,0,1,0}-{2728,10122,0,1,1}-{2735,10123,0,1,3}-{2733,10129,0,1,1}-{2728,10133,0,1,6}-{2722,10133,0,1,6}-{2736,10137,0,1,1}-{2732,10142,0,1,0}-{2726,10142,0,1,6}-{2729,10140,0,1,3}-{2720,10144,0,1,6}-{2712,10142,0,1,3}-{3565,3503,0,1,0}-{3567,3481,0,1,0}-{3580,3491,0,1,0}-" + }, + { + "npc_id": "419", + "loc_data": "{3195,5450,0,1,6}-{3181,5449,0,1,1}-" + }, + { + "npc_id": "420", + "loc_data": "{3175,5454,0,1,4}-" + }, + { + "npc_id": "421", + "loc_data": "{3195,5459,0,1,6}-{3186,5454,0,1,5}-{3173,5494,0,1,6}-{3179,5491,0,1,1}-{3185,5492,0,1,4}-{3184,5487,0,1,3}-" + }, + { + "npc_id": "423", + "loc_data": "{2731,5340,0,1,7}-" + }, + { + "npc_id": "424", + "loc_data": "{2724,5359,0,1,3}-" + }, + { + "npc_id": "425", + "loc_data": "{2717,5332,0,1,7}-" + }, + { + "npc_id": "429", + "loc_data": "{2363,5214,0,1,6}-{2358,5214,0,1,7}-{2361,5212,0,1,6}-{2361,5209,0,1,4}-{2359,5213,0,1,1}-{2364,5215,0,1,6}-{2361,5216,0,1,1}-{2361,5212,0,1,0}-" + }, + { + "npc_id": "437", + "loc_data": "{2807,3191,0,0,0}-" + }, + { + "npc_id": "444", + "loc_data": "{2551,3197,0,1,1}-{2555,3194,0,1,4}-{2551,3157,0,1,3}-{2525,3155,0,1,3}-{2518,3149,0,1,3}-{2502,3175,0,1,1}-" + }, + { + "npc_id": "445", + "loc_data": "{2550,3196,0,1,4}-{2557,3197,0,1,1}-{2538,3155,0,1,7}-" + }, + { + "npc_id": "446", + "loc_data": "{2994,3194,0,1,1}-{3285,9896,0,1,1}-{3279,9895,0,1,6}-" + }, + { + "npc_id": "447", + "loc_data": "{3114,3242,0,1,1}-" + }, + { + "npc_id": "448", + "loc_data": "{3120,3238,0,1,6}-" + }, + { + "npc_id": "449", + "loc_data": "{3127,3248,0,1,1}-" + }, + { + "npc_id": "450", + "loc_data": "{3081,3220,0,1,0}-{3081,3222,0,1,0}-" + }, + { + "npc_id": "451", + "loc_data": "{3077,3234,0,1,0}-{2994,3215,0,1,2}-" + }, + { + "npc_id": "452", + "loc_data": "{3223,3293,0,0,0}-" + }, + { + "npc_id": "454", + "loc_data": "{2899,3430,1,1,3}-" + }, + { + "npc_id": "455", + "loc_data": "{2925,3485,0,1,5}-" + }, + { + "npc_id": "456", + "loc_data": "{3244,3205,0,0,0}-" + }, + { + "npc_id": "458", + "loc_data": "{3148,3175,0,1,4}-" + }, + { + "npc_id": "459", + "loc_data": "{2566,9507,0,1,6}-{2565,9506,0,0,5}-{2566,9507,0,1,3}-" + }, + { + "npc_id": "460", + "loc_data": "{2590,9488,0,1,3}-" + }, + { + "npc_id": "461", + "loc_data": "{2588,3091,1,1,3}-" + }, + { + "npc_id": "462", + "loc_data": "{2590,3084,0,1,5}-" + }, + { + "npc_id": "469", + "loc_data": "{2542,3169,0,1,4}-" + }, + { + "npc_id": "470", + "loc_data": "{2519,3212,0,1,3}-" + }, + { + "npc_id": "471", + "loc_data": "{2526,3160,1,1,6}-" + }, + { + "npc_id": "472", + "loc_data": "{2521,3168,0,1,4}-" + }, + { + "npc_id": "473", + "loc_data": "{2503,3192,0,0,0}-{2514,3159,0,0,6}-" + }, + { + "npc_id": "475", + "loc_data": "{2454,3300,0,1,7}-{2455,3301,0,1,7}-{2513,3262,0,1,3}-{2522,3251,0,1,1}-" + }, + { + "npc_id": "477", + "loc_data": "{2455,3301,0,1,6}-" + }, + { + "npc_id": "478", + "loc_data": "{2509,3255,0,1,3}-{2503,3254,1,1,3}-" + }, + { + "npc_id": "479", + "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": "{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", + "loc_data": "{2499,3266,0,0,4}-" + }, + { + "npc_id": "482", + "loc_data": "{2524,3258,0,1,4}-" + }, + { + "npc_id": "483", + "loc_data": "{2501,3234,0,0,4}-" + }, + { + "npc_id": "484", + "loc_data": "{2537,3168,0,1,5}-{2542,3167,0,0,1}-{2541,3168,0,0,2}-{2541,3171,0,0,3}-{2542,3172,0,0,4}-" + }, + { + "npc_id": "487", + "loc_data": "{1885,5029,0,0,6}-" + }, + { + "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}-" + }, + { + "npc_id": "495", + "loc_data": "{2615,3091,0,0,3}-{2615,3091,0,0,3}-{2615,3330,0,0,0}-{2584,3421,0,0,4}-{2809,3443,0,0,6}-{2811,3443,0,0,6}-" + }, + { + "npc_id": "496", + "loc_data": "{3267,3164,0,0,4}-{3267,3167,0,0,4}-{3267,3169,0,0,4}-" + }, + { + "npc_id": "497", + "loc_data": "{3267,3166,0,0,4}-{3267,3168,0,0,4}-" + }, + { + "npc_id": "498", + "loc_data": "{2384,4457,0,0,7}-{2385,4461,0,0,1}-" + }, + { + "npc_id": "499", + "loc_data": "{2853,2953,0,0,1}-{2852,2953,0,0,1}-" + }, + { + "npc_id": "500", + "loc_data": "{2880,2951,0,0,6}-" + }, + { + "npc_id": "504", + "loc_data": "{2870,2950,0,0,0}-{2877,2959,0,0,0}-" + }, + { + "npc_id": "505", + "loc_data": "{2868,2954,0,0,6}-{2876,2948,0,0,1}-" + }, + { + "npc_id": "510", + "loc_data": "{2780,3211,0,0,0}-" + }, + { + "npc_id": "511", + "loc_data": "{2834,2956,0,0,0}-" + }, + { + "npc_id": "512", + "loc_data": "{2862,2995,1,0,0}-" + }, + { + "npc_id": "513", + "loc_data": "{2857,2962,0,0,6}-" + }, + { + "npc_id": "514", + "loc_data": "{2870,2974,1,0,6}-" + }, + { + "npc_id": "515", + "loc_data": "{2834,2987,0,0,0}-" + }, + { + "npc_id": "516", + "loc_data": "{2826,2958,0,1,3}-" + }, + { + "npc_id": "517", + "loc_data": "{2870,2970,0,0,0}-" + }, + { + "npc_id": "518", + "loc_data": "{2764,2959,1,0,0}-" + }, + { + "npc_id": "519", + "loc_data": "{3228,3203,0,1,6}-" + }, + { + "npc_id": "520", + "loc_data": "{3217,3241,0,1,4}-" + }, + { + "npc_id": "521", + "loc_data": "{3217,3240,0,1,3}-" + }, + { + "npc_id": "522", + "loc_data": "{3218,3415,0,1,3}-" + }, + { + "npc_id": "523", + "loc_data": "{3217,3411,0,1,3}-" + }, + { + "npc_id": "524", + "loc_data": "{3316,3184,0,1,3}-" + }, + { + "npc_id": "525", + "loc_data": "{3316,3182,0,1,3}-" + }, + { + "npc_id": "527", + "loc_data": "{2959,3390,0,1,1}-{2957,3388,0,1,3}-" + }, + { + "npc_id": "528", + "loc_data": "{3078,3512,0,1,3}-" + }, + { + "npc_id": "529", + "loc_data": "{3079,3507,0,1,7}-" + }, + { + "npc_id": "530", + "loc_data": "{2947,3217,0,1,1}-" + }, + { + "npc_id": "531", + "loc_data": "{2947,3212,0,1,1}-" + }, + { + "npc_id": "532", + "loc_data": "{2906,3145,0,1,3}-" + }, + { + "npc_id": "533", + "loc_data": "{2906,3144,0,1,3}-" + }, + { + "npc_id": "534", + "loc_data": "{2375,4449,0,1,5}-" + }, + { + "npc_id": "535", + "loc_data": "{2376,4447,0,0,0}-" + }, + { + "npc_id": "536", + "loc_data": "{3192,3359,1,1,3}-" + }, + { + "npc_id": "537", + "loc_data": "{3192,3355,1,1,6}-" + }, + { + "npc_id": "538", + "loc_data": "{3076,3429,0,1,3}-" + }, + { + "npc_id": "539", + "loc_data": "{3299,3204,0,0,0}-" + }, + { + "npc_id": "540", + "loc_data": "{3288,3212,0,1,6}-" + }, + { + "npc_id": "541", + "loc_data": "{3288,3190,0,1,3}-" + }, + { + "npc_id": "542", + "loc_data": "{3316,3175,0,1,3}-" + }, + { + "npc_id": "543", + "loc_data": "{3272,3182,0,1,6}-" + }, + { + "npc_id": "544", + "loc_data": "{3316,3164,0,1,3}-" + }, + { + "npc_id": "545", + "loc_data": "{3322,3195,0,1,3}-" + }, + { + "npc_id": "546", + "loc_data": "{3203,3433,0,1,3}-" + }, + { + "npc_id": "547", + "loc_data": "{3217,3435,0,0,3}-" + }, + { + "npc_id": "548", + "loc_data": "{3205,3416,0,1,6}-" + }, + { + "npc_id": "549", + "loc_data": "{3229,3438,0,1,6}-" + }, + { + "npc_id": "550", + "loc_data": "{3232,3423,0,1,6}-" + }, + { + "npc_id": "551", + "loc_data": "{3210,3400,0,1,3}-" + }, + { + "npc_id": "552", + "loc_data": "{3206,3400,0,1,3}-" + }, + { + "npc_id": "553", + "loc_data": "{3253,3403,0,1,1}-" + }, + { + "npc_id": "554", + "loc_data": "{3281,3398,0,1,3}-" + }, + { + "npc_id": "555", + "loc_data": "{2657,3153,0,1,0}-" + }, + { + "npc_id": "556", + "loc_data": "{3012,3244,0,1,6}-" + }, + { + "npc_id": "557", + "loc_data": "{3012,3203,0,1,1}-" + }, + { + "npc_id": "558", + "loc_data": "{3013,3225,0,1,1}-" + }, + { + "npc_id": "559", + "loc_data": "{3026,3252,0,1,3}-" + }, + { + "npc_id": "560", + "loc_data": "{2767,3122,0,1,6}-" + }, + { + "npc_id": "561", + "loc_data": "{2514,3385,0,1,0}-" + }, + { + "npc_id": "562", + "loc_data": "{2799,3438,0,1,1}-" + }, + { + "npc_id": "563", + "loc_data": "{2803,3430,0,1,6}-" + }, + { + "npc_id": "564", + "loc_data": "{2485,4450,0,1,3}-" + }, + { + "npc_id": "565", + "loc_data": "{2476,4467,0,1,2}-" + }, + { + "npc_id": "566", + "loc_data": "{2480,4471,0,1,4}-" + }, + { + "npc_id": "568", + "loc_data": "{2925,3143,0,1,3}-" + }, + { + "npc_id": "569", + "loc_data": "{2659,3316,0,1,2}-" + }, + { + "npc_id": "570", + "loc_data": "{2669,3303,0,1,5}-" + }, + { + "npc_id": "571", + "loc_data": "{2655,3310,0,1,1}-{2669,3310,0,1,3}-" + }, + { + "npc_id": "572", + "loc_data": "{2659,3296,0,1,4}-" + }, + { + "npc_id": "573", + "loc_data": "{2663,3296,0,1,4}-" + }, + { + "npc_id": "574", + "loc_data": "{2656,3300,0,1,3}-" + }, + { + "npc_id": "575", + "loc_data": "{2821,3442,0,1,6}-" + }, + { + "npc_id": "576", + "loc_data": "{2836,3447,0,1,4}-" + }, + { + "npc_id": "577", + "loc_data": "{2975,3384,0,1,3}-" + }, + { + "npc_id": "578", + "loc_data": "{2807,3342,0,1,6}-" + }, + { + "npc_id": "579", + "loc_data": "{3034,9845,0,1,3}-" + }, + { + "npc_id": "580", + "loc_data": "{2952,3388,0,1,3}-" + }, + { + "npc_id": "581", + "loc_data": "{2975,3314,0,1,3}-" + }, + { + "npc_id": "582", + "loc_data": "{2998,9828,0,1,1}-" + }, + { + "npc_id": "583", + "loc_data": "{3011,3257,0,1,3}-" + }, + { + "npc_id": "584", + "loc_data": "{2944,3332,0,1,3}-" + }, + { + "npc_id": "585", + "loc_data": "{2946,3205,0,1,6}-" + }, + { + "npc_id": "586", + "loc_data": "{2884,3450,0,1,3}-" + }, + { + "npc_id": "587", + "loc_data": "{2897,3428,0,1,3}-" + }, + { + "npc_id": "588", + "loc_data": "{2803,3152,0,1,3}-" + }, + { + "npc_id": "589", + "loc_data": "{2660,3291,0,1,3}-" + }, + { + "npc_id": "590", + "loc_data": "{2613,3294,0,1,3}-" + }, + { + "npc_id": "591", + "loc_data": "{2615,3292,0,1,6}-" + }, + { + "npc_id": "592", + "loc_data": "{2597,3401,0,1,2}-" + }, + { + "npc_id": "593", + "loc_data": "{2569,3098,0,1,3}-" + }, + { + "npc_id": "594", + "loc_data": "{2998,9840,0,1,3}-" + }, + { + "npc_id": "595", + "loc_data": "{3271,3412,0,1,3}-" + }, + { + "npc_id": "596", + "loc_data": "{3037,3705,0,0,0}-" + }, + { + "npc_id": "597", + "loc_data": "{3025,3701,0,1,5}-" + }, + { + "npc_id": "598", + "loc_data": "{2943,3384,0,1,3}-" + }, + { + "npc_id": "600", + "loc_data": "{2449,3511,1,1,6}-" + }, + { + "npc_id": "601", + "loc_data": "{2482,3510,1,1,0}-" + }, + { + "npc_id": "602", + "loc_data": "{2469,3488,2,1,6}-" + }, + { + "npc_id": "603", + "loc_data": "{2491,3488,1,1,6}-" + }, + { + "npc_id": "604", + "loc_data": "{3001,3144,0,0,0}-" + }, + { + "npc_id": "605", + "loc_data": "{2985,3343,2,1,2}-" + }, + { + "npc_id": "606", + "loc_data": "{2974,3343,0,1,0}-" + }, + { + "npc_id": "607", + "loc_data": "{2539,3547,0,1,4}-" + }, + { + "npc_id": "608", + "loc_data": "{2962,3337,2,1,4}-" + }, + { + "npc_id": "610", + "loc_data": "{3027,3506,0,0,0}-" + }, + { + "npc_id": "611", + "loc_data": "{3029,3505,0,1,0}-" + }, + { + "npc_id": "612", + "loc_data": "{3028,3510,0,0,0}-" + }, + { + "npc_id": "613", + "loc_data": "{3370,3387,0,1,6}-{3362,3416,0,1,6}-" + }, + { + "npc_id": "614", + "loc_data": "{3351,9756,0,1,1}-{3351,9820,0,1,1}-" + }, + { + "npc_id": "615", + "loc_data": "{3363,3397,0,1,3}-" + }, + { + "npc_id": "616", + "loc_data": "{3370,3416,0,1,3}-" + }, + { + "npc_id": "617", + "loc_data": "{3348,3424,0,1,3}-" + }, + { + "npc_id": "618", + "loc_data": "{3364,3342,0,1,3}-" + }, + { + "npc_id": "619", + "loc_data": "{3355,3333,0,1,3}-" + }, + { + "npc_id": "620", + "loc_data": "{3376,3377,0,1,0}-" + }, + { + "npc_id": "636", + "loc_data": "{2395,3494,0,0,6}-{2396,3494,0,0,6}-{2397,3494,0,0,6}-{2396,3493,0,0,6}-" + }, + { + "npc_id": "637", + "loc_data": "{3158,3425,1,1,5}-" + }, + { + "npc_id": "638", + "loc_data": "{3195,3404,0,0,0}-" + }, + { + "npc_id": "639", + "loc_data": "{3211,3425,0,1,7}-" + }, + { + "npc_id": "640", + "loc_data": "{3253,3487,0,1,6}-" + }, + { + "npc_id": "641", + "loc_data": "{3207,3392,0,1,6}-" + }, + { + "npc_id": "642", + "loc_data": "{3185,3385,0,1,6}-" + }, + { + "npc_id": "643", + "loc_data": "{3246,3383,1,1,5}-" + }, + { + "npc_id": "644", + "loc_data": "{3247,9781,0,1,6}-" + }, + { + "npc_id": "645", + "loc_data": "{3223,3401,0,1,2}-" + }, + { + "npc_id": "646", + "loc_data": "{3257,3447,0,1,0}-" + }, + { + "npc_id": "648", + "loc_data": "{3220,3471,0,0,6}-" + }, + { + "npc_id": "649", + "loc_data": "{3149,3210,0,1,0}-" + }, + { + "npc_id": "650", + "loc_data": "{3148,3205,0,1,0}-" + }, + { + "npc_id": "651", + "loc_data": "{3148,3207,0,1,0}-" + }, + { + "npc_id": "652", + "loc_data": "{3147,3209,0,1,0}-" + }, + { + "npc_id": "656", + "loc_data": "{2822,3374,0,0,3}-" + }, + { + "npc_id": "657", + "loc_data": "{3048,3236,0,1,2}-" + }, + { + "npc_id": "658", + "loc_data": "{2832,3336,0,1,2}-" + }, + { + "npc_id": "659", + "loc_data": "{3052,3373,0,0,0}-" + }, + { + "npc_id": "663", + "loc_data": "{3290,3290,0,1,2}-" + }, + { + "npc_id": "665", + "loc_data": "{2986,9811,0,0,0}-" + }, + { + "npc_id": "666", + "loc_data": "{2819,3451,0,0,3}-" + }, + { + "npc_id": "668", + "loc_data": "{3284,3503,1,1,1}-" + }, + { + "npc_id": "669", + "loc_data": "{2678,3086,1,1,1}-" + }, + { + "npc_id": "670", + "loc_data": "{2465,3496,0,1,6}-{2466,9896,0,0,6}-" + }, + { + "npc_id": "671", + "loc_data": "{2479,3463,1,1,6}-" + }, + { + "npc_id": "672", + "loc_data": "{2390,3515,1,1,6}-" + }, + { + "npc_id": "673", + "loc_data": "{2464,3495,3,0,6}-" + }, + { + "npc_id": "674", + "loc_data": "{3001,3041,0,0,0}-{2954,3025,0,0,0}-" + }, + { + "npc_id": "675", + "loc_data": "{2944,3041,0,0,0}-" + }, + { + "npc_id": "677", + "loc_data": "{2708,9486,0,0,5}-" + }, + { + "npc_id": "678", + "loc_data": "{2658,3436,0,1,3}-{2655,3431,0,1,0}-{2656,3424,0,1,1}-{2664,3426,0,1,4}-{2671,3438,0,1,1}-{2669,3441,0,1,7}-{2674,3438,0,1,5}-{2671,3418,0,1,7}-{2666,3417,0,1,3}-{2666,3415,0,1,2}-{2664,3416,0,1,3}-{2680,3431,0,1,6}-{2678,3431,0,1,1}-{2661,3430,0,1,4}-" + }, + { + "npc_id": "679", + "loc_data": "{2658,3439,0,0,0}-" + }, + { + "npc_id": "680", + "loc_data": "{2679,3432,0,1,6}-" + }, + { + "npc_id": "682", + "loc_data": "{2671,3434,0,1,4}-" + }, + { + "npc_id": "683", + "loc_data": "{2673,3434,0,1,3}-" + }, + { + "npc_id": "684", + "loc_data": "{2667,3430,2,0,6}-" + }, + { + "npc_id": "685", + "loc_data": "{2670,3429,2,0,3}-" + }, + { + "npc_id": "686", + "loc_data": "{2669,3426,2,0,1}-" + }, + { + "npc_id": "687", + "loc_data": "{2666,3427,2,0,4}-" + }, + { + "npc_id": "688", + "loc_data": "{2668,3442,2,1,6}-{2667,3444,2,1,6}-{2669,3444,2,1,6}-" + }, + { + "npc_id": "689", + "loc_data": "{2682,3428,2,1,3}-{2684,3427,2,1,3}-{2684,3429,2,1,3}-" + }, + { + "npc_id": "690", + "loc_data": "{2654,3428,2,1,1}-{2652,3427,2,1,1}-{2652,3429,2,1,1}-" + }, + { + "npc_id": "691", + "loc_data": "{2668,3414,2,1,4}-{2667,3412,2,1,4}-{2669,3412,2,1,4}-" + }, + { + "npc_id": "692", + "loc_data": "{2661,3419,0,1,7}-" + }, + { + "npc_id": "693", + "loc_data": "{2673,3416,0,0,0}-" + }, + { + "npc_id": "694", + "loc_data": "{2659,3431,0,0,3}-" + }, + { + "npc_id": "695", + "loc_data": "{2765,3276,0,1,3}-" + }, + { + "npc_id": "696", + "loc_data": "{2716,3303,0,1,1}-" + }, + { + "npc_id": "697", + "loc_data": "{2766,3288,1,0,6}-" + }, + { + "npc_id": "698", + "loc_data": "{2799,3320,0,1,1}-" + }, + { + "npc_id": "700", + "loc_data": "{2716,3303,0,1,1}-" + }, + { + "npc_id": "701", + "loc_data": "{2793,3321,0,1,1}-" + }, + { + "npc_id": "702", + "loc_data": "{2774,3273,0,1,0}-{2775,3284,0,1,0}-{2781,3290,1,1,0}-{2785,3284,1,1,0}-" + }, + { + "npc_id": "703", + "loc_data": "{2770,3284,0,1,0}-{2794,3279,0,1,0}-{2768,3285,1,1,0}-{2784,3277,1,1,0}-" + }, + { + "npc_id": "704", + "loc_data": "{2778,3291,0,1,0}-{2773,3282,1,1,0}-{2787,3280,1,1,0}-" + }, + { + "npc_id": "705", + "loc_data": "{3208,3252,0,0,6}-" + }, + { + "npc_id": "706", + "loc_data": "{3103,3163,2,0,0}-" + }, + { + "npc_id": "707", + "loc_data": "{3113,3160,2,0,0}-" + }, + { + "npc_id": "708", + "loc_data": "{2595,3125,0,1,1}-{2601,3135,0,1,3}-{2598,3120,0,1,6}-{2615,3120,0,1,4}-{2598,3108,0,1,4}-{2865,3175,0,1,0}-{2855,3188,0,1,1}-{2838,3194,0,1,3}-{2827,3170,0,1,5}-{2598,3145,0,1,1}-{2588,3145,0,1,7}-{2603,3221,0,1,7}-{2604,3212,0,1,3}-{2612,3230,0,1,0}-{2611,3204,0,1,7}-{2942,3247,0,1,7}-{2627,3231,0,1,4}-{2932,3322,0,0,5}-{2934,3301,0,1,5}-{2934,3301,0,1,3}-{2935,3343,0,1,1}-{2941,3356,0,0,7}-{3162,3410,0,1,2}-{3189,3503,0,1,6}-{3219,3221,0,1,4}-{2977,3202,0,1,7}-{2997,3300,0,1,3}-{2957,3308,0,1,0}-{2973,3274,0,1,3}-{2994,3270,0,1,0}-{3215,3282,0,1,1}-{3242,3306,0,1,1}-{2980,3337,0,1,6}-{2977,3377,0,0,3}-{3208,3357,0,1,3}-{2962,3424,0,1,6}-{3240,3393,0,1,3}-{2715,3451,0,1,4}-{2715,3436,0,1,3}-{2733,3459,0,1,3}-{2732,3457,0,1,6}-{3239,3523,0,1,4}-{3262,3523,0,1,2}-{2806,3181,0,1,3}-{2788,3179,0,1,3}-{2753,3178,0,1,3}-{2759,3155,0,1,0}-{2544,3184,0,1,3}-{3068,3251,0,1,6}-{3031,3322,0,1,1}-{3010,3271,0,1,6}-{3011,3513,0,1,1}-" + }, + { + "npc_id": "710", + "loc_data": "{2575,3333,0,1,0}-" + }, + { + "npc_id": "711", + "loc_data": "{2536,3314,1,0,0}-" + }, + { + "npc_id": "713", + "loc_data": "{2526,3319,0,0,6}-" + }, + { + "npc_id": "715", + "loc_data": "{2541,9672,0,1,0}-" + }, + { + "npc_id": "716", + "loc_data": "{2542,3286,0,1,3}-" + }, + { + "npc_id": "717", + "loc_data": "{2513,3294,0,1,0}-{2518,3320,0,1,0}-{2530,3274,0,1,0}-{2535,3296,0,1,0}-" + }, + { + "npc_id": "718", + "loc_data": "{2582,3329,0,1,2}-" + }, + { + "npc_id": "719", + "loc_data": "{2560,3288,0,1,2}-" + }, + { + "npc_id": "720", + "loc_data": "{2542,3306,0,1,0}-" + }, + { + "npc_id": "721", + "loc_data": "{2531,3331,0,1,3}-" + }, + { + "npc_id": "722", + "loc_data": "{2532,3331,0,1,3}-" + }, + { + "npc_id": "723", + "loc_data": "{2530,3331,0,1,3}-" + }, + { + "npc_id": "724", + "loc_data": "{2531,3331,1,1,6}-" + }, + { + "npc_id": "725", + "loc_data": "{2540,3305,0,1,0}-" + }, + { + "npc_id": "728", + "loc_data": "{2540,3308,0,1,0}-" + }, + { + "npc_id": "729", + "loc_data": "{2536,3308,0,1,0}-" + }, + { + "npc_id": "731", + "loc_data": "{3278,3487,0,1,1}-" + }, + { + "npc_id": "732", + "loc_data": "{3267,3392,0,0,0}-" + }, + { + "npc_id": "733", + "loc_data": "{3226,3399,0,0,0}-" + }, + { + "npc_id": "734", + "loc_data": "{3045,3256,0,1,3}-" + }, + { + "npc_id": "735", + "loc_data": "{2795,3155,0,1,7}-" + }, + { + "npc_id": "736", + "loc_data": "{2957,3373,0,1,7}-" + }, + { + "npc_id": "737", + "loc_data": "{2572,3319,0,1,3}-" + }, + { + "npc_id": "738", + "loc_data": "{2691,3492,0,0,0}-" + }, + { + "npc_id": "739", + "loc_data": "{2554,3080,0,1,3}-" + }, + { + "npc_id": "740", + "loc_data": "{2808,3086,0,1,0}-" + }, + { + "npc_id": "741", + "loc_data": "{3212,3220,1,1,1}-" + }, + { + "npc_id": "742", + "loc_data": "{2854,9636,0,1,4}-" + }, + { + "npc_id": "743", + "loc_data": "{3098,3257,0,1,4}-" + }, + { + "npc_id": "744", + "loc_data": "{3047,3204,0,0,0}-" + }, + { + "npc_id": "745", + "loc_data": "{3013,3189,0,1,1}-" + }, + { + "npc_id": "746", + "loc_data": "{3010,3502,0,1,3}-" + }, + { + "npc_id": "747", + "loc_data": "{3069,3516,0,1,6}-" + }, + { + "npc_id": "749", + "loc_data": "{1749,5323,0,1,4}-" + }, + { + "npc_id": "750", + "loc_data": "{1765,5321,0,1,2}-{1744,5326,0,1,4}-" + }, + { + "npc_id": "751", + "loc_data": "{1778,5323,0,1,6}-" + }, + { + "npc_id": "753", + "loc_data": "{2929,9649,0,1,4}-" + }, + { + "npc_id": "755", + "loc_data": "{3100,3268,0,0,0}-" + }, + { + "npc_id": "756", + "loc_data": "{3222,3398,0,0,3}-" + }, + { + "npc_id": "758", + "loc_data": "{3189,3273,0,0,4}-" + }, + { + "npc_id": "767", + "loc_data": "{3303,3511,0,0,1}-{3302,3514,0,0,1}-" + }, + { + "npc_id": "780", + "loc_data": "{3151,3410,0,1,6}-" + }, + { + "npc_id": "781", + "loc_data": "{3221,3434,0,1,0}-" + }, + { + "npc_id": "782", + "loc_data": "{3150,3406,0,0,0}-" + }, + { + "npc_id": "783", + "loc_data": "{3221,3435,0,1,0}-" + }, + { + "npc_id": "784", + "loc_data": "{3155,3406,0,0,0}-" + }, + { + "npc_id": "785", + "loc_data": "{2468,3304,0,1,0}-{2476,3317,0,1,0}-{2478,3291,0,1,0}-{2492,3312,0,1,0}-" + }, + { + "npc_id": "786", + "loc_data": "{2469,3309,0,1,0}-{2475,3298,0,1,0}-{2487,3308,0,1,0}-{2490,3289,0,1,0}-" + }, + { + "npc_id": "787", + "loc_data": "{2476,3313,0,1,0}-{2481,3289,0,1,0}-{2490,3313,0,1,0}-{2491,3300,0,1,0}-" + }, + { + "npc_id": "788", + "loc_data": "{2773,3187,0,0,6}-" + }, + { + "npc_id": "789", + "loc_data": "{2811,3167,0,1,0}-" + }, + { + "npc_id": "792", + "loc_data": "{2774,3197,0,1,0}-" + }, + { + "npc_id": "793", + "loc_data": "{2790,3189,0,1,1}-" + }, + { + "npc_id": "794", + "loc_data": "{2793,3191,0,1,3}-" + }, + { + "npc_id": "795", + "loc_data": "{2866,9956,0,1,6}-" + }, + { + "npc_id": "796", + "loc_data": "{2903,3511,0,1,3}-" + }, + { + "npc_id": "797", + "loc_data": "{2900,3511,1,1,6}-" + }, + { + "npc_id": "798", + "loc_data": "{2930,9686,0,1,0}-" + }, + { + "npc_id": "799", + "loc_data": "{2778,3197,0,1,4}-{2777,3194,0,1,4}-{2777,3199,0,1,3}-{2771,3193,0,1,3}-{2761,3192,0,1,5}-{2775,3190,0,1,4}-{2803,3195,0,1,4}-" + }, + { + "npc_id": "800", + "loc_data": "{2884,9765,0,0,0}-{2890,9766,0,0,0}-{2894,9764,0,0,0}-" + }, + { + "npc_id": "801", + "loc_data": "{3058,3485,0,1,6}-" + }, + { + "npc_id": "802", + "loc_data": "{3046,3486,1,0,0}-" + }, + { + "npc_id": "804", + "loc_data": "{2934,3285,1,1,0}-" + }, + { + "npc_id": "805", + "loc_data": "{2936,3288,0,1,4}-" + }, + { + "npc_id": "806", + "loc_data": "{2745,3576,1,0,4}-" + }, + { + "npc_id": "807", + "loc_data": "{2750,3576,0,0,4}-" + }, + { + "npc_id": "808", + "loc_data": "{2736,3575,0,1,1}-" + }, + { + "npc_id": "809", + "loc_data": "{2734,3581,0,1,1}-" + }, + { + "npc_id": "810", + "loc_data": "{2736,3576,1,1,1}-" + }, + { + "npc_id": "811", + "loc_data": "{2731,3579,0,1,4}-" + }, + { + "npc_id": "812", + "loc_data": "{2741,3562,0,1,0}-" + }, + { + "npc_id": "813", + "loc_data": "{2741,3552,0,1,1}-" + }, + { + "npc_id": "820", + "loc_data": "{2697,3496,0,0,0}-" + }, + { + "npc_id": "821", + "loc_data": "{2750,3580,0,1,0}-" + }, + { + "npc_id": "822", + "loc_data": "{3302,9466,0,1,6}-" + }, + { + "npc_id": "827", + "loc_data": "{3273,9417,0,1,6}-{3289,9443,0,1,4}-" + }, + { + "npc_id": "830", + "loc_data": "{3271,3028,0,1,4}-" + }, + { + "npc_id": "831", + "loc_data": "{3291,3032,1,0,1}-" + }, + { + "npc_id": "832", + "loc_data": "{3171,3025,0,1,4}-" + }, + { + "npc_id": "833", + "loc_data": "{3167,3033,0,1,2}-{3170,3027,0,1,5}-{3174,3028,0,1,6}-" + }, + { + "npc_id": "834", + "loc_data": "{3170,3045,0,0,3}-" + }, + { + "npc_id": "836", + "loc_data": "{3304,3124,0,1,6}-" + }, + { + "npc_id": "837", + "loc_data": "{3305,3121,0,1,0}-{3301,3120,0,1,1}-{3302,3123,0,1,6}-{3307,3125,0,1,1}-" + }, + { + "npc_id": "838", + "loc_data": "{3306,3117,0,1,4}-" + }, + { + "npc_id": "839", + "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": "{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", + "loc_data": "{3288,3022,0,1,1}-" + }, + { + "npc_id": "843", + "loc_data": "{2644,3274,0,1,3}-{2644,3276,0,1,5}-{2647,3271,0,1,6}-" + }, + { + "npc_id": "844", + "loc_data": "{2683,3323,0,0,0}-" + }, + { + "npc_id": "845", + "loc_data": "{2635,3311,0,0,0}-" + }, + { + "npc_id": "846", + "loc_data": "{2787,3184,0,1,4}-" + }, + { + "npc_id": "847", + "loc_data": "{3143,3447,0,1,5}-" + }, + { + "npc_id": "848", + "loc_data": "{2480,3489,1,1,3}-" + }, + { + "npc_id": "849", + "loc_data": "{2417,3498,1,1,0}-{2482,3487,1,1,0}-{2482,3489,1,1,0}-" + }, + { + "npc_id": "850", + "loc_data": "{2439,3502,1,0,4}-" + }, + { + "npc_id": "851", + "loc_data": "{2449,3503,1,1,4}-" + }, + { + "npc_id": "852", + "loc_data": "{2615,2987,0,1,0}-{2568,9432,0,1,0}-{2573,9447,0,1,0}-{2579,9441,0,1,0}-{2580,9431,0,1,0}-{2611,9454,0,1,0}-{2616,9447,0,1,0}-{2505,3032,0,1,0}-{2512,3022,0,1,0}-{2518,3041,0,1,0}-{2528,3044,0,1,0}-" + }, + { + "npc_id": "853", + "loc_data": "{2506,3115,0,1,3}-" + }, + { + "npc_id": "854", + "loc_data": "{2511,3085,0,1,4}-" + }, + { + "npc_id": "855", + "loc_data": "{2576,3027,0,1,3}-" + }, + { + "npc_id": "856", + "loc_data": "{2577,3021,0,1,1}-" + }, + { + "npc_id": "858", + "loc_data": "{2549,3029,0,1,0}-{2551,3031,0,1,0}-" + }, + { + "npc_id": "859", + "loc_data": "{2505,3062,0,1,0}-{2507,3062,0,1,0}-" + }, + { + "npc_id": "860", + "loc_data": "{2501,3012,0,1,0}-{2503,3011,0,1,0}-" + }, + { + "npc_id": "861", + "loc_data": "{2526,3018,0,1,0}-{2529,3019,0,1,0}-" + }, + { + "npc_id": "862", + "loc_data": "{2541,3034,0,1,3}-{2541,3029,0,1,3}-{2543,3031,0,1,3}-" + }, + { + "npc_id": "870", + "loc_data": "{2507,3040,0,1,0}-{2507,3036,0,1,0}-" + }, + { + "npc_id": "872", + "loc_data": "{2547,3117,2,1,3}-{2547,3112,2,1,6}-{2544,3114,2,1,3}-{2547,3118,2,1,3}-" + }, + { + "npc_id": "873", + "loc_data": "{2518,3035,0,1,1}-" + }, + { + "npc_id": "874", + "loc_data": "{2528,3048,0,1,6}-" + }, + { + "npc_id": "875", + "loc_data": "{2513,3034,0,1,1}-" + }, + { + "npc_id": "876", + "loc_data": "{2525,3043,0,1,3}-" + }, + { + "npc_id": "877", + "loc_data": "{2536,3092,0,1,1}-{2534,3091,0,1,1}-{2550,3118,0,1,4}-{2543,3117,0,1,3}-{2546,3113,0,1,4}-" + }, + { + "npc_id": "878", + "loc_data": "{2534,3092,0,1,1}-" + }, + { + "npc_id": "881", + "loc_data": "{3112,3162,1,0,0}-" + }, + { + "npc_id": "882", + "loc_data": "{3203,3424,0,0,6}-" + }, + { + "npc_id": "883", + "loc_data": "{3204,3470,0,1,3}-" + }, + { + "npc_id": "884", + "loc_data": "{3204,3495,2,1,6}-" + }, + { + "npc_id": "885", + "loc_data": "{2565,3271,0,0,0}-{2573,3268,1,0,0}-" + }, + { + "npc_id": "887", + "loc_data": "{2570,3275,0,1,3}-" + }, + { + "npc_id": "888", + "loc_data": "{2565,3273,0,0,0}-" + }, + { + "npc_id": "889", + "loc_data": "{2571,3270,0,0,0}-" + }, + { + "npc_id": "890", + "loc_data": "{2569,3272,0,0,0}-{2573,3269,1,0,0}-" + }, + { + "npc_id": "895", + "loc_data": "{2929,3456,0,1,4}-" + }, + { + "npc_id": "902", + "loc_data": "{2535,4715,0,1,6}-" + }, + { + "npc_id": "903", + "loc_data": "{2541,4715,0,1,3}-" + }, + { + "npc_id": "904", + "loc_data": "{2507,4693,0,1,3}-" + }, + { + "npc_id": "905", + "loc_data": "{2540,4719,0,1,6}-" + }, + { + "npc_id": "912", + "loc_data": "{3100,3927,0,1,0}-{3102,3938,0,1,0}-{3116,3934,0,1,0}-" + }, + { + "npc_id": "913", + "loc_data": "{3098,3925,0,1,0}-{3100,3940,0,1,0}-{3110,3934,0,1,0}-" + }, + { + "npc_id": "914", + "loc_data": "{3098,3942,0,1,0}-{3102,3929,0,1,0}-{3113,3934,0,1,0}-" + }, + { + "npc_id": "915", + "loc_data": "{3108,3264,0,1,2}-" + }, + { + "npc_id": "916", + "loc_data": "{3122,3245,0,0,0}-" + }, + { + "npc_id": "917", + "loc_data": "{3125,3250,0,1,6}-{3122,3249,0,1,6}-" + }, + { + "npc_id": "918", + "loc_data": "{3048,3209,1,1,3}-" + }, + { + "npc_id": "919", + "loc_data": "{3128,3245,0,1,4}-" + }, + { + "npc_id": "920", + "loc_data": "{3123,3241,0,1,1}-" + }, + { + "npc_id": "922", + "loc_data": "{3086,3261,0,1,7}-" + }, + { + "npc_id": "923", + "loc_data": "{3302,3163,0,0,0}-" + }, + { + "npc_id": "924", + "loc_data": "{3286,3180,0,0,0}-" + }, + { + "npc_id": "925", + "loc_data": "{3267,3226,0,0,3}-{3266,3229,0,0,3}-{3269,3229,0,0,4}-{3268,3226,0,0,4}-" + }, + { + "npc_id": "931", + "loc_data": "{2842,2935,0,0,0}-{2842,2929,0,0,0}-{2792,2929,0,0,0}-{2815,2935,0,0,0}-" + }, + { + "npc_id": "933", + "loc_data": "{2725,3367,0,0,2}-{2725,3380,2,0,6}-" + }, + { + "npc_id": "941", + "loc_data": "{3340,3678,0,1,1}-{3339,3695,0,1,6}-{3344,3705,0,1,3}-{3118,3820,0,1,0}-{2982,3618,0,1,3}-{3307,5462,0,1,0}-" + }, + { + "npc_id": "942", + "loc_data": "{3074,3086,0,0,0}-" + }, + { + "npc_id": "943", + "loc_data": "{3103,3096,0,0,0}-" + }, + { + "npc_id": "944", + "loc_data": "{3107,9511,0,1,5}-" + }, + { + "npc_id": "945", + "loc_data": "{3093,3107,0,0,0}-" + }, + { + "npc_id": "946", + "loc_data": "{3141,3090,0,0,6}-" + }, + { + "npc_id": "947", + "loc_data": "{3127,3124,0,0,3}-" + }, + { + "npc_id": "948", + "loc_data": "{3084,9506,0,0,0}-" + }, + { + "npc_id": "949", + "loc_data": "{3086,3122,0,0,0}-" + }, + { + "npc_id": "952", + "loc_data": "{3101,3092,0,0,4}-" + }, + { + "npc_id": "954", + "loc_data": "{3123,3107,0,1,5}-" + }, + { + "npc_id": "955", + "loc_data": "{3176,3317,0,1,3}-{3169,3319,0,1,1}-" + }, + { + "npc_id": "957", + "loc_data": "{3314,3241,0,0,1}-{3314,3240,1,0,0}-" + }, + { + "npc_id": "958", + "loc_data": "{3383,3272,0,1,3}-" + }, + { + "npc_id": "959", + "loc_data": "{3359,3272,0,1,5}-" + }, + { + "npc_id": "960", + "loc_data": "{3370,3276,0,1,3}-" + }, + { + "npc_id": "961", + "loc_data": "{3377,3276,0,1,4}-" + }, + { + "npc_id": "962", + "loc_data": "{3362,3276,0,1,3}-" + }, + { + "npc_id": "963", + "loc_data": "{3375,3274,0,1,2}-" + }, + { + "npc_id": "970", + "loc_data": "{3081,3247,0,0,1}-" + }, + { + "npc_id": "971", + "loc_data": "{2464,3287,0,1,0}-" + }, + { + "npc_id": "1005", + "loc_data": "{3057,3905,0,1,3}-" + }, + { + "npc_id": "1006", + "loc_data": "{2763,3284,0,1,0}-{2768,3274,0,1,0}-{2768,3288,0,1,0}-{2770,3277,0,1,0}-{2770,3290,0,1,0}-{2771,3279,0,1,0}-{2774,3291,0,1,0}-{2778,3285,0,1,0}-{2781,3278,0,1,0}-{2781,3285,0,1,0}-{2783,3275,0,1,0}-{2784,3279,0,1,0}-{2785,3276,0,1,0}-{2785,3287,0,1,0}-{2788,3274,0,1,0}-{2793,3275,0,1,0}-{2793,3280,0,1,0}-{2768,3282,1,1,0}-{2770,3282,1,1,0}-{2773,3291,1,1,0}-{2780,3283,1,1,0}-{2780,3290,1,1,0}-{2783,3286,1,1,0}-{2784,3279,1,1,0}-{2785,3282,1,1,0}-" + }, + { + "npc_id": "1010", + "loc_data": "{2629,2979,0,1,0}-" + }, + { + "npc_id": "1011", + "loc_data": "{2642,9394,0,1,0}-" + }, + { + "npc_id": "1012", + "loc_data": "{2650,9393,0,1,0}-" + }, + { + "npc_id": "1013", + "loc_data": "{2593,2971,0,1,0}-{2594,2971,0,1,0}-{2596,2961,0,1,0}-{2598,2971,0,1,0}-{2599,2960,0,1,0}-{2598,2960,0,1,0}-{2603,2966,0,1,0}-{2603,2970,0,1,0}-{2598,2960,0,1,0}-{2596,2963,0,1,0}-{2595,2964,0,1,0}-{2594,2964,0,1,0}-{2601,2964,0,1,0}-{2598,2969,0,1,0}-{2604,2971,0,1,0}-{2601,2972,0,1,0}-{2392,3057,0,1,0}-{2395,3056,0,1,0}-{2395,3052,0,1,0}-{2397,3048,0,1,0}-{2400,3050,0,1,0}-{2400,3053,0,1,0}-{2391,3052,0,1,0}-{2393,3047,0,1,0}-{2399,3046,0,1,0}-{2402,3044,0,1,0}-{2399,3040,0,1,0}-{2395,3042,0,1,0}-{2391,3045,0,1,0}-{2464,2912,0,1,0}-{2544,2904,0,1,0}-{2544,2988,0,1,0}-{2532,2983,0,1,0}-{2549,2983,0,1,0}-{2545,2983,0,1,0}-{2528,2986,0,1,0}-{2550,2979,0,1,0}-{2522,2978,0,1,0}-{2523,2976,0,1,0}-{2526,2974,0,1,0}-{2548,2990,0,1,0}-{2552,2988,0,1,0}-{2548,2979,0,1,0}-{2547,2991,0,1,0}-{2525,2975,0,1,0}-" + }, + { + "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}-" + }, + { + "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}-" + }, + { + "npc_id": "1020", + "loc_data": "{2697,9913,0,1,0}-{2695,9907,0,1,0}-{2693,9901,0,1,0}-{2701,9902,0,1,0}-{2704,9908,0,1,0}-{2694,9889,0,1,0}-{2694,9879,0,1,0}-{2699,9875,0,1,0}-{2700,9869,0,1,0}-{2695,9870,0,1,0}-" + }, + { + "npc_id": "1021", + "loc_data": "{2733,9882,0,1,0}-{2732,9888,0,1,0}-{2738,9889,0,1,0}-{2732,9892,0,1,0}-{2738,9894,0,1,0}-{2734,9896,0,1,0}-" + }, + { + "npc_id": "1022", + "loc_data": "{2714,9904,0,1,0}-{2718,9906,0,1,0}-{2717,9902,0,1,0}-{2720,9904,0,1,0}-{2724,9903,0,1,0}-{2727,9905,0,1,0}-{2726,9907,0,1,0}-" + }, + { + "npc_id": "1027", + "loc_data": "{2354,3608,0,0,0}-{3278,3155,0,0,0}-" + }, + { + "npc_id": "1036", + "loc_data": "{3514,3478,0,0,0}-{3514,3479,0,0,0}-{3514,3480,0,0,0}-{3514,3481,0,0,0}-{3514,3482,0,0,0}-{3514,3483,0,0,0}-" + }, + { + "npc_id": "1037", + "loc_data": "{2435,3412,0,1,3}-" + }, + { + "npc_id": "1038", + "loc_data": "{3507,3496,1,1,1}-{3508,3494,0,1,1}-" + }, + { + "npc_id": "1039", + "loc_data": "{3499,3506,1,1,5}-{3500,3505,0,1,5}-" + }, + { + "npc_id": "1040", + "loc_data": "{3475,3494,0,1,6}-" + }, + { + "npc_id": "1041", + "loc_data": "{3490,3503,1,0,0}-{3490,3500,0,1,4}-" + }, + { + "npc_id": "1042", + "loc_data": "{3495,3469,0,1,3}-" + }, + { + "npc_id": "1043", + "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", + "loc_data": "{3408,3484,0,1,6}-{3408,3493,0,1,1}-{3410,3489,1,1,3}-{3412,3487,1,1,4}-" + }, + { + "npc_id": "1045", + "loc_data": "{3413,3489,0,1,3}-{3409,3485,1,1,4}-{3413,3489,1,1,1}-{3213,3476,0,1,3}-" + }, + { + "npc_id": "1046", + "loc_data": "{3409,3489,0,1,4}-{3413,3485,0,1,4}-{3414,3491,1,1,6}-" + }, + { + "npc_id": "1048", + "loc_data": "{3437,3486,0,0,0}-" + }, + { + "npc_id": "1051", + "loc_data": "{3440,9738,1,1,0}-" + }, + { + "npc_id": "1052", + "loc_data": "{3451,3380,0,1,0}-{3445,3374,0,1,0}-{3438,3365,0,1,0}-{3429,3367,0,1,0}-{3417,3367,0,1,0}-{3413,3362,0,1,0}-{3417,3358,0,1,0}-{3426,3352,0,1,0}-{3435,3349,0,1,0}-{3442,3353,0,1,0}-{3448,3358,0,1,0}-{3426,3338,0,1,0}-{3423,3335,0,1,0}-{3420,3442,0,1,0}-{3429,3436,0,1,0}-{3439,3437,0,1,0}-{3448,3444,0,1,0}-{3449,3420,0,1,0}-{3441,3421,0,1,0}-{3427,3421,0,1,0}-{3414,3422,0,1,0}-{3411,3423,0,1,0}-{3415,3414,0,1,0}-{3410,3409,0,1,0}-{3422,3405,0,1,0}-{3431,3402,0,1,0}-{3444,3405,0,1,0}-{3453,3397,0,1,0}-{3470,3385,0,1,0}-{3463,3379,0,1,0}-{3457,3355,0,1,0}-{3461,3348,0,1,0}-{3457,3343,0,1,0}-{3471,3344,0,1,0}-{3458,3433,0,1,0}-{3459,3419,0,1,0}-{3467,3402,0,1,0}-{3473,3395,0,1,0}-" + }, + { + "npc_id": "1054", + "loc_data": "{3444,3459,0,0,0}-" + }, + { + "npc_id": "1055", + "loc_data": "{2811,3191,0,0,0}-" + }, + { + "npc_id": "1060", + "loc_data": "{2898,3528,0,0,1}-" + }, + { + "npc_id": "1061", + "loc_data": "{2898,3532,0,0,4}-" + }, + { + "npc_id": "1062", + "loc_data": "{2893,3540,0,0,3}-" + }, + { + "npc_id": "1063", + "loc_data": "{2900,3531,0,0,7}-{2900,3533,0,0,7}-" + }, + { + "npc_id": "1064", + "loc_data": "{2895,3537,0,0,1}-{2894,3537,0,0,1}-{2893,3537,0,0,1}-{2892,3537,0,0,1}-{2891,3537,0,0,1}-{2891,3538,0,0,1}-{2892,3538,0,0,1}-{2893,3538,0,0,1}-{2894,3538,0,0,1}-{2895,3538,0,0,1}-" + }, + { + "npc_id": "1065", + "loc_data": "{2889,3529,0,1,1}-{2904,3538,0,1,6}-{2882,3531,0,1,4}-{2903,3544,0,1,4}-{2911,3542,0,1,4}-{2906,3540,0,1,3}-" + }, + { + "npc_id": "1066", + "loc_data": "{2892,3532,0,0,4}-" + }, + { + "npc_id": "1067", + "loc_data": "{2893,3533,0,0,6}-" + }, + { + "npc_id": "1068", + "loc_data": "{2894,3532,0,0,3}-" + }, + { + "npc_id": "1069", + "loc_data": "{2840,3589,0,0,3}-" + }, + { + "npc_id": "1070", + "loc_data": "{2269,4758,0,1,3}-" + }, + { + "npc_id": "1071", + "loc_data": "{2820,3554,0,1,1}-" + }, + { + "npc_id": "1072", + "loc_data": "{2893,3558,0,1,3}-" + }, + { + "npc_id": "1073", + "loc_data": "{2900,3549,0,0,3}-{2897,3549,0,0,4}-{2902,3568,1,1,3}-{2903,3559,1,1,3}-{2903,3557,1,1,3}-{2903,3569,1,1,3}-{2894,3559,1,1,3}-{2892,3557,1,0,3}-" + }, + { + "npc_id": "1074", + "loc_data": "{2893,3569,1,1,3}-" + }, + { + "npc_id": "1076", + "loc_data": "{2897,3556,0,0,6}-{2896,3566,0,0,6}-" + }, + { + "npc_id": "1077", + "loc_data": "{2900,3556,0,0,6}-" + }, + { + "npc_id": "1078", + "loc_data": "{2905,3540,1,1,0}-" + }, + { + "npc_id": "1079", + "loc_data": "{2906,3537,0,1,5}-" + }, + { + "npc_id": "1080", + "loc_data": "{2900,3567,1,1,0}-" + }, + { + "npc_id": "1081", + "loc_data": "{2897,3565,0,1,4}-" + }, + { + "npc_id": "1082", + "loc_data": "{2919,3576,0,0,0}-" + }, + { + "npc_id": "1083", + "loc_data": "{2931,3546,0,1,3}-" + }, + { + "npc_id": "1084", + "loc_data": "{2915,3549,0,1,0}-" + }, + { + "npc_id": "1085", + "loc_data": "{2927,3558,0,1,6}-" + }, + { + "npc_id": "1086", + "loc_data": "{2965,3262,0,1,4}-" + }, + { + "npc_id": "1087", + "loc_data": "{2911,3540,0,1,7}-" + }, + { + "npc_id": "1088", + "loc_data": "{2918,3545,0,1,0}-" + }, + { + "npc_id": "1089", + "loc_data": "{2919,3529,0,1,0}-" + }, + { + "npc_id": "1090", + "loc_data": "{2931,3566,0,1,1}-" + }, + { + "npc_id": "1093", + "loc_data": "{2817,3560,0,1,3}-" + }, + { + "npc_id": "1094", + "loc_data": "{2802,3841,0,1,0}-{2798,3842,0,1,0}-{2804,3843,0,1,0}-{2807,3843,0,1,0}-" + }, + { + "npc_id": "1096", + "loc_data": "{2852,3591,0,1,7}-" + }, + { + "npc_id": "1097", + "loc_data": "{2858,3596,0,1,4}-" + }, + { + "npc_id": "1098", + "loc_data": "{2868,3591,0,1,6}-" + }, + { + "npc_id": "1099", + "loc_data": "{2857,3589,0,0,0}-" + }, + { + "npc_id": "1100", + "loc_data": "{2859,3589,0,0,0}-" + }, + { + "npc_id": "1101", + "loc_data": "{2854,3600,0,0,1}-" + }, + { + "npc_id": "1102", + "loc_data": "{2859,3600,0,0,1}-" + }, + { + "npc_id": "1103", + "loc_data": "{2863,3600,0,0,1}-" + }, + { + "npc_id": "1104", + "loc_data": "{2867,3600,0,0,1}-" + }, + { + "npc_id": "1105", + "loc_data": "{2870,3600,0,0,1}-{2851,3598,0,0,1}-" + }, + { + "npc_id": "1106", + "loc_data": "{2864,3594,0,1,1}-{2857,3587,0,1,7}-{2855,3596,0,1,4}-{2843,3682,0,1,1}-{2827,10078,1,1,6}-{2881,3592,0,1,5}-{2916,3631,0,1,0}-{2769,10149,0,1,0}-{2777,10142,0,1,0}-" + }, + { + "npc_id": "1107", + "loc_data": "{2874,3595,0,1,3}-{2875,3596,0,1,1}-{2873,3598,0,1,1}-{2845,3674,0,1,4}-{2836,10088,1,1,5}-{2917,3634,0,1,0}-{2781,10143,0,1,0}-" + }, + { + "npc_id": "1108", + "loc_data": "{2878,3592,0,1,6}-{2861,3586,0,1,6}-{2852,3673,0,1,2}-{2855,10058,2,1,4}-{2783,10140,0,1,0}-" + }, + { + "npc_id": "1109", + "loc_data": "{2875,3588,0,1,1}-{2851,3667,0,1,3}-{2825,10087,1,1,3}-{2836,10080,1,1,6}-{2883,3586,0,1,6}-{2909,3641,0,1,0}-{2923,10029,0,1,7}-{2784,10040,2,1,3}-{2784,10134,0,1,0}-" + }, + { + "npc_id": "1110", + "loc_data": "{2869,3594,0,1,3}-{2861,3587,0,1,4}-{2872,3594,0,1,5}-{2829,10080,1,1,6}-{2836,10097,2,1,4}-{2852,10110,2,1,4}-{2883,3591,0,1,4}-{2771,10143,0,1,0}-" + }, + { + "npc_id": "1111", + "loc_data": "{2863,3588,0,1,6}-{2865,3591,0,1,0}-{2866,3598,0,1,1}-{2855,3685,0,1,1}-{2840,10101,1,1,4}-{2857,10055,2,1,3}-{2836,10090,2,1,1}-{2925,10033,0,1,6}-{2788,10131,0,1,0}-" + }, + { + "npc_id": "1112", + "loc_data": "{2867,3595,0,1,6}-{2871,3597,0,1,4}-{2832,10089,1,1,1}-{2836,10077,2,1,1}-{2925,10030,0,1,1}-" + }, + { + "npc_id": "1113", + "loc_data": "{2829,10083,0,1,0}-" + }, + { + "npc_id": "1114", + "loc_data": "{2827,10077,0,1,0}-" + }, + { + "npc_id": "1115", + "loc_data": "{2831,10086,2,0,0}-" + }, + { + "npc_id": "1116", + "loc_data": "{2822,10073,2,0,0}-" + }, + { + "npc_id": "1117", + "loc_data": "{2829,10100,2,0,0}-" + }, + { + "npc_id": "1118", + "loc_data": "{2906,3623,0,0,6}-" + }, + { + "npc_id": "1119", + "loc_data": "{2920,3622,0,0,3}-" + }, + { + "npc_id": "1120", + "loc_data": "{2923,3616,0,0,3}-" + }, + { + "npc_id": "1121", + "loc_data": "{2923,3612,0,0,3}-" + }, + { + "npc_id": "1122", + "loc_data": "{2916,3601,0,0,0}-" + }, + { + "npc_id": "1123", + "loc_data": "{2902,3604,0,0,1}-" + }, + { + "npc_id": "1124", + "loc_data": "{2922,3606,0,0,3}-" + }, + { + "npc_id": "1125", + "loc_data": "{2911,3612,0,0,3}-" + }, + { + "npc_id": "1128", + "loc_data": "{2832,10079,0,0,4}-" + }, + { + "npc_id": "1129", + "loc_data": "{2832,10083,0,0,4}-" + }, + { + "npc_id": "1130", + "loc_data": "{2902,3696,0,0,1}-" + }, + { + "npc_id": "1131", + "loc_data": "{2898,3698,0,0,1}-" + }, + { + "npc_id": "1132", + "loc_data": "{2893,3700,0,0,1}-" + }, + { + "npc_id": "1133", + "loc_data": "{2889,3699,0,0,1}-" + }, + { + "npc_id": "1134", + "loc_data": "{2886,3698,0,0,1}-" + }, + { + "npc_id": "1135", + "loc_data": "{2843,10057,1,0,0}-" + }, + { + "npc_id": "1136", + "loc_data": "{2840,10056,1,0,0}-" + }, + { + "npc_id": "1137", + "loc_data": "{2847,10056,1,0,0}-" + }, + { + "npc_id": "1138", + "loc_data": "{2826,10083,1,1,0}-{2837,10083,2,1,1}-{2904,3642,0,1,3}-{2923,10031,0,1,3}-" + }, + { + "npc_id": "1139", + "loc_data": "{2854,3689,0,0,6}-" + }, + { + "npc_id": "1140", + "loc_data": "{2819,3586,0,1,4}-{2856,10054,1,0,0}-{2860,10057,1,0,0}-{2892,3685,0,1,0}-{2900,3671,0,1,0}-" + }, + { + "npc_id": "1141", + "loc_data": "{2857,10059,1,0,0}-{2857,10057,1,0,0}-{2893,3678,0,1,0}-" + }, + { + "npc_id": "1142", + "loc_data": "{2851,10089,0,1,1}-{2854,10092,0,1,3}-" + }, + { + "npc_id": "1143", + "loc_data": "{2850,10088,0,1,3}-{2854,10086,0,1,6}-" + }, + { + "npc_id": "1144", + "loc_data": "{2856,10089,0,1,4}-{2858,10089,0,1,6}-" + }, + { + "npc_id": "1146", + "loc_data": "{2864,10081,0,1,7}-" + }, + { + "npc_id": "1147", + "loc_data": "{2853,10076,0,1,5}-" + }, + { + "npc_id": "1148", + "loc_data": "{2858,10082,0,1,3}-" + }, + { + "npc_id": "1149", + "loc_data": "{2861,10085,0,1,2}-" + }, + { + "npc_id": "1150", + "loc_data": "{2857,10075,0,1,1}-" + }, + { + "npc_id": "1151", + "loc_data": "{2845,10058,1,0,0}-" + }, + { + "npc_id": "1153", + "loc_data": "{3489,9509,2,1,4}-{3499,9515,2,1,6}-{3505,9527,2,1,5}-{3510,9521,2,1,1}-{3490,9526,2,1,3}-{3475,9524,2,1,5}-{3465,9505,2,1,1}-{3479,9501,2,1,3}-{3487,9491,2,1,3}-" + }, + { + "npc_id": "1154", + "loc_data": "{3472,9501,2,1,3}-{3495,9494,2,1,4}-{3479,9484,2,1,1}-{3500,9477,2,1,6}-{3470,9482,2,1,6}-{3465,9481,2,1,3}-{3465,9491,2,1,6}-{3468,9488,2,1,0}-" + }, + { + "npc_id": "1155", + "loc_data": "{3505,9496,2,1,6}-{3513,9496,2,1,7}-" + }, + { + "npc_id": "1157", + "loc_data": "{3480,9508,0,1,0}-{3495,9505,0,1,2}-{3492,9490,0,1,1}-{3472,9497,0,1,6}-" + }, + { + "npc_id": "1158", + "loc_data": "{3484,9491,0,1,2}-" + }, + { + "npc_id": "1161", + "loc_data": "{3487,9526,2,1,5}-{3474,9519,2,1,1}-{3465,9512,2,1,0}-{3489,9501,2,1,4}-{3494,9501,2,1,3}-{3480,9489,2,1,6}-{3483,9477,2,1,3}-{3494,9476,2,1,3}-{3473,9487,0,1,3}-{3476,9496,0,1,6}-{3473,9501,0,1,7}-{3481,9504,0,1,6}-{3488,9514,0,1,6}-{3497,9501,0,1,3}-{3486,9490,0,1,0}-" + }, + { + "npc_id": "1162", + "loc_data": "{2781,3088,1,1,0}-" + }, + { + "npc_id": "1164", + "loc_data": "{2780,3058,0,1,0}-" + }, + { + "npc_id": "1171", + "loc_data": "{2766,3168,0,1,6}-" + }, + { + "npc_id": "1172", + "loc_data": "{2909,3086,0,0,0}-" + }, + { + "npc_id": "1174", + "loc_data": "{2792,3015,0,0,0}-{2792,3019,0,0,0}-{2801,3010,0,0,0}-{2801,3010,0,0,4}-" + }, + { + "npc_id": "1176", + "loc_data": "{2928,3112,0,0,0}-" + }, + { + "npc_id": "1177", + "loc_data": "{2912,3119,0,0,0}-" + }, + { + "npc_id": "1178", + "loc_data": "{2768,3165,0,0,0}-" + }, + { + "npc_id": "1179", + "loc_data": "{2941,3100,0,1,6}-" + }, + { + "npc_id": "1180", + "loc_data": "{2916,3110,0,1,7}-" + }, + { + "npc_id": "1181", + "loc_data": "{2902,3101,0,1,1}-{2922,3105,0,1,6}-" + }, + { + "npc_id": "1183", + "loc_data": "{2206,3258,0,1,3}-{2200,3261,0,1,3}-{2193,3253,0,1,3}-{2192,3243,0,1,3}-" + }, + { + "npc_id": "1184", + "loc_data": "{2192,3249,0,1,3}-{2200,3249,0,1,3}-{2195,3247,0,1,3}-{2212,3258,0,1,3}-" + }, + { + "npc_id": "1192", + "loc_data": "{2924,3506,0,1,0}-{2927,3498,0,0,0}-{2297,3176,0,1,6}-" + }, + { + "npc_id": "1193", + "loc_data": "{2297,3176,0,1,6}-" + }, + { + "npc_id": "1194", + "loc_data": "{2298,3177,0,1,4}-" + }, + { + "npc_id": "1195", + "loc_data": "{2215,3171,0,1,4}-{2200,3224,0,1,0}-{2293,3156,0,1,3}-" + }, + { + "npc_id": "1196", + "loc_data": "{2217,3171,0,1,0}-{2199,3222,0,1,0}-{2201,3218,0,1,0}-{2291,3156,0,1,0}-" + }, + { + "npc_id": "1197", + "loc_data": "{2197,3218,0,1,0}-{2292,3153,0,1,0}-" + }, + { + "npc_id": "1198", + "loc_data": "{2296,3192,0,1,2}-" + }, + { + "npc_id": "1199", + "loc_data": "{2194,3255,0,0,6}-{2262,3151,0,1,0}-" + }, + { + "npc_id": "1201", + "loc_data": "{2198,3250,0,0,6}-{2203,3250,0,0,4}-{2264,3147,0,1,1}-{2266,3145,0,1,4}-" + }, + { + "npc_id": "1202", + "loc_data": "{2352,3172,0,1,4}-" + }, + { + "npc_id": "1203", + "loc_data": "{2189,3155,0,0,1}-{2183,3156,0,1,4}-{2194,3156,0,1,6}-{2182,3148,0,1,4}-{2179,3137,0,1,3}-{2189,3136,0,1,3}-{2182,3146,0,1,3}-{2187,3172,0,0,1}-{2180,3180,0,1,1}-" + }, + { + "npc_id": "1205", + "loc_data": "{3054,3254,0,0,0}-" + }, + { + "npc_id": "1206", + "loc_data": "{2185,3144,0,0,4}-" + }, + { + "npc_id": "1207", + "loc_data": "{3055,3254,0,0,0}-" + }, + { + "npc_id": "1208", + "loc_data": "{2194,3140,0,1,5}-" + }, + { + "npc_id": "1211", + "loc_data": "{2914,3418,0,0,6}-" + }, + { + "npc_id": "1212", + "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": "{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", + "loc_data": "{2891,3676,0,0,1}-" + }, + { + "npc_id": "1215", + "loc_data": "{2612,3287,0,0,0}-{2613,3287,0,0,0}-" + }, + { + "npc_id": "1216", + "loc_data": "{2611,3285,0,0,0}-" + }, + { + "npc_id": "1217", + "loc_data": "{3000,3386,0,1,0}-{3019,3368,0,1,0}-" + }, + { + "npc_id": "1218", + "loc_data": "{3412,3512,0,1,4}-{3412,3515,0,1,1}-{3413,3514,0,1,6}-{3414,3512,0,1,5}-{3415,3518,0,1,5}-{3416,3509,0,1,7}-{3416,3511,0,1,4}-{3417,3518,0,1,4}-{3418,3509,0,1,4}-{3419,3512,0,1,7}-{3420,3517,0,1,7}-{3420,3518,0,1,6}-{3423,3461,0,1,5}-{3426,3465,0,1,4}-{3427,3463,0,1,5}-{3428,3458,0,1,4}-{3428,3465,0,1,2}-{3430,3462,0,1,1}-{3430,3467,0,1,3}-{3433,3458,0,1,1}-{3433,3468,0,1,4}-{3434,3464,0,1,6}-" + }, + { + "npc_id": "1219", + "loc_data": "{3588,3461,0,1,0}-{3589,3493,0,1,0}-{3590,3509,0,1,0}-{3596,3476,0,1,0}-{3599,3483,0,1,0}-{3607,3460,0,1,0}-{3613,3499,0,1,0}-{3614,3515,0,1,0}-{3620,3472,0,1,0}-{3623,3504,0,1,0}-{3626,3475,0,1,0}-{3626,3491,0,1,0}-{3629,3511,0,1,0}-{3633,3463,0,1,0}-{3644,3498,0,1,0}-{3537,3496,0,1,0}-{3545,3502,0,1,0}-{3546,3462,0,1,0}-{3548,3480,0,1,0}-{3551,3489,0,1,0}-{3554,3506,0,1,0}-{3560,3462,0,1,0}-{3566,3483,0,1,0}-{3567,3474,0,1,0}-{3572,3492,0,1,0}-{3574,3507,0,1,0}-" + }, + { + "npc_id": "1221", + "loc_data": "{3597,3489,0,1,0}-{3611,3462,0,1,0}-{3620,3500,0,1,0}-{3636,3484,0,1,0}-{3566,3457,0,1,0}-{3567,3503,0,1,0}-" + }, + { + "npc_id": "1223", + "loc_data": "{3585,3478,0,1,0}-{3586,3480,0,1,0}-{3586,3496,0,1,0}-{3592,3477,0,1,0}-{3593,3458,0,1,0}-{3593,3508,0,1,0}-{3595,3491,0,1,0}-{3597,3479,0,1,0}-{3607,3466,0,1,0}-{3617,3477,0,1,0}-{3618,3514,0,1,0}-{3631,3471,0,1,0}-{3631,3499,0,1,0}-{3632,3459,0,1,0}-{3632,3475,0,1,0}-{3638,3472,0,1,0}-{3646,3467,0,1,0}-" + }, + { + "npc_id": "1227", + "loc_data": "{3424,3318,0,1,0}-{3423,3312,0,1,4}-{3494,3388,0,1,0}-{3507,3435,0,1,4}-{3511,3455,0,1,0}-{3508,3420,0,1,3}-{3508,3420,0,1,4}-{3506,3402,0,1,7}-{3502,3392,0,1,3}-{3484,3456,0,1,0}-{3486,3458,0,1,4}-" + }, + { + "npc_id": "1228", + "loc_data": "{3469,3346,0,1,0}-{3494,3447,0,1,0}-{3530,3440,0,1,0}-{3556,3453,0,1,0}-" + }, + { + "npc_id": "1229", + "loc_data": "{3421,3302,0,1,4}-{3417,3291,0,1,4}-{3423,3288,0,1,4}-{3418,3284,0,1,6}-{3427,3298,0,1,3}-{3431,3303,0,1,1}-{3432,3311,0,1,1}-{3502,3455,0,1,2}-{3516,3415,0,1,4}-{3511,3401,0,1,1}-{3492,3457,0,1,1}-" + }, + { + "npc_id": "1230", + "loc_data": "{3419,3280,0,1,4}-{3421,3277,0,1,4}-{3431,3275,0,1,3}-{3428,3314,0,1,1}-{3436,3318,0,1,5}-{3434,3322,0,1,6}-{3496,3447,0,1,7}-{3520,3437,0,1,4}-" + }, + { + "npc_id": "1231", + "loc_data": "{3429,3313,0,1,0}-{3429,3314,0,1,0}-{3437,3312,0,1,0}-{3437,3311,0,1,0}-{3451,3308,0,1,0}-{3447,3268,0,1,0}-{3438,3354,0,1,0}-{3504,3437,0,1,0}-{3509,3437,0,1,0}-{3498,3419,0,1,0}-{3493,3413,0,1,0}-{3524,3397,0,1,0}-" + }, + { + "npc_id": "1232", + "loc_data": "{3441,3265,0,1,5}-{3438,3277,0,1,7}-{3438,3283,0,1,4}-{3439,3315,0,1,3}-{3439,3310,0,1,1}-{3435,3302,0,1,3}-{3508,3420,0,1,1}-{3514,3415,0,1,7}-{3525,3417,0,1,3}-" + }, + { + "npc_id": "1233", + "loc_data": "{3485,3443,0,1,0}-{3516,3441,0,1,0}-{3537,3428,0,1,0}-" + }, + { + "npc_id": "1234", + "loc_data": "{3446,3282,0,1,1}-{3446,3298,0,1,3}-{3449,3304,0,1,1}-{3513,3383,0,1,0}-{3491,3388,0,1,3}-{3493,3454,0,1,2}-{3493,3454,0,1,0}-{3518,3454,0,1,2}-{3519,3441,0,1,7}-{3506,3417,0,1,5}-{3519,3401,0,1,2}-{3499,3420,0,1,3}-{3516,3410,0,1,7}-{3520,3412,0,1,0}-" + }, + { + "npc_id": "1235", + "loc_data": "{3425,3306,0,1,1}-{3442,3303,0,1,3}-{3437,3299,0,1,1}-{3422,3321,0,1,1}-{3509,3386,0,1,2}-{3503,3420,0,1,3}-{3514,3398,0,1,4}-{3486,3458,0,1,7}-{3524,3419,0,1,1}-" + }, + { + "npc_id": "1239", + "loc_data": "{3166,3033,0,1,1}-{3174,3043,0,1,6}-{3177,3041,0,1,4}-{3176,3038,0,1,4}-{3177,3035,0,1,7}-{3183,3035,0,1,6}-{3182,3032,0,1,6}-" + }, + { + "npc_id": "1240", + "loc_data": "{3502,3288,0,1,4}-{3505,3284,0,1,4}-{3490,3292,0,1,4}-{3502,3300,0,1,4}-{3492,3303,0,1,4}-{3516,3303,0,1,4}-{3478,3281,0,1,4}-{3473,3273,0,1,4}-{3469,3288,0,1,4}-{3469,3304,0,1,4}-" + }, + { + "npc_id": "1251", + "loc_data": "{3474,3309,0,1,5}-{3486,3277,0,1,4}-{3496,3289,0,0,3}-" + }, + { + "npc_id": "1253", + "loc_data": "{3488,3297,0,0,6}-" + }, + { + "npc_id": "1257", + "loc_data": "{3490,3290,0,0,3}-{3487,3303,0,1,5}-{3496,3296,0,1,4}-{3479,3303,0,1,4}-{3498,3282,0,1,3}-{3473,3281,0,1,3}-{3478,3308,0,1,1}-" + }, + { + "npc_id": "1258", + "loc_data": "{3488,3287,0,1,1}-{3501,3285,0,1,5}-{3478,3274,0,1,4}-{3497,3283,0,1,1}-" + }, + { + "npc_id": "1261", + "loc_data": "{3492,3300,0,1,4}-{3498,3300,0,1,5}-{3487,3302,0,1,6}-{3497,3299,0,1,5}-{3488,3273,0,1,6}-" + }, + { + "npc_id": "1262", + "loc_data": "{3487,3283,0,1,1}-{3477,3300,0,1,4}-{3497,3276,0,1,1}-{3502,3279,0,1,1}-{3481,3296,0,1,2}-" + }, + { + "npc_id": "1263", + "loc_data": "{3108,3160,1,1,1}-" + }, + { + "npc_id": "1266", + "loc_data": "{2680,3719,0,0,7}-{2684,3722,0,0,1}-{2684,3726,0,0,1}-{2678,3728,0,0,2}-{2673,3728,0,0,0}-{2670,3724,0,0,3}-{2671,3726,0,0,2}-{2679,3731,0,0,2}-{2684,3729,0,0,6}-{2706,3711,0,0,1}-{2688,3721,0,0,1}-{2693,3715,0,0,5}-{2699,3715,0,0,2}-{2712,3718,0,0,5}-{2716,3721,0,0,7}-{2722,3726,0,0,1}-{2524,3764,0,0,7}-{2508,3762,0,0,0}-{2510,3755,0,0,2}-{2523,3756,0,0,1}-{2532,3753,0,0,0}-{2529,3755,0,0,1}-{2528,3739,0,0,6}-{2529,3730,0,0,0}-{2518,3724,0,0,1}-{2508,3723,0,0,1}-{2525,3718,0,0,1}-{2549,3733,0,0,3}-" + }, + { + "npc_id": "1268", + "loc_data": "{2681,3716,0,0,2}-{2678,3721,0,0,2}-{2684,3719,0,0,1}-{2699,3719,0,0,4}-{2701,3717,0,0,2}-{2707,3719,0,0,0}-{2700,3726,0,0,2}-{2695,3713,0,0,2}-{2716,3718,0,0,5}-{2706,3713,0,0,2}-{2539,3761,0,0,7}-{2539,3759,0,0,7}-{2528,3767,0,0,3}-{2526,3753,0,0,2}-{2513,3755,0,0,3}-{2505,3753,0,0,3}-{2530,3751,0,0,1}-{2526,3755,0,0,1}-{2532,3758,0,0,6}-{2532,3751,0,0,3}-{2530,3737,0,0,2}-{2531,3728,0,0,0}-{2523,3723,0,0,1}-{2520,3724,0,0,3}-{2515,3718,0,0,3}-{2505,3724,0,0,4}-{2505,3729,0,0,6}-{2535,3718,0,0,4}-{2536,3724,0,0,1}-{2543,3729,0,0,6}-{2550,3734,0,0,1}-{2552,3747,0,0,6}-{2552,3752,0,0,6}-" + }, + { + "npc_id": "1269", + "loc_data": "{2672,3683,0,1,3}-" + }, + { + "npc_id": "1270", + "loc_data": "{2770,3624,0,0,0}-" + }, + { + "npc_id": "1274", + "loc_data": "{2660,3680,0,0,6}-" + }, + { + "npc_id": "1275", + "loc_data": "{2657,3680,0,0,6}-" + }, + { + "npc_id": "1276", + "loc_data": "{2658,3679,0,0,6}-" + }, + { + "npc_id": "1277", + "loc_data": "{2659,3678,0,0,6}-" + }, + { + "npc_id": "1278", + "loc_data": "{2659,3664,0,1,3}-" + }, + { + "npc_id": "1281", + "loc_data": "{2661,3651,0,1,3}-" + }, + { + "npc_id": "1282", + "loc_data": "{2640,3681,0,1,4}-" + }, + { + "npc_id": "1283", + "loc_data": "{2647,3660,0,1,5}-" + }, + { + "npc_id": "1284", + "loc_data": "{2658,3673,0,1,1}-" + }, + { + "npc_id": "1285", + "loc_data": "{2657,3669,0,1,1}-" + }, + { + "npc_id": "1286", + "loc_data": "{2656,3676,0,1,1}-" + }, + { + "npc_id": "1287", + "loc_data": "{2652,3588,0,1,1}-" + }, + { + "npc_id": "1288", + "loc_data": "{2634,3667,0,1,1}-" + }, + { + "npc_id": "1289", + "loc_data": "{2667,3692,0,1,1}-" + }, + { + "npc_id": "1294", + "loc_data": "{2658,3673,0,1,7}-" + }, + { + "npc_id": "1296", + "loc_data": "{2660,3663,0,0,6}-{2657,3663,0,0,6}-" + }, + { + "npc_id": "1298", + "loc_data": "{2663,3646,0,0,6}-" + }, + { + "npc_id": "1299", + "loc_data": "{2660,3646,0,0,6}-" + }, + { + "npc_id": "1300", + "loc_data": "{2662,3674,0,1,1}-" + }, + { + "npc_id": "1301", + "loc_data": "{2623,3675,0,1,6}-" + }, + { + "npc_id": "1302", + "loc_data": "{2641,3699,0,0,4}-" + }, + { + "npc_id": "1303", + "loc_data": "{2664,3692,0,1,3}-" + }, + { + "npc_id": "1304", + "loc_data": "{2582,3845,0,0,3}-" + }, + { + "npc_id": "1305", + "loc_data": "{2643,3676,0,1,7}-" + }, + { + "npc_id": "1306", + "loc_data": "{2674,3677,0,1,6}-" + }, + { + "npc_id": "1307", + "loc_data": "{2658,3673,0,1,7}-" + }, + { + "npc_id": "1308", + "loc_data": "{2672,3662,0,1,3}-" + }, + { + "npc_id": "1309", + "loc_data": "{2667,3653,0,1,3}-" + }, + { + "npc_id": "1310", + "loc_data": "{2667,3701,0,1,4}-" + }, + { + "npc_id": "1311", + "loc_data": "{2655,3651,0,1,3}-" + }, + { + "npc_id": "1312", + "loc_data": "{2639,3651,0,1,6}-" + }, + { + "npc_id": "1313", + "loc_data": "{2626,3653,0,1,5}-" + }, + { + "npc_id": "1314", + "loc_data": "{2675,3677,0,1,3}-" + }, + { + "npc_id": "1315", + "loc_data": "{2646,3675,0,1,3}-" + }, + { + "npc_id": "1317", + "loc_data": "{2632,3677,0,1,2}-{2641,3682,0,1,3}-{2643,3675,0,1,6}-{2643,3674,0,1,4}-{2646,3668,0,1,3}-{2651,3669,0,1,3}-{2648,3688,0,1,4}-{2656,3692,0,1,1}-" + }, + { + "npc_id": "1318", + "loc_data": "{2613,3668,0,1,3}-{2671,3658,0,1,2}-{2638,3648,0,1,3}-{2626,3658,0,1,4}-{2626,3668,0,1,1}-{2630,3673,0,1,7}-{2635,3680,0,1,4}-{2644,3675,0,1,5}-{2642,3681,0,1,3}-{2631,3678,0,1,6}-{2661,3698,0,1,1}-{2663,3687,0,1,3}-{2673,3695,0,1,3}-{2674,3710,0,1,1}-{2676,3693,0,1,3}-{2675,3685,0,1,4}-{2669,3677,0,1,1}-{2667,3670,0,1,2}-{2675,3670,0,1,4}-{2679,3685,0,1,2}-{2667,3671,0,1,3}-{2666,3663,0,1,6}-{2658,3657,0,1,4}-{2684,3649,0,1,4}-{2679,3649,0,1,4}-{2646,3664,0,1,4}-{2646,3655,0,1,1}-{2637,3655,0,1,3}-{2667,3658,0,1,3}-{2688,3659,0,1,2}-" + }, + { + "npc_id": "1320", + "loc_data": "{2738,3636,0,1,7}-{2739,3636,0,1,7}-{2738,3637,0,1,4}-{2735,3637,0,1,3}-" + }, + { + "npc_id": "1321", + "loc_data": "{2736,3642,0,1,4}-{2744,3638,0,1,4}-" + }, + { + "npc_id": "1329", + "loc_data": "{3085,3640,0,1,0}-" + }, + { + "npc_id": "1334", + "loc_data": "{2509,3639,1,1,3}-" + }, + { + "npc_id": "1335", + "loc_data": "{2518,4633,0,1,1}-" + }, + { + "npc_id": "1336", + "loc_data": "{2508,3635,0,1,3}-" + }, + { + "npc_id": "1337", + "loc_data": "{2445,4599,1,1,3}-" + }, + { + "npc_id": "1338", + "loc_data": "{3151,5559,0,1,2}-{3157,5548,0,1,3}-{3155,5553,0,1,4}-{3145,5559,0,1,6}-{3152,5556,0,1,3}-{3149,5544,0,1,0}-{3145,5548,0,1,3}-" + }, + { + "npc_id": "1339", + "loc_data": "{3153,5547,0,1,0}-{3150,5545,0,1,1}-" + }, + { + "npc_id": "1340", + "loc_data": "{3147,5557,0,1,3}-{3154,5559,0,1,4}-" + }, + { + "npc_id": "1343", + "loc_data": "{3145,5548,0,1,6}-" + }, + { + "npc_id": "1357", + "loc_data": "{2201,4942,0,1,4}-" + }, + { + "npc_id": "1358", + "loc_data": "{2208,4959,0,1,6}-" + }, + { + "npc_id": "1359", + "loc_data": "{2612,3875,1,1,6}-" + }, + { + "npc_id": "1360", + "loc_data": "{2620,3896,0,0,3}-" + }, + { + "npc_id": "1369", + "loc_data": "{2527,5876,0,1,0}-" + }, + { + "npc_id": "1373", + "loc_data": "{2500,3860,1,1,4}-" + }, + { + "npc_id": "1374", + "loc_data": "{2505,3854,1,0,4}-{2509,3849,0,0,4}-" + }, + { + "npc_id": "1375", + "loc_data": "{2501,3858,1,0,4}-" + }, + { + "npc_id": "1376", + "loc_data": "{2548,3895,0,1,0}-" + }, + { + "npc_id": "1377", + "loc_data": "{2544,3843,0,1,6}-" + }, + { + "npc_id": "1378", + "loc_data": "{2516,3865,0,1,4}-" + }, + { + "npc_id": "1385", + "loc_data": "{2629,3694,0,0,6}-" + }, + { + "npc_id": "1389", + "loc_data": "{2603,3877,0,1,1}-" + }, + { + "npc_id": "1390", + "loc_data": "{2604,3876,0,1,4}-" + }, + { + "npc_id": "1391", + "loc_data": "{2604,3876,0,1,3}-" + }, + { + "npc_id": "1393", + "loc_data": "{2519,3868,0,0,3}-" + }, + { + "npc_id": "1394", + "loc_data": "{2519,3863,0,0,1}-" + }, + { + "npc_id": "1395", + "loc_data": "{2547,3868,0,0,1}-" + }, + { + "npc_id": "1396", + "loc_data": "{2525,3892,0,0,4}-" + }, + { + "npc_id": "1397", + "loc_data": "{2573,3856,0,0,4}-" + }, + { + "npc_id": "1398", + "loc_data": "{2526,3850,0,0,6}-" + }, + { + "npc_id": "1399", + "loc_data": "{2577,3854,0,0,1}-" + }, + { + "npc_id": "1400", + "loc_data": "{3037,4512,0,1,0}-{3049,4498,0,1,1}-" + }, + { + "npc_id": "1401", + "loc_data": "{2605,3878,0,1,1}-" + }, + { + "npc_id": "1402", + "loc_data": "{2603,3877,0,1,1}-" + }, + { + "npc_id": "1404", + "loc_data": "{2239,3164,0,1,2}-{2241,3166,0,1,3}-{2242,3163,0,1,3}-{3037,4492,0,1,4}-{3036,4502,0,1,4}-{3029,4520,0,1,1}-{3019,4512,0,1,1}-" + }, + { + "npc_id": "1405", + "loc_data": "{2576,3843,0,0,6}-" + }, + { + "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}-" + }, + { + "npc_id": "1447", + "loc_data": "{2761,2785,0,1,3}-" + }, + { + "npc_id": "1452", + "loc_data": "{2743,2796,0,1,6}-" + }, + { + "npc_id": "1454", + "loc_data": "{2742,2790,0,1,1}-" + }, + { + "npc_id": "1457", + "loc_data": "{2747,2782,0,0,1}-{2754,2778,0,1,1}-{2761,2778,0,1,1}-{2765,2779,0,1,4}-{2767,2784,0,1,6}-{2772,2781,0,1,5}-{2774,2787,0,1,1}-{2777,2790,0,1,4}-{2762,2793,0,1,3}-{2756,2789,0,1,3}-" + }, + { + "npc_id": "1458", + "loc_data": "{2728,2751,0,1,1}-{2715,2749,0,1,2}-{2728,2758,0,1,1}-{2727,2762,0,1,6}-{2728,2755,0,1,6}-{2714,2753,0,1,1}-{2714,2757,0,1,6}-{2714,2761,0,1,6}-" + }, + { + "npc_id": "1459", + "loc_data": "{2790,2791,0,1,4}-{2803,2785,0,1,7}-{2797,2792,0,1,0}-{2800,2779,0,1,5}-{2788,2779,0,1,7}-{2795,2776,0,1,3}-" + }, + { + "npc_id": "1460", + "loc_data": "{2787,2794,0,1,3}-" + }, + { + "npc_id": "1463", + "loc_data": "{2600,3276,0,1,4}-{2601,3276,0,1,6}-{2601,3277,0,1,6}-{2601,3278,0,1,2}-{2602,3279,0,1,5}-{2602,3282,0,1,4}-{2604,3276,0,1,4}-{2605,3280,0,1,4}-{2605,3281,0,1,7}-{2606,3280,0,1,4}-" + }, + { + "npc_id": "1469", + "loc_data": "{2595,3277,0,0,0}-" + }, + { + "npc_id": "1471", + "loc_data": "{2707,9102,0,1,0}-{2704,9107,0,1,0}-{2705,9110,0,1,0}-{2708,9107,0,1,0}-{2743,9105,0,1,0}-{2741,9103,0,1,0}-{2744,9101,0,1,0}-{2743,9099,0,1,0}-{2737,9124,0,1,0}-{2741,9123,0,1,0}-{2740,9120,0,1,0}-{2736,9119,0,1,0}-{2734,9117,0,1,0}-{2712,9135,0,1,0}-{2714,9131,0,1,0}-{2709,9131,0,1,0}-{2708,9135,0,1,0}-" + }, + { + "npc_id": "1514", + "loc_data": "{3432,3487,0,0,0}-" + }, + { + "npc_id": "1515", + "loc_data": "{3438,3487,0,0,0}-" + }, + { + "npc_id": "1516", + "loc_data": "{3437,3487,0,0,0}-" + }, + { + "npc_id": "1517", + "loc_data": "{3434,3483,0,0,0}-" + }, + { + "npc_id": "1518", + "loc_data": "{3435,3483,0,0,0}-" + }, + { + "npc_id": "1519", + "loc_data": "{3433,3486,0,0,0}-" + }, + { + "npc_id": "1520", + "loc_data": "{3434,3486,0,0,0}-" + }, + { + "npc_id": "1526", + "loc_data": "{2443,3089,0,0,0}-" + }, + { + "npc_id": "1544", + "loc_data": "{2911,10175,0,0,4}-{2912,10175,0,0,4}-{2913,10175,0,0,4}-{2914,10175,0,0,4}-{2909,10173,0,0,4}-{2910,10173,0,0,4}-{2911,10173,0,0,4}-{2912,10173,0,0,4}-{2913,10173,0,0,4}-{2909,10171,0,0,4}-{2910,10171,0,0,4}-{2911,10169,0,0,4}-{2912,10169,0,0,4}-{2913,10169,0,0,4}-" + }, + { + "npc_id": "1553", + "loc_data": "{2828,10065,1,0,0}-" + }, + { + "npc_id": "1554", + "loc_data": "{2829,10104,1,0,0}-" + }, + { + "npc_id": "1555", + "loc_data": "{2829,10096,1,0,0}-" + }, + { + "npc_id": "1558", + "loc_data": "{2800,3859,0,1,0}-{2803,3864,0,1,0}-{2805,3859,0,1,0}-" + }, + { + "npc_id": "1560", + "loc_data": "{3150,5454,0,1,4}-{3144,5446,0,1,7}-{3166,5446,0,1,3}-{3158,5451,0,1,7}-{2775,10224,0,1,0}-{2783,10199,0,1,0}-{2784,10233,0,1,0}-" + }, + { + "npc_id": "1561", + "loc_data": "{3144,5450,0,1,1}-{3153,5454,0,1,5}-{3160,5450,0,1,5}-{3167,5450,0,1,3}-{3161,5451,0,1,1}-{2777,10224,0,1,0}-{2784,10231,0,1,0}-{2792,10222,0,1,0}-" + }, + { + "npc_id": "1562", + "loc_data": "{3147,5448,0,1,4}-{2772,10221,0,1,0}-{2785,10201,0,1,0}-{2787,10232,0,1,0}-" + }, + { + "npc_id": "1563", + "loc_data": "{3152,5453,0,1,0}-{2773,10220,0,1,0}-{2788,10198,0,1,0}-{2790,10220,0,1,0}-{2800,10218,0,1,0}-" + }, + { + "npc_id": "1564", + "loc_data": "{2770,10219,0,1,0}-{2788,10230,0,1,0}-{2798,10216,0,1,0}-" + }, + { + "npc_id": "1565", + "loc_data": "{2788,10221,0,1,0}-{2801,10216,0,1,0}-" + }, + { + "npc_id": "1566", + "loc_data": "{2786,10199,0,1,0}-{2790,10222,0,1,0}-{2799,10214,0,1,0}-" + }, + { + "npc_id": "1582", + "loc_data": "{2568,9889,0,1,6}-{2578,9898,0,1,1}-{2657,9500,0,0,1}-{2632,9589,2,0,1}-{2628,9549,2,0,5}-{2629,9543,2,0,2}-{3249,5517,0,1,3}-" + }, + { + "npc_id": "1583", + "loc_data": "{2563,9885,0,1,7}-{2575,9897,0,1,1}-{2655,9481,0,0,3}-{2631,9579,2,0,4}-{3211,5519,0,1,0}-{3254,5513,0,1,7}-" + }, + { + "npc_id": "1584", + "loc_data": "{2661,9481,0,0,4}-{2635,9570,2,0,1}-{2627,9539,2,0,5}-{3231,5494,0,1,7}-{3209,5512,0,1,6}-" + }, + { + "npc_id": "1585", + "loc_data": "{2575,9892,0,1,6}-{2666,9482,0,0,5}-{2641,9563,2,0,5}-{3257,5523,0,1,1}-{3204,5516,0,1,3}-{3244,9369,0,1,3}-{3252,9359,0,1,3}-{3306,9400,0,1,3}-" + }, + { + "npc_id": "1586", + "loc_data": "{2668,9497,0,0,4}-{2635,9565,2,0,4}-{2633,9554,2,0,4}-{3247,5520,0,1,5}-{3205,5513,0,1,6}-{3213,5519,0,1,7}-" + }, + { + "npc_id": "1587", + "loc_data": "{2830,3241,0,1,6}-{3145,3812,0,1,0}-{3141,3826,0,1,0}-{2645,9489,0,0,7}-{2681,9577,0,0,0}-{2673,9591,0,0,5}-{2655,9575,0,0,5}-{2640,9580,0,0,0}-{2646,9553,0,0,1}-{2650,9536,0,0,7}-{2670,9543,0,0,0}-{2663,9555,0,0,7}-{2692,3202,0,1,4}-{2695,3216,0,1,0}-{3237,5557,0,1,3}-" + }, + { + "npc_id": "1588", + "loc_data": "{2826,3245,0,1,3}-{2637,9532,0,0,5}-{2634,9521,0,0,0}-{2642,9493,0,0,0}-{2645,9484,0,0,7}-{2679,9592,0,0,2}-{2654,9587,0,0,3}-{2650,9564,0,0,6}-{2647,9548,0,0,6}-{2637,9545,0,0,5}-{2678,9548,0,0,4}-{2658,9551,0,0,1}-{2691,3215,0,1,3}-{3246,5558,0,1,4}-" + }, + { + "npc_id": "1589", + "loc_data": "{2698,9503,0,0,7}-{2713,9505,0,0,0}-{2717,9502,0,0,7}-{2699,9523,0,0,0}-{2706,9525,0,0,2}-{2719,9517,0,0,5}-{2724,9515,0,0,3}-" + }, + { + "npc_id": "1590", + "loc_data": "{3184,5557,0,1,7}-{3148,5514,0,1,6}-{3144,5514,0,1,3}-{2730,9490,0,1,0}-{2731,9482,0,1,7}-{2742,9491,0,1,0}-" + }, + { + "npc_id": "1591", + "loc_data": "{2703,9456,0,0,0}-{2717,9459,0,0,7}-{2731,9460,0,0,3}-{2737,9454,0,0,3}-{2737,9444,0,0,3}-{2736,9432,0,0,3}-{2738,9421,0,0,5}-{2727,9418,0,0,1}-{2724,9439,0,0,0}-{2715,9443,0,0,4}-{2702,9443,0,0,3}-{2700,9428,0,0,3}-{2705,9419,0,0,7}-" + }, + { + "npc_id": "1592", + "loc_data": "{2728,9426,0,0,4}-{2714,9432,0,0,0}-{2707,9450,0,0,6}-{2724,9451,0,0,6}-" + }, + { + "npc_id": "1593", + "loc_data": "{2652,9526,0,0,7}-{2656,9514,0,0,7}-{2666,9522,0,0,4}-{2665,9513,0,0,3}-{2666,9489,0,0,1}-{2626,9535,2,0,6}-{2628,9507,2,0,5}-{2635,9508,2,0,2}-{2634,9483,2,0,7}-{2681,9563,0,0,5}-{2677,9586,0,0,2}-{2644,9587,0,0,1}-{2638,9592,2,0,5}-{2633,9583,2,0,7}-{2627,9545,2,0,6}-{2744,9503,0,0,2}-{2702,9525,0,0,2}-{2704,9487,0,0,7}-{2711,9485,0,0,2}-{2722,9486,0,0,4}-{2717,9480,0,0,6}-{2733,9494,0,0,1}-{2742,9489,0,0,0}-" + }, + { + "npc_id": "1594", + "loc_data": "{2646,9530,0,0,7}-{2643,9519,0,0,7}-{2660,9521,0,0,1}-{2665,9527,0,0,2}-{2671,9521,0,0,7}-{2670,9516,0,0,6}-{2660,9497,0,0,7}-{2643,9487,0,0,5}-{2653,9488,0,0,2}-{2677,9501,0,0,2}-{2683,9476,0,0,7}-{2630,9515,2,0,7}-{2642,9506,2,0,7}-{2629,9487,2,0,5}-{2648,9476,2,0,7}-{2672,9569,0,0,1}-{2657,9577,0,0,3}-{2643,9543,0,0,7}-{2658,9546,0,0,6}-{2636,9574,2,0,7}-{2643,9566,2,0,7}-{2635,9559,2,0,7}-{2699,9516,0,0,0}-{2709,9524,0,0,7}-{2700,9489,0,0,5}-{2703,9484,0,0,5}-{2710,9481,0,0,2}-{2716,9486,0,0,2}-{2726,9489,0,0,1}-{2735,9481,0,0,4}-{2740,9497,0,0,2}-{2739,9506,0,0,0}-" + }, + { + "npc_id": "1595", + "loc_data": "{2744,3152,0,1,7}-" + }, + { + "npc_id": "1597", + "loc_data": "{3147,9914,0,1,6}-" + }, + { + "npc_id": "1598", + "loc_data": "{2447,4429,0,1,6}-" + }, + { + "npc_id": "1600", + "loc_data": "{3190,9574,0,1,6}-{2791,9997,0,1,3}-" + }, + { + "npc_id": "1601", + "loc_data": "{2797,9997,0,1,6}-{2781,9996,0,1,7}-" + }, + { + "npc_id": "1602", + "loc_data": "{2793,9995,0,1,3}-{2783,9998,0,1,6}-" + }, + { + "npc_id": "1603", + "loc_data": "{2789,9994,0,1,1}-{2786,9998,0,1,5}-" + }, + { + "npc_id": "1604", + "loc_data": "{3434,3550,1,1,3}-{3439,3549,1,1,4}-{3425,3549,1,1,5}-{3423,3553,1,1,4}-{3426,3541,1,1,1}-{3425,3541,1,1,2}-{3426,3539,1,1,0}-{3420,3537,1,1,3}-{3417,3536,1,1,6}-" + }, + { + "npc_id": "1608", + "loc_data": "{2698,9999,0,1,6}-{2695,9997,0,1,7}-{2697,9996,0,1,5}-{2695,9999,0,1,6}-" + }, + { + "npc_id": "1609", + "loc_data": "{2700,9994,0,1,6}-{2698,10001,0,1,4}-{2703,9994,0,1,4}-{2701,10000,0,1,4}-" + }, + { + "npc_id": "1610", + "loc_data": "{3443,3537,2,1,4}-{3434,3536,2,1,4}-{3443,3540,2,1,3}-{3446,3548,2,1,4}-{3439,3549,2,1,3}-{3434,3547,2,1,1}-{3444,3537,2,1,3}-{3174,5474,0,1,4}-{3175,5479,0,1,6}-{3183,5473,0,1,7}-{3178,5468,0,1,4}-" + }, + { + "npc_id": "1612", + "loc_data": "{3438,3562,0,1,1}-{3430,3564,0,1,3}-{3434,3555,0,1,5}-{3443,3551,0,1,4}-{3442,3545,0,1,1}-{3449,3536,0,1,1}-{3440,3539,0,1,4}-" + }, + { + "npc_id": "1613", + "loc_data": "{3439,3565,2,1,6}-{3443,3571,2,1,1}-{3435,3565,2,1,3}-{3443,3564,2,1,2}-{3444,3559,2,1,2}-{3427,3558,2,1,0}-{3425,3557,2,1,4}-{3430,3557,2,1,3}-{3170,5554,0,1,2}-{3166,5556,0,1,5}-{3171,5560,0,1,4}-{3173,5556,0,1,3}-" + }, + { + "npc_id": "1615", + "loc_data": "{3422,3566,2,1,5}-{3426,3568,2,1,6}-{3418,3562,2,1,4}-{3414,3566,2,1,3}-{3412,3570,2,1,4}-{3421,3572,2,1,2}-{3422,3571,2,1,1}-{3033,4873,0,1,7}-{3039,4885,0,1,3}-{3050,4890,0,1,3}-{3062,4892,0,1,5}-{3064,4901,0,1,3}-{3056,4912,0,1,3}-{3048,4916,0,1,5}-{3038,4911,0,1,6}-{3022,4914,0,1,6}-{3018,4902,0,1,6}-{3025,4898,0,1,4}-{3033,4885,0,1,0}-{3021,4877,0,1,3}-{3048,4872,0,1,1}-{3061,4880,0,1,3}-{3061,4891,0,1,1}-{3054,4901,0,1,1}-" + }, + { + "npc_id": "1616", + "loc_data": "{2741,10011,0,1,2}-{2742,10010,0,1,3}-{2739,10014,0,1,1}-{2740,10006,0,1,3}-" + }, + { + "npc_id": "1617", + "loc_data": "{2743,10003,0,1,1}-{2746,10008,0,1,2}-{2743,10015,0,1,3}-{2745,10011,0,1,5}-{2744,10016,0,1,1}-" + }, + { + "npc_id": "1618", + "loc_data": "{3424,3565,1,1,1}-{3416,3562,1,1,6}-{3423,3559,1,1,0}-{3424,3560,1,1,4}-{3412,3558,1,1,1}-{3412,3568,1,1,1}-{3417,3565,1,1,3}-" + }, + { + "npc_id": "1620", + "loc_data": "{2794,10036,0,1,4}-{2788,10037,0,1,0}-{2795,10034,0,1,3}-{2785,10035,0,1,3}-" + }, + { + "npc_id": "1621", + "loc_data": "{2797,10035,0,1,6}-{2787,10032,0,1,3}-{2802,10031,0,1,3}-" + }, + { + "npc_id": "1623", + "loc_data": "{2725,9999,0,1,6}-" + }, + { + "npc_id": "1624", + "loc_data": "{3179,5522,0,1,6}-{3168,5513,0,1,3}-{3174,5514,0,1,6}-{3178,5517,0,1,4}-{3178,5528,0,1,1}-{3169,5516,0,1,3}-{3179,5524,0,1,4}-{3223,9393,0,1,5}-{3225,9401,0,1,0}-{3222,9375,0,1,6}-{3220,9380,0,1,6}-{3215,9364,0,1,3}-{3213,9361,0,1,1}-{3212,9355,0,1,2}-{3230,9367,0,1,3}-{3240,9350,0,1,5}-{3248,9348,0,1,4}-{3255,9350,0,1,4}-{3256,9392,0,1,4}-" + }, + { + "npc_id": "1626", + "loc_data": "{2723,10005,0,1,0}-{2726,9998,0,1,1}-{2723,10009,0,1,3}-" + }, + { + "npc_id": "1627", + "loc_data": "{2724,10008,0,1,4}-{2721,10005,0,1,1}-{2724,10005,0,1,3}-{2729,10004,0,1,3}-{2724,10012,0,1,4}-{2718,10009,0,1,1}-{2721,10001,0,1,7}-{2720,9996,0,1,7}-{2721,10006,0,1,3}-" + }, + { + "npc_id": "1628", + "loc_data": "{3155,5536,0,1,4}-{3156,5540,0,1,3}-{3157,5538,0,1,2}-{3166,5537,0,1,1}-{3165,5541,0,1,3}-{2723,10004,0,1,4}-{2722,9999,0,1,1}-{2729,10002,0,1,1}-" + }, + { + "npc_id": "1629", + "loc_data": "{3162,5538,0,1,2}-{3157,5538,0,1,4}-{3155,5537,0,1,1}-{3164,5538,0,1,2}-" + }, + { + "npc_id": "1631", + "loc_data": "{3160,9589,0,1,3}-{3167,9589,0,1,6}-{3179,9585,0,1,1}-{3188,9572,0,1,7}-{3190,9580,0,1,3}-{3206,9586,0,1,3}-{3221,9586,0,1,7}-{3226,9573,0,1,5}-{3223,9569,0,1,1}-{2800,10017,0,1,1}-{2796,10021,0,1,1}-{2805,10021,0,1,1}-{2803,10013,0,1,1}-" + }, + { + "npc_id": "1632", + "loc_data": "{2798,10019,0,1,1}-{2800,10015,0,1,1}-{2797,10018,0,1,1}-" + }, + { + "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}-{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", + "loc_data": "{2757,10008,0,1,6}-{2760,10005,0,1,5}-" + }, + { + "npc_id": "1635", + "loc_data": "{2758,10001,0,1,1}-{2763,10006,0,1,1}-{2766,10000,0,1,6}-{2759,9999,0,1,7}-" + }, + { + "npc_id": "1636", + "loc_data": "{2760,10004,0,1,1}-{2760,10002,0,1,6}-" + }, + { + "npc_id": "1637", + "loc_data": "{2700,10027,0,1,6}-{3276,5454,0,1,4}-{3279,5450,0,1,0}-{3271,5446,0,1,3}-{3277,5512,0,1,1}-{3278,5535,0,0,0}-{3277,5520,0,1,1}-" + }, + { + "npc_id": "1638", + "loc_data": "{2704,10022,0,1,4}-{2707,10023,0,1,4}-{3284,5456,0,1,1}-{3278,5453,0,1,1}-{3271,5512,0,1,4}-" + }, + { + "npc_id": "1639", + "loc_data": "{2711,10028,0,1,2}-{2710,10030,0,1,6}-{3283,5461,0,1,4}-{3285,5456,0,1,6}-{3275,5455,0,1,5}-{3272,5446,0,1,4}-{3277,5445,0,1,3}-" + }, + { + "npc_id": "1640", + "loc_data": "{2705,10028,0,1,3}-{2704,10031,0,1,6}-{3272,5445,0,1,4}-" + }, + { + "npc_id": "1641", + "loc_data": "{2708,10025,0,1,6}-" + }, + { + "npc_id": "1642", + "loc_data": "{2703,10020,0,1,0}-" + }, + { + "npc_id": "1643", + "loc_data": "{3434,3562,1,1,6}-{3439,3558,1,1,5}-{3444,3567,1,1,4}-{3439,3571,1,1,3}-{3435,3570,1,1,7}-{3447,3561,1,1,4}-{3442,3573,1,1,6}-{3246,5446,0,1,3}-{3237,5455,0,1,0}-{3246,5450,0,1,4}-" + }, + { + "npc_id": "1644", + "loc_data": "{3240,5448,0,1,7}-" + }, + { + "npc_id": "1648", + "loc_data": "{3426,3537,0,1,4}-{3413,3536,0,1,3}-{3419,3545,0,1,2}-{3426,3544,0,1,6}-{3410,3535,0,1,2}-{3425,3556,0,1,6}-{3418,3557,0,1,1}-{3428,3571,0,1,3}-{3432,3573,0,1,4}-" + }, + { + "npc_id": "1650", + "loc_data": "{3411,3541,0,1,3}-" + }, + { + "npc_id": "1655", + "loc_data": "{3424,3544,0,1,1}-{3423,3546,0,1,1}-{3409,3538,0,1,3}-{3421,3559,0,1,4}-{3413,3559,0,1,6}-{3433,3574,0,1,3}-{3428,3571,0,1,6}-" + }, + { + "npc_id": "1658", + "loc_data": "{2595,3087,1,1,1}-" + }, + { + "npc_id": "1665", + "loc_data": "{3544,3462,0,0,1}-" + }, + { + "npc_id": "1668", + "loc_data": "{3551,3550,0,1,0}-" + }, + { + "npc_id": "1669", + "loc_data": "{3549,3555,2,0,3}-" + }, + { + "npc_id": "1671", + "loc_data": "{3547,3555,2,0,6}-" + }, + { + "npc_id": "1672", + "loc_data": "{3552,3550,0,1,0}-" + }, + { + "npc_id": "1675", + "loc_data": "{3551,3561,0,1,0}-" + }, + { + "npc_id": "1676", + "loc_data": "{3481,9942,0,1,0}-{3493,9925,0,1,0}-{3525,9932,0,1,0}-{3555,9927,0,1,0}-{3554,9947,0,1,0}-{3529,9968,0,1,0}-" + }, + { + "npc_id": "1677", + "loc_data": "{3474,9933,0,1,0}-{3488,9934,0,1,0}-{3547,9932,0,1,0}-{3540,9958,0,1,0}-{3564,9959,0,1,0}-" + }, + { + "npc_id": "1678", + "loc_data": "{3473,9943,0,1,0}-{3488,9944,0,1,0}-{3498,9937,0,1,0}-{3534,9926,0,1,0}-{3553,9941,0,1,0}-{3548,9956,0,1,0}-{3564,9949,0,1,0}-" + }, + { + "npc_id": "1679", + "loc_data": "{2291,3145,0,1,2}-" + }, + { + "npc_id": "1680", + "loc_data": "{2290,3143,0,1,3}-" + }, + { + "npc_id": "1681", + "loc_data": "{3202,5483,0,1,4}-{2544,9843,0,1,3}-" + }, + { + "npc_id": "1683", + "loc_data": "{3678,3511,0,1,4}-" + }, + { + "npc_id": "1684", + "loc_data": "{3660,3517,0,1,7}-" + }, + { + "npc_id": "1685", + "loc_data": "{3659,3497,0,1,0}-" + }, + { + "npc_id": "1686", + "loc_data": "{3657,3516,0,1,3}-{3654,3518,1,1,6}-{3663,3522,0,1,7}-" + }, + { + "npc_id": "1688", + "loc_data": "{3709,3497,0,0,3}-" + }, + { + "npc_id": "1691", + "loc_data": "{3624,3530,0,1,1}-{3623,3524,0,1,2}-{3618,3526,0,1,6}-{3612,3527,0,1,6}-{3609,3527,0,1,4}-" + }, + { + "npc_id": "1692", + "loc_data": "{3632,3528,0,1,5}-{3633,3525,0,1,6}-{3631,3529,0,1,3}-{3631,3529,0,1,2}-{3628,3530,0,1,1}-{3631,3530,0,1,4}-{3629,3529,0,1,4}-" + }, + { + "npc_id": "1694", + "loc_data": "{3673,3491,0,1,3}-" + }, + { + "npc_id": "1695", + "loc_data": "{3461,3558,0,0,3}-" + }, + { + "npc_id": "1697", + "loc_data": "{3662,3502,0,1,4}-{3664,3493,0,1,6}-{3672,3486,0,1,2}-{3685,3485,0,1,3}-{3684,3496,0,1,3}-{3675,3502,0,1,3}-{3667,3484,0,1,0}-{3666,3471,0,1,4}-{3676,3464,0,1,3}-{3683,3467,0,1,6}-{3685,3474,0,1,4}-{3690,3474,0,1,1}-{3695,3473,0,1,3}-{3693,3466,0,1,7}-{3698,3463,0,1,3}-{3688,3467,0,1,3}-{3675,3474,0,1,4}-{3665,3476,0,1,6}-{3662,3491,0,1,1}-{3662,3495,0,1,4}-{3661,3501,0,1,6}-{3682,3486,0,1,4}-{3678,3493,0,1,4}-{3673,3491,0,1,3}-{3658,3502,0,1,4}-{3660,3505,0,1,1}-{3665,3469,0,1,6}-{3659,3465,0,1,6}-{3667,3461,0,1,1}-" + }, + { + "npc_id": "1698", + "loc_data": "{3647,3473,0,1,3}-{3649,3503,0,1,2}-{3665,3529,0,1,4}-{3669,3522,0,1,4}-{3672,3529,0,1,4}-{3668,3534,0,1,4}-{3657,3534,0,1,4}-{3654,3537,0,1,4}-" + }, + { + "npc_id": "1699", + "loc_data": "{3659,3480,0,1,3}-" + }, + { + "npc_id": "1700", + "loc_data": "{3681,3493,0,1,5}-" + }, + { + "npc_id": "1702", + "loc_data": "{3690,3464,0,0,1}-{3691,3464,0,0,1}-{3688,3464,0,0,1}-{3687,3464,0,0,1}-" + }, + { + "npc_id": "1703", + "loc_data": "{3697,3496,0,1,3}-{3699,3497,0,1,2}-{3689,3503,0,1,3}-{3690,3508,0,1,6}-{3690,3512,0,1,6}-{3689,3517,0,1,1}-{3690,3518,0,1,5}-{3689,3503,0,1,3}-{3689,3496,0,1,0}-{3690,3524,0,1,6}-{3689,3528,0,1,5}-" + }, + { + "npc_id": "1704", + "loc_data": "{3703,3487,0,0,3}-" + }, + { + "npc_id": "1706", + "loc_data": "{3661,3508,0,0,1}-{3658,3508,0,0,1}-{3652,3487,0,0,3}-{3652,3484,0,0,3}-" + }, + { + "npc_id": "1710", + "loc_data": "{3165,9631,0,1,6}-{3165,9633,0,1,0}-{3160,9634,0,1,7}-{3156,9630,0,1,6}-{3152,9627,0,1,4}-{3148,9621,0,1,4}-{3175,9630,0,1,1}-{3174,9634,0,1,3}-{3172,9638,0,1,0}-" + }, + { + "npc_id": "1711", + "loc_data": "{2566,5197,0,1,4}-{3160,9638,0,1,1}-{3159,9633,0,1,6}-{3161,9626,0,1,6}-{3169,9626,0,1,4}-{3171,9634,0,1,4}-{3164,9634,0,1,3}-{3165,9631,0,1,7}-{3180,9631,0,1,7}-{3183,9629,0,1,6}-{3182,9633,0,1,1}-{3172,9632,0,1,4}-{3158,9631,0,1,1}-{3160,9630,0,1,1}-{3159,9633,0,1,4}-{3150,9623,0,1,6}-{3152,9620,0,1,6}-{3159,9622,0,1,0}-{3169,9623,0,1,6}-{3169,9623,0,1,4}-" + }, + { + "npc_id": "1712", + "loc_data": "{2573,5201,0,1,4}-{2577,5195,0,1,4}-{2577,5189,0,0,6}-{2571,5195,0,0,6}-{3153,9646,0,1,3}-{3151,9651,0,1,1}-" + }, + { + "npc_id": "1714", + "loc_data": "{3172,9624,0,1,2}-{3161,9623,0,1,1}-{3151,9624,0,1,1}-{3150,9622,0,1,4}-{3153,9627,0,1,3}-{3157,9634,0,1,2}-{3171,9636,0,1,6}-{3174,9639,0,1,3}-{3174,9634,0,1,3}-" + }, + { + "npc_id": "1715", + "loc_data": "{3171,9635,0,1,3}-{3159,9636,0,1,2}-{3169,9630,0,1,3}-{3172,9625,0,1,0}-{3159,9627,0,1,3}-" + }, + { + "npc_id": "1752", + "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", + "loc_data": "{3100,3246,0,1,2}-{3131,3263,0,1,5}-{3122,3278,0,1,2}-{3259,3308,0,1,4}-" + }, + { + "npc_id": "1755", + "loc_data": "{3106,3275,0,1,1}-{3106,3266,0,1,3}-{3114,3273,0,1,4}-{3258,3312,0,1,3}-" + }, + { + "npc_id": "1756", + "loc_data": "{3096,3251,0,1,3}-{3102,3261,0,1,1}-{3117,3278,0,1,0}-{3262,3320,0,1,1}-" + }, + { + "npc_id": "1757", + "loc_data": "{3233,3308,0,1,4}-" + }, + { + "npc_id": "1758", + "loc_data": "{2895,3403,0,0,0}-{3262,3325,0,0,0}-" + }, + { + "npc_id": "1759", + "loc_data": "{3250,3310,0,0,0}-" + }, + { + "npc_id": "1761", + "loc_data": "{3121,3274,0,0,0}-" + }, + { + "npc_id": "1765", + "loc_data": "{3198,3270,0,1,0}-{2923,3320,0,1,7}-{2921,3320,0,1,4}-{2923,3320,0,1,5}-{3206,3265,0,1,4}-{3201,3265,0,1,0}-" + }, + { + "npc_id": "1766", + "loc_data": "{3171,3320,0,1,0}-{3259,3262,0,1,4}-{3262,3274,0,1,3}-{3259,3278,0,1,1}-{3263,3273,0,1,7}-" + }, + { + "npc_id": "1767", + "loc_data": "{2924,3288,0,0,1}-{2921,3290,0,1,1}-{2926,3279,0,1,1}-{2931,3272,0,1,1}-{2936,3273,0,1,6}-{3262,3262,0,1,6}-{3025,3311,0,1,5}-" + }, + { + "npc_id": "1769", + "loc_data": "{2596,9822,0,1,4}-{2979,3203,0,1,4}-{3264,3249,0,1,4}-" + }, + { + "npc_id": "1770", + "loc_data": "{2595,9826,0,1,1}-{3189,3246,0,1,0}-{3143,3231,0,1,0}-{3140,3263,0,1,0}-{3262,3253,0,1,6}-{3259,3254,0,1,3}-{2999,3212,0,1,1}-" + }, + { + "npc_id": "1771", + "loc_data": "{3192,3245,0,1,0}-{3184,3248,0,1,0}-{3140,3230,0,1,0}-{3141,3261,0,1,0}-{3187,3286,0,1,0}-{3174,3294,0,1,0}-{3178,3294,0,1,0}-{3144,3302,0,1,0}-{3142,3303,0,1,0}-{3151,3300,0,1,0}-{3139,3299,0,1,0}-{2993,3199,0,1,6}-{3248,3233,0,1,6}-{3247,3240,0,1,3}-{3209,3277,0,1,0}-" + }, + { + "npc_id": "1772", + "loc_data": "{3194,3255,0,1,0}-{3143,3234,0,1,0}-{3251,3231,0,1,1}-{3260,3241,0,1,0}-{3259,3222,0,0,4}-{2993,3203,0,1,0}-" + }, + { + "npc_id": "1773", + "loc_data": "{3252,3227,0,1,1}-{3263,3219,0,1,0}-{2991,3219,0,1,1}-" + }, + { + "npc_id": "1774", + "loc_data": "{2592,9834,0,1,6}-{3255,3228,0,1,4}-{3265,3219,0,1,2}-{3012,3204,0,1,3}-" + }, + { + "npc_id": "1775", + "loc_data": "{2585,9829,0,1,1}-{3254,3224,0,1,1}-{3010,3211,0,1,6}-" + }, + { + "npc_id": "1776", + "loc_data": "{2999,3216,0,1,6}-{3264,3247,0,1,2}-" + }, + { + "npc_id": "1778", + "loc_data": "{3260,3859,0,0,0}-" + }, + { + "npc_id": "1779", + "loc_data": "{2978,3752,0,1,6}-" + }, + { + "npc_id": "1780", + "loc_data": "{3272,3687,0,1,3}-" + }, + { + "npc_id": "1781", + "loc_data": "{3008,3862,0,0,0}-" + }, + { + "npc_id": "1782", + "loc_data": "{3363,3883,0,1,4}-" + }, + { + "npc_id": "1783", + "loc_data": "{3079,3516,0,1,6}-" + }, + { + "npc_id": "1784", + "loc_data": "{3025,3705,0,1,1}-" + }, + { + "npc_id": "1785", + "loc_data": "{3144,3788,0,1,6}-" + }, + { + "npc_id": "1786", + "loc_data": "{3234,3633,0,1,1}-" + }, + { + "npc_id": "1787", + "loc_data": "{3074,3609,0,1,3}-" + }, + { + "npc_id": "1795", + "loc_data": "{2967,9811,0,0,0}-" + }, + { + "npc_id": "1796", + "loc_data": "{2968,9809,0,0,0}-" + }, + { + "npc_id": "1798", + "loc_data": "{2701,3471,0,0,0}-" + }, + { + "npc_id": "1805", + "loc_data": "{2760,3660,0,1,6}-" + }, + { + "npc_id": "1806", + "loc_data": "{2798,3667,0,1,3}-" + }, + { + "npc_id": "1807", + "loc_data": "{2811,3673,0,1,3}-" + }, + { + "npc_id": "1808", + "loc_data": "{2765,3676,0,1,3}-" + }, + { + "npc_id": "1810", + "loc_data": "{2811,3681,0,1,3}-" + }, + { + "npc_id": "1814", + "loc_data": "{2794,3668,0,1,0}-" + }, + { + "npc_id": "1815", + "loc_data": "{2801,3669,0,1,0}-" + }, + { + "npc_id": "1816", + "loc_data": "{2803,3668,0,1,0}-" + }, + { + "npc_id": "1817", + "loc_data": "{2797,3672,0,1,0}-" + }, + { + "npc_id": "1818", + "loc_data": "{2802,3674,0,1,0}-" + }, + { + "npc_id": "1819", + "loc_data": "{2813,3687,0,1,0}-" + }, + { + "npc_id": "1820", + "loc_data": "{2812,3685,0,1,0}-" + }, + { + "npc_id": "1821", + "loc_data": "{2757,3674,0,1,0}-{2762,3671,0,1,0}-{2785,3658,0,1,0}-" + }, + { + "npc_id": "1828", + "loc_data": "{3231,9545,0,1,6}-" + }, + { + "npc_id": "1829", + "loc_data": "{3153,9548,0,1,1}-{3154,9547,0,1,4}-{3176,9546,0,1,4}-{3221,9558,0,1,4}-{3226,9548,0,1,7}-{3224,9551,0,1,1}-{3219,9558,0,1,7}-" + }, + { + "npc_id": "1831", + "loc_data": "{3165,9542,0,1,3}-{3164,9542,0,1,3}-{3166,9542,0,1,3}-{3208,9555,0,1,1}-{3211,9570,0,1,5}-{3211,9570,0,1,5}-{3208,9555,0,1,1}-{3208,9555,0,1,1}-{3207,9555,0,1,1}-{3213,9570,0,1,5}-" + }, + { + "npc_id": "1832", + "loc_data": "{3150,9573,0,1,1}-{3188,9550,0,1,0}-{3148,9578,0,1,4}-{3152,9556,0,1,3}-{3150,9553,0,1,2}-{3167,9547,0,1,1}-{3168,9546,0,1,3}-{3184,9551,0,1,3}-{3191,9583,0,1,4}-{3187,9583,0,1,3}-{3220,9573,0,1,4}-{3224,9574,0,1,1}-{3208,9588,0,1,6}-{3210,9585,0,1,4}-{3272,5549,0,1,0}-{3275,5546,0,1,0}-{3281,5545,0,1,2}-{3286,5544,0,1,0}-{3285,5539,0,1,3}-{3268,5553,0,1,1}-{3274,5558,0,1,6}-{3281,5557,0,1,4}-{3282,5554,0,1,4}-{3269,5551,0,1,3}-{3287,5549,0,1,3}-{3279,5546,0,1,6}-{3283,5545,0,1,0}-{3269,5548,0,1,1}-" + }, + { + "npc_id": "1833", + "loc_data": "{3151,9575,0,1,3}-{3150,9577,0,1,4}-{3148,9554,0,1,6}-{3165,9547,0,1,1}-{3165,9546,0,1,2}-{3184,9551,0,1,3}-{3189,9581,0,1,4}-{3222,9575,0,1,4}-{3226,9576,0,1,3}-{3210,9587,0,1,2}-" + }, + { + "npc_id": "1834", + "loc_data": "{3169,3174,0,0,6}-" + }, + { + "npc_id": "1839", + "loc_data": "{2825,10167,0,1,0}-" + }, + { + "npc_id": "1841", + "loc_data": "{3020,3452,0,0,0}-" + }, + { + "npc_id": "1843", + "loc_data": "{2839,10128,0,1,0}-" + }, + { + "npc_id": "1844", + "loc_data": "{2855,10144,0,1,0}-" + }, + { + "npc_id": "1846", + "loc_data": "{2873,10166,0,1,3}-" + }, + { + "npc_id": "1847", + "loc_data": "{2868,10168,0,1,0}-" + }, + { + "npc_id": "1848", + "loc_data": "{2868,10168,0,1,0}-" + }, + { + "npc_id": "1849", + "loc_data": "{2868,10168,0,1,0}-" + }, + { + "npc_id": "1860", + "loc_data": "{2957,3203,0,1,3}-" + }, + { + "npc_id": "1861", + "loc_data": "{3208,3256,0,0,1}-" + }, + { + "npc_id": "1862", + "loc_data": "{3304,3211,0,1,1}-" + }, + { + "npc_id": "1863", + "loc_data": "{3358,2956,0,0,6}-" + }, + { + "npc_id": "1864", + "loc_data": "{3363,2956,0,0,3}-" + }, + { + "npc_id": "1865", + "loc_data": "{3352,2974,0,1,4}-" + }, + { + "npc_id": "1866", + "loc_data": "{3360,2983,0,1,4}-" + }, + { + "npc_id": "1867", + "loc_data": "{3347,2966,0,0,6}-" + }, + { + "npc_id": "1870", + "loc_data": "{3373,2972,0,1,5}-" + }, + { + "npc_id": "1871", + "loc_data": "{3344,2986,0,1,3}-" + }, + { + "npc_id": "1872", + "loc_data": "{3354,2953,0,0,6}-" + }, + { + "npc_id": "1873", + "loc_data": "{3343,2962,0,1,0}-{3343,2964,0,1,1}-" + }, + { + "npc_id": "1874", + "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", + "loc_data": "{3354,2952,0,0,1}-" + }, + { + "npc_id": "1884", + "loc_data": "{2811,3174,0,1,0}-" + }, + { + "npc_id": "1902", + "loc_data": "{3333,2946,0,0,1}-" + }, + { + "npc_id": "1905", + "loc_data": "{3335,2948,0,1,2}-{3331,2948,0,1,6}-{3333,2950,0,1,0}-" + }, + { + "npc_id": "1907", + "loc_data": "{3488,3091,0,1,0}-" + }, + { + "npc_id": "1911", + "loc_data": "{3416,3155,0,1,0}-" + }, + { + "npc_id": "1912", + "loc_data": "{3376,3429,0,1,6}-" + }, + { + "npc_id": "1916", + "loc_data": "{3112,9690,0,0,7}-" + }, + { + "npc_id": "1917", + "loc_data": "{3177,2987,0,1,6}-" + }, + { + "npc_id": "1918", + "loc_data": "{3180,3043,0,1,2}-" + }, + { + "npc_id": "1920", + "loc_data": "{3497,3478,0,0,0}-" + }, + { + "npc_id": "1921", + "loc_data": "{3159,2980,0,1,1}-" + }, + { + "npc_id": "1923", + "loc_data": "{3185,2983,0,1,4}-" + }, + { + "npc_id": "1924", + "loc_data": "{3213,2956,0,1,1}-" + }, + { + "npc_id": "1926", + "loc_data": "{3179,2967,0,1,3}-{3172,2966,0,1,1}-{3171,2980,0,1,3}-{3166,2972,0,1,0}-{3181,2979,0,1,3}-{3187,2976,0,1,7}-{3174,2984,0,1,3}-{3183,2982,0,1,4}-{3187,2985,0,1,1}-{3182,2988,0,1,5}-{3188,2990,0,1,2}-{3172,2992,0,1,4}-{3171,2987,0,1,4}-{3161,2982,0,1,2}-{3161,2986,0,1,3}-{3159,2987,0,1,3}-{3163,2976,0,1,4}-{3161,2977,0,1,4}-{3161,2979,0,1,6}-{3162,2982,0,1,6}-{3164,2985,0,1,2}-{3158,2970,0,1,3}-{3155,2979,0,1,4}-{3167,2972,0,1,6}-{3172,2973,0,1,5}-{3175,2973,0,1,4}-{3185,2967,0,1,2}-" + }, + { + "npc_id": "1927", + "loc_data": "{3160,2979,0,0,5}-{3162,2989,0,0,1}-" + }, + { + "npc_id": "1928", + "loc_data": "{3161,2977,0,0,4}-{3161,2987,0,0,1}-" + }, + { + "npc_id": "1929", + "loc_data": "{3158,2987,0,0,3}-" + }, + { + "npc_id": "1930", + "loc_data": "{3161,2985,0,0,6}-{3161,2983,0,0,6}-{3161,2982,0,0,6}-{3161,2981,0,0,6}-{3161,2984,0,0,6}-" + }, + { + "npc_id": "1932", + "loc_data": "{2836,3740,0,0,6}-" + }, + { + "npc_id": "1935", + "loc_data": "{2841,3738,0,0,3}-" + }, + { + "npc_id": "1936", + "loc_data": "{2852,3735,0,1,0}-{2855,3729,0,1,0}-{2861,3725,0,1,0}-" + }, + { + "npc_id": "1937", + "loc_data": "{2850,3732,0,1,0}-{2857,3726,0,1,0}-{2865,3729,0,1,0}-" + }, + { + "npc_id": "1938", + "loc_data": "{2849,3735,0,1,0}-{2853,3731,0,1,0}-{2859,3728,0,1,0}-" + }, + { + "npc_id": "1939", + "loc_data": "{2856,3728,0,1,0}-{2862,3722,0,1,0}-{2864,3723,0,1,0}-" + }, + { + "npc_id": "1940", + "loc_data": "{2853,3728,0,1,0}-{2856,3732,0,1,0}-{2863,3720,0,1,0}-" + }, + { + "npc_id": "1941", + "loc_data": "{2854,3725,0,1,0}-{2862,3728,0,1,0}-{2862,3731,0,1,0}-" + }, + { + "npc_id": "1942", + "loc_data": "{2855,3735,0,1,0}-{2858,3730,0,1,0}-{2865,3726,0,1,0}-" + }, + { + "npc_id": "1943", + "loc_data": "{2825,3807,2,0,1}-" + }, + { + "npc_id": "1945", + "loc_data": "{2825,3811,2,0,6}-" + }, + { + "npc_id": "1947", + "loc_data": "{2835,3740,0,0,6}-" + }, + { + "npc_id": "1949", + "loc_data": "{2837,3740,0,0,6}-" + }, + { + "npc_id": "1951", + "loc_data": "{2886,3723,0,1,3}-{2887,3720,0,1,4}-{2890,3721,0,1,7}-{2890,3724,0,1,1}-{2909,3736,0,1,5}-{2910,3736,0,1,2}-{2891,3724,0,1,7}-" + }, + { + "npc_id": "1952", + "loc_data": "{2892,3726,0,1,0}-{2894,3726,0,1,6}-{2895,3728,0,1,0}-{2895,3730,0,1,5}-{2922,3755,0,1,3}-{2893,3725,0,1,2}-{2894,3727,0,1,6}-{2895,3725,0,1,3}-{2898,3735,0,1,6}-" + }, + { + "npc_id": "1953", + "loc_data": "{2897,3733,0,1,5}-{2897,3737,0,1,5}-{2913,3736,0,1,3}-{2897,3737,0,1,6}-{2915,3740,0,1,1}-{2894,3742,0,1,0}-" + }, + { + "npc_id": "1954", + "loc_data": "{2895,3751,0,1,1}-{2895,3755,0,1,6}-{2898,3751,0,1,3}-{2898,3755,0,1,6}-{2930,3765,0,1,5}-{2893,3748,0,1,5}-{2896,3749,0,1,1}-{2929,3762,0,1,3}-" + }, + { + "npc_id": "1955", + "loc_data": "{2895,3740,0,1,1}-{2895,3744,0,1,3}-{2898,3740,0,1,6}-{2898,3744,0,1,1}-{2908,3739,0,1,0}-{2928,3763,0,1,3}-{2899,3737,0,1,2}-{2897,3739,0,1,6}-{2912,3742,0,1,2}-{2898,3750,0,1,3}-{2895,3751,0,1,4}-" + }, + { + "npc_id": "1956", + "loc_data": "{2886,3758,0,1,6}-{2886,3762,0,1,3}-{2890,3756,0,1,4}-{2890,3762,0,1,1}-{2928,3753,0,1,2}-{2926,3753,0,1,6}-{2895,3754,0,1,3}-" + }, + { + "npc_id": "1958", + "loc_data": "{3147,5467,0,1,1}-{3144,5466,0,1,2}-{3147,5479,0,1,2}-{3144,5475,0,1,6}-{3161,5482,0,1,5}-{3147,5475,0,1,6}-{3240,9326,0,1,3}-{3209,9310,0,1,4}-{3245,9333,0,1,4}-{3213,9307,0,1,4}-{3221,9318,0,1,3}-{3229,9333,0,1,3}-{3216,9329,0,1,4}-{3208,9294,0,1,3}-{3201,9286,0,1,1}-{3212,9326,0,1,4}-{3217,9284,0,1,7}-{3226,9284,0,1,4}-{3232,9299,0,1,6}-{3242,9295,0,1,1}-{3220,9302,0,1,0}-{3238,9305,0,1,6}-{3220,9296,0,1,1}-{3246,9291,0,1,3}-{3240,9289,0,1,4}-{3237,9305,0,1,3}-{3257,9292,0,0,5}-{3246,9321,0,1,1}-{3261,9310,0,1,3}-{3258,9309,0,1,4}-{3251,9316,0,1,3}-{3247,9310,0,1,3}-" + }, + { + "npc_id": "1961", + "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": "{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": "{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": "{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", + "loc_data": "{3233,9317,0,1,1}-" + }, + { + "npc_id": "1972", + "loc_data": "{2536,3426,0,1,0}-" + }, + { + "npc_id": "1973", + "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", + "loc_data": "{2694,5067,0,1,0}-{2717,5081,0,1,0}-{2718,5108,0,1,0}-{2721,5104,0,1,0}-{2726,5091,0,1,0}-{2731,5060,0,1,0}-{2732,5073,0,1,0}-{2740,5083,0,1,0}-{2742,5103,0,1,0}-{2747,5094,0,1,0}-{3315,5508,0,1,5}-{3308,5510,0,1,3}-{3304,5507,0,1,4}-{3299,5509,0,1,3}-{3317,5514,0,1,3}-{3319,5518,0,1,4}-{3320,5507,0,1,4}-{3324,5513,0,1,1}-{3283,5510,0,1,5}-{3320,5550,0,1,0}-{3313,5523,0,1,4}-{3310,5541,0,1,4}-{3308,5535,0,1,5}-{3301,5531,0,1,4}-{3315,5530,0,1,1}-{3322,5536,0,1,4}-{3320,5539,0,1,7}-{3311,5519,0,1,3}-{3289,5525,0,1,2}-{3300,5519,0,1,6}-{3289,5515,0,1,7}-{3287,5523,0,1,3}-" + }, + { + "npc_id": "1990", + "loc_data": "{3300,2797,0,0,6}-" + }, + { + "npc_id": "1993", + "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": "{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", + "loc_data": "{3309,2806,0,1,0}-{3312,2806,0,1,0}-{3314,2752,0,1,0}-{3314,2806,0,1,0}-{3315,2805,0,1,0}-{3316,2780,0,1,0}-{3316,2795,0,1,0}-{3317,2752,0,1,0}-{3317,2762,0,1,0}-{3317,2763,0,1,0}-{3317,2779,0,1,0}-{3317,2794,0,1,0}-{3318,2761,0,1,0}-{3318,2778,0,1,0}-{3318,2781,0,1,0}-{3318,2794,0,1,0}-{3318,2795,0,1,0}-{3319,2760,0,1,0}-{3319,2763,0,1,0}-" + }, + { + "npc_id": "1997", + "loc_data": "{3272,2802,0,1,0}-{3273,2802,0,1,0}-{3273,2805,0,1,0}-{3274,2798,0,1,0}-{3274,2799,0,1,0}-{3274,2802,0,1,0}-{3274,2804,0,1,0}-{3275,2795,0,1,0}-{3275,2796,0,1,0}-{3275,2798,0,1,0}-{3277,2752,0,1,0}-{3277,2753,0,1,0}-{3277,2756,0,1,0}-{3277,2757,0,1,0}-{3278,2752,0,1,0}-{3278,2753,0,1,0}-{3278,2754,0,1,0}-{3278,2755,0,1,0}-" + }, + { + "npc_id": "1998", + "loc_data": "{3275,2803,0,1,0}-" + }, + { + "npc_id": "1999", + "loc_data": "{3278,2804,0,1,0}-" + }, + { + "npc_id": "2000", + "loc_data": "{3277,2802,0,1,0}-" + }, + { + "npc_id": "2002", + "loc_data": "{3315,2849,0,1,0}-" + }, + { + "npc_id": "2003", + "loc_data": "{3304,9196,0,1,0}-" + }, + { + "npc_id": "2004", + "loc_data": "{3304,9195,0,1,0}-" + }, + { + "npc_id": "2014", + "loc_data": "{3293,2788,0,1,0}-" + }, + { + "npc_id": "2020", + "loc_data": "{3503,3477,0,0,0}-" + }, + { + "npc_id": "2021", + "loc_data": "{3224,9516,2,1,0}-{3224,9517,2,1,0}-{3224,9518,2,1,0}-{3225,9516,2,1,0}-{3225,9517,2,1,0}-{3225,9518,2,1,0}-{3226,9516,2,1,0}-{3226,9517,2,1,0}-{3226,9518,2,1,0}-" + }, + { + "npc_id": "2031", + "loc_data": "{3290,5448,0,1,6}-{3291,5452,0,1,1}-{3293,5456,0,1,6}-{3551,9680,0,0,2}-{3553,9679,0,0,1}-{3551,9675,0,0,6}-{3554,9676,0,0,7}-{3548,9676,0,0,5}-{3552,9691,0,0,2}-{3569,9681,0,0,7}-{3577,9711,0,0,5}-{3567,9675,0,0,5}-{3567,9675,0,0,5}-{3570,9678,0,0,2}-{3568,9686,0,0,1}-{3545,9721,0,0,7}-{3560,9712,0,0,6}-{3552,9685,0,0,1}-{3525,9698,0,0,6}-{3538,9668,0,0,4}-" + }, + { + "npc_id": "2032", + "loc_data": "{3187,5513,0,1,3}-{3186,5510,0,1,6}-{3186,5516,0,1,6}-{3194,5511,0,1,1}-{3185,5514,0,1,5}-{3549,9678,0,0,0}-{3569,9676,0,0,5}-{3566,9678,0,0,1}-{3560,9678,0,0,2}-{3526,9678,0,0,2}-{3532,9693,0,0,5}-{3534,9690,0,0,7}-{3536,9695,0,0,2}-{3542,9694,0,0,7}-{3548,9695,0,0,7}-{3556,9695,0,0,5}-{3554,9709,0,0,2}-{3533,9708,0,0,5}-{3534,9703,0,0,5}-{3538,9721,0,0,7}-{3567,9720,0,0,6}-{3561,9711,0,0,7}-{3552,9703,0,0,1}-{3551,9698,0,0,1}-{3532,9678,0,0,5}-{3531,9675,0,0,5}-{3537,9677,0,0,2}-" + }, + { + "npc_id": "2033", + "loc_data": "{3188,5511,0,1,7}-{3190,5511,0,1,6}-{3300,5494,0,1,2}-{3285,5492,0,1,6}-{3285,5495,0,1,0}-{3288,5497,0,1,3}-{3268,5487,0,0,0}-{3533,9691,0,0,4}-{3537,9694,0,0,2}-{3533,9696,0,0,2}-{3533,9693,0,0,5}-" + }, + { + "npc_id": "2034", + "loc_data": "{3288,5484,0,1,4}-{3288,5467,0,1,0}-{3293,5468,0,1,2}-{3296,5475,0,1,7}-{3296,5473,0,1,2}-{3297,5480,0,1,4}-{3565,9695,0,0,1}-{3566,9692,0,0,5}-{3572,9695,0,0,7}-" + }, + { + "npc_id": "2035", + "loc_data": "{3569,9696,0,0,2}-{3569,9693,0,0,6}-{3566,9694,0,0,6}-{3567,9698,0,0,2}-{3572,9692,0,0,5}-" + }, + { + "npc_id": "2036", + "loc_data": "{3569,9674,0,0,0}-{3561,9677,0,0,6}-{3535,9686,0,0,6}-{3535,9698,0,0,1}-{3576,9677,0,0,3}-{3577,9679,0,0,1}-{3568,9691,0,0,2}-{3560,9694,0,0,6}-{3570,9682,0,0,1}-{3569,9687,0,0,2}-{3550,9711,0,0,5}-{3551,9708,0,0,6}-{3548,9713,0,0,6}-{3537,9711,0,0,4}-{3535,9702,0,0,7}-{3553,9722,0,0,4}-{3567,9712,0,0,5}-{3569,9704,0,0,6}-{3579,9693,0,0,7}-{3534,9675,0,0,2}-{3526,9711,0,0,1}-{3566,9668,0,0,4}-" + }, + { + "npc_id": "2037", + "loc_data": "{3543,9677,0,0,3}-{3572,9680,0,0,7}-{3559,9677,0,0,4}-{3535,9685,0,0,1}-{3537,9693,0,0,1}-{3570,9697,0,0,2}-{3552,9713,0,0,6}-{3553,9711,0,0,3}-{3567,9708,0,0,6}-{3535,9679,0,0,1}-{3525,9683,0,0,6}-" + }, + { + "npc_id": "2038", + "loc_data": "{2443,3051,0,0,1}-" + }, + { + "npc_id": "2039", + "loc_data": "{2442,3049,0,0,3}-" + }, + { + "npc_id": "2040", + "loc_data": "{2444,3047,0,0,0}-" + }, + { + "npc_id": "2041", + "loc_data": "{2447,3050,0,0,0}-" + }, + { + "npc_id": "2042", + "loc_data": "{2454,3047,0,0,2}-" + }, + { + "npc_id": "2043", + "loc_data": "{2443,3038,0,1,1}-{2452,3030,0,1,1}-" + }, + { + "npc_id": "2044", + "loc_data": "{2486,3048,0,1,3}-{2484,9392,2,1,0}-{2465,9454,2,1,0}-{2471,9422,2,1,0}-{2448,9442,2,1,0}-" + }, + { + "npc_id": "2045", + "loc_data": "{2482,3046,0,1,3}-{2459,9394,2,1,0}-{2473,9430,2,1,0}-{2478,9442,2,1,0}-{2487,9455,2,1,0}-" + }, + { + "npc_id": "2046", + "loc_data": "{2466,9389,2,1,0}-{2449,9429,0,1,0}-{2436,9452,2,1,0}-{2436,9465,2,1,0}-{2450,9460,2,1,0}-{2455,9440,2,1,0}-{2473,9439,2,1,0}-{2483,9408,2,1,0}-" + }, + { + "npc_id": "2047", + "loc_data": "{2480,3046,0,1,3}-{2477,9384,2,1,0}-{2449,9434,0,1,0}-{2437,9429,2,1,0}-{2440,9438,2,1,0}-{2458,9408,2,1,0}-" + }, + { + "npc_id": "2048", + "loc_data": "{2485,9398,2,1,0}-{2455,9435,0,1,0}-{2457,9420,2,1,0}-{2462,9431,2,1,0}-{2482,9422,2,1,0}-" + }, + { + "npc_id": "2049", + "loc_data": "{2452,9440,0,1,0}-" + }, + { + "npc_id": "2050", + "loc_data": "{2461,3047,0,1,3}-{2466,3052,0,1,6}-" + }, + { + "npc_id": "2051", + "loc_data": "{2464,3049,0,1,2}-" + }, + { + "npc_id": "2052", + "loc_data": "{2464,3045,0,1,2}-" + }, + { + "npc_id": "2053", + "loc_data": "{2467,3049,0,1,2}-" + }, + { + "npc_id": "2054", + "loc_data": "{2468,3046,0,1,2}-" + }, + { + "npc_id": "2055", + "loc_data": "{2470,3055,0,0,5}-" + }, + { + "npc_id": "2056", + "loc_data": "{2470,3040,0,0,0}-" + }, + { + "npc_id": "2057", + "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", + "loc_data": "{3161,9547,0,0,7}-{3164,9556,0,0,7}-{3162,9574,0,0,7}-{3198,9554,0,0,7}-{3198,9572,0,0,7}-{3215,9560,0,0,7}-{3216,9588,0,0,7}-" + }, + { + "npc_id": "2059", + "loc_data": "{2588,3088,0,1,3}-" + }, + { + "npc_id": "2067", + "loc_data": "{3154,9544,0,0,0}-{3245,9570,0,0,0}-" + }, + { + "npc_id": "2069", + "loc_data": "{3313,9652,0,1,0}-" + }, + { + "npc_id": "2070", + "loc_data": "{3324,9621,0,1,0}-" + }, + { + "npc_id": "2071", + "loc_data": "{3323,9643,0,1,0}-" + }, + { + "npc_id": "2072", + "loc_data": "{3319,9632,0,1,0}-" + }, + { + "npc_id": "2074", + "loc_data": "{3319,9605,0,0,1}-" + }, + { + "npc_id": "2075", + "loc_data": "{3312,9638,0,0,3}-" + }, + { + "npc_id": "2076", + "loc_data": "{3312,9628,0,0,3}-" + }, + { + "npc_id": "2077", + "loc_data": "{3323,9616,0,0,6}-" + }, + { + "npc_id": "2078", + "loc_data": "{3313,9622,0,0,3}-" + }, + { + "npc_id": "2082", + "loc_data": "{3210,3220,1,1,6}-" + }, + { + "npc_id": "2084", + "loc_data": "{3314,9612,0,1,0}-" + }, + { + "npc_id": "2085", + "loc_data": "{3231,9610,0,1,0}-" + }, + { + "npc_id": "2092", + "loc_data": "{2869,10196,1,1,0}-" + }, + { + "npc_id": "2093", + "loc_data": "{2871,10207,1,1,0}-" + }, + { + "npc_id": "2094", + "loc_data": "{2869,10205,1,0,6}-" + }, + { + "npc_id": "2095", + "loc_data": "{2889,10211,1,0,6}-" + }, + { + "npc_id": "2096", + "loc_data": "{2890,10207,1,0,6}-" + }, + { + "npc_id": "2097", + "loc_data": "{2891,10196,1,0,6}-" + }, + { + "npc_id": "2098", + "loc_data": "{2890,10192,1,0,6}-" + }, + { + "npc_id": "2100", + "loc_data": "{2870,10196,1,1,0}-" + }, + { + "npc_id": "2101", + "loc_data": "{2868,10202,1,1,0}-" + }, + { + "npc_id": "2102", + "loc_data": "{2869,10208,1,1,0}-" + }, + { + "npc_id": "2103", + "loc_data": "{2892,10209,1,1,0}-" + }, + { + "npc_id": "2104", + "loc_data": "{2890,10204,1,1,0}-" + }, + { + "npc_id": "2105", + "loc_data": "{2890,10196,1,1,0}-" + }, + { + "npc_id": "2106", + "loc_data": "{2892,10190,1,1,0}-" + }, + { + "npc_id": "2110", + "loc_data": "{2877,10198,1,1,0}-{2878,10196,1,1,0}-" + }, + { + "npc_id": "2111", + "loc_data": "{2878,10201,1,1,0}-{2876,10201,1,1,0}-" + }, + { + "npc_id": "2113", + "loc_data": "{2878,10201,1,1,0}-{2876,10201,1,1,0}-" + }, + { + "npc_id": "2115", + "loc_data": "{2882,10201,1,1,0}-{2883,10200,1,1,0}-" + }, + { + "npc_id": "2117", + "loc_data": "{2883,10198,1,1,0}-{2884,10199,1,1,0}-" + }, + { + "npc_id": "2119", + "loc_data": "{2882,10197,1,1,0}-{2883,10196,1,1,0}-" + }, + { + "npc_id": "2121", + "loc_data": "{2879,10193,1,1,0}-{2880,10195,1,1,0}-" + }, + { + "npc_id": "2127", + "loc_data": "{2880,10199,1,1,0}-" + }, + { + "npc_id": "2130", + "loc_data": "{2889,10207,0,1,0}-" + }, + { + "npc_id": "2131", + "loc_data": "{2822,10212,0,1,0}-{2891,10192,0,1,0}-" + }, + { + "npc_id": "2132", + "loc_data": "{2828,10210,0,1,0}-{2891,10199,0,1,0}-" + }, + { + "npc_id": "2133", + "loc_data": "{2847,10190,0,1,0}-" + }, + { + "npc_id": "2134", + "loc_data": "{2823,10220,0,1,0}-" + }, + { + "npc_id": "2135", + "loc_data": "{2825,10209,0,1,0}-" + }, + { + "npc_id": "2136", + "loc_data": "{2827,10212,1,1,0}-" + }, + { + "npc_id": "2138", + "loc_data": "{2836,10192,1,1,0}-" + }, + { + "npc_id": "2139", + "loc_data": "{2835,10193,1,0,1}-" + }, + { + "npc_id": "2140", + "loc_data": "{2835,10226,0,0,6}-" + }, + { + "npc_id": "2141", + "loc_data": "{2906,10204,0,1,0}-" + }, + { + "npc_id": "2142", + "loc_data": "{2904,10206,0,1,0}-" + }, + { + "npc_id": "2151", + "loc_data": "{2873,10210,0,1,0}-" + }, + { + "npc_id": "2152", + "loc_data": "{2827,10231,0,1,0}-" + }, + { + "npc_id": "2153", + "loc_data": "{2826,10197,0,1,0}-" + }, + { + "npc_id": "2154", + "loc_data": "{2869,10190,0,1,0}-" + }, + { + "npc_id": "2155", + "loc_data": "{2871,10199,0,1,0}-" + }, + { + "npc_id": "2156", + "loc_data": "{2892,10212,0,1,0}-" + }, + { + "npc_id": "2157", + "loc_data": "{2887,10212,0,1,0}-" + }, + { + "npc_id": "2158", + "loc_data": "{2885,10207,0,1,0}-" + }, + { + "npc_id": "2159", + "loc_data": "{2885,10196,0,1,0}-" + }, + { + "npc_id": "2160", + "loc_data": "{2925,10210,0,1,0}-" + }, + { + "npc_id": "2161", + "loc_data": "{2869,10211,0,1,0}-" + }, + { + "npc_id": "2162", + "loc_data": "{2886,10187,0,1,0}-" + }, + { + "npc_id": "2163", + "loc_data": "{2836,10205,0,1,0}-" + }, + { + "npc_id": "2164", + "loc_data": "{2838,10205,0,0,1}-" + }, + { + "npc_id": "2169", + "loc_data": "{2836,10222,0,1,0}-" + }, + { + "npc_id": "2170", + "loc_data": "{2853,10196,0,1,0}-" + }, + { + "npc_id": "2171", + "loc_data": "{2931,10184,0,1,0}-" + }, + { + "npc_id": "2172", + "loc_data": "{2929,10191,0,1,0}-" + }, + { + "npc_id": "2173", + "loc_data": "{2931,10191,0,1,0}-" + }, + { + "npc_id": "2174", + "loc_data": "{2930,10188,0,1,0}-" + }, + { + "npc_id": "2175", + "loc_data": "{2931,10187,0,1,0}-" + }, + { + "npc_id": "2177", + "loc_data": "{2843,10191,1,0,1}-" + }, + { + "npc_id": "2178", + "loc_data": "{2916,10195,0,1,0}-" + }, + { + "npc_id": "2179", + "loc_data": "{2840,10199,0,0,6}-" + }, + { + "npc_id": "2180", + "loc_data": "{2997,9837,0,1,3}-" + }, + { + "npc_id": "2181", + "loc_data": "{2874,9871,0,1,0}-" + }, + { + "npc_id": "2182", + "loc_data": "{2908,10176,0,1,0}-" + }, + { + "npc_id": "2187", + "loc_data": "{2906,10198,0,1,0}-" + }, + { + "npc_id": "2188", + "loc_data": "{2851,10223,0,1,0}-" + }, + { + "npc_id": "2189", + "loc_data": "{2851,10223,0,1,0}-" + }, + { + "npc_id": "2190", + "loc_data": "{2923,10203,0,1,0}-" + }, + { + "npc_id": "2191", + "loc_data": "{2924,10213,0,1,0}-" + }, + { + "npc_id": "2192", + "loc_data": "{2933,10219,0,0,3}-" + }, + { + "npc_id": "2193", + "loc_data": "{2899,10218,0,1,0}-" + }, + { + "npc_id": "2194", + "loc_data": "{2926,10225,0,1,0}-" + }, + { + "npc_id": "2195", + "loc_data": "{2900,10227,0,1,0}-" + }, + { + "npc_id": "2196", + "loc_data": "{2838,10196,0,1,0}-" + }, + { + "npc_id": "2197", + "loc_data": "{2848,10198,0,1,0}-" + }, + { + "npc_id": "2198", + "loc_data": "{2906,10216,0,1,0}-" + }, + { + "npc_id": "2199", + "loc_data": "{2855,10199,0,1,0}-" + }, + { + "npc_id": "2201", + "loc_data": "{2866,10208,0,1,0}-" + }, + { + "npc_id": "2203", + "loc_data": "{2912,10222,0,1,0}-" + }, + { + "npc_id": "2205", + "loc_data": "{2842,10129,0,0,3}-" + }, + { + "npc_id": "2206", + "loc_data": "{2888,10227,0,0,6}-" + }, + { + "npc_id": "2234", + "loc_data": "{3081,3251,0,1,6}-{2636,3365,0,1,6}-" + }, + { + "npc_id": "2235", + "loc_data": "{3241,3345,0,1,4}-" + }, + { + "npc_id": "2236", + "loc_data": "{3078,3250,0,1,1}-{3081,3250,0,1,2}-" + }, + { + "npc_id": "2237", + "loc_data": "{3249,3266,0,1,5}-" + }, + { + "npc_id": "2238", + "loc_data": "{3219,3247,0,1,6}-" + }, + { + "npc_id": "2240", + "loc_data": "{3077,3259,0,1,4}-" + }, + { + "npc_id": "2241", + "loc_data": "{3078,3260,0,1,3}-" + }, + { + "npc_id": "2242", + "loc_data": "{3076,3260,0,1,1}-{3078,3260,0,1,6}-" + }, + { + "npc_id": "2243", + "loc_data": "{3078,3259,0,1,3}-" + }, + { + "npc_id": "2244", + "loc_data": "{3233,3228,0,1,3}-" + }, + { + "npc_id": "2245", + "loc_data": "{2505,3240,0,1,4}-{2512,3250,0,1,1}-{2506,3242,0,1,3}-{2510,3252,0,1,4}-{2516,3248,0,1,1}-{2518,3245,0,1,7}-{2518,3245,0,1,1}-{2517,3242,0,1,6}-{2523,3241,0,1,4}-{2525,3241,0,1,1}-{2515,3256,0,1,6}-{2519,3257,0,1,4}-" + }, + { + "npc_id": "2246", + "loc_data": "{2521,3239,0,1,5}-" + }, + { + "npc_id": "2247", + "loc_data": "{2543,3225,0,1,7}-{2553,3220,0,1,3}-{2545,3220,0,1,4}-{2525,3220,0,1,4}-{2518,3218,0,1,1}-" + }, + { + "npc_id": "2248", + "loc_data": "{2540,3218,0,1,1}-{2515,3216,0,1,1}-{2512,3215,0,1,7}-" + }, + { + "npc_id": "2249", + "loc_data": "{2530,3224,0,1,4}-{2524,3221,0,1,2}-{2543,3217,0,1,4}-{2530,3212,0,1,4}-{2557,3234,0,1,4}-{2540,3226,0,1,5}-{2532,3213,0,1,4}-{2527,3201,0,1,6}-{2544,3216,0,1,6}-{2519,3222,0,1,6}-{2526,3227,0,1,4}-{2550,3234,0,1,3}-{2553,3220,0,1,3}-{2521,3202,0,1,6}-{2525,3220,0,1,4}-{2544,3230,0,1,3}-{2547,3212,0,1,4}-{2545,3212,0,1,0}-{2540,3208,0,1,4}-{2533,3202,0,1,6}-{2536,3236,0,1,6}-{2525,3204,0,1,0}-{2539,3238,0,1,4}-" + }, + { + "npc_id": "2250", + "loc_data": "{2525,3209,0,1,4}-{2558,3228,0,1,4}-{2543,3212,0,1,3}-{2538,3223,0,1,0}-{2527,3201,0,1,7}-{2517,3222,0,1,3}-{2547,3231,0,1,6}-{2550,3220,0,1,4}-{2538,3224,0,1,3}-{2545,3213,0,1,0}-{2542,3208,0,1,5}-" + }, + { + "npc_id": "2251", + "loc_data": "{2520,3217,0,1,3}-{2524,3209,0,1,1}-{2529,3200,0,1,5}-{2530,3209,0,1,1}-{2519,3223,0,1,4}-{2518,3224,0,1,2}-{2527,3201,0,1,7}-{2521,3202,0,1,4}-{2542,3208,0,1,4}-{2539,3233,0,1,6}-" + }, + { + "npc_id": "2252", + "loc_data": "{3079,3257,0,1,1}-{3091,3250,0,1,6}-{3137,3254,0,1,0}-{3199,3302,0,1,3}-" + }, + { + "npc_id": "2256", + "loc_data": "{2571,3305,0,1,7}-{2572,3297,0,1,2}-{2572,3304,0,1,1}-{2577,3308,0,1,6}-{2582,3287,0,1,3}-{2582,3299,0,1,0}-{2583,3292,0,1,4}-{2572,3292,1,1,1}-{2577,3295,1,1,6}-{2576,3286,1,1,1}-{2585,3289,1,1,7}-{2585,3304,1,1,1}-{2582,3306,1,1,5}-{2582,3286,1,1,3}-{2653,3315,0,1,6}-{2658,3306,0,1,4}-" + }, + { + "npc_id": "2257", + "loc_data": "{3105,3554,0,1,1}-" + }, + { + "npc_id": "2260", + "loc_data": "{3259,3385,0,1,6}-" + }, + { + "npc_id": "2262", + "loc_data": "{3039,4834,0,0,7}-" + }, + { + "npc_id": "2263", + "loc_data": "{3021,4812,0,1,5}-{3014,4838,0,1,6}-{3061,4836,0,1,4}-{3017,4812,0,1,6}-{3061,4842,0,1,3}-{3048,4808,0,1,3}-{3021,4847,0,1,6}-{3054,4849,0,1,6}-{3015,4833,0,1,0}-{3035,4854,0,1,6}-{3016,4829,0,1,4}-{3040,4810,0,1,3}-{3059,4815,0,1,1}-{3027,4851,0,1,6}-{3020,4819,0,1,4}-{3020,4845,0,1,2}-{3061,4829,0,1,0}-{3018,4842,0,1,4}-{3037,4906,0,1,2}-{3057,4875,0,0,6}-{3016,4892,0,1,6}-{3058,4877,0,0,6}-{3051,4874,0,1,0}-{3029,4890,0,1,6}-{3032,4901,0,1,2}-{3024,4916,0,1,3}-{3020,4903,0,1,4}-{3061,4883,0,0,3}-{3058,4889,0,0,3}-{3053,4896,0,0,3}-{3038,4871,0,1,6}-{3053,4900,0,0,7}-{3055,4915,0,0,5}-{3037,4906,0,0,5}-{3047,4886,0,1,4}-{3033,4884,0,1,4}-{3030,4877,0,1,2}-{3026,4879,0,1,3}-{3024,4879,0,1,3}-" + }, + { + "npc_id": "2264", + "loc_data": "{3017,4821,0,1,4}-{3059,4813,0,1,0}-{3032,4808,0,1,3}-{3059,4820,0,1,3}-{3062,4828,0,1,6}-{3036,4856,0,1,7}-{3062,4837,0,1,5}-{3025,4850,0,1,1}-{3052,4858,0,1,1}-{3024,4813,0,1,4}-{3048,4854,0,1,3}-{3034,4852,0,1,1}-{3028,4808,0,1,1}-{3018,4843,0,1,2}-{3028,4851,0,1,0}-{3016,4831,0,1,1}-{3043,4810,0,1,3}-{3020,4888,0,1,3}-{3039,4871,0,1,4}-{3049,4921,0,1,7}-{3019,4877,0,1,6}-{3017,4893,0,1,7}-{3019,4898,0,1,4}-{3020,4912,0,1,2}-{3054,4915,0,1,7}-{3052,4898,0,1,1}-{3034,4905,0,1,4}-{3052,4896,0,1,3}-{3046,4901,0,1,2}-{3062,4884,0,1,6}-{3049,4873,0,1,1}-{3041,4871,0,1,1}-" + }, + { + "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}-" + }, + { + "npc_id": "2266", + "loc_data": "{3053,4978,1,1,1}-" + }, + { + "npc_id": "2270", + "loc_data": "{3048,4975,1,1,6}-" + }, + { + "npc_id": "2271", + "loc_data": "{3045,4971,1,0,4}-" + }, + { + "npc_id": "2274", + "loc_data": "{3243,3245,0,1,4}-" + }, + { + "npc_id": "2275", + "loc_data": "{3244,3248,0,1,1}-" + }, + { + "npc_id": "2276", + "loc_data": "{3243,3247,0,1,1}-" + }, + { + "npc_id": "2277", + "loc_data": "{3248,3247,0,1,4}-" + }, + { + "npc_id": "2278", + "loc_data": "{3190,3251,0,1,0}-{3179,3243,0,1,0}-{3179,3248,0,1,0}-{3146,3235,0,1,0}-{3146,3261,0,1,0}-{3247,3244,0,1,2}-" + }, + { + "npc_id": "2279", + "loc_data": "{3181,3243,0,1,0}-{3176,3248,0,1,0}-{3182,3248,0,1,0}-{3140,3226,0,1,0}-{3140,3259,0,1,0}-{3143,3260,0,1,0}-{3244,3247,0,1,3}-" + }, + { + "npc_id": "2280", + "loc_data": "{3144,3228,0,1,0}-{3245,3244,0,1,4}-" + }, + { + "npc_id": "2281", + "loc_data": "{3187,3241,0,1,0}-{3177,3243,0,1,0}-{3246,3245,0,1,4}-" + }, + { + "npc_id": "2282", + "loc_data": "{2488,4973,0,0,6}-" + }, + { + "npc_id": "2283", + "loc_data": "{2458,4980,0,0,6}-" + }, + { + "npc_id": "2284", + "loc_data": "{2457,4966,0,0,6}-" + }, + { + "npc_id": "2286", + "loc_data": "{2476,4958,0,0,3}-" + }, + { + "npc_id": "2287", + "loc_data": "{2443,4956,0,0,4}-" + }, + { + "npc_id": "2288", + "loc_data": "{2469,4941,0,0,6}-" + }, + { + "npc_id": "2289", + "loc_data": "{2451,4939,0,0,6}-" + }, + { + "npc_id": "2290", + "loc_data": "{2997,3373,0,0,0}-" + }, + { + "npc_id": "2291", + "loc_data": "{3311,3109,0,0,3}-" + }, + { + "npc_id": "2292", + "loc_data": "{3182,3043,0,0,3}-" + }, + { + "npc_id": "2293", + "loc_data": "{3469,3111,0,1,6}-" + }, + { + "npc_id": "2294", + "loc_data": "{3350,3004,0,0,6}-" + }, + { + "npc_id": "2296", + "loc_data": "{3401,2918,0,0,6}-" + }, + { + "npc_id": "2298", + "loc_data": "{3287,2813,0,1,1}-" + }, + { + "npc_id": "2300", + "loc_data": "{3243,2813,0,0,1}-" + }, + { + "npc_id": "2301", + "loc_data": "{3181,3043,0,1,3}-{3310,3107,0,1,4}-" + }, + { + "npc_id": "2302", + "loc_data": "{3243,2812,0,1,1}-" + }, + { + "npc_id": "2304", + "loc_data": "{3039,3292,0,1,6}-" + }, + { + "npc_id": "2305", + "loc_data": "{2821,3463,0,1,3}-" + }, + { + "npc_id": "2306", + "loc_data": "{2643,3363,0,0,5}-" + }, + { + "npc_id": "2307", + "loc_data": "{3627,3526,0,1,6}-" + }, + { + "npc_id": "2310", + "loc_data": "{2928,3282,0,1,1}-{2927,3269,0,1,1}-{3025,3312,0,1,4}-{3043,3308,0,1,6}-{3023,3297,0,1,5}-" + }, + { + "npc_id": "2311", + "loc_data": "{3035,3296,0,0,0}-" + }, + { + "npc_id": "2312", + "loc_data": "{3014,3292,0,0,0}-" + }, + { + "npc_id": "2313", + "loc_data": "{3020,3297,0,1,6}-{3040,3296,0,1,3}-{3031,3283,0,1,4}-{3020,3288,0,1,0}-" + }, + { + "npc_id": "2314", + "loc_data": "{3018,3296,0,1,1}-{3016,3288,0,1,4}-{3030,3287,0,1,4}-{3017,3282,0,1,3}-{2549,3566,0,1,2}-{2553,3564,0,1,4}-" + }, + { + "npc_id": "2315", + "loc_data": "{3018,3282,0,1,5}-{3037,3289,0,1,1}-{2551,3562,0,1,1}-{2554,3561,0,1,3}-" + }, + { + "npc_id": "2316", + "loc_data": "{3015,3311,0,1,4}-" + }, + { + "npc_id": "2317", + "loc_data": "{3016,3309,0,1,6}-" + }, + { + "npc_id": "2318", + "loc_data": "{3017,3307,0,0,0}-" + }, + { + "npc_id": "2319", + "loc_data": "{3017,3309,0,0,0}-" + }, + { + "npc_id": "2321", + "loc_data": "{2915,10194,1,1,0}-" + }, + { + "npc_id": "2323", + "loc_data": "{3056,3306,0,1,1}-" + }, + { + "npc_id": "2324", + "loc_data": "{2807,3464,0,1,3}-" + }, + { + "npc_id": "2325", + "loc_data": "{2664,3374,0,1,1}-" + }, + { + "npc_id": "2326", + "loc_data": "{3600,3527,0,1,1}-" + }, + { + "npc_id": "2327", + "loc_data": "{2814,3337,0,1,6}-" + }, + { + "npc_id": "2330", + "loc_data": "{2766,3211,0,1,0}-" + }, + { + "npc_id": "2331", + "loc_data": "{2860,3430,0,1,4}-" + }, + { + "npc_id": "2332", + "loc_data": "{2573,3102,0,1,4}-" + }, + { + "npc_id": "2333", + "loc_data": "{3226,3311,0,0,0}-" + }, + { + "npc_id": "2334", + "loc_data": "{2662,3525,0,1,1}-" + }, + { + "npc_id": "2335", + "loc_data": "{3181,3359,0,0,0}-" + }, + { + "npc_id": "2336", + "loc_data": "{2938,3221,0,1,0}-" + }, + { + "npc_id": "2337", + "loc_data": "{2589,3861,0,1,0}-" + }, + { + "npc_id": "2338", + "loc_data": "{2615,3229,0,1,0}-" + }, + { + "npc_id": "2339", + "loc_data": "{2934,3438,0,1,6}-" + }, + { + "npc_id": "2340", + "loc_data": "{3007,3373,0,1,4}-" + }, + { + "npc_id": "2341", + "loc_data": "{3229,3456,0,1,0}-" + }, + { + "npc_id": "2342", + "loc_data": "{3196,3231,0,1,4}-" + }, + { + "npc_id": "2343", + "loc_data": "{2473,3446,0,1,6}-" + }, + { + "npc_id": "2344", + "loc_data": "{2490,3183,0,1,3}-" + }, + { + "npc_id": "2352", + "loc_data": "{2334,3183,0,1,0}-" + }, + { + "npc_id": "2353", + "loc_data": "{2324,3179,0,1,5}-" + }, + { + "npc_id": "2355", + "loc_data": "{2353,3163,0,0,4}-" + }, + { + "npc_id": "2356", + "loc_data": "{2323,3163,0,1,0}-" + }, + { + "npc_id": "2357", + "loc_data": "{2340,3157,1,1,0}-" + }, + { + "npc_id": "2359", + "loc_data": "{2335,3169,0,1,3}-" + }, + { + "npc_id": "2360", + "loc_data": "{2335,3174,0,1,4}-{2322,3172,0,1,5}-" + }, + { + "npc_id": "2361", + "loc_data": "{2345,3172,0,1,2}-{2340,3178,0,1,3}-" + }, + { + "npc_id": "2362", + "loc_data": "{2330,3163,0,1,3}-" + }, + { + "npc_id": "2372", + "loc_data": "{2044,4628,0,1,0}-" + }, + { + "npc_id": "2373", + "loc_data": "{2037,4636,0,1,0}-{2038,4644,0,1,0}-{2041,4638,0,1,0}-{2044,4642,0,1,0}-" + }, + { + "npc_id": "2374", + "loc_data": "{2036,4633,0,0,6}-" + }, + { + "npc_id": "2381", + "loc_data": "{2555,3444,0,1,0}-" + }, + { + "npc_id": "2382", + "loc_data": "{3022,3946,0,1,0}-" + }, + { + "npc_id": "2383", + "loc_data": "{3034,3701,0,1,0}-" + }, + { + "npc_id": "2384", + "loc_data": "{3162,2982,0,1,0}-" + }, + { + "npc_id": "2385", + "loc_data": "{3112,3158,0,1,0}-" + }, + { + "npc_id": "2386", + "loc_data": "{3052,3496,1,1,0}-" + }, + { + "npc_id": "2387", + "loc_data": "{3053,3378,1,1,0}-" + }, + { + "npc_id": "2388", + "loc_data": "{2950,3820,0,1,0}-" + }, + { + "npc_id": "2389", + "loc_data": "{3218,3677,0,1,0}-" + }, + { + "npc_id": "2390", + "loc_data": "{3068,3858,0,1,0}-" + }, + { + "npc_id": "2391", + "loc_data": "{2850,3348,0,1,0}-" + }, + { + "npc_id": "2392", + "loc_data": "{3044,3204,0,1,0}-" + }, + { + "npc_id": "2393", + "loc_data": "{2396,3481,0,1,0}-" + }, + { + "npc_id": "2394", + "loc_data": "{3294,3934,1,1,0}-" + }, + { + "npc_id": "2395", + "loc_data": "{3448,3550,1,1,0}-" + }, + { + "npc_id": "2396", + "loc_data": "{3119,9996,0,1,0}-" + }, + { + "npc_id": "2414", + "loc_data": "{3263,3400,1,0,0}-" + }, + { + "npc_id": "2435", + "loc_data": "{2621,3682,0,0,6}-" + }, + { + "npc_id": "2438", + "loc_data": "{2544,3761,0,0,6}-" + }, + { + "npc_id": "2439", + "loc_data": "{2659,3657,0,1,3}-" + }, + { + "npc_id": "2440", + "loc_data": "{2545,10141,0,0,6}-" + }, + { + "npc_id": "2443", + "loc_data": "{2543,10143,0,0,3}-" + }, + { + "npc_id": "2446", + "loc_data": "{2545,10145,0,0,6}-" + }, + { + "npc_id": "2449", + "loc_data": "{2546,10144,0,0,1}-{2546,10142,0,0,6}-" + }, + { + "npc_id": "2452", + "loc_data": "{2526,10166,0,1,6}-" + }, + { + "npc_id": "2453", + "loc_data": "{1846,4405,3,0,2}-{1839,4400,3,0,3}-{1835,4394,3,0,3}-{1834,4406,3,0,1}-{1843,4406,3,0,2}-{1899,4372,0,0,2}-{1888,4362,0,0,7}-{1896,4362,0,0,2}-{1897,4368,0,0,5}-{2521,10160,0,0,1}-{2535,10163,0,0,6}-{2520,10165,0,0,3}-{2533,10162,0,0,3}-{2545,10158,0,0,5}-{2547,10151,0,0,3}-{2543,10166,0,0,3}-{2538,10160,0,0,6}-{2506,10163,0,0,3}-{2516,10163,0,0,3}-{2498,10164,0,0,5}-{2544,10160,0,0,0}-{2525,10162,0,0,3}-{2530,10161,0,0,0}-{2537,10167,0,0,7}-{2519,10163,0,0,3}-{2511,10163,0,0,3}-{2530,10161,0,0,7}-{2524,10166,0,0,3}-{2508,10159,0,0,0}-" + }, + { + "npc_id": "2455", + "loc_data": "{1815,4405,2,1,6}-{1818,4403,2,1,3}-{1813,4404,2,1,7}-{1814,4407,2,1,7}-{1821,4393,2,1,1}-{1823,4389,2,1,1}-{1828,4387,2,1,3}-{1821,4392,2,1,0}-{1826,4391,2,1,0}-{1844,4360,1,1,4}-{1844,4361,1,1,5}-{1851,4360,1,1,3}-{1831,4378,3,1,2}-{1838,4379,3,1,2}-{1824,4376,3,1,1}-{1832,4380,3,1,6}-{1844,4373,2,1,4}-{1849,4381,2,1,4}-{1846,4374,2,1,1}-{1865,4377,2,1,1}-{1865,4397,1,1,6}-{1857,4360,1,1,1}-{1862,4362,1,1,1}-{1862,4362,1,1,7}-{1864,4366,1,1,1}-{1865,4383,2,1,4}-{1864,4390,1,1,1}-{1865,4408,1,1,4}-{1877,4410,1,1,1}-{1883,4411,1,1,4}-{1889,4410,1,1,4}-{1887,4374,0,1,6}-{1890,4372,0,1,4}-{1890,4366,0,1,4}-{1896,4371,0,1,3}-{1890,4364,0,1,7}-{2457,10139,0,1,2}-{2465,10133,0,1,4}-{2449,10148,0,1,1}-{2471,10133,0,1,6}-{2446,10145,0,1,4}-{2443,10148,0,1,1}-{2452,10161,0,1,3}-{2444,10145,0,1,1}-{2462,10163,0,1,3}-{2446,10141,0,1,3}-{2454,10149,0,1,0}-{2476,10155,0,1,4}-{2456,10153,0,1,0}-{2485,10142,0,1,3}-{2457,10152,0,1,4}-{2485,10126,0,1,4}-{2475,10148,0,1,3}-{2471,10160,0,1,1}-{2453,10145,0,1,3}-{2463,10158,0,1,4}-{2457,10163,0,1,2}-{2471,10154,0,1,6}-{2476,10151,0,1,7}-{2479,10154,0,1,1}-{2486,10158,0,1,4}-{2485,10165,0,1,5}-{2485,10153,0,1,4}-{2474,10140,0,1,7}-{2466,10130,0,1,6}-{2464,10138,0,1,6}-{2486,10150,0,1,6}-{2473,10127,0,1,4}-{2483,10126,0,1,3}-{2456,10133,0,1,6}-{2467,10163,0,1,6}-{2477,10143,0,1,3}-{2489,10139,0,1,4}-{2486,10127,0,1,1}-{2480,10141,0,1,7}-{2490,10155,0,1,3}-{2459,10133,0,1,3}-{2455,10163,0,1,0}-{2494,10162,0,1,1}-{2493,10132,0,1,3}-{2494,10129,0,1,5}-{2496,10166,0,1,1}-{2502,10167,0,1,1}-{2499,10162,0,1,0}-{2502,10159,0,1,1}-{2502,10166,0,1,4}-{2496,10130,0,1,3}-{2498,10128,0,1,1}-{2497,10126,0,1,4}-{2499,10128,0,1,5}-{2501,10126,0,1,3}-{2506,10125,0,1,4}-{2506,10127,0,1,4}-{2507,10128,0,1,3}-{2507,10126,0,1,6}-{2511,10126,0,1,0}-{2513,10126,0,1,4}-{2517,10129,0,1,3}-{2513,10127,0,1,3}-{2516,10124,0,1,4}-{2520,10122,0,1,5}-{2522,10126,0,1,3}-{2524,10129,0,1,4}-{2525,10125,0,1,4}-{2529,10122,0,1,4}-{2529,10124,0,1,2}-{2526,10126,0,1,3}-{2529,10130,0,1,6}-{2530,10127,0,1,5}-{2532,10121,0,1,0}-{2532,10123,0,1,3}-{2538,10126,0,1,0}-{2535,10125,0,1,7}-{2536,10127,0,1,1}-{2539,10130,0,1,6}-{2543,10130,0,1,6}-{2548,10131,0,1,1}-{2542,10128,0,1,3}-" + }, + { + "npc_id": "2456", + "loc_data": "{2452,10145,0,0,4}-{2445,10138,0,0,7}-" + }, + { + "npc_id": "2457", + "loc_data": "{1804,4369,2,1,2}-{1812,4363,2,1,5}-{1817,4368,2,1,6}-{1801,4404,3,1,6}-{1805,4405,3,1,1}-{1803,4404,3,1,7}-{1811,4394,1,1,7}-{1804,4391,1,1,2}-{1798,4391,1,1,4}-{1802,4389,1,1,4}-{1799,4386,2,1,1}-{1799,4381,2,1,6}-{1798,4375,1,1,4}-{1803,4376,1,1,6}-{1797,4380,1,1,6}-{1848,4394,1,1,3}-{1850,4389,1,1,1}-{1850,4391,1,1,2}-{1902,4367,0,1,6}-{1894,4367,0,1,1}-{1893,4361,0,1,3}-{1886,4369,0,1,1}-{2500,10148,0,1,3}-{2505,10150,0,1,2}-{2510,10148,0,1,1}-{2511,10147,0,1,6}-{2517,10151,0,1,3}-{2519,10143,0,1,7}-{2529,10148,0,1,3}-{2528,10144,0,1,0}-{2532,10145,0,1,1}-{2532,10140,0,1,7}-{2520,10144,0,1,2}-" + }, + { + "npc_id": "2458", + "loc_data": "{2599,4774,0,0,3}-" + }, + { + "npc_id": "2459", + "loc_data": "{2604,4772,0,1,4}-{2606,4771,0,1,6}-{2604,4769,0,1,2}-" + }, + { + "npc_id": "2460", + "loc_data": "{2600,4778,0,1,1}-{2607,4777,0,1,1}-" + }, + { + "npc_id": "2461", + "loc_data": "{2597,4776,0,1,1}-{2606,4771,0,1,1}-" + }, + { + "npc_id": "2462", + "loc_data": "{2596,4773,0,1,2}-{2602,4771,0,1,6}-{2596,4780,0,1,4}-" + }, + { + "npc_id": "2477", + "loc_data": "{1952,4768,1,0,6}-" + }, + { + "npc_id": "2479", + "loc_data": "{3420,4777,0,0,0}-" + }, + { + "npc_id": "2481", + "loc_data": "{3423,4777,0,0,5}-" + }, + { + "npc_id": "2483", + "loc_data": "{2914,3116,0,0,0}-" + }, + { + "npc_id": "2484", + "loc_data": "{2782,3058,0,0,0}-" + }, + { + "npc_id": "2486", + "loc_data": "{2789,3055,0,0,0}-" + }, + { + "npc_id": "2487", + "loc_data": "{2848,3042,0,0,0}-" + }, + { + "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}-" + }, + { + "npc_id": "2504", + "loc_data": "{2796,3077,1,1,3}-" + }, + { + "npc_id": "2507", + "loc_data": "{2794,3077,0,1,6}-" + }, + { + "npc_id": "2510", + "loc_data": "{2786,3075,1,1,4}-" + }, + { + "npc_id": "2513", + "loc_data": "{2796,3088,0,1,7}-" + }, + { + "npc_id": "2516", + "loc_data": "{2791,3102,0,1,3}-" + }, + { + "npc_id": "2519", + "loc_data": "{2794,3064,0,1,5}-" + }, + { + "npc_id": "2522", + "loc_data": "{2758,3099,0,1,1}-" + }, + { + "npc_id": "2525", + "loc_data": "{2800,3048,0,1,2}-" + }, + { + "npc_id": "2528", + "loc_data": "{2816,3085,0,1,3}-" + }, + { + "npc_id": "2531", + "loc_data": "{2784,3089,0,1,1}-" + }, + { + "npc_id": "2547", + "loc_data": "{2615,9525,0,1,4}-{2615,9521,0,1,1}-{3109,9942,0,1,4}-{3111,9938,0,1,4}-{3116,9926,0,1,4}-" + }, + { + "npc_id": "2548", + "loc_data": "{3349,2970,0,0,4}-{3358,2987,0,0,4}-" + }, + { + "npc_id": "2549", + "loc_data": "{3364,3001,0,0,6}-" + }, + { + "npc_id": "2553", + "loc_data": "{1944,4958,0,1,0}-" + }, + { + "npc_id": "2564", + "loc_data": "{1936,4966,0,0,4}-" + }, + { + "npc_id": "2565", + "loc_data": "{1939,4968,0,1,0}-" + }, + { + "npc_id": "2568", + "loc_data": "{2729,3495,0,0,6}-{2724,3495,0,0,6}-{2728,3495,0,0,6}-{2721,3495,0,0,6}-" + }, + { + "npc_id": "2569", + "loc_data": "{2727,3495,0,0,6}-{2722,3495,0,0,6}-" + }, + { + "npc_id": "2572", + "loc_data": "{3079,3250,0,1,2}-" + }, + { + "npc_id": "2574", + "loc_data": "{3087,3247,0,0,7}-" + }, + { + "npc_id": "2580", + "loc_data": "{2918,3535,0,1,0}-" + }, + { + "npc_id": "2591", + "loc_data": "{4786,5106,0,1,6}-{2474,5145,0,1,2}-" + }, + { + "npc_id": "2592", + "loc_data": "{4680,5117,0,1,6}-{2461,5142,0,1,6}-" + }, + { + "npc_id": "2594", + "loc_data": "{4730,5077,0,1,5}-{4703,5123,0,1,5}-{4680,5126,0,1,7}-" + }, + { + "npc_id": "2595", + "loc_data": "{4633,5069,0,1,4}-{4647,5132,0,1,7}-{4699,5100,0,1,6}-{4757,5100,0,1,2}-" + }, + { + "npc_id": "2596", + "loc_data": "{4628,5117,0,1,4}-{4787,5118,0,1,7}-{2483,5155,0,1,4}-" + }, + { + "npc_id": "2597", + "loc_data": "{4621,5094,0,1,5}-{2446,5144,0,1,1}-{2470,5141,0,1,6}-" + }, + { + "npc_id": "2598", + "loc_data": "{2485,5159,0,1,4}-{2489,5155,0,1,1}-{2471,5144,0,1,3}-{2476,5153,0,1,0}-{2477,5150,0,1,1}-" + }, + { + "npc_id": "2600", + "loc_data": "{4656,5125,0,1,5}-{4711,5072,0,1,4}-{4751,5087,0,1,3}-" + }, + { + "npc_id": "2601", + "loc_data": "{4724,5107,0,1,5}-{4676,5123,0,1,5}-{4693,5127,0,1,7}-{4788,5110,0,1,4}-" + }, + { + "npc_id": "2602", + "loc_data": "{4668,5133,0,1,7}-{4627,5122,0,1,5}-{4714,5113,0,1,6}-{4752,5074,0,1,7}-{4789,5122,0,1,2}-" + }, + { + "npc_id": "2603", + "loc_data": "{4711,5086,0,1,2}-{4760,5091,0,1,4}-" + }, + { + "npc_id": "2604", + "loc_data": "{2439,5170,0,1,3}-{2451,5170,0,1,3}-{2453,5161,0,1,6}-{2460,5160,0,1,7}-{2457,5155,0,1,3}-{2480,5168,0,1,6}-{2469,5166,0,1,1}-{2490,5173,0,1,3}-{2488,5171,0,1,4}-{2483,5156,0,1,3}-{2470,5147,0,1,4}-{2468,5139,0,1,7}-{2458,5141,0,1,3}-{2454,5141,0,1,1}-{2452,5124,0,1,3}-{2454,5122,0,1,2}-{2453,5143,0,1,3}-{2443,5144,0,1,6}-" + }, + { + "npc_id": "2605", + "loc_data": "{4631,5088,0,1,7}-{4633,5125,0,1,7}-{4760,5074,0,1,4}-" + }, + { + "npc_id": "2606", + "loc_data": "{4670,5081,0,1,6}-{4642,5115,0,1,7}-{4756,5086,0,1,4}-" + }, + { + "npc_id": "2607", + "loc_data": "{4638,5095,0,1,2}-{4647,5141,0,1,4}-{4699,5141,0,1,3}-{4774,5098,0,1,3}-" + }, + { + "npc_id": "2608", + "loc_data": "{4656,5091,0,1,5}-{4637,5093,0,1,0}-" + }, + { + "npc_id": "2609", + "loc_data": "{4617,5073,0,1,6}-{4691,5090,0,1,7}-{4727,5082,0,1,6}-{4711,5125,0,1,4}-{4748,5070,0,1,4}-{2440,5128,0,1,3}-{2436,5133,0,1,3}-{2450,5168,0,1,2}-{2443,5170,0,1,4}-{2445,5177,0,1,3}-" + }, + { + "npc_id": "2610", + "loc_data": "{2446,5169,0,1,4}-{2458,5159,0,1,3}-{2466,5166,0,1,7}-{2471,5166,0,1,3}-{2478,5164,0,1,4}-{2490,5174,0,1,4}-{2485,5159,0,1,5}-{2442,5173,0,1,0}-{2440,5171,0,1,7}-{2440,5170,0,1,3}-" + }, + { + "npc_id": "2611", + "loc_data": "{4646,5107,0,1,6}-{4683,5098,0,1,4}-{4690,5113,0,1,5}-{4720,5118,0,1,7}-{4716,5097,0,1,5}-{4762,5088,0,1,0}-" + }, + { + "npc_id": "2614", + "loc_data": "{4666,5082,0,1,0}-{4669,5103,0,1,2}-{4639,5074,0,1,3}-{4664,5121,0,1,4}-{4688,5016,0,1,3}-{4704,5077,0,1,5}-" + }, + { + "npc_id": "2615", + "loc_data": "{4625,5084,0,1,7}-{4628,5134,0,1,3}-{4725,5067,0,1,5}-{4695,5128,0,1,7}-{4748,5076,0,1,6}-{4790,5127,0,1,0}-" + }, + { + "npc_id": "2616", + "loc_data": "{4631,5106,0,1,0}-{4673,5114,0,1,1}-{2441,5130,0,1,1}-{2438,5135,0,1,6}-{2443,5138,0,1,2}-" + }, + { + "npc_id": "2617", + "loc_data": "{4611,5132,0,1,5}-{2440,5168,0,0,1}-" + }, + { + "npc_id": "2618", + "loc_data": "{2399,5178,0,1,6}-{4603,5065,0,1,1}-" + }, + { + "npc_id": "2619", + "loc_data": "{4644,5149,0,1,7}-{2418,5203,0,1,6}-{2446,5179,0,0,6}-" + }, + { + "npc_id": "2620", + "loc_data": "{4701,5153,0,1,0}-{2478,5146,0,1,2}-" + }, + { + "npc_id": "2621", + "loc_data": "{2724,3729,0,0,6}-" + }, + { + "npc_id": "2622", + "loc_data": "{4689,5173,0,0,0}-{2464,5149,0,0,7}-" + }, + { + "npc_id": "2623", + "loc_data": "{4655,5172,0,1,0}-{2462,5125,0,1,5}-" + }, + { + "npc_id": "2624", + "loc_data": "{4718,5148,0,1,6}-{4708,5173,0,1,6}-{4716,5178,0,1,7}-{4726,5173,0,1,0}-{4730,5163,0,1,3}-{4726,5153,0,1,5}-" + }, + { + "npc_id": "2625", + "loc_data": "{4668,5177,0,1,4}-{4676,5177,0,1,2}-" + }, + { + "npc_id": "2634", + "loc_data": "{3095,3252,0,0,0}-" + }, + { + "npc_id": "2652", + "loc_data": "{2868,3579,0,0,0}-{2851,3582,0,0,0}-" + }, + { + "npc_id": "2655", + "loc_data": "{2921,3558,0,1,6}-" + }, + { + "npc_id": "2660", + "loc_data": "{3208,3496,0,1,3}-" + }, + { + "npc_id": "2674", + "loc_data": "{3017,3231,0,1,1}-" + }, + { + "npc_id": "2676", + "loc_data": "{2914,3322,0,0,0}-" + }, + { + "npc_id": "2677", + "loc_data": "{2945,3307,0,0,0}-" + }, + { + "npc_id": "2678", + "loc_data": "{2988,3210,0,1,7}-" + }, + { + "npc_id": "2679", + "loc_data": "{3000,3222,0,1,1}-" + }, + { + "npc_id": "2680", + "loc_data": "{2582,9836,0,1,2}-{2985,3193,0,1,1}-" + }, + { + "npc_id": "2681", + "loc_data": "{2991,3199,0,1,3}-" + }, + { + "npc_id": "2682", + "loc_data": "{2904,3208,0,1,0}-{1866,4237,0,1,0}-{1866,4240,0,1,0}-{1867,4243,0,1,0}-{2950,3204,0,1,5}-{2971,3212,0,1,4}-{2960,3201,0,1,1}-{2959,3202,0,1,4}-{2956,3203,0,1,1}-{2969,3204,0,1,4}-" + }, + { + "npc_id": "2683", + "loc_data": "{2967,3212,1,0,7}-" + }, + { + "npc_id": "2684", + "loc_data": "{2965,3213,1,0,0}-" + }, + { + "npc_id": "2685", + "loc_data": "{3124,9873,0,1,3}-{2911,3285,0,1,1}-{2696,9689,0,1,1}-" + }, + { + "npc_id": "2686", + "loc_data": "{2907,3293,0,1,4}-{2700,9696,0,1,1}-" + }, + { + "npc_id": "2687", + "loc_data": "{2907,3293,0,1,6}-{2908,3290,0,1,3}-{2703,9698,0,1,1}-" + }, + { + "npc_id": "2688", + "loc_data": "{3127,9874,0,1,4}-{2906,3288,0,1,1}-{2910,3293,0,1,4}-{2699,9690,0,1,1}-" + }, + { + "npc_id": "2689", + "loc_data": "{2918,3319,0,0,0}-" + }, + { + "npc_id": "2690", + "loc_data": "{3048,3258,0,1,3}-" + }, + { + "npc_id": "2691", + "loc_data": "{3048,3256,0,1,4}-" + }, + { + "npc_id": "2692", + "loc_data": "{3049,3256,0,0,0}-" + }, + { + "npc_id": "2693", + "loc_data": "{3238,3238,0,1,0}-{3238,3246,0,1,0}-{3239,3235,0,1,0}-{3214,3313,0,1,0}-{3214,3320,0,1,0}-{3215,3303,0,1,0}-{3219,3285,0,1,0}-{3219,3288,0,1,0}-{3234,3267,0,1,0}-{3234,3268,0,1,0}-{2976,3359,0,1,0}-{2977,3357,0,1,0}-{2988,3383,0,1,1}-{2992,3385,0,1,3}-{3015,3285,0,1,0}-" + }, + { + "npc_id": "2695", + "loc_data": "{3013,3192,0,1,1}-" + }, + { + "npc_id": "2696", + "loc_data": "{3013,3196,0,1,1}-" + }, + { + "npc_id": "2697", + "loc_data": "{3018,3189,0,1,4}-" + }, + { + "npc_id": "2698", + "loc_data": "{3018,3181,0,1,6}-" + }, + { + "npc_id": "2699", + "loc_data": "{3016,3184,1,1,0}-" + }, + { + "npc_id": "2700", + "loc_data": "{3016,3182,1,1,0}-" + }, + { + "npc_id": "2701", + "loc_data": "{3011,3181,1,1,0}-" + }, + { + "npc_id": "2702", + "loc_data": "{3019,3180,1,1,0}-" + }, + { + "npc_id": "2703", + "loc_data": "{3012,3185,1,1,0}-" + }, + { + "npc_id": "2704", + "loc_data": "{3019,3185,0,0,0}-" + }, + { + "npc_id": "2705", + "loc_data": "{3018,3185,2,1,0}-" + }, + { + "npc_id": "2706", + "loc_data": "{3725,3015,0,1,4}-{3745,3026,0,1,6}-{3741,3033,0,1,6}-{3734,3045,0,1,1}-{3019,3145,0,1,3}-{3015,3142,0,1,4}-{3022,3139,0,1,4}-" + }, + { + "npc_id": "2709", + "loc_data": "{3002,3266,0,1,6}-" + }, + { + "npc_id": "2710", + "loc_data": "{3001,3268,0,1,4}-" + }, + { + "npc_id": "2711", + "loc_data": "{3002,3268,0,1,4}-" + }, + { + "npc_id": "2712", + "loc_data": "{3001,3268,0,1,3}-" + }, + { + "npc_id": "2713", + "loc_data": "{3002,3267,0,1,2}-" + }, + { + "npc_id": "2725", + "loc_data": "{2500,3488,0,1,2}-" + }, + { + "npc_id": "2726", + "loc_data": "{2994,3138,0,1,0}-" + }, + { + "npc_id": "2727", + "loc_data": "{2969,3146,0,1,0}-" + }, + { + "npc_id": "2728", + "loc_data": "{3043,3235,0,1,7}-" + }, + { + "npc_id": "2729", + "loc_data": "{3047,3234,0,1,7}-" + }, + { + "npc_id": "2730", + "loc_data": "{2835,3335,0,0,5}-" + }, + { + "npc_id": "2731", + "loc_data": "{2833,3335,0,0,3}-" + }, + { + "npc_id": "2732", + "loc_data": "{2933,3281,0,1,1}-" + }, + { + "npc_id": "2733", + "loc_data": "{2932,3286,0,1,1}-" + }, + { + "npc_id": "2759", + "loc_data": "{3104,3368,0,0,0}-{3096,3493,0,0,0}-{3097,3494,0,0,0}-{3180,3438,0,0,0}-{3180,3442,0,0,0}-{3191,3441,0,0,0}-{3191,3437,0,0,0}-{3191,3445,0,0,0}-{3254,3418,0,0,0}-{3255,3418,0,0,0}-{3256,3418,0,0,0}-" + }, + { + "npc_id": "2782", + "loc_data": "{1925,4636,0,1,7}-" + }, + { + "npc_id": "2783", + "loc_data": "{1995,4660,0,1,7}-{1989,4660,0,1,0}-{1989,4664,0,1,7}-{1997,4664,0,1,3}-{1989,4658,0,1,0}-{2001,4646,0,1,4}-{2000,4645,0,1,3}-{1997,4644,0,1,1}-{1999,4636,0,1,3}-{2022,4665,0,1,0}-{2024,4662,0,1,0}-{2020,4660,0,1,0}-{2023,4656,0,1,0}-{2029,4660,0,1,0}-{2031,4665,0,1,0}-{2034,4661,0,1,0}-{2032,4658,0,1,0}-{1991,4653,0,1,0}-{1995,4651,0,1,0}-" + }, + { + "npc_id": "2784", + "loc_data": "{2022,4616,0,1,5}-{2024,4620,0,1,5}-" + }, + { + "npc_id": "2785", + "loc_data": "{2000,4613,0,0,0}-{2000,4611,0,0,5}-" + }, + { + "npc_id": "2786", + "loc_data": "{2008,4613,0,1,0}-" + }, + { + "npc_id": "2787", + "loc_data": "{2023,4612,0,1,0}-" + }, + { + "npc_id": "2790", + "loc_data": "{3163,4822,0,0,6}-" + }, + { + "npc_id": "2792", + "loc_data": "{3228,3412,0,0,0}-" + }, + { + "npc_id": "2794", + "loc_data": "{2606,3100,0,0,0}-" + }, + { + "npc_id": "2795", + "loc_data": "{2979,3197,0,1,0}-" + }, + { + "npc_id": "2796", + "loc_data": "{3096,3108,0,0,6}-" + }, + { + "npc_id": "2800", + "loc_data": "{2981,3190,0,0,0}-" + }, + { + "npc_id": "2801", + "loc_data": "{2523,3373,0,1,0}-{2523,3376,0,1,0}-{2526,3373,0,1,0}-{2526,3376,0,1,0}-{2529,3373,0,1,0}-{2529,3376,0,1,0}-{2531,3376,0,1,0}-{2532,3373,0,1,0}-" + }, + { + "npc_id": "2802", + "loc_data": "{2406,3498,0,1,0}-" + }, + { + "npc_id": "2803", + "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", + "loc_data": "{3388,3067,0,1,0}-{3396,3064,0,1,6}-{3402,3063,0,1,1}-{3423,3019,0,1,3}-{3420,3015,0,1,6}-{3430,3014,0,1,2}-{3426,3020,0,0,0}-{3416,3013,0,1,4}-{3410,3016,0,1,6}-" + }, + { + "npc_id": "2805", + "loc_data": "{3391,3071,0,1,2}-{3385,3064,0,1,6}-{3397,3069,0,1,2}-{3398,3068,0,1,1}-{3406,3064,0,1,6}-{3413,3053,0,1,6}-{3424,3061,0,1,2}-{3423,3020,0,1,0}-{3429,3019,0,0,0}-" + }, + { + "npc_id": "2806", + "loc_data": "{3426,3052,0,1,4}-{3423,3044,0,1,3}-{3415,3018,0,1,5}-{3425,3009,0,1,3}-" + }, + { + "npc_id": "2807", + "loc_data": "{3384,3013,0,1,0}-{3387,3014,0,1,0}-{3441,3059,0,1,0}-{3443,3033,0,1,0}-{3443,3059,0,1,0}-" + }, + { + "npc_id": "2808", + "loc_data": "{3385,3067,0,1,0}-{3389,3065,0,1,0}-{3400,3032,0,1,0}-{3402,3061,0,1,0}-{3404,3060,0,1,0}-{3407,3067,0,1,0}-{3408,3060,0,1,0}-{3411,3065,0,1,0}-{3412,3032,0,1,0}-{3412,3049,0,1,0}-{3414,3029,0,1,0}-{3417,3036,0,1,0}-{3418,3032,0,1,0}-{3419,3034,0,1,0}-{3422,3058,0,1,0}-{3424,3059,0,1,0}-{3435,3057,0,1,0}-{3435,3062,0,1,0}-{3439,3013,0,1,0}-{3439,3065,0,1,0}-{3440,3016,0,1,0}-{3443,3012,0,1,0}-{3443,3017,0,1,0}-{3443,3034,0,1,0}-{3443,3047,0,1,0}-{3446,3014,0,1,0}-" + }, + { + "npc_id": "2809", + "loc_data": "{3274,3168,0,1,0}-" + }, + { + "npc_id": "2810", + "loc_data": "{3311,3208,0,1,0}-" + }, + { + "npc_id": "2811", + "loc_data": "{3289,3209,0,1,0}-" + }, + { + "npc_id": "2812", + "loc_data": "{3296,3227,0,0,0}-" + }, + { + "npc_id": "2813", + "loc_data": "{3285,3201,0,0,0}-" + }, + { + "npc_id": "2816", + "loc_data": "{3301,3190,0,0,0}-" + }, + { + "npc_id": "2817", + "loc_data": "{3306,3197,0,0,0}-" + }, + { + "npc_id": "2818", + "loc_data": "{3303,3204,0,0,0}-" + }, + { + "npc_id": "2819", + "loc_data": "{3295,3193,0,0,0}-" + }, + { + "npc_id": "2820", + "loc_data": "{3302,3185,0,1,4}-" + }, + { + "npc_id": "2821", + "loc_data": "{3306,3196,0,0,4}-" + }, + { + "npc_id": "2823", + "loc_data": "{3302,3197,0,1,4}-" + }, + { + "npc_id": "2824", + "loc_data": "{3275,3193,0,0,0}-" + }, + { + "npc_id": "2860", + "loc_data": "{2345,3165,0,1,4}-" + }, + { + "npc_id": "2861", + "loc_data": "{2347,3165,0,1,3}-" + }, + { + "npc_id": "2881", + "loc_data": "{2907,4440,0,1,6}-" + }, + { + "npc_id": "2882", + "loc_data": "{2910,4457,0,1,6}-" + }, + { + "npc_id": "2883", + "loc_data": "{2923,4443,0,1,4}-" + }, + { + "npc_id": "2890", + "loc_data": "{1889,4411,0,0,4}-{1895,4409,0,0,2}-{1903,4409,0,0,3}-{1906,4407,0,0,5}-{1903,4400,0,0,1}-{1899,4400,0,0,2}-{1893,4398,0,0,2}-{1888,4386,0,0,6}-{1884,4383,0,0,5}-{1879,4376,0,0,5}-{1883,4376,0,0,1}-{1921,4368,0,0,2}-{1927,4362,0,0,7}-{1935,4360,0,0,7}-{1942,4356,0,0,7}-{1948,4355,0,0,7}-{1951,4358,0,0,2}-{1957,4365,0,0,2}-{1958,4368,0,0,2}-" + }, + { + "npc_id": "2894", + "loc_data": "{2908,4435,0,0,1}-{2924,4436,0,0,5}-{2924,4460,0,0,6}-{2908,4463,0,0,3}-{2901,4458,0,0,3}-{2928,4441,0,0,3}-{2929,4449,0,0,6}-{2902,4437,0,0,1}-" + }, + { + "npc_id": "2910", + "loc_data": "{3082,9885,0,1,4}-" + }, + { + "npc_id": "2932", + "loc_data": "{2437,3347,0,1,0}-" + }, + { + "npc_id": "2935", + "loc_data": "{3671,3484,0,1,4}-" + }, + { + "npc_id": "2939", + "loc_data": "{2657,3701,0,1,0}-" + }, + { + "npc_id": "2940", + "loc_data": "{2675,3671,0,1,3}-" + }, + { + "npc_id": "2942", + "loc_data": "{3017,3234,0,1,7}-" + }, + { + "npc_id": "2943", + "loc_data": "{3265,3400,0,0,0}-" + }, + { + "npc_id": "2945", + "loc_data": "{2562,3319,0,0,0}-" + }, + { + "npc_id": "2946", + "loc_data": "{3246,9865,0,1,1}-" + }, + { + "npc_id": "2947", + "loc_data": "{3245,9868,0,1,3}-" + }, + { + "npc_id": "2948", + "loc_data": "{3265,3403,0,0,0}-" + }, + { + "npc_id": "2949", + "loc_data": "{2562,3320,0,0,0}-" + }, + { + "npc_id": "2950", + "loc_data": "{3020,3229,0,0,7}-" + }, + { + "npc_id": "2997", + "loc_data": "{3308,3511,1,1,1}-" + }, + { + "npc_id": "2998", + "loc_data": "{2657,9641,0,1,0}-" + }, + { + "npc_id": "2999", + "loc_data": "{2658,9634,0,1,1}-" + }, + { + "npc_id": "3001", + "loc_data": "{2646,9639,0,1,4}-" + }, + { + "npc_id": "3002", + "loc_data": "{2666,9630,0,1,6}-" + }, + { + "npc_id": "3003", + "loc_data": "{2660,9624,0,1,6}-" + }, + { + "npc_id": "3020", + "loc_data": "{3348,2942,0,1,3}-" + }, + { + "npc_id": "3021", + "loc_data": "{2572,3105,0,1,0}-{2614,3226,0,1,4}-{3088,3357,0,1,0}-{2856,3435,0,1,0}-{3603,3529,0,1,6}-{2611,3859,0,1,6}-{2589,3863,0,1,0}-{3189,3234,0,1,0}-{2940,3225,0,1,6}-{2663,3374,0,1,4}-{3182,3354,0,1,0}-{2933,3434,0,1,4}-{3449,3471,0,1,6}-{2666,3521,0,1,4}-{2491,3177,0,1,1}-{3007,3371,0,1,2}-{2474,3446,0,1,4}-{2434,3415,0,1,3}-{3232,3459,0,1,3}-{2796,3104,0,1,0}-{2764,3210,0,1,1}-{3315,3205,0,1,6}-{3060,3263,0,1,0}-{3053,3304,0,1,1}-{2812,3333,0,1,4}-{2815,3466,0,1,3}-" + }, + { + "npc_id": "3022", + "loc_data": "{3371,9320,0,0,6}-" + }, + { + "npc_id": "3029", + "loc_data": "{3422,2937,0,1,1}-" + }, + { + "npc_id": "3030", + "loc_data": "{3421,2912,0,0,1}-" + }, + { + "npc_id": "3031", + "loc_data": "{3439,2890,0,0,3}-" + }, + { + "npc_id": "3032", + "loc_data": "{3439,2927,0,1,5}-" + }, + { + "npc_id": "3034", + "loc_data": "{3409,2929,1,1,4}-" + }, + { + "npc_id": "3035", + "loc_data": "{3450,2895,0,0,3}-" + }, + { + "npc_id": "3036", + "loc_data": "{3424,2900,0,1,3}-" + }, + { + "npc_id": "3037", + "loc_data": "{3424,2906,0,0,1}-" + }, + { + "npc_id": "3038", + "loc_data": "{3406,2924,0,1,3}-" + }, + { + "npc_id": "3039", + "loc_data": "{3414,2907,0,1,3}-" + }, + { + "npc_id": "3040", + "loc_data": "{3443,2914,0,0,3}-" + }, + { + "npc_id": "3041", + "loc_data": "{3445,2910,0,0,1}-" + }, + { + "npc_id": "3042", + "loc_data": "{3441,2920,0,1,4}-" + }, + { + "npc_id": "3043", + "loc_data": "{3445,2917,0,0,3}-" + }, + { + "npc_id": "3044", + "loc_data": "{3428,2928,0,1,5}-" + }, + { + "npc_id": "3045", + "loc_data": "{3432,2912,0,0,1}-" + }, + { + "npc_id": "3046", + "loc_data": "{3425,2891,0,0,4}-{3425,2894,0,0,4}-{3425,2893,0,0,4}-{3425,2889,0,0,4}-" + }, + { + "npc_id": "3050", + "loc_data": "{3187,9758,0,1,3}-" + }, + { + "npc_id": "3068", + "loc_data": "{3062,9550,0,1,5}-{3050,9554,0,1,7}-{3048,9545,0,1,4}-{3061,9542,0,1,3}-{3065,9548,0,1,1}-{3034,9544,0,1,4}-{3033,9552,0,1,3}-{3029,9545,0,1,7}-{3027,9555,0,1,3}-" + }, + { + "npc_id": "3072", + "loc_data": "{3207,5448,0,1,3}-{3223,5446,0,1,4}-{3210,5562,0,1,3}-" + }, + { + "npc_id": "3073", + "loc_data": "{3217,5442,0,1,4}-{3231,5444,0,1,0}-" + }, + { + "npc_id": "3074", + "loc_data": "{3404,3492,0,0,6}-" + }, + { + "npc_id": "3076", + "loc_data": "{3403,3492,0,0,0}-" + }, + { + "npc_id": "3090", + "loc_data": "{3082,3442,0,0,0}-" + }, + { + "npc_id": "3094", + "loc_data": "{3361,3306,0,1,6}-" + }, + { + "npc_id": "3095", + "loc_data": "{3364,3309,0,1,3}-{3363,3317,1,1,1}-" + }, + { + "npc_id": "3097", + "loc_data": "{3364,3304,0,1,6}-" + }, + { + "npc_id": "3099", + "loc_data": "{3364,9626,2,1,1}-" + }, + { + "npc_id": "3100", + "loc_data": "{3363,9646,0,1,1}-" + }, + { + "npc_id": "3101", + "loc_data": "{3364,9644,1,1,6}-" + }, + { + "npc_id": "3103", + "loc_data": "{3362,3318,1,0,6}-" + }, + { + "npc_id": "3104", + "loc_data": "{3364,3310,0,1,1}-{3379,9627,0,1,0}-{3367,9647,2,1,4}-" + }, + { + "npc_id": "3105", + "loc_data": "{3367,3304,1,1,1}-{3376,9655,0,1,7}-" + }, + { + "npc_id": "3106", + "loc_data": "{3360,3305,1,1,3}-{3348,9654,0,1,4}-{3362,9648,2,1,4}-" + }, + { + "npc_id": "3107", + "loc_data": "{3361,3306,0,1,3}-{3348,9626,0,1,6}-" + }, + { + "npc_id": "3108", + "loc_data": "{2549,3100,0,0,4}-" + }, + { + "npc_id": "3109", + "loc_data": "{2551,3079,0,1,3}-" + }, + { + "npc_id": "3110", + "loc_data": "{2789,3175,0,0,4}-" + }, + { + "npc_id": "3111", + "loc_data": "{2788,3176,0,0,0}-" + }, + { + "npc_id": "3114", + "loc_data": "{2815,3344,0,1,3}-" + }, + { + "npc_id": "3115", + "loc_data": "{2439,4444,0,1,3}-" + }, + { + "npc_id": "3123", + "loc_data": "{3344,2827,0,1,5}-" + }, + { + "npc_id": "3124", + "loc_data": "{3372,2847,1,0,3}-" + }, + { + "npc_id": "3125", + "loc_data": "{3366,2845,3,0,6}-" + }, + { + "npc_id": "3127", + "loc_data": "{3331,2948,0,1,4}-" + }, + { + "npc_id": "3153", + "loc_data": "{2871,3091,0,1,4}-{2862,3111,0,1,1}-{2861,3101,0,1,3}-{2870,3113,0,1,4}-{2870,3110,0,1,6}-{2865,3103,0,1,3}-{2873,3103,0,1,2}-{2863,3118,0,1,4}-{2886,3109,0,1,1}-{2892,3109,0,1,2}-{2883,3105,0,1,4}-{2889,3111,0,1,6}-{2883,3110,0,1,3}-" + }, + { + "npc_id": "3155", + "loc_data": "{3687,2953,0,1,1}-{3679,3494,0,1,3}-" + }, + { + "npc_id": "3162", + "loc_data": "{3691,2954,0,1,3}-" + }, + { + "npc_id": "3164", + "loc_data": "{3667,2990,0,1,3}-" + }, + { + "npc_id": "3166", + "loc_data": "{3693,2979,0,1,3}-" + }, + { + "npc_id": "3167", + "loc_data": "{3662,2978,1,1,4}-" + }, + { + "npc_id": "3168", + "loc_data": "{3663,2981,0,1,3}-" + }, + { + "npc_id": "3170", + "loc_data": "{3674,2970,0,1,0}-" + }, + { + "npc_id": "3188", + "loc_data": "{3664,2971,0,1,6}-" + }, + { + "npc_id": "3189", + "loc_data": "{3690,2976,0,1,1}-{3671,3006,0,1,6}-" + }, + { + "npc_id": "3191", + "loc_data": "{3659,2955,0,1,3}-{3683,2994,0,1,4}-{3662,3005,0,1,4}-" + }, + { + "npc_id": "3192", + "loc_data": "{3700,2994,0,1,4}-{3683,3005,0,1,3}-" + }, + { + "npc_id": "3193", + "loc_data": "{3696,2993,0,1,3}-{3693,3006,0,1,3}-" + }, + { + "npc_id": "3194", + "loc_data": "{3654,2992,0,1,4}-{3682,2962,0,1,3}-{3704,3006,0,1,0}-" + }, + { + "npc_id": "3195", + "loc_data": "{3696,2978,0,1,1}-" + }, + { + "npc_id": "3196", + "loc_data": "{3649,3004,0,1,6}-" + }, + { + "npc_id": "3198", + "loc_data": "{3682,2982,0,0,3}-" + }, + { + "npc_id": "3199", + "loc_data": "{3682,2981,0,0,3}-{3682,2983,0,0,3}-" + }, + { + "npc_id": "3200", + "loc_data": "{3263,3932,0,1,3}-" + }, + { + "npc_id": "3205", + "loc_data": "{3139,3448,0,1,3}-" + }, + { + "npc_id": "3207", + "loc_data": "{1970,5002,0,1,4}-" + }, + { + "npc_id": "3213", + "loc_data": "{2568,3334,0,1,0}-" + }, + { + "npc_id": "3214", + "loc_data": "{2517,9755,0,1,0}-" + }, + { + "npc_id": "3216", + "loc_data": "{2534,3273,0,0,1}-{2539,3273,0,0,1}-" + }, + { + "npc_id": "3217", + "loc_data": "{2956,3370,0,1,3}-" + }, + { + "npc_id": "3219", + "loc_data": "{3027,3344,0,1,4}-" + }, + { + "npc_id": "3220", + "loc_data": "{3013,3338,0,1,4}-" + }, + { + "npc_id": "3221", + "loc_data": "{3016,3334,0,1,3}-{3016,3339,0,1,6}-{3019,3341,0,1,4}-" + }, + { + "npc_id": "3223", + "loc_data": "{2994,3365,0,1,4}-" + }, + { + "npc_id": "3224", + "loc_data": "{3047,3365,0,1,6}-" + }, + { + "npc_id": "3226", + "loc_data": "{2996,3375,0,1,4}-" + }, + { + "npc_id": "3227", + "loc_data": "{2756,3182,0,1,6}-" + }, + { + "npc_id": "3228", + "loc_data": "{2969,3373,0,1,3}-{2948,3353,0,1,3}-{3036,3357,0,1,3}-" + }, + { + "npc_id": "3229", + "loc_data": "{2912,3344,0,1,7}-{3040,3354,0,1,2}-" + }, + { + "npc_id": "3230", + "loc_data": "{2986,3370,0,1,1}-" + }, + { + "npc_id": "3234", + "loc_data": "{3011,3383,0,1,0}-" + }, + { + "npc_id": "3235", + "loc_data": "{2970,3369,1,1,6}-" + }, + { + "npc_id": "3236", + "loc_data": "{2973,3369,1,1,6}-" + }, + { + "npc_id": "3237", + "loc_data": "{2984,3435,0,0,0}-" + }, + { + "npc_id": "3238", + "loc_data": "{2981,3434,0,0,0}-" + }, + { + "npc_id": "3239", + "loc_data": "{2975,3436,0,0,0}-" + }, + { + "npc_id": "3240", + "loc_data": "{2977,3442,0,0,0}-" + }, + { + "npc_id": "3241", + "loc_data": "{2969,3451,0,1,4}-" + }, + { + "npc_id": "3242", + "loc_data": "{2910,3332,0,1,0}-{2910,3337,0,1,4}-{2904,3331,0,1,3}-{2979,3568,0,1,7}-{2983,3566,0,1,3}-{2979,3566,0,1,3}-{2981,3570,0,1,6}-{2984,3561,0,1,6}-" + }, + { + "npc_id": "3243", + "loc_data": "{2906,3331,0,1,6}-{2909,3333,0,1,6}-" + }, + { + "npc_id": "3244", + "loc_data": "{2909,3333,0,1,1}-" + }, + { + "npc_id": "3245", + "loc_data": "{2907,3336,0,1,0}-" + }, + { + "npc_id": "3246", + "loc_data": "{3071,3446,0,1,6}-{2548,3567,0,1,4}-" + }, + { + "npc_id": "3247", + "loc_data": "{2548,3567,0,1,1}-" + }, + { + "npc_id": "3248", + "loc_data": "{3073,3442,0,1,5}-" + }, + { + "npc_id": "3249", + "loc_data": "{3082,3441,0,1,0}-" + }, + { + "npc_id": "3250", + "loc_data": "{3088,3426,0,1,3}-{2547,3571,0,1,7}-" + }, + { + "npc_id": "3251", + "loc_data": "{3073,3422,0,1,6}-" + }, + { + "npc_id": "3252", + "loc_data": "{3077,3424,0,1,6}-" + }, + { + "npc_id": "3253", + "loc_data": "{2542,3578,0,1,3}-" + }, + { + "npc_id": "3255", + "loc_data": "{3073,3423,0,1,3}-" + }, + { + "npc_id": "3256", + "loc_data": "{3080,3415,0,1,3}-{3084,3415,0,1,6}-" + }, + { + "npc_id": "3257", + "loc_data": "{3096,3432,0,1,0}-" + }, + { + "npc_id": "3258", + "loc_data": "{3080,3444,0,1,3}-" + }, + { + "npc_id": "3259", + "loc_data": "{3075,3438,0,1,4}-" + }, + { + "npc_id": "3260", + "loc_data": "{3083,3425,0,1,3}-{2554,3564,0,1,6}-{2547,3558,0,1,6}-" + }, + { + "npc_id": "3261", + "loc_data": "{3086,3425,0,1,3}-" + }, + { + "npc_id": "3262", + "loc_data": "{3080,3423,0,1,1}-" + }, + { + "npc_id": "3263", + "loc_data": "{3084,3419,0,1,4}-" + }, + { + "npc_id": "3264", + "loc_data": "{3126,3450,0,1,6}-{3121,3422,0,1,3}-{2565,9843,0,1,0}-{3259,3338,0,1,7}-{3311,3375,0,1,0}-" + }, + { + "npc_id": "3265", + "loc_data": "{3121,3427,0,1,2}-{2582,9836,0,1,1}-" + }, + { + "npc_id": "3266", + "loc_data": "{3094,3406,0,1,3}-" + }, + { + "npc_id": "3267", + "loc_data": "{3134,3459,0,1,1}-" + }, + { + "npc_id": "3268", + "loc_data": "{3012,3454,0,1,1}-" + }, + { + "npc_id": "3269", + "loc_data": "{3022,3447,0,1,4}-" + }, + { + "npc_id": "3270", + "loc_data": "{3010,3452,0,1,3}-" + }, + { + "npc_id": "3272", + "loc_data": "{3042,3468,0,1,1}-" + }, + { + "npc_id": "3273", + "loc_data": "{3030,3465,0,1,5}-" + }, + { + "npc_id": "3276", + "loc_data": "{3039,3475,0,1,4}-" + }, + { + "npc_id": "3277", + "loc_data": "{3035,3471,0,1,2}-" + }, + { + "npc_id": "3278", + "loc_data": "{3045,3477,0,1,1}-" + }, + { + "npc_id": "3279", + "loc_data": "{3039,3471,0,1,6}-" + }, + { + "npc_id": "3280", + "loc_data": "{3015,3451,0,0,0}-" + }, + { + "npc_id": "3281", + "loc_data": "{3018,3450,0,0,0}-" + }, + { + "npc_id": "3282", + "loc_data": "{3008,3442,0,0,0}-" + }, + { + "npc_id": "3283", + "loc_data": "{2964,3455,0,0,0}-{3039,3424,0,1,5}-{3043,3429,0,1,4}-{3047,3427,0,1,1}-{3048,3441,0,1,3}-" + }, + { + "npc_id": "3284", + "loc_data": "{2969,3437,0,0,0}-{3058,3419,0,0,0}-{3048,3423,0,1,6}-{3054,3432,0,1,5}-" + }, + { + "npc_id": "3285", + "loc_data": "{2952,3376,0,0,0}-{3036,3430,0,1,3}-{3025,3442,0,0,0}-" + }, + { + "npc_id": "3286", + "loc_data": "{2968,3448,0,1,1}-{3034,3450,0,1,3}-{3057,3447,0,1,6}-" + }, + { + "npc_id": "3287", + "loc_data": "{3073,3443,0,1,0}-{2987,3444,0,1,0}-{3046,3453,0,1,6}-" + }, + { + "npc_id": "3288", + "loc_data": "{2987,3446,0,1,7}-{3034,3437,0,1,0}-" + }, + { + "npc_id": "3293", + "loc_data": "{3113,3369,1,0,0}-{3122,3353,0,0,3}-{3124,3360,0,0,2}-{3115,3362,0,0,1}-" + }, + { + "npc_id": "3294", + "loc_data": "{3014,3339,0,1,2}-" + }, + { + "npc_id": "3295", + "loc_data": "{3020,3337,0,1,1}-" + }, + { + "npc_id": "3296", + "loc_data": "{2957,3359,0,1,0}-{2958,3358,0,1,0}-{2967,3360,0,1,0}-{2970,3361,0,1,0}-{2973,3359,0,1,0}-{2977,3357,0,1,0}-{2989,3384,0,1,0}-" + }, + { + "npc_id": "3298", + "loc_data": "{3366,9637,2,1,3}-" + }, + { + "npc_id": "3299", + "loc_data": "{3077,3257,0,1,1}-" + }, + { + "npc_id": "3302", + "loc_data": "{2453,4449,0,1,2}-" + }, + { + "npc_id": "3307", + "loc_data": "{2467,4438,0,1,4}-{2471,4439,0,1,7}-{2465,4436,0,1,3}-{2464,4432,0,1,4}-" + }, + { + "npc_id": "3308", + "loc_data": "{2905,3333,2,1,0}-" + }, + { + "npc_id": "3310", + "loc_data": "{2415,4451,0,1,6}-{2416,4435,0,1,7}-{2423,4432,0,1,4}-" + }, + { + "npc_id": "3318", + "loc_data": "{2401,4378,0,0,0}-" + }, + { + "npc_id": "3322", + "loc_data": "{2384,4439,0,1,0}-" + }, + { + "npc_id": "3324", + "loc_data": "{3159,3433,0,1,0}-" + }, + { + "npc_id": "3325", + "loc_data": "{3161,3432,1,1,4}-" + }, + { + "npc_id": "3328", + "loc_data": "{3203,3344,0,0,0}-" + }, + { + "npc_id": "3329", + "loc_data": "{3112,3414,0,0,0}-" + }, + { + "npc_id": "3330", + "loc_data": "{3132,3508,0,0,0}-" + }, + { + "npc_id": "3331", + "loc_data": "{3242,3240,0,0,4}-" + }, + { + "npc_id": "3340", + "loc_data": "{1759,5182,0,1,0}-" + }, + { + "npc_id": "3341", + "loc_data": "{1739,5154,0,1,0}-{1759,5191,0,1,0}-{1754,5200,0,1,0}-{1748,5223,0,1,0}-{1784,5199,0,1,0}-" + }, + { + "npc_id": "3342", + "loc_data": "{1768,5175,0,1,0}-{1760,5152,0,1,0}-{1745,5169,0,1,0}-{1745,5208,0,1,0}-{1737,5215,0,1,0}-{1758,5210,0,1,0}-" + }, + { + "npc_id": "3343", + "loc_data": "{1773,5149,0,1,0}-{1764,5198,0,1,0}-{1774,5195,0,1,0}-{1742,5292,0,1,0}-" + }, + { + "npc_id": "3344", + "loc_data": "{2412,4470,0,0,0}-{2414,4467,0,0,0}-{2415,4475,0,0,0}-{2419,4467,0,0,0}-{2419,4474,0,0,0}-" + }, + { + "npc_id": "3345", + "loc_data": "{2412,4370,0,0,0}-{2416,4376,0,0,0}-{2419,4374,0,0,0}-{2420,4377,0,0,0}-{2421,4370,0,0,0}-" + }, + { + "npc_id": "3348", + "loc_data": "{2978,3343,0,1,4}-{2978,3337,0,1,1}-{2966,3337,1,1,6}-{2970,3330,2,1,1}-" + }, + { + "npc_id": "3349", + "loc_data": "{2961,3330,2,1,0}-{2966,3352,2,1,1}-{2974,3328,2,1,6}-" + }, + { + "npc_id": "3350", + "loc_data": "{2959,3340,2,1,3}-" + }, + { + "npc_id": "3376", + "loc_data": "{2438,4393,0,1,1}-{3313,5453,0,1,3}-{3312,5452,0,1,3}-{3310,5452,0,1,6}-{3311,5462,0,1,5}-{3313,5554,0,1,6}-{3297,5562,0,1,1}-{3305,5554,0,1,1}-{3300,5540,0,1,1}-" + }, + { + "npc_id": "3382", + "loc_data": "{3080,9885,0,1,4}-{3076,9886,0,1,4}-{3081,9890,0,1,4}-{3081,9895,0,1,4}-" + }, + { + "npc_id": "3384", + "loc_data": "{3211,3225,0,0,0}-" + }, + { + "npc_id": "3402", + "loc_data": "{2560,2842,0,0,1}-" + }, + { + "npc_id": "3407", + "loc_data": "{3114,3515,0,1,4}-" + }, + { + "npc_id": "3408", + "loc_data": "{3113,3510,0,1,7}-" + }, + { + "npc_id": "3419", + "loc_data": "{2568,2991,0,1,0}-{2574,2988,0,1,0}-{2584,2967,0,1,0}-{2585,2977,0,1,0}-{2592,2965,0,1,0}-{2608,2980,0,1,0}-{2609,2990,0,1,0}-{2614,2998,0,1,0}-{2561,3020,0,1,0}-{2567,3046,0,1,0}-{2585,9608,0,1,0}-{2586,9614,0,1,0}-{2588,9606,0,1,0}-{2581,9738,0,1,0}-{2581,9742,0,1,0}-{2584,9734,0,1,0}-{2585,9738,0,1,0}-{2587,9733,0,1,0}-{2587,9740,0,1,0}-{2590,9737,0,1,0}-{2387,9685,0,1,0}-{2389,9683,0,1,0}-{2719,9668,0,1,0}-{2719,9715,0,1,0}-{2720,9702,0,1,0}-{2722,9669,0,1,0}-{2723,9707,0,1,0}-{2724,9713,0,1,0}-{2504,2965,0,1,0}-{2504,2984,0,1,0}-{2520,2973,0,1,0}-{2525,2986,0,1,0}-{2533,2977,0,1,0}-{2536,2975,0,1,0}-{2539,2990,0,1,0}-{2549,2979,0,1,0}-{2551,2955,0,1,0}-{2552,2961,0,1,0}-{2513,3026,0,1,0}-{2522,3055,0,1,0}-{2523,3057,0,1,0}-{2529,3032,0,1,0}-{2539,3018,0,1,0}-{2541,3016,0,1,0}-{2550,3043,0,1,0}-{2550,3046,0,1,0}-{2553,3047,0,1,0}-{2496,3092,0,1,0}-{2497,3089,0,1,0}-{2500,3092,0,1,0}-{2500,3097,0,1,0}-{2503,3095,0,1,0}-{2504,3133,0,1,0}-{2554,3192,0,1,0}-" + }, + { + "npc_id": "3450", + "loc_data": "{2901,2947,0,0,0}-{2925,2959,0,0,0}-{2920,3053,0,0,0}-{2924,3063,0,0,0}-" + }, + { + "npc_id": "3469", + "loc_data": "{2657,2966,0,0,0}-" + }, + { + "npc_id": "3470", + "loc_data": "{2755,3083,0,0,0}-" + }, + { + "npc_id": "3479", + "loc_data": "{2906,3514,1,0,0}-" + }, + { + "npc_id": "3541", + "loc_data": "{3517,3240,0,1,6}-" + }, + { + "npc_id": "3579", + "loc_data": "{3195,3263,0,1,0}-" + }, + { + "npc_id": "3638", + "loc_data": "{3309,3453,0,0,1}-" + }, + { + "npc_id": "3661", + "loc_data": "{3276,9836,0,1,0}-{3287,9840,0,1,0}-" + }, + { + "npc_id": "3662", + "loc_data": "{3280,9849,0,1,0}-{3286,9830,0,1,0}-" + }, + { + "npc_id": "3663", + "loc_data": "{3269,9849,0,1,0}-{3291,9843,0,1,0}-" + }, + { + "npc_id": "3664", + "loc_data": "{3269,9843,0,1,0}-{3275,9833,0,1,0}-" + }, + { + "npc_id": "3665", + "loc_data": "{3305,9832,0,1,0}-{3305,9843,0,1,0}-{3311,9850,0,1,0}-{3312,9845,0,1,0}-{3313,9827,0,1,0}-{3315,9837,0,1,0}-{3319,9832,0,1,0}-{3319,9849,0,1,0}-{3323,9842,0,1,0}-" + }, + { + "npc_id": "3666", + "loc_data": "{3302,9806,0,1,0}-{3302,9816,0,1,0}-{3304,9801,0,1,0}-{3310,9796,0,1,0}-{3312,9811,0,1,0}-{3313,9805,0,1,0}-{3313,9817,0,1,0}-{3314,9797,0,1,0}-{3319,9800,0,1,0}-{3319,9806,0,1,0}-{3321,9811,0,1,0}-" + }, + { + "npc_id": "3667", + "loc_data": "{3269,9798,0,1,0}-{3269,9806,0,1,0}-{3269,9814,0,1,0}-{3275,9817,0,1,0}-{3276,9809,0,1,0}-{3280,9803,0,1,0}-{3282,9797,0,1,0}-{3285,9812,0,1,0}-{3288,9819,0,1,0}-{3289,9809,0,1,0}-{3290,9797,0,1,0}-" + }, + { + "npc_id": "3670", + "loc_data": "{3361,3505,0,1,0}-" + }, + { + "npc_id": "3671", + "loc_data": "{3085,3251,0,0,3}-" + }, + { + "npc_id": "3673", + "loc_data": "{2322,3189,0,1,0}-{3195,3265,0,1,0}-{2924,3318,0,1,0}-{2924,3325,0,1,4}-{2656,3359,0,1,0}-{2656,3375,0,1,0}-{2653,4716,0,1,0}-{3248,3351,0,1,0}-" + }, + { + "npc_id": "3675", + "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", + "loc_data": "{2637,3440,0,0,0}-" + }, + { + "npc_id": "3679", + "loc_data": "{2875,3482,0,0,4}-" + }, + { + "npc_id": "3680", + "loc_data": "{3282,3327,0,1,7}-" + }, + { + "npc_id": "3695", + "loc_data": "{2661,3701,0,1,0}-" + }, + { + "npc_id": "3696", + "loc_data": "{2673,3658,0,1,3}-" + }, + { + "npc_id": "3707", + "loc_data": "{2713,10133,0,1,2}-{2708,10133,0,1,4}-{2706,10135,0,0,3}-{2708,10133,0,1,3}-{2703,10135,0,1,6}-{2702,10131,0,1,4}-{2717,10133,0,1,0}-{2708,10130,0,1,1}-" + }, + { + "npc_id": "3709", + "loc_data": "{2847,5075,0,0,0}-{2974,2906,0,0,0}-" + }, + { + "npc_id": "3711", + "loc_data": "{2575,9746,0,1,0}-{2581,9747,0,1,0}-{2577,9743,0,1,1}-{2579,9740,0,1,0}-{2588,9733,0,1,4}-" + }, + { + "npc_id": "3777", + "loc_data": "{3230,3231,0,1,5}-" + }, + { + "npc_id": "3778", + "loc_data": "{1867,4233,0,0,0}-" + }, + { + "npc_id": "3780", + "loc_data": "{1906,4281,0,0,0}-" + }, + { + "npc_id": "3781", + "loc_data": "{2659,2675,0,1,1}-" + }, + { + "npc_id": "3786", + "loc_data": "{2658,2666,0,1,1}-" + }, + { + "npc_id": "3787", + "loc_data": "{2754,3509,0,0,0}-" + }, + { + "npc_id": "3788", + "loc_data": "{2664,2653,0,1,3}-" + }, + { + "npc_id": "3789", + "loc_data": "{2660,2649,0,1,3}-" + }, + { + "npc_id": "3790", + "loc_data": "{3041,3203,0,0,6}-" + }, + { + "npc_id": "3791", + "loc_data": "{2653,2655,0,1,1}-" + }, + { + "npc_id": "3793", + "loc_data": "{2659,2660,0,1,6}-" + }, + { + "npc_id": "3796", + "loc_data": "{2667,2661,0,0,3}-" + }, + { + "npc_id": "3797", + "loc_data": "{2649,2659,0,0,2}-" + }, + { + "npc_id": "3798", + "loc_data": "{2658,2652,0,1,6}-" + }, + { + "npc_id": "3799", + "loc_data": "{2650,2663,0,0,4}-" + }, + { + "npc_id": "3802", + "loc_data": "{2657,2637,0,0,6}-" + }, + { + "npc_id": "3804", + "loc_data": "{3349,3793,0,0,0}-" + }, + { + "npc_id": "3805", + "loc_data": "{2445,3097,0,1,5}-" + }, + { + "npc_id": "3806", + "loc_data": "{3166,3304,0,1,0}-" + }, + { + "npc_id": "3807", + "loc_data": "{3254,3274,0,0,0}-" + }, + { + "npc_id": "3808", + "loc_data": "{2422,3526,0,1,1}-{2517,3205,0,1,3}-{2513,3204,0,1,3}-" + }, + { + "npc_id": "3809", + "loc_data": "{3285,3213,0,1,1}-" + }, + { + "npc_id": "3810", + "loc_data": "{2847,3499,0,1,7}-{2970,2973,0,0,0}-" + }, + { + "npc_id": "3811", + "loc_data": "{2894,2730,0,1,2}-{2918,3057,0,0,0}-{2465,3501,3,1,3}-" + }, + { + "npc_id": "3813", + "loc_data": "{2541,2969,0,1,4}-" + }, + { + "npc_id": "3817", + "loc_data": "{2521,3208,0,1,1}-" + }, + { + "npc_id": "3819", + "loc_data": "{2416,3525,0,1,3}-{2412,3527,0,1,6}-" + }, + { + "npc_id": "3820", + "loc_data": "{3088,3255,0,0,6}-" + }, + { + "npc_id": "3822", + "loc_data": "{2353,3680,0,1,3}-" + }, + { + "npc_id": "3823", + "loc_data": "{2343,3668,0,1,3}-" + }, + { + "npc_id": "3824", + "loc_data": "{2329,3689,0,1,6}-" + }, + { + "npc_id": "3831", + "loc_data": "{2357,3637,0,0,0}-" + }, + { + "npc_id": "3832", + "loc_data": "{2345,3651,0,0,0}-" + }, + { + "npc_id": "3848", + "loc_data": "{2337,3703,0,0,6}-{2340,3702,0,0,6}-{2343,3702,0,0,3}-{2349,3702,0,0,6}-{2353,3703,0,0,6}-{2310,3704,0,0,6}-{2311,3696,0,0,4}-{2343,3702,0,0,3}-" + }, + { + "npc_id": "3851", + "loc_data": "{2332,3669,0,0,0}-{2325,3671,0,0,4}-{2331,3670,0,0,1}-{2333,3671,0,0,3}-" + }, + { + "npc_id": "3915", + "loc_data": "{2945,3146,0,1,3}-" + }, + { + "npc_id": "3916", + "loc_data": "{2595,3890,0,0,3}-" + }, + { + "npc_id": "3917", + "loc_data": "{2581,3861,0,0,1}-" + }, + { + "npc_id": "3920", + "loc_data": "{2527,10259,0,1,1}-" + }, + { + "npc_id": "3921", + "loc_data": "{2506,10251,0,1,1}-" + }, + { + "npc_id": "3922", + "loc_data": "{2508,10260,0,1,1}-" + }, + { + "npc_id": "3923", + "loc_data": "{2521,10251,0,1,1}-" + }, + { + "npc_id": "3937", + "loc_data": "{2508,3866,0,0,3}-" + }, + { + "npc_id": "4235", + "loc_data": "{2669,3332,0,0,1}-" + }, + { + "npc_id": "4237", + "loc_data": "{2669,3331,0,0,6}-" + }, + { + "npc_id": "4239", + "loc_data": "{2669,3331,0,0,6}-" + }, + { + "npc_id": "4241", + "loc_data": "{2669,3331,0,0,6}-" + }, + { + "npc_id": "4243", + "loc_data": "{2669,3331,0,0,6}-" + }, + { + "npc_id": "4246", + "loc_data": "{3480,3484,1,0,0}-{3479,3484,0,1,3}-" + }, + { + "npc_id": "4247", + "loc_data": "{2639,3291,0,0,0}-{2981,3370,0,0,0}-{2737,3500,0,1,6}-{3240,3474,0,0,0}-" + }, + { + "npc_id": "4248", + "loc_data": "{2849,10183,0,1,0}-" + }, + { + "npc_id": "4249", + "loc_data": "{2981,3341,1,0,0}-" + }, + { + "npc_id": "4250", + "loc_data": "{3302,3492,0,1,6}-" + }, + { + "npc_id": "4251", + "loc_data": "{3009,3380,0,1,0}-" + }, + { + "npc_id": "4261", + "loc_data": "{2569,9851,0,1,3}-{2579,9851,0,1,4}-" + }, + { + "npc_id": "4263", + "loc_data": "{2584,9838,0,1,6}-" + }, + { + "npc_id": "4264", + "loc_data": "{2588,9837,0,1,4}-" + }, + { + "npc_id": "4269", + "loc_data": "{2608,9817,0,1,3}-" + }, + { + "npc_id": "4270", + "loc_data": "{2593,9820,0,1,4}-" + }, + { + "npc_id": "4271", + "loc_data": "{2596,9822,0,1,0}-" + }, + { + "npc_id": "4273", + "loc_data": "{2564,9851,0,1,4}-" + }, + { + "npc_id": "4274", + "loc_data": "{2586,9826,0,1,7}-{2569,9849,0,1,4}-{2581,9847,0,1,0}-{2609,9826,0,1,1}-" + }, + { + "npc_id": "4275", + "loc_data": "{2594,9817,0,1,0}-" + }, + { + "npc_id": "4276", + "loc_data": "{2581,9851,0,1,1}-" + }, + { + "npc_id": "4285", + "loc_data": "{2880,3546,0,0,4}-" + }, + { + "npc_id": "4287", + "loc_data": "{2840,3541,1,0,2}-" + }, + { + "npc_id": "4288", + "loc_data": "{2851,3554,0,1,6}-" + }, + { + "npc_id": "4289", + "loc_data": "{2845,3539,2,0,7}-{2792,3488,2,0,0}-" + }, + { + "npc_id": "4290", + "loc_data": "{2854,3542,0,1,3}-" + }, + { + "npc_id": "4291", + "loc_data": "{2863,3546,2,1,3}-{2870,3551,2,1,0}-{2850,3547,2,1,0}-{2844,3552,2,1,5}-{2843,3547,2,1,5}-{2872,3542,2,1,3}-{2861,3551,2,1,1}-{2865,3538,2,1,2}-" + }, + { + "npc_id": "4292", + "loc_data": "{2847,3541,2,1,3}-{2855,3554,2,1,5}-{2860,3542,2,1,0}-{2869,3554,2,1,3}-{2872,3537,2,1,4}-{2850,3534,2,1,3}-{2857,3546,2,1,0}-{2838,3554,2,1,6}-{2869,3552,2,1,4}-" + }, + { + "npc_id": "4293", + "loc_data": "{2840,3554,0,1,1}-" + }, + { + "npc_id": "4294", + "loc_data": "{2847,3553,0,1,6}-" + }, + { + "npc_id": "4295", + "loc_data": "{2856,3537,1,1,1}-" + }, + { + "npc_id": "4296", + "loc_data": "{2841,3543,0,0,4}-" + }, + { + "npc_id": "4297", + "loc_data": "{2856,3549,1,0,2}-" + }, + { + "npc_id": "4298", + "loc_data": "{2864,3541,1,1,3}-" + }, + { + "npc_id": "4299", + "loc_data": "{2866,3556,1,0,5}-" + }, + { + "npc_id": "4300", + "loc_data": "{2865,3544,1,0,6}-" + }, + { + "npc_id": "4312", + "loc_data": "{3323,9609,0,0,6}-" + }, + { + "npc_id": "4315", + "loc_data": "{3224,3286,0,0,0}-" + }, + { + "npc_id": "4317", + "loc_data": "{3230,3285,0,0,0}-" + }, + { + "npc_id": "4344", + "loc_data": "{3733,2972,0,1,4}-{3747,3013,0,1,4}-{3766,3034,0,1,6}-" + }, + { + "npc_id": "4346", + "loc_data": "{3714,2952,0,1,1}-{3786,9463,0,1,6}-" + }, + { + "npc_id": "4347", + "loc_data": "{3749,2978,0,1,7}-{3756,2966,0,1,2}-{3759,2962,0,1,2}-{3749,2968,0,1,2}-{3757,2980,0,0,5}-{3728,3043,0,1,4}-{3725,3015,0,1,0}-{3722,3021,0,1,6}-{3717,3017,0,1,7}-{3723,3019,0,1,5}-{3731,3018,0,1,1}-{3734,3028,0,1,7}-{3743,3038,0,1,4}-{3742,3036,0,1,1}-{3747,3040,0,1,4}-{3748,3030,0,1,6}-{3748,3019,0,1,1}-{3745,3016,0,1,4}-{3741,3020,0,1,3}-{3727,3039,0,1,3}-{3734,3046,0,1,1}-{3736,3050,0,1,6}-{3735,3053,0,1,3}-{3741,3053,0,1,6}-{3756,3030,0,1,3}-{3754,3024,0,1,4}-{3769,3039,0,1,6}-{3766,3045,0,1,7}-{3764,3042,0,1,1}-" + }, + { + "npc_id": "4348", + "loc_data": "{3676,3017,0,1,3}-{3687,3018,0,1,3}-{3707,3015,0,1,6}-{3729,3021,0,1,3}-" + }, + { + "npc_id": "4349", + "loc_data": "{3664,3018,0,1,6}-{3658,3012,0,1,4}-{3729,2984,0,1,4}-{3720,3000,0,1,4}-{3718,3030,0,1,4}-{3757,3009,0,1,3}-{3727,3051,0,1,4}-{3809,3028,0,1,6}-" + }, + { + "npc_id": "4350", + "loc_data": "{3680,3026,0,1,4}-{3675,3029,0,1,3}-{3711,3028,0,1,4}-{3698,3046,0,1,6}-{3733,3011,0,1,4}-{3793,3036,0,1,4}-" + }, + { + "npc_id": "4351", + "loc_data": "{3694,3019,0,1,4}-{3728,2965,0,1,7}-{3760,3015,0,1,1}-{3758,3034,0,1,2}-{3714,3049,0,1,7}-{3770,3008,0,1,4}-{3787,3026,0,1,6}-" + }, + { + "npc_id": "4352", + "loc_data": "{3683,3034,0,1,4}-{3747,3007,0,1,4}-{3735,2957,0,1,2}-{3727,3003,0,1,3}-{3719,3037,0,1,7}-{3770,3027,0,1,5}-{3719,3011,0,1,3}-{3787,3035,0,1,1}-" + }, + { + "npc_id": "4353", + "loc_data": "{3719,9358,0,1,6}-{3738,9378,0,1,0}-{3765,9422,0,1,1}-{3741,9436,0,1,6}-{3727,9455,0,1,2}-{3737,9413,0,1,0}-{3735,9444,0,1,3}-{3780,9456,0,1,2}-{3827,9430,0,1,2}-{3819,9422,0,1,2}-" + }, + { + "npc_id": "4354", + "loc_data": "{3739,9369,0,1,0}-{3737,9350,0,1,4}-{3721,9349,0,1,4}-{3733,9396,0,1,6}-{3764,9404,0,1,6}-{3732,9368,0,1,1}-{3749,9461,0,1,0}-{3746,9423,0,1,0}-{3760,9417,0,1,3}-{3760,9451,0,1,6}-{3813,9445,0,1,0}-{3786,9443,0,1,6}-{3783,9430,0,1,5}-{3821,9443,0,1,4}-{3790,9456,0,1,7}-" + }, + { + "npc_id": "4355", + "loc_data": "{3728,9349,0,1,3}-{3752,9398,0,1,4}-{3723,9377,0,1,4}-{3749,9413,0,1,4}-{3721,9419,0,1,3}-{3754,9408,0,1,4}-{3810,9448,0,1,4}-{3786,9412,0,1,3}-{3797,9452,0,1,4}-" + }, + { + "npc_id": "4356", + "loc_data": "{3755,9391,0,1,5}-{3723,9441,0,1,4}-{3766,9459,0,1,4}-{3748,9450,0,1,6}-{3765,9424,0,1,2}-{3800,9414,0,1,2}-{3821,9418,0,1,3}-{3810,9462,0,1,0}-{3809,9440,0,1,2}-{3787,9464,0,1,0}-" + }, + { + "npc_id": "4357", + "loc_data": "{3771,9450,0,1,4}-{3730,9443,0,1,4}-{3808,9423,0,1,7}-{3797,9427,0,1,4}-{3833,9438,0,1,6}-{3834,9458,0,1,6}-{3797,9440,0,1,1}-{3793,9441,0,1,4}-{3829,9425,0,1,7}-" + }, + { + "npc_id": "4358", + "loc_data": "{3751,2977,0,1,2}-" + }, + { + "npc_id": "4359", + "loc_data": "{3669,2977,0,1,3}-" + }, + { + "npc_id": "4375", + "loc_data": "{3013,3192,1,0,6}-" + }, + { + "npc_id": "4376", + "loc_data": "{3081,3421,0,1,3}-" + }, + { + "npc_id": "4381", + "loc_data": "{2361,5241,0,1,1}-{2358,5240,0,1,1}-{2362,5239,0,1,1}-{2364,5240,0,1,1}-{2364,5242,0,1,1}-{2357,5242,0,1,1}-{2356,5240,0,1,1}-{2356,5242,0,1,1}-{2360,5243,0,1,1}-" + }, + { + "npc_id": "4382", + "loc_data": "{2324,5210,0,1,2}-{2322,5201,0,1,7}-{2324,5210,0,1,2}-{2322,5201,0,1,7}-{2327,5205,0,1,7}-{2328,5200,0,1,7}-{2326,5198,0,1,7}-{2331,5198,0,1,4}-{2333,5201,0,1,4}-{2333,5199,0,1,2}-{3139,3747,0,1,0}-{3141,3737,0,1,0}-{3157,3735,0,1,0}-{3165,3746,0,1,0}-{3164,3727,0,1,0}-{3172,3713,0,1,0}-{3177,3724,0,1,0}-" + }, + { + "npc_id": "4383", + "loc_data": "{2321,5225,0,1,3}-{2321,5223,0,1,4}-{2317,5229,0,1,7}-{2322,5225,0,1,1}-{2325,5231,0,1,6}-{2321,5225,0,1,1}-{2314,5227,0,1,2}-{2312,5229,0,1,2}-{2330,5229,0,1,1}-{2324,5233,0,1,4}-{2320,5232,0,1,3}-{2324,5237,0,1,1}-{2329,5229,0,1,6}-{2315,5227,0,1,7}-{2318,5226,0,1,6}-{2322,5222,0,1,6}-" + }, + { + "npc_id": "4384", + "loc_data": "{2355,5204,0,1,3}-{2347,5195,0,1,0}-{2336,5202,0,1,4}-{2332,5201,0,1,3}-{2327,5201,0,1,1}-{2328,5199,0,1,6}-" + }, + { + "npc_id": "4385", + "loc_data": "{2350,5201,0,1,1}-{2353,5192,0,1,1}-{2324,5203,0,1,1}-{2333,5204,0,1,4}-{2326,5197,0,1,5}-{2264,5136,0,1,6}-" + }, + { + "npc_id": "4386", + "loc_data": "{3097,3728,0,1,0}-{3130,3754,0,1,0}-{3128,3719,0,1,0}-{2311,5187,0,1,6}-" + }, + { + "npc_id": "4387", + "loc_data": "{2350,5192,0,1,6}-{2350,5191,0,1,4}-{2347,5188,0,1,2}-" + }, + { + "npc_id": "4388", + "loc_data": "{2305,5231,0,1,2}-{2312,5225,0,1,2}-{2314,5240,0,1,2}-{2310,5241,0,1,2}-{2308,5246,0,1,2}-{2309,5245,0,1,2}-{2315,5237,0,1,2}-" + }, + { + "npc_id": "4389", + "loc_data": "{2013,5238,0,1,0}-{2043,5233,0,1,0}-{2041,5231,0,1,0}-{2029,5233,0,1,0}-{2020,5238,0,1,0}-" + }, + { + "npc_id": "4390", + "loc_data": "{2040,5190,0,1,3}-{2044,5187,0,1,2}-{2042,5191,0,1,1}-{2045,5190,0,1,7}-{2041,5187,0,1,2}-{2045,5186,0,1,2}-{2044,5188,0,1,2}-{2042,5192,0,1,2}-" + }, + { + "npc_id": "4391", + "loc_data": "{1952,5192,0,1,2}-{1993,5240,0,1,4}-{1991,5235,0,1,4}-{1990,5240,0,1,3}-{1989,5237,0,1,6}-{2009,5202,0,1,1}-{2005,5199,0,1,3}-{1994,5236,0,1,6}-{1991,5242,0,1,6}-{2007,5201,0,1,6}-{2009,5201,0,1,6}-{2004,5197,0,1,6}-{2004,5207,0,1,6}-{2006,5205,0,1,6}-" + }, + { + "npc_id": "4392", + "loc_data": "{2033,5233,0,1,4}-{2033,5233,0,1,7}-{2026,5236,0,1,1}-" + }, + { + "npc_id": "4393", + "loc_data": "{2045,5222,0,1,4}-{1993,5190,0,1,6}-{1993,5190,0,1,4}-{2044,5216,0,1,4}-{2008,5203,0,1,1}-{2006,5208,0,1,1}-{2037,5214,0,1,3}-{2037,5210,0,1,1}-{2043,5217,0,1,1}-{2040,5214,0,1,1}-{2042,5217,0,1,1}-{2038,5214,0,1,1}-{2041,5218,0,1,1}-" + }, + { + "npc_id": "4394", + "loc_data": "{1997,5244,0,1,3}-{1992,5238,0,1,5}-{1990,5234,0,1,1}-{2010,5191,0,1,7}-{2018,5188,0,1,4}-{1989,5231,0,1,4}-{2018,5187,0,1,6}-" + }, + { + "npc_id": "4395", + "loc_data": "{2016,5237,0,1,7}-{1988,5190,0,1,2}-{2039,5216,0,1,6}-{2017,5238,0,1,6}-" + }, + { + "npc_id": "4396", + "loc_data": "{2034,5243,0,1,5}-{2034,5239,0,1,5}-{2032,5243,0,1,4}-{2034,5232,0,1,1}-{2033,5240,0,1,1}-{2029,5230,0,1,6}-{2030,5234,0,1,4}-{2029,5230,0,1,1}-{2029,5233,0,1,2}-{2031,5244,0,1,1}-{2039,5208,0,1,4}-{2029,5235,0,1,6}-{2028,5234,0,1,6}-{2030,5237,0,1,4}-{2033,5238,0,1,4}-{2030,5238,0,1,4}-{2032,5238,0,1,3}-{2027,5238,0,1,6}-{2034,5239,0,1,0}-{2016,5235,0,1,5}-{2015,5237,0,1,3}-{2004,5244,0,1,4}-{2005,5241,0,1,4}-{2035,5245,0,1,5}-{2004,5240,0,1,1}-{2027,5235,0,1,5}-{2027,5235,0,1,4}-{2027,5236,0,1,6}-{1999,5244,0,1,4}-{1995,5234,0,1,6}-{1993,5242,0,1,1}-{1990,5239,0,1,1}-{1990,5243,0,1,7}-{1989,5230,0,1,6}-{1990,5224,0,1,5}-{1993,5222,0,1,7}-{1986,5221,0,1,3}-{1990,5221,0,1,3}-{1986,5222,0,1,6}-{2002,5215,0,1,1}-{1996,5215,0,1,1}-{1994,5215,0,1,2}-{2002,5215,0,1,6}-{1995,5208,0,1,1}-{1993,5206,0,1,4}-{1986,5204,0,1,3}-{1986,5201,0,1,6}-{2001,5201,0,1,6}-{1987,5200,0,1,3}-{1986,5199,0,1,6}-{2009,5198,0,1,6}-{1995,5197,0,1,3}-{2009,5194,0,1,3}-{2005,5191,0,1,3}-{2004,5190,0,1,4}-{1991,5190,0,1,1}-{2003,5185,0,1,4}-{2004,5190,0,1,4}-{2004,5188,0,1,1}-{2005,5188,0,1,6}-{2008,5192,0,1,6}-{2008,5193,0,1,3}-{2009,5200,0,1,7}-{2010,5192,0,1,4}-{2010,5194,0,1,4}-{2013,5190,0,1,6}-{2018,5189,0,1,6}-{2018,5192,0,1,6}-{2018,5187,0,1,4}-{1993,5205,0,1,4}-{1994,5206,0,1,3}-{1989,5206,0,1,0}-{1993,5210,0,1,3}-{1989,5200,0,1,4}-{2002,5215,0,1,1}-{2022,5193,0,1,3}-{1995,5220,0,1,1}-{1992,5222,0,1,6}-{1992,5222,0,1,6}-{1988,5201,0,1,4}-{1988,5224,0,1,4}-{1989,5206,0,1,4}-{1987,5225,0,1,4}-{1988,5225,0,1,4}-{1986,5204,0,1,3}-{1987,5226,0,1,4}-{1990,5227,0,1,3}-{1993,5200,0,1,7}-{1990,5228,0,1,4}-{2004,5200,0,1,6}-{1990,5227,0,1,4}-{1990,5224,0,1,6}-{1994,5223,0,1,4}-{2036,5201,0,1,6}-{2038,5215,0,1,4}-{2042,5208,0,1,1}-{2045,5204,0,1,1}-{2046,5205,0,1,3}-{2028,5195,0,1,3}-{2026,5195,0,1,0}-{2039,5193,0,1,3}-{2025,5193,0,1,1}-{2022,5193,0,1,6}-{2043,5192,0,1,2}-{2018,5192,0,1,1}-{2045,5192,0,1,1}-{2032,5190,0,1,1}-{2030,5189,0,1,4}-{2024,5187,0,1,3}-{2038,5186,0,1,4}-{2038,5185,0,1,3}-{2033,5185,0,1,3}-{2016,5186,0,1,1}-{2017,5194,0,1,1}-{2024,5193,0,1,4}-{2008,5201,0,1,4}-{2005,5220,0,1,1}-{2005,5204,0,1,4}-{2037,5203,0,1,3}-{2037,5206,0,1,4}-{2041,5208,0,1,1}-" + }, + { + "npc_id": "4397", + "loc_data": "{2072,5200,0,1,5}-{2145,5251,0,1,3}-{2153,5253,0,1,1}-{2149,5255,0,1,3}-{2146,5252,0,1,5}-{2146,5261,0,1,6}-{2147,5254,0,1,4}-" + }, + { + "npc_id": "4398", + "loc_data": "{2160,5282,0,1,3}-{2162,5284,0,1,1}-{2164,5281,0,1,7}-" + }, + { + "npc_id": "4399", + "loc_data": "{2080,5240,0,1,2}-{2122,5290,0,1,0}-{2131,5297,0,1,6}-{2119,5297,0,1,4}-{2120,5302,0,1,3}-{2129,5306,0,1,3}-{2123,5304,0,1,1}-{2130,5306,0,1,4}-{2163,5301,0,1,5}-{2166,5306,0,1,0}-{2169,5303,0,1,4}-{2122,5291,0,1,0}-{2119,5296,0,1,3}-{2131,5297,0,1,6}-{2125,5302,0,1,0}-{2127,5304,0,1,4}-{2165,5302,0,1,3}-{2163,5301,0,1,4}-{2168,5306,0,1,4}-{2132,5300,0,1,6}-{2132,5302,0,1,4}-" + }, + { + "npc_id": "4400", + "loc_data": "{2072,5200,0,1,7}-{2130,5267,0,1,7}-{2140,5252,0,1,5}-{2141,5252,0,1,1}-{2142,5252,0,1,0}-{2148,5255,0,1,3}-{2125,5270,0,1,1}-{2148,5257,0,1,1}-{2117,5274,0,1,4}-{2146,5307,0,1,2}-{2145,5305,0,1,5}-{2152,5307,0,1,4}-{2148,5252,0,1,0}-{2142,5262,0,1,3}-{2124,5275,0,1,4}-{2121,5276,0,1,3}-{2148,5304,0,1,0}-{2146,5305,0,1,5}-{2148,5304,0,1,1}-{2151,5304,0,1,6}-{2145,5306,0,1,3}-{2134,5272,0,1,6}-{2151,5307,0,1,6}-{2151,5308,0,1,6}-{2149,5307,0,1,6}-{2150,5308,0,1,6}-{2150,5305,0,1,6}-{2153,5307,0,1,6}-{2152,5305,0,1,6}-{2128,5272,0,1,6}-{2129,5272,0,1,6}-{2124,5271,0,1,6}-{2124,5272,0,1,6}-{2122,5272,0,1,6}-{2121,5275,0,1,6}-{3018,10313,0,1,4}-{3016,10315,0,1,4}-{3018,10322,0,1,4}-{3024,10326,0,1,3}-{3030,10332,0,1,4}-{3029,10337,0,1,6}-{3034,10342,0,1,3}-" + }, + { + "npc_id": "4401", + "loc_data": "{2072,5200,0,1,6}-{2096,5248,0,1,6}-{2124,5273,0,1,1}-{2128,5270,0,1,4}-{2117,5274,0,1,3}-{2154,5273,0,1,3}-{2167,5259,0,1,6}-{2170,5257,0,1,1}-{2170,5256,0,1,0}-{2171,5256,0,1,4}-{2167,5256,0,1,3}-{2153,5273,0,1,3}-{2153,5270,0,1,1}-{2171,5255,0,1,2}-{2168,5250,0,1,4}-{2168,5251,0,1,3}-{2131,5264,0,1,6}-{2157,5272,0,1,6}-{2150,5268,0,1,0}-{2133,5272,0,1,2}-{2157,5274,0,1,1}-{2156,5270,0,1,3}-{2154,5271,0,1,5}-" + }, + { + "npc_id": "4402", + "loc_data": "{2080,5240,0,1,5}-{2171,5283,0,1,6}-{2170,5287,0,1,6}-{2170,5281,0,1,6}-{2172,5272,0,1,4}-{2172,5272,0,1,4}-" + }, + { + "npc_id": "4403", + "loc_data": "{2096,5248,0,1,4}-{2172,5276,0,1,1}-{2172,5284,0,1,1}-{2172,5281,0,1,2}-" + }, + { + "npc_id": "4404", + "loc_data": "{1872,5242,0,1,3}-{1873,5230,0,1,2}-{1870,5235,0,1,3}-{1872,5217,0,1,6}-{1873,5212,0,1,4}-{1877,5218,0,1,2}-{1877,5216,0,1,3}-{1875,5218,0,1,6}-{1871,5191,0,1,4}-{1868,5188,0,1,4}-{1861,5189,0,1,3}-{1882,5216,0,1,6}-{1874,5215,0,1,5}-{1873,5238,0,1,3}-" + }, + { + "npc_id": "4406", + "loc_data": "{1848,5176,0,1,2}-{1898,5197,0,1,6}-{1898,5191,0,1,7}-{1890,5195,0,1,3}-{1893,5191,0,1,4}-{1903,5190,0,1,0}-{1889,5190,0,1,3}-{1889,5190,0,1,4}-{1885,5191,0,1,4}-{1900,5208,0,1,1}-{1900,5210,0,1,1}-{1903,5243,0,1,6}-{1901,5245,0,1,4}-{1894,5243,0,1,5}-{1890,5242,0,1,6}-{1889,5244,0,1,4}-{1897,5196,0,1,1}-{1902,5207,0,1,3}-" + }, + { + "npc_id": "4407", + "loc_data": "{1860,5226,0,1,0}-{1859,5200,0,1,0}-{1860,5222,0,1,4}-{1865,5205,0,1,4}-{1862,5205,0,1,7}-{1862,5205,0,1,7}-{1864,5206,0,1,7}-" + }, + { + "npc_id": "4408", + "loc_data": "{1860,5215,0,1,5}-{1864,5218,0,1,1}-" + }, + { + "npc_id": "4409", + "loc_data": "{1864,5226,0,1,1}-{1859,5220,0,1,4}-{1861,5224,0,1,6}-" + }, + { + "npc_id": "4410", + "loc_data": "{1848,5176,0,1,2}-{1890,5205,0,1,6}-{1897,5232,0,1,6}-{1906,5236,0,1,3}-{1904,5235,0,1,6}-{1895,5227,0,1,3}-{1863,5221,0,1,4}-" + }, + { + "npc_id": "4411", + "loc_data": "{1882,5201,0,1,0}-{1884,5207,0,1,3}-{1912,5239,0,1,4}-{1912,5244,0,1,5}-{1890,5204,0,1,5}-" + }, + { + "npc_id": "4412", + "loc_data": "{1880,5200,0,1,6}-{1911,5241,0,1,0}-{1893,5228,0,1,3}-" + }, + { + "npc_id": "4413", + "loc_data": "{2645,3580,0,1,4}-{1871,5228,0,1,6}-{1872,5233,0,1,1}-" + }, + { + "npc_id": "4414", + "loc_data": "{1890,5215,0,1,5}-{1892,5216,0,1,1}-{1889,5223,0,1,3}-{1892,5224,0,1,2}-{1893,5219,0,1,5}-" + }, + { + "npc_id": "4415", + "loc_data": "{1848,5176,0,1,2}-{1874,5230,0,1,3}-{1870,5232,0,1,4}-{1858,5235,0,1,3}-{1871,5241,0,1,7}-{1860,5223,0,1,4}-{1862,5222,0,1,4}-{1874,5234,0,1,5}-{1874,5230,0,1,5}-{1875,5219,0,1,5}-{1876,5215,0,1,3}-{1876,5220,0,1,6}-{1859,5206,0,1,5}-{1877,5216,0,1,4}-{1862,5201,0,1,5}-{1858,5201,0,1,6}-{1874,5201,0,1,2}-{1858,5202,0,1,0}-{1860,5200,0,1,7}-{1875,5197,0,1,6}-{1875,5201,0,1,2}-{1861,5194,0,1,3}-{1861,5224,0,1,6}-{1860,5192,0,1,2}-{1874,5188,0,1,7}-{1867,5188,0,1,4}-{1866,5189,0,1,4}-{1876,5189,0,1,1}-{1876,5196,0,1,3}-{1876,5188,0,1,4}-{1876,5191,0,1,4}-{1877,5191,0,1,4}-{1877,5197,0,1,6}-{1881,5198,0,1,4}-{1882,5197,0,1,5}-{1883,5203,0,1,3}-{1882,5198,0,1,4}-{1863,5205,0,1,4}-{1883,5205,0,1,5}-{1861,5205,0,1,5}-{1887,5204,0,1,3}-{1883,5205,0,1,3}-{1884,5206,0,1,1}-{1876,5215,0,1,6}-{1879,5215,0,1,2}-{1870,5217,0,1,6}-{1881,5221,0,1,1}-{1871,5218,0,1,1}-{1872,5191,0,1,3}-{1874,5219,0,1,5}-{1884,5228,0,1,4}-{1882,5231,0,1,7}-{1882,5234,0,1,5}-{1879,5234,0,1,6}-{1897,5234,0,1,6}-{1884,5234,0,1,4}-{1884,5235,0,1,3}-{1885,5236,0,1,6}-{1879,5239,0,1,4}-{1893,5243,0,1,7}-{1880,5243,0,1,7}-{1882,5241,0,1,1}-{1902,5242,0,1,4}-{1901,5244,0,1,3}-{1910,5243,0,1,5}-{1911,5235,0,1,3}-{1881,5243,0,1,2}-{1879,5234,0,1,3}-{1880,5234,0,1,4}-{1879,5239,0,1,1}-{1874,5230,0,1,4}-{1874,5236,0,1,4}-{1911,5235,0,1,5}-{1912,5243,0,1,4}-{1912,5238,0,1,3}-{1913,5244,0,1,4}-{1890,5244,0,1,4}-{1907,5204,0,1,1}-{1909,5204,0,1,1}-{1908,5204,0,1,3}-{1889,5206,0,1,3}-{1890,5207,0,1,1}-{1890,5207,0,1,2}-{1912,5237,0,1,7}-" + }, + { + "npc_id": "4422", + "loc_data": "{2377,4450,0,0,0}-{2381,4459,0,0,0}-{2388,4425,0,0,0}-{2394,4440,0,0,0}-{2418,4447,0,0,0}-{2427,4461,0,0,0}-" + }, + { + "npc_id": "4423", + "loc_data": "{2387,4456,0,0,0}-{2423,4461,0,0,0}-" + }, + { + "npc_id": "4424", + "loc_data": "{2396,4455,0,0,0}-" + }, + { + "npc_id": "4425", + "loc_data": "{2398,4453,0,0,0}-" + }, + { + "npc_id": "4431", + "loc_data": "{2388,4470,0,0,0}-" + }, + { + "npc_id": "4437", + "loc_data": "{2447,4429,0,1,0}-" + }, + { + "npc_id": "4438", + "loc_data": "{3028,4498,0,1,3}-{3046,4493,0,1,4}-" + }, + { + "npc_id": "4439", + "loc_data": "{3035,4507,0,1,6}-{3041,4493,0,1,0}-" + }, + { + "npc_id": "4443", + "loc_data": "{2451,4424,0,1,6}-{2452,4428,0,1,3}-{2443,4423,0,1,7}-{2446,4422,0,1,3}-{2443,4431,0,1,1}-{2441,4427,0,1,1}-{2463,4422,0,1,4}-{2478,4420,0,1,6}-{2489,4428,0,1,6}-{2478,4452,0,1,3}-{2474,4444,0,1,6}-{2493,4454,0,1,3}-{2486,4463,0,1,4}-{2486,4469,0,1,7}-{2479,4456,0,1,4}-{2477,4462,0,1,3}-" + }, + { + "npc_id": "4455", + "loc_data": "{2410,4434,0,0,0}-" + }, + { + "npc_id": "4472", + "loc_data": "{2524,3056,0,1,2}-" + }, + { + "npc_id": "4474", + "loc_data": "{3214,3252,0,0,0}-{3214,3253,0,0,0}-{3214,3254,0,0,0}-" + }, + { + "npc_id": "4478", + "loc_data": "{3291,2787,0,0,6}-" + }, + { + "npc_id": "4479", + "loc_data": "{2952,3477,0,1,0}-{2959,3501,0,1,3}-" + }, + { + "npc_id": "4481", + "loc_data": "{2943,3474,0,1,3}-" + }, + { + "npc_id": "4482", + "loc_data": "{2952,3505,0,1,7}-{2950,3501,0,1,4}-" + }, + { + "npc_id": "4483", + "loc_data": "{2944,3467,0,1,6}-{2961,3508,0,1,6}-{2953,3467,0,1,3}-" + }, + { + "npc_id": "4484", + "loc_data": "{2956,3496,0,1,6}-" + }, + { + "npc_id": "4485", + "loc_data": "{2951,3509,0,1,4}-" + }, + { + "npc_id": "4487", + "loc_data": "{2943,3478,0,1,3}-" + }, + { + "npc_id": "4488", + "loc_data": "{2946,3474,0,1,4}-{2956,3494,0,1,3}-" + }, + { + "npc_id": "4489", + "loc_data": "{2943,3487,0,1,3}-{2946,3469,0,1,5}-" + }, + { + "npc_id": "4491", + "loc_data": "{2963,3510,0,1,2}-{2949,3475,0,1,3}-" + }, + { + "npc_id": "4492", + "loc_data": "{2987,3426,0,1,6}-{2953,3509,0,1,4}-" + }, + { + "npc_id": "4493", + "loc_data": "{2957,3513,0,1,6}-" + }, + { + "npc_id": "4494", + "loc_data": "{2957,3512,0,1,4}-" + }, + { + "npc_id": "4495", + "loc_data": "{2957,3512,0,1,5}-" + }, + { + "npc_id": "4511", + "loc_data": "{2149,3866,0,0,7}-" + }, + { + "npc_id": "4512", + "loc_data": "{2092,3930,0,1,4}-" + }, + { + "npc_id": "4513", + "loc_data": "{2450,4647,0,1,5}-" + }, + { + "npc_id": "4514", + "loc_data": "{2079,3923,0,1,7}-" + }, + { + "npc_id": "4515", + "loc_data": "{2081,3899,0,1,1}-" + }, + { + "npc_id": "4516", + "loc_data": "{2096,3907,0,1,6}-" + }, + { + "npc_id": "4517", + "loc_data": "{2086,3915,0,1,0}-" + }, + { + "npc_id": "4518", + "loc_data": "{2106,3907,0,1,6}-" + }, + { + "npc_id": "4519", + "loc_data": "{2098,3921,0,0,6}-{2099,3921,0,0,6}-{2097,3921,0,0,6}-" + }, + { + "npc_id": "4524", + "loc_data": "{2083,3903,0,1,3}-" + }, + { + "npc_id": "4525", + "loc_data": "{2098,3894,0,1,3}-" + }, + { + "npc_id": "4526", + "loc_data": "{2103,3914,0,1,7}-" + }, + { + "npc_id": "4527", + "loc_data": "{2071,3877,0,1,2}-{2084,3868,0,1,1}-{2089,3864,0,1,4}-" + }, + { + "npc_id": "4528", + "loc_data": "{2105,3848,0,1,1}-{2115,3864,0,1,6}-{2122,3946,0,1,3}-" + }, + { + "npc_id": "4529", + "loc_data": "{2070,3879,0,1,7}-{2097,3862,0,1,3}-{2118,3852,0,1,4}-{2144,3853,0,1,5}-" + }, + { + "npc_id": "4530", + "loc_data": "{2076,3866,0,1,1}-{2107,3945,0,1,1}-{2133,3874,0,1,4}-" + }, + { + "npc_id": "4531", + "loc_data": "{2103,3866,0,1,3}-{2134,3851,0,1,4}-{2116,3950,0,1,0}-" + }, + { + "npc_id": "4534", + "loc_data": "{3206,5487,0,1,6}-{3200,5492,0,1,1}-" + }, + { + "npc_id": "4536", + "loc_data": "{2213,3794,0,0,3}-" + }, + { + "npc_id": "4537", + "loc_data": "{2621,3688,0,0,6}-" + }, + { + "npc_id": "4539", + "loc_data": "{2079,3925,0,1,1}-" + }, + { + "npc_id": "4540", + "loc_data": "{2137,3899,2,1,2}-{2221,3797,2,1,3}-" + }, + { + "npc_id": "4558", + "loc_data": "{2885,10202,0,1,0}-" + }, + { + "npc_id": "4560", + "loc_data": "{3062,3260,0,1,6}-" + }, + { + "npc_id": "4561", + "loc_data": "{2615,3855,0,1,0}-" + }, + { + "npc_id": "4562", + "loc_data": "{2799,3200,0,1,0}-" + }, + { + "npc_id": "4563", + "loc_data": "{3003,9798,0,1,3}-" + }, + { + "npc_id": "4564", + "loc_data": "{3356,3385,0,1,0}-{3354,3408,0,1,0}-{3365,3429,0,1,0}-{3373,3418,0,1,0}-" + }, + { + "npc_id": "4565", + "loc_data": "{3350,3401,0,1,0}-{3362,3408,0,1,0}-{3366,3425,0,1,0}-" + }, + { + "npc_id": "4566", + "loc_data": "{3360,3343,0,1,6}-" + }, + { + "npc_id": "4567", + "loc_data": "{3364,3339,0,1,6}-" + }, + { + "npc_id": "4568", + "loc_data": "{3365,3333,0,1,6}-" + }, + { + "npc_id": "4569", + "loc_data": "{3381,3379,0,0,0}-" + }, + { + "npc_id": "4570", + "loc_data": "{3115,3276,0,1,6}-{3201,3301,0,1,3}-" + }, + { + "npc_id": "4571", + "loc_data": "{3098,3225,0,1,4}-{3095,3286,0,1,4}-{3205,3298,0,1,3}-{3056,3248,0,1,0}-" + }, + { + "npc_id": "4572", + "loc_data": "{2448,3510,1,0,4}-" + }, + { + "npc_id": "4573", + "loc_data": "{2441,3501,1,1,3}-" + }, + { + "npc_id": "4575", + "loc_data": "{2441,3503,1,1,4}-" + }, + { + "npc_id": "4576", + "loc_data": "{2786,3862,0,1,0}-" + }, + { + "npc_id": "4578", + "loc_data": "{2933,2972,0,0,0}-" + }, + { + "npc_id": "4580", + "loc_data": "{2572,3299,1,0,0}-" + }, + { + "npc_id": "4585", + "loc_data": "{3115,3160,0,0,0}-" + }, + { + "npc_id": "4586", + "loc_data": "{2593,3087,0,1,4}-" + }, + { + "npc_id": "4587", + "loc_data": "{2449,3503,1,0,6}-" + }, + { + "npc_id": "4588", + "loc_data": "{2448,3500,1,0,3}-" + }, + { + "npc_id": "4597", + "loc_data": "{2375,3433,0,1,0}-" + }, + { + "npc_id": "4599", + "loc_data": "{2417,3513,0,1,4}-" + }, + { + "npc_id": "4603", + "loc_data": "{3008,3514,0,1,3}-" + }, + { + "npc_id": "4604", + "loc_data": "{3017,3513,0,1,0}-{3018,3515,0,1,1}-" + }, + { + "npc_id": "4605", + "loc_data": "{3018,3512,0,1,7}-{3019,3514,0,1,1}-{3019,3516,0,1,1}-" + }, + { + "npc_id": "4606", + "loc_data": "{3017,3514,0,1,4}-" + }, + { + "npc_id": "4607", + "loc_data": "{3030,3505,0,0,0}-" + }, + { + "npc_id": "4611", + "loc_data": "{3076,3259,0,0,0}-" + }, + { + "npc_id": "4648", + "loc_data": "{3376,3373,0,0,0}-" + }, + { + "npc_id": "4649", + "loc_data": "{2142,3122,0,1,4}-{2147,3122,0,1,3}-" + }, + { + "npc_id": "4650", + "loc_data": "{3041,3194,0,1,4}-" + }, + { + "npc_id": "4651", + "loc_data": "{2143,3121,0,1,6}-{2760,3239,0,1,3}-" + }, + { + "npc_id": "4652", + "loc_data": "{2674,3144,0,1,3}-{3701,3501,0,1,6}-" + }, + { + "npc_id": "4653", + "loc_data": "{3671,2930,0,1,6}-{2954,3154,0,1,6}-{3040,3192,0,1,3}-{2794,3414,0,1,6}-" + }, + { + "npc_id": "4654", + "loc_data": "{2145,3122,0,1,3}-{2674,3146,0,1,6}-{3001,3033,0,1,6}-" + }, + { + "npc_id": "4655", + "loc_data": "{3701,3503,0,1,1}-{2954,3157,0,1,3}-" + }, + { + "npc_id": "4656", + "loc_data": "{3038,3192,0,1,3}-{2760,3238,0,1,3}-{2795,3414,0,1,6}-" + }, + { + "npc_id": "4659", + "loc_data": "{3095,3232,0,1,6}-{3095,3231,0,1,0}-{3225,3370,0,1,4}-{3230,3370,0,1,3}-" + }, + { + "npc_id": "4660", + "loc_data": "{3225,3369,0,1,3}-{3226,3371,0,1,7}-{3230,3372,0,1,0}-{3230,3370,0,1,3}-{3229,3368,0,1,1}-{3227,3367,0,1,1}-" + }, + { + "npc_id": "4665", + "loc_data": "{2894,9766,0,1,0}-{2904,9797,0,1,0}-{2918,9800,0,1,0}-" + }, + { + "npc_id": "4666", + "loc_data": "{2893,9769,0,1,0}-{2912,9797,0,1,0}-{2909,9807,0,1,0}-" + }, + { + "npc_id": "4670", + "loc_data": "{3181,3850,0,1,6}-" + }, + { + "npc_id": "4672", + "loc_data": "{3190,3834,0,1,6}-" + }, + { + "npc_id": "4673", + "loc_data": "{2829,9827,0,1,0}-{2454,4368,0,1,0}-" + }, + { + "npc_id": "4674", + "loc_data": "{2464,4365,0,1,4}-" + }, + { + "npc_id": "4675", + "loc_data": "{2460,4374,0,1,4}-{3048,10266,0,1,4}-" + }, + { + "npc_id": "4676", + "loc_data": "{2451,4362,0,1,4}-{3054,10269,0,1,4}-" + }, + { + "npc_id": "4677", + "loc_data": "{3335,3687,0,1,4}-{3337,3701,0,1,2}-{3107,3812,0,1,0}-{3316,5448,0,1,2}-{3299,5545,0,1,6}-" + }, + { + "npc_id": "4678", + "loc_data": "{3333,3699,0,1,6}-{3350,3687,0,1,5}-{3098,3821,0,1,0}-{3305,5454,0,1,6}-" + }, + { + "npc_id": "4679", + "loc_data": "{3332,3693,0,1,6}-{3341,3685,0,1,4}-{3092,3810,0,1,0}-{2973,3620,0,1,3}-{3312,5558,0,1,1}-" + }, + { + "npc_id": "4680", + "loc_data": "{3331,3672,0,1,0}-{3347,3694,0,1,5}-{3078,3810,0,1,0}-{2977,3611,0,1,3}-" + }, + { + "npc_id": "4681", + "loc_data": "{2904,9802,0,1,0}-" + }, + { + "npc_id": "4682", + "loc_data": "{2896,9796,0,1,0}-" + }, + { + "npc_id": "4685", + "loc_data": "{2817,3514,0,1,0}-{2884,9959,0,1,0}-{2890,9950,0,1,0}-{3209,5552,0,1,1}-{3223,5555,0,1,4}-{3220,5548,0,1,6}-{3220,5557,0,1,4}-" + }, + { + "npc_id": "4686", + "loc_data": "{2824,3510,0,1,5}-{2880,9927,0,1,0}-{2887,9955,0,1,0}-{2891,9941,0,1,0}-{2954,3894,0,1,4}-{2950,3932,0,1,0}-{2955,3945,0,1,0}-{3213,5548,0,1,4}-{2804,3507,0,1,0}-" + }, + { + "npc_id": "4687", + "loc_data": "{2957,3898,0,1,6}-{2946,3898,0,1,7}-{2978,3956,0,1,0}-" + }, + { + "npc_id": "4688", + "loc_data": "{3139,3818,0,1,0}-{2700,3212,0,1,6}-" + }, + { + "npc_id": "4689", + "loc_data": "{2369,3401,0,1,0}-{2902,9736,0,1,0}-" + }, + { + "npc_id": "4690", + "loc_data": "{3104,3875,0,1,2}-{2372,3401,0,1,0}-{2912,9731,0,1,0}-{3256,3624,0,1,5}-{3308,3661,0,1,2}-" + }, + { + "npc_id": "4691", + "loc_data": "{3110,3854,0,1,0}-{2371,3398,0,1,0}-" + }, + { + "npc_id": "4692", + "loc_data": "{3116,3858,0,1,0}-{2372,3395,0,1,0}-" + }, + { + "npc_id": "4693", + "loc_data": "{3094,3849,0,1,0}-{2369,3394,0,1,0}-{2912,9741,0,1,0}-{2906,9736,0,1,0}-" + }, + { + "npc_id": "4694", + "loc_data": "{3103,3709,0,1,0}-{3094,3869,0,1,1}-{2843,9558,0,1,1}-{2840,9605,0,1,4}-{3017,3852,0,1,7}-" + }, + { + "npc_id": "4695", + "loc_data": "{2840,3279,0,1,6}-{3098,3699,0,1,4}-{2839,9565,0,1,3}-{2837,9601,0,1,4}-{2631,9880,0,1,1}-{3015,3851,0,1,2}-" + }, + { + "npc_id": "4696", + "loc_data": "{3096,3691,0,1,6}-{3091,3861,0,1,0}-{2839,9560,0,1,4}-{2846,9613,0,1,6}-{2931,9808,0,1,0}-{2930,9799,0,1,0}-{2632,9874,0,1,1}-{3014,3848,0,1,1}-" + }, + { + "npc_id": "4697", + "loc_data": "{2825,3277,0,1,3}-{3105,3695,0,1,5}-{2845,9557,0,1,2}-{2839,9624,0,1,6}-{2630,9867,0,1,1}-{3016,3845,0,1,4}-{3324,3856,0,1,2}-" + }, + { + "npc_id": "4698", + "loc_data": "{2861,9752,0,1,4}-{3139,3712,0,1,5}-{3304,3886,0,1,6}-" + }, + { + "npc_id": "4699", + "loc_data": "{3159,3707,0,1,3}-{2629,9477,2,0,0}-{2640,9502,2,0,1}-{3296,3872,0,1,5}-{3035,10245,0,1,4}-" + }, + { + "npc_id": "4700", + "loc_data": "{2862,9750,0,1,7}-{3144,3701,0,1,4}-{2633,9488,2,0,6}-{3282,3880,0,1,4}-" + }, + { + "npc_id": "4701", + "loc_data": "{3149,3690,0,1,4}-{3287,3894,0,1,3}-{3028,10250,0,1,4}-" + }, + { + "npc_id": "4702", + "loc_data": "{2869,9775,0,1,0}-{2853,9775,0,1,0}-{3084,9957,0,1,6}-{2712,9480,0,0,5}-" + }, + { + "npc_id": "4703", + "loc_data": "{2857,9774,0,1,0}-{2860,9781,0,1,0}-{3092,9958,0,1,2}-" + }, + { + "npc_id": "4704", + "loc_data": "{2863,9767,0,1,0}-{2871,9782,0,1,0}-{3160,5521,0,1,1}-" + }, + { + "npc_id": "4705", + "loc_data": "{3088,9961,0,1,1}-{3169,5528,0,1,0}-" + }, + { + "npc_id": "4706", + "loc_data": "{2195,3808,0,1,0}-{2196,3822,0,1,0}-{2197,3805,0,1,0}-{2197,3813,0,1,0}-{2201,3812,0,1,0}-{2202,3824,0,1,0}-{2203,3804,0,1,0}-{2207,3813,0,1,0}-{2211,3818,0,1,0}-{2214,3819,0,1,0}-{3237,5559,0,1,3}-{3244,5554,0,1,3}-{3239,5553,0,1,5}-{3249,5559,0,1,5}-" + }, + { + "npc_id": "4707", + "loc_data": "{3213,3253,0,0,6}-" + }, + { + "npc_id": "4710", + "loc_data": "{3222,3475,0,0,5}-" + }, + { + "npc_id": "4732", + "loc_data": "{3429,3701,0,0,0}-" + }, + { + "npc_id": "4856", + "loc_data": "{2732,3292,0,1,1}-" + }, + { + "npc_id": "4871", + "loc_data": "{2782,3276,0,1,0}-" + }, + { + "npc_id": "4872", + "loc_data": "{2740,3310,0,0,0}-" + }, + { + "npc_id": "4874", + "loc_data": "{2709,3291,0,0,0}-" + }, + { + "npc_id": "4878", + "loc_data": "{2726,3283,0,1,1}-" + }, + { + "npc_id": "4883", + "loc_data": "{2713,3278,0,1,1}-" + }, + { + "npc_id": "4885", + "loc_data": "{2705,3287,0,1,1}-{2716,3287,0,1,1}-{2732,3282,0,1,1}-" + }, + { + "npc_id": "4887", + "loc_data": "{2738,3303,0,1,1}-{2723,3274,0,1,1}-" + }, + { + "npc_id": "4891", + "loc_data": "{2768,3286,0,1,0}-{2777,3278,0,1,0}-{2783,3289,0,1,0}-{2784,3277,0,1,0}-{2768,3289,1,1,0}-{2780,3288,1,1,0}-" + }, + { + "npc_id": "4895", + "loc_data": "{2718,3302,0,1,1}-" + }, + { + "npc_id": "4898", + "loc_data": "{3124,9874,0,1,3}-{3128,9875,0,1,6}-" + }, + { + "npc_id": "4899", + "loc_data": "{3231,3197,0,1,6}-" + }, + { + "npc_id": "4900", + "loc_data": "{3210,3213,1,1,1}-" + }, + { + "npc_id": "4901", + "loc_data": "{3245,3157,0,0,7}-" + }, + { + "npc_id": "4902", + "loc_data": "{3227,3147,0,0,1}-" + }, + { + "npc_id": "4903", + "loc_data": "{3244,3214,0,1,1}-" + }, + { + "npc_id": "4904", + "loc_data": "{3228,3254,0,1,3}-" + }, + { + "npc_id": "4906", + "loc_data": "{3230,3244,0,1,4}-" + }, + { + "npc_id": "4907", + "loc_data": "{3208,3222,2,0,6}-" + }, + { + "npc_id": "4909", + "loc_data": "{2831,3352,0,0,7}-" + }, + { + "npc_id": "4911", + "loc_data": "{2703,9894,0,0,0}-{2705,9897,0,0,0}-{2706,9904,0,0,0}-{2707,9906,0,0,0}-{2704,9906,0,0,0}-{2706,9910,0,0,0}-{2703,9911,0,0,0}-{2699,9915,0,0,0}-{2697,9915,0,0,0}-{2694,9915,0,0,0}-{2692,9914,0,0,0}-{2696,9910,0,0,0}-{2694,9908,0,0,0}-{2693,9906,0,0,0}-{2696,9906,0,0,0}-{2698,9901,0,0,0}-{2698,9900,0,0,0}-{2696,9900,0,0,0}-{2690,9903,0,0,0}-{2692,9902,0,0,0}-{2693,9900,0,0,0}-{2692,9900,0,0,0}-{2691,9899,0,0,0}-{2692,9895,0,0,0}-{2693,9891,0,0,0}-{2695,9889,0,0,0}-{2692,9885,0,0,0}-{2699,9882,0,0,0}-{2695,9881,0,0,0}-{2693,9881,0,0,0}-{2690,9880,0,0,0}-{2700,9879,0,0,0}-{2696,9880,0,0,0}-{2694,9878,0,0,0}-{2695,9877,0,0,0}-{2699,9876,0,0,0}-{2692,9876,0,0,0}-{2692,9874,0,0,0}-{2699,9872,0,0,0}-{2698,9871,0,0,0}-{2701,9870,0,0,0}-{2695,9870,0,0,0}-{2695,9869,0,0,0}-{2702,9868,0,0,0}-{2701,9867,0,0,0}-{2697,9866,0,0,0}-{2699,9865,0,0,0}-" + }, + { + "npc_id": "4920", + "loc_data": "{3188,5510,0,1,6}-{3194,5510,0,1,2}-{3289,5496,0,0,5}-{3272,5490,0,0,0}-" + }, + { + "npc_id": "4921", + "loc_data": "{3304,5493,0,1,3}-{3303,5496,0,1,0}-{3271,5490,0,0,5}-" + }, + { + "npc_id": "4926", + "loc_data": "{3227,3547,0,1,1}-{3251,3528,0,1,1}-{2969,3686,0,1,1}-" + }, + { + "npc_id": "4927", + "loc_data": "{3227,3533,0,1,1}-{3236,3549,0,1,1}-{2962,3689,0,1,1}-{2971,3690,0,1,1}-{3049,3695,0,1,1}-" + }, + { + "npc_id": "4930", + "loc_data": "{3412,2889,0,1,3}-{3414,2892,0,1,1}-{3414,2890,0,1,1}-{3416,2886,0,1,4}-{3416,2893,0,1,6}-" + }, + { + "npc_id": "4932", + "loc_data": "{3414,2891,0,1,1}-{3410,2888,0,1,7}-" + }, + { + "npc_id": "4942", + "loc_data": "{3191,3210,0,1,0}-" + }, + { + "npc_id": "4944", + "loc_data": "{2042,5213,0,1,5}-{2020,5236,0,1,2}-{1993,5188,0,1,2}-{1994,5191,0,1,2}-" + }, + { + "npc_id": "4945", + "loc_data": "{1994,5192,0,1,4}-{1997,5190,0,1,1}-" + }, + { + "npc_id": "4946", + "loc_data": "{2721,3432,0,1,4}-" + }, + { + "npc_id": "4947", + "loc_data": "{2825,3696,0,1,3}-" + }, + { + "npc_id": "4950", + "loc_data": "{2855,10051,1,0,0}-" + }, + { + "npc_id": "4953", + "loc_data": "{2684,3274,1,0,0}-" + }, + { + "npc_id": "4955", + "loc_data": "{2772,3223,0,0,0}-" + }, + { + "npc_id": "4956", + "loc_data": "{2783,3119,0,0,0}-" + }, + { + "npc_id": "4961", + "loc_data": "{2683,3275,1,0,0}-" + }, + { + "npc_id": "4965", + "loc_data": "{2826,3685,0,0,1}-" + }, + { + "npc_id": "4975", + "loc_data": "{3034,2971,0,0,5}-{3030,2971,0,0,1}-{3303,3024,0,0,7}-{3299,3016,0,0,2}-{3280,9438,0,0,6}-{3275,9460,0,0,3}-{3267,9458,0,0,3}-{3270,9415,0,0,6}-{3272,9429,0,0,4}-{3272,9421,0,0,4}-{3271,9450,0,0,1}-{3288,9413,0,0,6}-{3297,9414,0,0,6}-{3300,9414,0,0,6}-{3304,9424,0,0,1}-{3292,9426,0,0,4}-{3295,9421,0,0,3}-{3297,9412,0,0,1}-{3292,9413,0,0,3}-{3324,9429,0,0,3}-{3311,9440,0,0,6}-{3294,9459,0,0,1}-{3291,9463,0,0,6}-{3296,9468,0,0,1}-{3288,9468,0,0,1}-{3313,9449,0,0,1}-" + }, + { + "npc_id": "4976", + "loc_data": "{3036,2972,0,0,1}-{3039,2968,0,0,1}-{3046,2982,0,0,6}-{3045,2974,0,0,6}-{3042,2972,0,0,6}-{3301,3017,0,0,7}-{3281,9446,0,0,1}-{3270,9451,0,0,4}-{3269,9418,0,0,1}-{3267,9441,0,0,1}-{3280,9436,0,0,1}-{3291,9414,0,0,6}-{3305,9413,0,0,6}-{3308,9415,0,0,1}-{3309,9420,0,0,1}-{3296,9425,0,0,1}-{3294,9420,0,0,1}-{3295,9412,0,0,3}-{3323,9439,0,0,6}-{3295,9460,0,0,3}-{3294,9464,0,0,6}-{3294,9467,0,0,1}-{3311,9454,0,0,6}-" + }, + { + "npc_id": "4977", + "loc_data": "{3038,2973,0,0,1}-{3027,2974,0,0,1}-{3023,2977,0,0,1}-{3035,2989,0,0,4}-{3039,2985,0,0,4}-{3294,3016,0,0,6}-{3287,3016,0,0,3}-{3277,9452,0,0,1}-{3267,9443,0,0,6}-{3267,9431,0,0,6}-{3269,9420,0,0,6}-{3273,9460,0,0,4}-{3277,9454,0,0,6}-{3281,9448,0,0,6}-{3294,9413,0,0,6}-{3306,9423,0,0,1}-{3298,9436,0,0,6}-{3292,9466,0,0,6}-{3296,9462,0,0,3}-{3290,9467,0,0,1}-{3315,9451,0,0,1}-{3316,9454,0,0,3}-" + }, + { + "npc_id": "4978", + "loc_data": "{3042,2969,0,0,1}-{3038,2976,0,0,6}-{3041,2983,0,0,6}-{3043,2983,0,0,6}-{3044,2981,0,0,6}-{3043,2979,0,0,1}-{3038,2989,0,0,1}-{3025,2984,0,0,1}-{3026,2988,0,0,1}-{3030,2990,0,0,1}-{3024,2979,0,0,3}-{3291,3017,0,0,5}-{3275,9463,0,0,1}-{3273,9430,0,0,6}-{3273,9422,0,0,6}-{3267,9428,0,0,1}-{3294,9411,0,0,6}-{3300,9424,0,0,1}-{3289,9412,0,0,3}-" + }, + { + "npc_id": "4985", + "loc_data": "{3304,3027,0,1,2}-" + }, + { + "npc_id": "4986", + "loc_data": "{3308,3112,0,1,1}-" + }, + { + "npc_id": "4989", + "loc_data": "{3272,3029,0,1,4}-" + }, + { + "npc_id": "4990", + "loc_data": "{3269,3029,0,1,4}-" + }, + { + "npc_id": "4991", + "loc_data": "{3270,3030,0,1,6}-" + }, + { + "npc_id": "4992", + "loc_data": "{3267,3029,0,1,0}-" + }, + { + "npc_id": "4993", + "loc_data": "{3277,3028,0,1,1}-{3279,9441,0,1,4}-" + }, + { + "npc_id": "4994", + "loc_data": "{3285,3027,0,1,7}-" + }, + { + "npc_id": "4995", + "loc_data": "{3279,3024,0,1,3}-{3286,3036,1,1,4}-" + }, + { + "npc_id": "4996", + "loc_data": "{3284,3022,0,1,2}-" + }, + { + "npc_id": "4997", + "loc_data": "{3289,3026,0,1,3}-{3317,9438,0,0,3}-" + }, + { + "npc_id": "4998", + "loc_data": "{3293,3027,0,1,5}-{3286,9419,0,1,3}-{3296,9433,0,1,7}-{3310,9457,0,1,1}-" + }, + { + "npc_id": "4999", + "loc_data": "{3280,3020,0,1,6}-{3319,9439,0,1,0}-{3299,9467,0,1,3}-" + }, + { + "npc_id": "5000", + "loc_data": "{3285,3022,0,1,6}-{3295,9434,0,1,3}-" + }, + { + "npc_id": "5001", + "loc_data": "{3297,3020,0,1,6}-{3280,9414,0,1,1}-{3279,9416,0,1,1}-" + }, + { + "npc_id": "5002", + "loc_data": "{3295,3028,0,1,3}-{3291,9423,0,1,1}-" + }, + { + "npc_id": "5020", + "loc_data": "{2905,3180,0,0,0}-" + }, + { + "npc_id": "5021", + "loc_data": "{2906,3173,0,0,0}-" + }, + { + "npc_id": "5022", + "loc_data": "{2907,3169,0,0,0}-" + }, + { + "npc_id": "5029", + "loc_data": "{2534,3575,0,1,0}-" + }, + { + "npc_id": "5030", + "loc_data": "{2533,3570,0,1,4}-" + }, + { + "npc_id": "5031", + "loc_data": "{2525,3575,0,1,3}-" + }, + { + "npc_id": "5032", + "loc_data": "{2525,3566,0,1,3}-" + }, + { + "npc_id": "5033", + "loc_data": "{2520,3569,0,1,3}-" + }, + { + "npc_id": "5049", + "loc_data": "{2808,3355,0,1,6}-" + }, + { + "npc_id": "5062", + "loc_data": "{2927,3410,0,0,0}-" + }, + { + "npc_id": "5063", + "loc_data": "{2460,3108,0,1,3}-" + }, + { + "npc_id": "5065", + "loc_data": "{2925,3303,0,0,0}-" + }, + { + "npc_id": "5070", + "loc_data": "{2912,3480,0,0,0}-{2911,3480,0,0,0}-" + }, + { + "npc_id": "5071", + "loc_data": "{2609,2922,0,0,0}-" + }, + { + "npc_id": "5072", + "loc_data": "{2464,2832,0,1,2}-{2510,2876,0,1,3}-{2517,2878,0,1,6}-{2527,2877,0,1,2}-{2507,2877,0,1,1}-{2536,2877,0,1,3}-{2530,2872,0,1,3}-{2517,2870,0,1,4}-{2526,2928,0,1,6}-{2532,2938,0,1,4}-{2527,2933,0,1,4}-{2521,2938,0,1,6}-{2507,2914,0,1,0}-{2512,2910,0,1,4}-{2517,2912,0,1,1}-{2517,2921,0,1,6}-{2511,2911,0,1,3}-{2521,2917,0,1,7}-{2522,2929,0,1,4}-{2510,2888,0,1,1}-{2502,2890,0,1,3}-{2520,2882,0,1,7}-{2500,2893,0,1,4}-{2548,2887,0,1,1}-{2544,2889,0,1,7}-{2520,2885,0,1,0}-{2498,2883,0,1,6}-{2501,2886,0,1,7}-{2508,2916,0,1,4}-{2502,2888,0,1,7}-{2514,2915,0,1,4}-{2520,2908,0,1,6}-{2526,2943,0,1,4}-{2525,2893,0,1,4}-{2521,2896,0,1,1}-{2515,2944,0,1,4}-" + }, + { + "npc_id": "5073", + "loc_data": "{2588,2879,0,1,6}-{2603,2892,0,1,1}-{2604,2892,0,1,6}-{2611,2917,0,1,6}-{2611,2916,0,1,3}-{2613,2894,0,1,1}-{2614,2892,0,1,6}-{2608,2927,0,1,2}-{2612,2934,0,1,4}-{2609,2930,0,1,0}-{2594,2929,0,1,2}-{2616,2922,0,1,4}-{2593,2911,0,1,1}-{2617,2911,0,1,1}-{2602,2900,0,1,3}-{2601,2886,0,1,6}-{2587,2897,0,1,4}-{2612,2936,0,1,3}-{2606,2929,0,1,5}-{2615,2930,0,1,3}-{2576,2896,0,1,1}-{2593,2880,0,1,7}-{2583,2886,0,1,5}-{2609,2897,0,1,3}-{2608,2905,0,1,2}-{2606,2917,0,1,4}-{2604,2918,0,1,4}-{2613,2890,0,1,5}-{2608,2921,0,1,6}-{2604,2886,0,1,6}-{2602,2920,0,1,1}-{2607,2932,0,1,6}-{2578,2885,0,1,1}-" + }, + { + "npc_id": "5074", + "loc_data": "{2727,3763,0,1,1}-{2724,3765,0,1,2}-{2725,3768,0,1,2}-{2730,3768,0,1,2}-{2731,3764,0,1,2}-{2731,3763,0,1,2}-{2731,3765,0,1,2}-{2733,3765,0,1,2}-{2709,3823,1,1,2}-{2713,3823,1,1,2}-{2709,3825,1,1,2}-{2710,3826,1,1,2}-{2709,3825,1,1,2}-" + }, + { + "npc_id": "5075", + "loc_data": "{3409,3078,0,1,5}-{3410,3075,0,1,5}-{3411,3079,0,1,5}-{3408,3076,0,1,5}-{3414,3077,0,1,5}-{3402,3140,0,1,2}-{3402,3138,0,1,3}-{3404,3139,0,1,5}-{3400,3142,0,1,6}-{3405,3143,0,1,5}-" + }, + { + "npc_id": "5076", + "loc_data": "{2310,3581,0,1,2}-{2314,3580,0,1,2}-{2311,3583,0,1,2}-{2304,3588,0,1,6}-{2305,3601,0,1,7}-{2341,3610,0,1,7}-{2344,3606,0,1,3}-{2340,3603,0,1,4}-{2348,3591,0,1,1}-{2356,3595,0,1,6}-{2361,3595,0,1,1}-{2359,3589,0,1,4}-{2346,3585,0,1,6}-{2360,3584,0,1,7}-" + }, + { + "npc_id": "5077", + "loc_data": "{2560,2911,0,0,0}-{2561,2914,0,0,0}-{2560,2909,0,0,0}-{2561,2916,0,0,0}-{2467,2879,0,0,0}-{2484,2877,0,0,0}-{2485,2876,0,0,0}-{2456,2832,0,0,0}-{2456,2832,0,0,0}-{2456,2832,0,0,0}-{2456,2832,0,0,0}-{2494,2940,0,0,0}-{2495,2940,0,0,0}-{2512,2864,0,0,0}-{2558,2911,0,0,0}-{2558,2913,0,0,0}-{2556,2913,0,0,0}-{2557,2912,0,0,0}-{2559,2910,0,0,0}-{2559,2909,0,0,0}-{2559,2917,0,0,0}-{2558,2914,0,0,0}-{2559,2915,0,0,0}-{2558,2916,0,0,0}-{2559,2932,0,0,0}-{2559,2928,0,0,0}-{2558,2928,0,0,0}-{2558,2931,0,0,0}-{2558,2929,0,0,0}-{2556,2911,0,0,0}-{2557,2911,0,0,0}-{2556,2910,0,0,0}-{2556,2912,0,0,0}-{2557,2919,0,0,0}-{2556,2914,0,0,0}-{2556,2913,0,0,0}-{2556,2918,0,0,0}-{2556,2929,0,0,0}-{2557,2933,0,0,0}-{2556,2932,0,0,0}-{2557,2932,0,0,0}-{2557,2928,0,0,0}-{2559,2934,0,0,0}-{2554,2911,0,0,0}-{2555,2911,0,0,0}-{2554,2912,0,0,0}-{2555,2915,0,0,0}-{2555,2919,0,0,0}-{2554,2919,0,0,0}-{2554,2913,0,0,0}-{2556,2915,0,0,0}-{2555,2931,0,0,0}-{2554,2933,0,0,0}-{2555,2934,0,0,0}-{2555,2933,0,0,0}-{2555,2932,0,0,0}-{2554,2935,0,0,0}-{2554,2934,0,0,0}-{2556,2936,0,0,0}-{2558,2936,0,0,0}-{2554,2936,0,0,0}-{2553,2935,0,0,0}-{2556,2933,0,0,0}-{2558,2938,0,0,0}-{2556,2938,0,0,0}-{2557,2937,0,0,0}-{2557,2917,0,0,0}-{2555,2917,0,0,0}-{2555,2912,0,0,0}-{2555,2935,0,0,0}-{2547,2940,0,0,0}-{2546,2939,0,0,0}-{2548,2939,0,0,0}-{2557,2930,0,0,0}-{2556,2929,0,0,0}-{2546,2941,0,0,0}-{2548,2941,0,0,0}-{2557,2931,0,0,0}-{2556,2935,0,0,0}-{2555,2936,0,0,0}-{2553,2935,0,0,0}-{2559,2930,0,0,0}-{2547,2940,0,0,0}-{2509,2910,0,0,0}-{2507,2910,0,0,0}-{2509,2908,0,0,0}-{2508,2908,0,0,0}-{2508,2909,0,0,0}-{2510,2908,0,0,0}-{2506,2906,0,0,0}-{2508,2906,0,0,0}-{2507,2906,0,0,0}-{2507,2905,0,0,0}-{2509,2905,0,0,0}-{2505,2907,0,0,0}-{2504,2908,0,0,0}-{2511,2908,0,0,0}-{2504,2906,0,0,0}-{2508,2907,0,0,0}-{2509,2906,0,0,0}-{2508,2909,0,0,0}-{2506,2906,0,0,0}-{2510,2907,0,0,0}-{2503,2907,0,0,0}-{2509,2908,0,0,0}-{2507,2908,0,0,0}-{2508,2906,0,0,0}-{2505,2907,0,0,0}-{2507,2905,0,0,0}-{2506,2904,0,0,0}-{2505,2903,0,0,0}-{2508,2902,0,0,0}-{2504,2901,0,0,0}-{2506,2907,0,0,0}-{2507,2906,0,0,0}-{2524,2898,0,0,0}-{2524,2896,0,0,0}-{2525,2897,0,0,0}-{2521,2895,0,0,0}-{2525,2895,0,0,0}-{2507,2903,0,0,0}-{2526,2898,0,0,0}-{2507,2889,0,0,0}-{2506,2903,0,0,0}-{2503,2905,0,0,0}-{2507,2905,0,0,0}-{2511,2908,0,0,0}-{2507,2887,0,0,0}-{2554,2937,0,0,0}-{2558,2938,0,0,0}-{2557,2937,0,0,0}-{2556,2938,0,0,0}-{2553,2937,0,0,0}-{2556,2937,0,0,0}-{2553,2935,0,0,0}-{2556,2935,0,0,0}-{2558,2936,0,0,0}-{2555,2936,0,0,0}-{2552,2936,0,0,0}-{2554,2936,0,0,0}-{2552,2934,0,0,0}-{2553,2934,0,0,0}-{2557,2931,0,0,0}-{2559,2932,0,0,0}-{2558,2932,0,0,0}-{2556,2936,0,0,0}-{2553,2936,0,0,0}-{2554,2935,0,0,0}-{2559,2917,0,0,0}-{2556,2916,0,0,0}-{2555,2917,0,0,0}-{2559,2914,0,0,0}-{2557,2915,0,0,0}-{2557,2912,0,0,0}-{2559,2912,0,0,0}-{2558,2913,0,0,0}-{2555,2911,0,0,0}-{2554,2910,0,0,0}-{2559,2916,0,0,0}-{2557,2932,0,0,0}-{2557,2909,0,0,0}-{2559,2913,0,0,0}-{2552,2935,0,0,0}-{2559,2909,0,0,0}-{2557,2914,0,0,0}-{2555,2915,0,0,0}-{2527,2896,0,0,0}-{2524,2895,0,0,0}-{2524,2894,0,0,0}-{2525,2896,0,0,0}-{2524,2896,0,0,0}-{2523,2893,0,0,0}-{2523,2896,0,0,0}-{2523,2897,0,0,0}-{2521,2895,0,0,0}-{2521,2893,0,0,0}-{2526,2895,0,0,0}-{2507,2887,0,0,0}-{2507,2890,0,0,0}-{2506,2886,0,0,0}-{2505,2887,0,0,0}-{2505,2888,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2504,2903,0,0,0}-{2505,2907,0,0,0}-{2504,2906,0,0,0}-{2505,2905,0,0,0}-{2506,2907,0,0,0}-{2506,2905,0,0,0}-{2506,2906,0,0,0}-{2507,2903,0,0,0}-{2507,2906,0,0,0}-{2507,2907,0,0,0}-{2505,2893,0,0,0}-{2504,2892,0,0,0}-{2508,2909,0,0,0}-{2502,2891,0,0,0}-{2503,2890,0,0,0}-{2503,2891,0,0,0}-{2508,2891,0,0,0}-{2507,2891,0,0,0}-{2507,2890,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2505,2888,0,0,0}-{2506,2909,0,0,0}-{2507,2887,0,0,0}-{2505,2887,0,0,0}-{2506,2887,0,0,0}-{2508,2890,0,0,0}-{2504,2894,0,0,0}-{2502,2891,0,0,0}-{2524,2899,0,0,0}-{2524,2898,0,0,0}-{2526,2899,0,0,0}-{2526,2895,0,0,0}-{2526,2900,0,0,0}-{2524,2900,0,0,0}-{2525,2900,0,0,0}-{2525,2899,0,0,0}-{2524,2893,0,0,0}-{2522,2894,0,0,0}-{2521,2895,0,0,0}-{2523,2895,0,0,0}-{2524,2895,0,0,0}-{2506,2886,0,0,0}-{2521,2893,0,0,0}-{2526,2898,0,0,0}-{2524,2899,0,0,0}-{2523,2896,0,0,0}-{2522,2894,0,0,0}-{2524,2901,0,0,0}-{2524,2900,0,0,0}-{2523,2895,0,0,0}-{2526,2901,0,0,0}-{2525,2896,0,0,0}-{2526,2900,0,0,0}-{2523,2893,0,0,0}-{2525,2900,0,0,0}-{2524,2898,0,0,0}-{2525,2894,0,0,0}-{2527,2894,0,0,0}-{2524,2899,0,0,0}-{2508,2904,0,0,0}-{2507,2907,0,0,0}-{2506,2902,0,0,0}-{2506,2907,0,0,0}-{2506,2906,0,0,0}-{2503,2907,0,0,0}-{2503,2905,0,0,0}-{2505,2893,0,0,0}-{2503,2891,0,0,0}-{2506,2891,0,0,0}-{2508,2891,0,0,0}-{2506,2892,0,0,0}-{2506,2909,0,0,0}-{2503,2890,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2507,2890,0,0,0}-{2505,2887,0,0,0}-{2505,2888,0,0,0}-{2508,2890,0,0,0}-{2507,2905,0,0,0}-{2503,2890,0,0,0}-{2503,2891,0,0,0}-{2504,2892,0,0,0}-{2504,2894,0,0,0}-{2506,2887,0,0,0}-{2507,2887,0,0,0}-{2507,2891,0,0,0}-{2504,2889,0,0,0}-{2508,2891,0,0,0}-{2505,2888,0,0,0}-{2506,2894,0,0,0}-{2506,2902,0,0,0}-{2506,2904,0,0,0}-{2506,2886,0,0,0}-{2507,2903,0,0,0}-{2505,2893,0,0,0}-{2506,2891,0,0,0}-{2503,2889,0,0,0}-{2506,2887,0,0,0}-{2506,2892,0,0,0}-{2507,2891,0,0,0}-{2507,2887,0,0,0}-{2507,2890,0,0,0}-{2503,2905,0,0,0}-{2505,2906,0,0,0}-{2506,2905,0,0,0}-{2506,2906,0,0,0}-{2507,2905,0,0,0}-{2505,2905,0,0,0}-{2504,2906,0,0,0}-{2503,2907,0,0,0}-{2506,2894,0,0,0}-{2507,2908,0,0,0}-{2507,2907,0,0,0}-{2508,2909,0,0,0}-{2505,2888,0,0,0}-{2505,2907,0,0,0}-{2496,2939,0,0,0}-{2497,2938,0,0,0}-{2498,2942,0,0,0}-{2499,2943,0,0,0}-{2500,2942,0,0,0}-{2503,2940,0,0,0}-{2496,2939,0,0,0}-{2502,2939,0,0,0}-{2498,2944,0,0,0}-{2500,2944,0,0,0}-" + }, + { + "npc_id": "5078", + "loc_data": "{2560,2909,0,0,0}-{2560,2933,0,0,0}-{2560,2918,0,0,0}-{2561,2916,0,0,0}-{2560,2915,0,0,0}-{2467,2878,0,0,0}-{2486,2877,0,0,0}-{2495,2938,0,0,0}-{2556,2911,0,0,0}-{2558,2910,0,0,0}-{2558,2909,0,0,0}-{2558,2918,0,0,0}-{2559,2930,0,0,0}-{2557,2915,0,0,0}-{2557,2931,0,0,0}-{2558,2935,0,0,0}-{2555,2936,0,0,0}-{2553,2936,0,0,0}-{2553,2933,0,0,0}-{2553,2934,0,0,0}-{2506,2908,0,0,0}-{2507,2908,0,0,0}-{2510,2906,0,0,0}-{2509,2907,0,0,0}-{2508,2907,0,0,0}-{2506,2904,0,0,0}-{2508,2904,0,0,0}-{2504,2906,0,0,0}-{2506,2909,0,0,0}-{2508,2904,0,0,0}-{2506,2902,0,0,0}-{2504,2903,0,0,0}-{2504,2902,0,0,0}-{2525,2899,0,0,0}-{2523,2897,0,0,0}-{2522,2897,0,0,0}-{2523,2895,0,0,0}-{2521,2893,0,0,0}-{2523,2893,0,0,0}-{2506,2888,0,0,0}-{2509,2889,0,0,0}-{2505,2889,0,0,0}-{2508,2888,0,0,0}-{2506,2887,0,0,0}-{2505,2887,0,0,0}-{2554,2935,0,0,0}-{2552,2935,0,0,0}-{2557,2934,0,0,0}-{2554,2934,0,0,0}-{2558,2933,0,0,0}-{2559,2931,0,0,0}-{2558,2916,0,0,0}-{2557,2917,0,0,0}-{2555,2915,0,0,0}-{2557,2914,0,0,0}-{2554,2912,0,0,0}-{2556,2912,0,0,0}-{2559,2909,0,0,0}-{2558,2909,0,0,0}-{2527,2894,0,0,0}-{2525,2894,0,0,0}-{2523,2895,0,0,0}-{2522,2894,0,0,0}-{2508,2890,0,0,0}-{2508,2891,0,0,0}-{2503,2905,0,0,0}-{2503,2907,0,0,0}-{2506,2902,0,0,0}-{2506,2904,0,0,0}-{2504,2894,0,0,0}-{2506,2894,0,0,0}-{2506,2892,0,0,0}-{2508,2904,0,0,0}-{2506,2891,0,0,0}-{2524,2901,0,0,0}-{2526,2901,0,0,0}-{2524,2894,0,0,0}-{2525,2894,0,0,0}-{2527,2894,0,0,0}-{2523,2896,0,0,0}-{2525,2896,0,0,0}-{2507,2903,0,0,0}-{2507,2905,0,0,0}-{2505,2902,0,0,0}-{2506,2904,0,0,0}-{2505,2907,0,0,0}-{2506,2905,0,0,0}-{2505,2905,0,0,0}-{2504,2906,0,0,0}-{2506,2894,0,0,0}-{2504,2894,0,0,0}-{2507,2891,0,0,0}-{2507,2887,0,0,0}-{2506,2891,0,0,0}-{2506,2907,0,0,0}-{2501,2938,0,0,0}-{2501,2940,0,0,0}-{2503,2938,0,0,0}-" + }, + { + "npc_id": "5079", + "loc_data": "{2323,3538,0,1,6}-{2319,3539,0,1,6}-{2321,3545,0,1,5}-{2324,3556,0,1,1}-{2358,3573,0,1,3}-{2360,3571,0,1,4}-{2342,3549,0,1,0}-{2357,3539,0,1,1}-{2357,3537,0,1,4}-{2357,3549,0,1,6}-{2322,3594,0,1,5}-{2323,3594,0,1,1}-{2323,3602,0,1,1}-{2326,3602,0,1,4}-{2324,3622,0,1,1}-{2326,3630,0,1,6}-{2347,3617,0,1,7}-{2341,3615,0,1,3}-{2339,3596,0,1,3}-{2329,3584,0,1,6}-{2337,3589,0,1,5}-{2371,3560,0,1,3}-" + }, + { + "npc_id": "5080", + "loc_data": "{2562,2927,0,1,4}-{2466,2878,0,1,6}-{2464,2832,0,1,2}-{2490,2877,0,1,1}-{2467,2881,0,1,7}-{2557,2915,0,1,3}-{2557,2921,0,1,1}-{2556,2938,0,1,7}-{2551,2930,0,1,6}-{2558,2912,0,1,4}-{2558,2909,0,1,3}-{2557,2932,0,1,6}-{2556,2935,0,1,1}-{2555,2915,0,1,2}-{2553,2935,0,1,4}-{2559,2918,0,1,4}-{2508,2909,0,1,3}-{2501,2912,0,1,6}-{2507,2907,0,1,1}-{2505,2902,0,1,2}-{2504,2896,0,1,2}-{2522,2895,0,1,3}-{2526,2897,0,1,1}-{2527,2904,0,1,0}-{2507,2889,0,1,4}-{2553,2935,0,1,1}-{2556,2931,0,1,5}-{2555,2915,0,1,6}-{2559,2911,0,1,4}-{2559,2918,0,1,3}-{2527,2896,0,1,4}-{2533,2896,0,1,5}-{2508,2890,0,1,4}-{2522,2895,0,1,2}-{2496,2897,0,1,1}-{2506,2906,0,1,0}-{2506,2894,0,1,3}-{2506,2902,0,1,6}-{2499,2942,0,1,7}-{2496,2936,0,1,4}-{2498,2939,0,1,3}-{2545,2948,0,1,2}-" + }, + { + "npc_id": "5081", + "loc_data": "{2318,3501,0,1,5}-{2316,3502,0,1,6}-{2317,3512,0,1,6}-{2315,3512,0,1,5}-{2312,3504,0,1,1}-{2307,3513,0,1,6}-{2311,3517,0,1,0}-{2320,3517,0,1,1}-{2309,3520,0,1,6}-" + }, + { + "npc_id": "5082", + "loc_data": "{2561,2913,0,1,1}-{2561,2918,0,1,7}-{2560,2915,0,1,1}-{2566,2881,0,1,7}-{2568,2917,0,1,6}-{2568,2918,0,1,4}-{2568,2885,0,1,4}-{2487,2878,0,1,4}-{2554,2894,0,1,4}-{2538,2897,0,1,6}-{2545,2899,0,1,3}-{2559,2908,0,1,6}-{2557,2920,0,1,4}-{2553,2913,0,1,4}-{2544,2910,0,1,2}-{2547,2909,0,1,4}-{2537,2906,0,1,5}-{2525,2914,0,1,3}-{2555,2920,0,1,6}-{2557,2908,0,1,6}-{2539,2920,0,1,1}-{2528,2915,0,1,4}-{2527,2904,0,1,0}-{2518,2901,0,1,5}-{2528,2902,0,1,1}-{2529,2918,0,1,6}-{2557,2915,0,1,7}-{2546,2912,0,1,3}-{2542,2911,0,1,5}-{2548,2910,0,1,6}-{2554,2907,0,1,1}-{2548,2896,0,1,5}-{2545,2895,0,1,6}-{2544,2910,0,1,7}-{2536,2895,0,1,3}-{2538,2902,0,1,6}-{2523,2910,0,1,3}-{2533,2910,0,1,2}-" + }, + { + "npc_id": "5083", + "loc_data": "{2621,3835,1,1,4}-{2708,3772,0,1,0}-{2714,3765,0,1,0}-{2699,3780,0,1,4}-{2694,3785,0,1,4}-{2717,3776,0,1,0}-{2718,3784,0,1,0}-{2723,3786,0,1,0}-{2725,3833,1,1,0}-{2728,3776,0,1,0}-" + }, + { + "npc_id": "5084", + "loc_data": "{2724,3771,0,1,0}-{2738,3769,0,1,0}-{2703,3829,1,1,0}-{2708,3824,1,1,0}-{2714,3826,1,1,0}-{2722,3833,1,1,0}-{2737,3776,0,1,0}-" + }, + { + "npc_id": "5085", + "loc_data": "{2328,3526,0,0,0}-{2313,3528,0,0,0}-{2312,3542,0,0,0}-{2312,3544,0,0,0}-{2323,3543,0,0,0}-{2325,3544,0,0,0}-{2328,3539,0,0,0}-{2311,3564,0,0,0}-{2332,3548,0,0,0}-{2335,3545,0,0,0}-{2320,3580,0,0,0}-{2325,3582,0,0,0}-{2330,3583,0,0,0}-{2355,3573,0,0,0}-{2359,3571,0,0,0}-{2352,3567,0,0,0}-{2361,3554,0,0,0}-{2329,3561,0,0,0}-{2341,3560,0,0,0}-{2344,3543,0,0,0}-{2342,3539,0,0,0}-{2349,3547,0,0,0}-{2352,3545,0,0,0}-{2334,3522,0,0,0}-{2310,3585,0,0,0}-{2324,3584,0,0,0}-{2312,3587,0,0,0}-{2316,3599,0,0,0}-{2324,3599,0,0,0}-{2323,3600,0,0,0}-{2315,3602,0,0,0}-{2309,3605,0,0,0}-{2316,3608,0,0,0}-{2315,3608,0,0,0}-{2325,3614,0,0,0}-{2313,3620,0,0,0}-{2333,3612,0,0,0}-{2331,3608,0,0,0}-{2331,3604,0,0,0}-{2346,3591,0,0,0}-{2369,3573,0,0,0}-{2372,3561,0,0,0}-" + }, + { + "npc_id": "5086", + "loc_data": "{2321,3593,0,1,1}-{2322,3597,0,1,0}-{2309,3603,0,1,5}-{2306,3607,0,1,0}-{2305,3619,0,1,1}-{2309,3618,0,1,6}-{2322,3627,0,1,3}-{2324,3614,0,1,1}-{2323,3614,0,1,1}-{2323,3629,0,1,6}-{2329,3633,0,1,4}-{2328,3634,0,1,3}-{2311,3642,0,1,1}-{2307,3644,0,1,6}-{2318,3645,0,1,1}-{2323,3645,0,1,3}-{2333,3629,0,1,3}-{2340,3641,0,1,7}-{2343,3640,0,1,6}-{2335,3633,0,1,6}-" + }, + { + "npc_id": "5087", + "loc_data": "{2712,3774,0,1,3}-{2715,3772,0,1,3}-{2720,3767,0,1,4}-{2712,3766,0,1,0}-{2718,3767,0,1,7}-{2719,3771,0,1,3}-{2706,3776,0,1,4}-{2716,3778,0,1,4}-" + }, + { + "npc_id": "5088", + "loc_data": "{2586,2883,0,1,6}-{2589,2909,0,1,1}-{2574,2915,0,1,3}-{2572,2928,0,1,3}-{2567,2932,0,1,6}-{2568,2933,0,1,0}-{2585,2884,0,1,5}-{2577,2890,0,1,0}-{2578,2918,0,1,1}-{2568,2934,0,1,1}-{2574,2930,0,1,1}-{2576,2928,0,1,4}-{2580,2889,0,1,1}-{2580,2886,0,1,1}-{2580,2886,0,1,0}-{2572,2900,0,1,4}-{2572,2896,0,1,3}-{2568,2905,0,1,4}-{2536,2872,0,1,4}-{2536,2872,0,1,6}-{2536,2872,0,1,4}-{2536,2872,0,1,6}-{2536,2872,0,1,3}-{2536,2864,0,1,7}-{2512,2864,0,1,6}-{2512,2864,0,1,1}-{2538,2889,0,1,3}-{2538,2902,0,1,4}-" + }, + { + "npc_id": "5089", + "loc_data": "{2315,3523,0,1,1}-{2315,3530,0,1,6}-{2308,3536,0,1,4}-{2307,3538,0,1,1}-{2325,3551,0,1,3}-{2316,3559,0,1,1}-{2312,3559,0,1,1}-{2310,3572,0,1,3}-{2334,3552,0,1,4}-{2308,3582,0,1,2}-{2363,3579,0,1,1}-{2363,3577,0,1,1}-{2350,3568,0,1,1}-{2352,3561,0,1,1}-{2346,3547,0,1,7}-{2347,3546,0,1,3}-{2363,3541,0,1,1}-{2362,3536,0,1,2}-{2350,3526,0,1,7}-{2350,3521,0,1,4}-{2351,3562,0,1,0}-{2310,3574,0,1,6}-" + }, + { + "npc_id": "5093", + "loc_data": "{2375,3607,0,0,0}-" + }, + { + "npc_id": "5098", + "loc_data": "{2376,3582,0,1,6}-{2378,3582,0,1,1}-{2383,3582,0,1,5}-{2372,3592,0,1,5}-{2370,3587,0,1,3}-{2371,3586,0,1,6}-{2393,3590,0,1,6}-" + }, + { + "npc_id": "5099", + "loc_data": "{2366,3577,0,1,4}-{2365,3597,0,1,5}-{2372,3573,0,1,1}-{2369,3588,0,1,4}-{2377,3584,0,1,1}-{2385,3590,0,1,3}-{2388,3598,0,1,3}-" + }, + { + "npc_id": "5100", + "loc_data": "{2369,3579,0,1,5}-{2373,3575,0,1,1}-{2390,3588,0,1,4}-" + }, + { + "npc_id": "5103", + "loc_data": "{2733,3777,0,1,6}-{2731,3784,0,1,4}-{2735,3790,0,1,6}-{2741,3783,0,1,2}-{2738,3790,0,1,1}-{2731,3783,0,1,1}-{2724,3791,0,1,4}-" + }, + { + "npc_id": "5104", + "loc_data": "{2564,2896,0,0,4}-{2571,2882,0,1,1}-{2573,2900,0,1,3}-{2551,2890,0,0,1}-{2541,2899,0,0,0}-{2552,2910,0,0,0}-{2558,2881,0,0,0}-{2543,2899,0,0,0}-{2545,2925,0,0,0}-{2549,2905,0,1,3}-" + }, + { + "npc_id": "5105", + "loc_data": "{2785,3006,0,1,0}-{2775,2996,0,1,0}-{2780,3017,0,1,0}-{2766,3013,0,1,0}-" + }, + { + "npc_id": "5109", + "loc_data": "{3443,2902,0,1,3}-" + }, + { + "npc_id": "5110", + "loc_data": "{2566,3084,0,1,6}-" + }, + { + "npc_id": "5111", + "loc_data": "{2569,3083,0,1,1}-" + }, + { + "npc_id": "5112", + "loc_data": "{2712,3833,1,1,1}-" + }, + { + "npc_id": "5113", + "loc_data": "{2525,2916,0,1,5}-" + }, + { + "npc_id": "5114", + "loc_data": "{3411,3076,0,1,6}-{3415,3075,0,1,1}-{3406,3093,0,1,3}-{3413,3078,0,1,1}-{3403,3093,0,1,1}-{3412,3081,0,1,3}-{3405,3099,0,1,3}-{3405,3102,0,1,0}-{3406,3098,0,1,4}-{3401,3099,0,1,7}-{3405,3093,0,1,4}-{3404,3085,0,1,5}-{3402,3094,0,1,6}-{3399,3096,0,1,1}-{3405,3088,0,1,3}-{3411,3088,0,1,2}-" + }, + { + "npc_id": "5115", + "loc_data": "{2451,3219,0,1,3}-{2448,3221,0,1,3}-{2476,3236,0,1,3}-{2450,3226,0,1,6}-{2474,3239,0,1,0}-{2470,3240,0,1,2}-{2468,3243,0,1,0}-{2455,3219,0,1,6}-{2449,3225,0,1,7}-{2445,3226,0,1,2}-{2467,3241,0,1,1}-{2452,3221,0,1,3}-{2473,3235,0,1,7}-{2472,3241,0,1,4}-{2475,3237,0,1,3}-" + }, + { + "npc_id": "5116", + "loc_data": "{3291,3675,0,1,6}-{3293,3678,0,1,1}-{3302,3671,0,1,0}-{3299,3666,0,1,5}-{3319,3662,0,1,2}-{3309,3661,0,1,5}-{3321,3661,0,1,1}-{3298,3672,0,1,0}-{3316,3657,0,1,4}-{3302,3667,0,1,1}-{3302,3666,0,1,3}-{3315,3663,0,1,1}-" + }, + { + "npc_id": "5117", + "loc_data": "{3537,3449,0,1,2}-{3535,3447,0,1,6}-{3539,3447,0,1,2}-{3533,3446,0,1,1}-{3549,3439,0,1,4}-{3547,3439,0,1,7}-{3549,3439,0,1,4}-" + }, + { + "npc_id": "5120", + "loc_data": "{2326,3488,0,1,2}-{2325,3483,0,1,2}-{2315,3482,0,1,2}-{2336,3491,0,1,2}-" + }, + { + "npc_id": "5125", + "loc_data": "{2316,3501,0,0,0}-" + }, + { + "npc_id": "5138", + "loc_data": "{2607,3264,0,0,0}-" + }, + { + "npc_id": "5139", + "loc_data": "{2607,3260,0,0,0}-" + }, + { + "npc_id": "5146", + "loc_data": "{2925,3324,0,1,0}-{3205,3263,0,0,0}-" + }, + { + "npc_id": "5147", + "loc_data": "{3207,3270,0,1,0}-" + }, + { + "npc_id": "5148", + "loc_data": "{3229,3347,0,1,1}-{3238,3347,0,1,1}-" + }, + { + "npc_id": "5149", + "loc_data": "{3233,3350,0,1,0}-" + }, + { + "npc_id": "5156", + "loc_data": "{3195,3265,0,1,0}-{3199,3267,0,1,0}-{3203,3262,0,1,5}-" + }, + { + "npc_id": "5157", + "loc_data": "{3194,3265,0,1,0}-{3205,3262,0,1,1}-{3201,3274,0,1,0}-" + }, + { + "npc_id": "5160", + "loc_data": "{3195,3272,0,1,0}-{3195,3268,0,1,0}-{3205,3270,0,1,4}-{3249,3345,0,1,5}-" + }, + { + "npc_id": "5161", + "loc_data": "{2926,3326,0,1,6}-{2924,3324,0,1,1}-{3210,3273,0,1,6}-{3233,3340,0,1,5}-{3241,3344,0,1,3}-" + }, + { + "npc_id": "5164", + "loc_data": "{2410,4456,0,1,1}-{2415,4429,0,1,2}-" + }, + { + "npc_id": "5166", + "loc_data": "{2811,3687,0,1,0}-" + }, + { + "npc_id": "5167", + "loc_data": "{2810,3684,0,1,0}-" + }, + { + "npc_id": "5168", + "loc_data": "{3199,3270,0,1,1}-{2658,4713,0,1,0}-{3209,3259,0,1,3}-{3260,3349,0,1,0}-" + }, + { + "npc_id": "5169", + "loc_data": "{3196,3275,0,1,0}-{2657,4705,0,1,0}-{3234,3345,0,1,0}-" + }, + { + "npc_id": "5170", + "loc_data": "{3052,3516,0,1,0}-" + }, + { + "npc_id": "5171", + "loc_data": "{2860,10054,1,0,0}-" + }, + { + "npc_id": "5173", + "loc_data": "{2769,3611,0,1,7}-{2768,3607,0,1,6}-{2764,3612,0,1,2}-{2763,3608,0,1,6}-{2762,3611,0,1,4}-{2765,3606,0,1,0}-" + }, + { + "npc_id": "5199", + "loc_data": "{3093,3357,0,0,0}-" + }, + { + "npc_id": "5200", + "loc_data": "{3099,3369,0,1,1}-" + }, + { + "npc_id": "5202", + "loc_data": "{3621,3528,0,1,4}-" + }, + { + "npc_id": "5249", + "loc_data": "{3321,3431,0,0,1}-" + }, + { + "npc_id": "5258", + "loc_data": "{2666,2651,0,0,1}-{2668,2651,0,0,1}-" + }, + { + "npc_id": "5260", + "loc_data": "{2665,2651,0,0,1}-{2667,2651,0,0,1}-" + }, + { + "npc_id": "5277", + "loc_data": "{3264,2784,0,0,4}-{3264,2785,0,0,4}-" + }, + { + "npc_id": "5291", + "loc_data": "{3300,2792,0,0,6}-{3282,2807,0,0,6}-{3285,2811,0,0,1}-{3283,2772,0,1,5}-{3281,2771,0,1,7}-{3283,2776,0,1,6}-{3278,2770,0,1,6}-" + }, + { + "npc_id": "5298", + "loc_data": "{2499,3286,0,1,6}-" + }, + { + "npc_id": "5300", + "loc_data": "{2500,3283,0,1,0}-" + }, + { + "npc_id": "5303", + "loc_data": "{2506,3284,0,1,6}-" + }, + { + "npc_id": "5310", + "loc_data": "{3212,3682,0,1,0}-{3228,3680,0,1,6}-{3230,3683,0,1,1}-{3234,3692,0,1,5}-" + }, + { + "npc_id": "5311", + "loc_data": "{3231,3673,0,1,0}-{3225,3677,0,1,3}-{3229,3689,0,1,4}-{3239,3681,0,1,4}-{3236,3694,0,1,6}-" + }, + { + "npc_id": "5312", + "loc_data": "{3210,3667,0,1,4}-{3217,3688,0,1,4}-{3241,3694,0,1,3}-" + }, + { + "npc_id": "5313", + "loc_data": "{3224,3686,0,1,5}-{3243,3688,0,1,1}-{3242,3687,0,1,0}-" + }, + { + "npc_id": "5314", + "loc_data": "{2588,9491,0,1,3}-{2585,9492,0,1,3}-{2586,9491,0,1,5}-{2591,9492,0,1,3}-" + }, + { + "npc_id": "5316", + "loc_data": "{2497,3292,0,1,3}-" + }, + { + "npc_id": "5318", + "loc_data": "{2511,3287,0,1,3}-" + }, + { + "npc_id": "5323", + "loc_data": "{3221,3693,0,1,6}-" + }, + { + "npc_id": "5324", + "loc_data": "{3221,3682,0,1,4}-" + }, + { + "npc_id": "5325", + "loc_data": "{3223,3675,0,1,6}-{3237,3692,0,1,7}-" + }, + { + "npc_id": "5326", + "loc_data": "{3232,3685,0,1,0}-" + }, + { + "npc_id": "5327", + "loc_data": "{3229,3678,0,1,6}-" + }, + { + "npc_id": "5332", + "loc_data": "{3102,3571,0,1,3}-{2855,9571,0,1,0}-{3368,9748,0,1,0}-{3378,9757,0,1,0}-{3383,9746,0,1,0}-{3368,9812,0,1,0}-{3378,9821,0,1,0}-{3383,9810,0,1,0}-{2887,9822,0,1,0}-{2884,9824,0,1,0}-{3008,3595,0,1,0}-{3018,3584,0,1,0}-{3022,3595,0,1,0}-" + }, + { + "npc_id": "5333", + "loc_data": "{3116,3524,0,1,4}-{3106,3548,0,1,2}-{2860,9571,0,1,7}-{3376,9751,0,1,0}-{3380,9756,0,1,0}-{3376,9815,0,1,0}-{3380,9820,0,1,0}-{2886,9816,0,1,0}-{3013,3586,0,1,0}-{3013,3602,0,1,0}-{3025,3597,0,1,0}-" + }, + { + "npc_id": "5334", + "loc_data": "{3107,3528,0,1,1}-{2859,9572,0,1,6}-{3377,9743,0,1,0}-{3377,9754,0,1,0}-{3377,9807,0,1,0}-{3377,9818,0,1,0}-{2885,9819,0,1,0}-{3012,3597,0,1,0}-{3018,3592,0,1,0}-{3018,3599,0,1,0}-{3025,3590,0,1,0}-" + }, + { + "npc_id": "5337", + "loc_data": "{2846,3250,0,1,7}-{2834,3233,0,1,5}-{3100,3563,0,1,1}-{2832,9656,0,1,6}-{3179,3786,0,1,0}-{2885,9825,0,1,0}-{3003,10347,0,1,7}-{3003,10350,0,1,6}-{3003,10357,0,1,4}-{2996,10343,0,1,3}-{2993,10346,0,1,4}-" + }, + { + "npc_id": "5338", + "loc_data": "{2843,3248,0,1,1}-{2840,3240,0,1,3}-{3119,3565,0,1,1}-{2838,9648,0,1,5}-{2831,9654,0,1,1}-{2989,3944,0,1,4}-{2994,10349,0,1,5}-{2998,10350,0,1,0}-{3003,10350,0,1,4}-" + }, + { + "npc_id": "5339", + "loc_data": "{2844,3239,0,1,0}-{3095,3554,0,1,4}-{2831,9655,0,1,7}-" + }, + { + "npc_id": "5340", + "loc_data": "{2848,3246,0,1,3}-{3112,3573,0,1,6}-{2833,9655,0,1,6}-{2982,3947,0,1,0}-" + }, + { + "npc_id": "5341", + "loc_data": "{2840,9633,0,1,1}-{2844,9634,0,1,6}-{2842,9637,0,1,3}-{2641,9889,0,1,0}-{2648,9890,0,1,0}-{2657,9894,0,1,0}-{2658,9888,0,1,0}-" + }, + { + "npc_id": "5342", + "loc_data": "{3276,3659,0,1,0}-" + }, + { + "npc_id": "5343", + "loc_data": "{3282,3662,0,1,0}-" + }, + { + "npc_id": "5345", + "loc_data": "{2501,3294,0,1,3}-" + }, + { + "npc_id": "5346", + "loc_data": "{3279,3654,0,1,0}-" + }, + { + "npc_id": "5347", + "loc_data": "{3288,3650,0,1,0}-" + }, + { + "npc_id": "5348", + "loc_data": "{3290,3653,0,1,0}-" + }, + { + "npc_id": "5349", + "loc_data": "{3099,3354,1,1,3}-" + }, + { + "npc_id": "5350", + "loc_data": "{3116,3367,1,1,3}-" + }, + { + "npc_id": "5351", + "loc_data": "{3110,3358,1,1,1}-" + }, + { + "npc_id": "5352", + "loc_data": "{3121,3359,1,1,4}-" + }, + { + "npc_id": "5355", + "loc_data": "{2873,2950,0,0,0}-{2876,2951,0,0,0}-" + }, + { + "npc_id": "5356", + "loc_data": "{2877,2958,0,0,0}-" + }, + { + "npc_id": "5357", + "loc_data": "{2876,2958,0,0,0}-" + }, + { + "npc_id": "5358", + "loc_data": "{2874,2954,0,0,6}-" + }, + { + "npc_id": "5359", + "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", + "loc_data": "{3176,5543,0,1,3}-{3191,5542,0,1,4}-{3175,5543,0,1,2}-{3178,5541,0,1,2}-{3187,5542,0,1,3}-{3192,5545,0,1,2}-{3192,5542,0,1,3}-{3179,5549,0,1,6}-{1757,5358,0,1,5}-{1760,5357,0,1,5}-{1755,5352,0,1,5}-{1751,5342,0,1,3}-{1745,5342,0,1,6}-{1739,5342,0,1,3}-{1737,5344,0,1,7}-{1739,5347,0,1,6}-{1740,5350,0,1,5}-{1739,5355,0,1,3}-{1747,5363,0,1,4}-{1749,5363,0,1,4}-{1743,5360,0,1,3}-{1739,5356,0,1,0}-" + }, + { + "npc_id": "5362", + "loc_data": "{1774,5361,0,1,3}-{1776,5350,0,1,3}-{1772,5342,0,1,1}-{1772,5336,0,1,4}-{1778,5328,0,1,7}-{1761,5334,0,1,4}-{1754,5337,0,1,7}-{1749,5331,0,0,1}-" + }, + { + "npc_id": "5363", + "loc_data": "{1781,5340,1,1,5}-{1782,5356,1,1,1}-{1767,5343,1,1,6}-{1761,5338,1,1,3}-{1767,5332,1,1,6}-{1757,5326,1,1,7}-{1784,5329,1,1,1}-" + }, + { + "npc_id": "5364", + "loc_data": "{1765,5344,0,1,3}-" + }, + { + "npc_id": "5365", + "loc_data": "{2354,5191,0,1,4}-{2351,5191,0,1,5}-" + }, + { + "npc_id": "5366", + "loc_data": "{2357,5196,0,1,2}-{2356,5188,0,1,4}-{2343,5195,0,1,6}-{2335,5202,0,1,3}-{2323,5211,0,1,1}-" + }, + { + "npc_id": "5367", + "loc_data": "{3092,3717,0,1,0}-{3105,3721,0,1,0}-{3113,3740,0,1,0}-{2312,5189,0,1,7}-" + }, + { + "npc_id": "5368", + "loc_data": "{3096,3738,0,1,0}-{3102,3716,0,1,0}-{3118,3733,0,1,0}-{3128,3741,0,1,0}-" + }, + { + "npc_id": "5369", + "loc_data": "{2345,5195,0,1,3}-{2323,5227,0,1,5}-{2319,5223,0,1,6}-{2264,5136,0,1,1}-" + }, + { + "npc_id": "5370", + "loc_data": "{2347,5195,0,1,3}-{2323,5225,0,1,3}-{2319,5227,0,1,0}-{2323,5227,0,1,3}-{2318,5227,0,1,0}-{2326,5227,0,1,6}-" + }, + { + "npc_id": "5371", + "loc_data": "{2356,5204,0,1,1}-{2324,5223,0,1,1}-" + }, + { + "npc_id": "5375", + "loc_data": "{2029,5236,0,1,6}-{2031,5235,0,1,1}-{2029,5230,0,1,3}-" + }, + { + "npc_id": "5376", + "loc_data": "{2031,5233,0,1,1}-" + }, + { + "npc_id": "5377", + "loc_data": "{2004,5189,0,1,3}-{2002,5207,0,1,3}-{2009,5199,0,1,5}-{2004,5203,0,1,6}-{1989,5192,0,1,6}-{1988,5187,0,1,6}-" + }, + { + "npc_id": "5378", + "loc_data": "{1993,5192,0,1,5}-{1999,5190,0,1,6}-{1989,5187,0,1,4}-{2008,5205,0,1,4}-" + }, + { + "npc_id": "5379", + "loc_data": "{1952,5192,0,1,5}-{1995,5240,0,1,5}-{2010,5186,0,1,4}-{2023,5190,0,1,1}-" + }, + { + "npc_id": "5380", + "loc_data": "{1952,5192,0,1,2}-{2028,5192,0,1,0}-{2031,5186,0,1,0}-{2027,5194,0,1,0}-{2021,5188,0,1,0}-" + }, + { + "npc_id": "5383", + "loc_data": "{3194,4569,0,0,0}-" + }, + { + "npc_id": "5384", + "loc_data": "{3165,4598,0,0,0}-{3167,4577,0,0,0}-" + }, + { + "npc_id": "5385", + "loc_data": "{3191,4595,1,1,1}-{3191,4601,1,1,2}-" + }, + { + "npc_id": "5386", + "loc_data": "{3143,4595,0,1,1}-{3145,4598,0,1,3}-{3182,4577,0,1,1}-{3187,4578,0,1,0}-{3158,4589,1,1,7}-{3194,4599,1,1,4}-" + }, + { + "npc_id": "5387", + "loc_data": "{3158,4584,0,1,4}-{3159,4577,0,1,6}-{3169,4587,0,1,1}-{3152,4569,1,1,1}-" + }, + { + "npc_id": "5388", + "loc_data": "{3156,4567,2,1,0}-" + }, + { + "npc_id": "5389", + "loc_data": "{3157,4568,0,1,6}-{3185,4562,0,1,3}-" + }, + { + "npc_id": "5390", + "loc_data": "{3182,4562,0,1,3}-{3185,4560,0,1,3}-" + }, + { + "npc_id": "5391", + "loc_data": "{3158,4552,0,1,4}-{3163,4569,0,1,1}-{3177,4549,0,1,3}-{3184,4550,0,1,3}-{3158,4560,1,1,4}-{3174,4566,1,1,1}-{3195,4577,1,1,6}-" + }, + { + "npc_id": "5393", + "loc_data": "{3146,4596,0,1,1}-{3163,4598,0,1,3}-{3155,4591,1,1,3}-" + }, + { + "npc_id": "5394", + "loc_data": "{3167,4589,0,1,6}-{3172,4598,0,1,1}-{2677,9688,0,1,1}-{2676,9679,0,1,4}-{2669,9681,0,1,1}-" + }, + { + "npc_id": "5395", + "loc_data": "{3141,4598,0,1,3}-{3170,4592,0,1,6}-{3144,4589,1,1,3}-" + }, + { + "npc_id": "5396", + "loc_data": "{3145,4583,1,1,0}-" + }, + { + "npc_id": "5397", + "loc_data": "{3155,4583,0,1,3}-{3176,4576,1,1,3}-" + }, + { + "npc_id": "5398", + "loc_data": "{3145,4574,1,1,3}-{3182,4586,1,1,3}-" + }, + { + "npc_id": "5399", + "loc_data": "{3140,4557,2,1,4}-{3188,4578,2,1,1}-" + }, + { + "npc_id": "5400", + "loc_data": "{3175,4579,1,1,6}-{3142,4560,2,1,3}-{3159,4569,2,1,1}-" + }, + { + "npc_id": "5401", + "loc_data": "{3151,4573,1,1,4}-" + }, + { + "npc_id": "5402", + "loc_data": "{3154,4559,0,1,6}-{3171,4571,1,1,6}-{3179,4586,1,1,1}-" + }, + { + "npc_id": "5403", + "loc_data": "{3141,4557,0,1,4}-{3182,4577,2,1,6}-" + }, + { + "npc_id": "5404", + "loc_data": "{3143,4555,0,1,4}-{3144,4549,1,1,4}-{3190,4587,1,1,1}-{3186,4576,2,1,3}-" + }, + { + "npc_id": "5405", + "loc_data": "{3163,4559,0,1,4}-{3146,4565,1,1,3}-{3195,4582,1,1,6}-" + }, + { + "npc_id": "5406", + "loc_data": "{3159,4603,1,1,1}-" + }, + { + "npc_id": "5407", + "loc_data": "{3141,4547,1,1,3}-" + }, + { + "npc_id": "5408", + "loc_data": "{3170,4559,1,1,4}-" + }, + { + "npc_id": "5409", + "loc_data": "{3173,4550,0,1,6}-{3190,4551,0,1,2}-" + }, + { + "npc_id": "5410", + "loc_data": "{3190,4561,0,1,5}-" + }, + { + "npc_id": "5413", + "loc_data": "{3187,5450,0,1,6}-{3177,5453,0,1,6}-" + }, + { + "npc_id": "5414", + "loc_data": "{3176,5454,0,1,3}-{3182,5447,0,1,3}-" + }, + { + "npc_id": "5423", + "loc_data": "{3212,3263,0,0,0}-" + }, + { + "npc_id": "5424", + "loc_data": "{2594,3269,0,0,0}-" + }, + { + "npc_id": "5439", + "loc_data": "{3017,3277,0,0,0}-" + }, + { + "npc_id": "5445", + "loc_data": "{2596,3269,0,1,4}-{2571,3907,0,1,7}-{2571,3907,0,1,3}-{2569,3908,0,1,3}-{2571,3908,0,1,3}-{2572,3906,0,1,6}-" + }, + { + "npc_id": "5463", + "loc_data": "{2324,3809,0,1,0}-{2317,3802,0,1,0}-" + }, + { + "npc_id": "5473", + "loc_data": "{2377,10212,0,1,0}-{2392,10226,0,1,0}-{2407,10228,0,1,0}-{2406,10214,0,1,0}-{2420,10214,0,1,0}-" + }, + { + "npc_id": "5474", + "loc_data": "{2390,10216,0,1,0}-{2417,10228,0,1,0}-{2385,10227,0,1,0}-" + }, + { + "npc_id": "5475", + "loc_data": "{2396,10232,0,1,0}-{2402,10225,0,1,0}-{2420,10221,0,1,0}-" + }, + { + "npc_id": "5478", + "loc_data": "{2407,3803,0,1,3}-" + }, + { + "npc_id": "5479", + "loc_data": "{2407,3803,0,1,3}-" + }, + { + "npc_id": "5480", + "loc_data": "{2406,3803,0,1,3}-" + }, + { + "npc_id": "5481", + "loc_data": "{2644,3709,0,1,0}-" + }, + { + "npc_id": "5482", + "loc_data": "{2422,3781,0,1,0}-" + }, + { + "npc_id": "5483", + "loc_data": "{2395,3796,0,1,2}-" + }, + { + "npc_id": "5484", + "loc_data": "{2418,3816,0,1,3}-" + }, + { + "npc_id": "5485", + "loc_data": "{2393,3796,0,1,2}-" + }, + { + "npc_id": "5486", + "loc_data": "{2396,3805,0,1,3}-" + }, + { + "npc_id": "5487", + "loc_data": "{2417,3815,0,1,3}-" + }, + { + "npc_id": "5488", + "loc_data": "{2416,3799,0,0,1}-" + }, + { + "npc_id": "5489", + "loc_data": "{2372,3801,2,0,3}-" + }, + { + "npc_id": "5490", + "loc_data": "{2364,3800,2,0,4}-" + }, + { + "npc_id": "5491", + "loc_data": "{2387,3800,0,0,3}-{2411,3795,0,0,6}-{2417,3825,0,0,1}-" + }, + { + "npc_id": "5492", + "loc_data": "{2387,3797,0,0,3}-{2414,3825,0,0,1}-{2414,3795,0,0,6}-" + }, + { + "npc_id": "5493", + "loc_data": "{2391,3800,0,1,5}-" + }, + { + "npc_id": "5494", + "loc_data": "{2416,3808,0,1,5}-" + }, + { + "npc_id": "5495", + "loc_data": "{2406,3813,0,1,3}-" + }, + { + "npc_id": "5496", + "loc_data": "{2402,3800,0,1,4}-" + }, + { + "npc_id": "5497", + "loc_data": "{2376,10198,0,1,0}-{2384,10202,0,1,0}-{2390,10207,0,1,0}-{2385,10193,0,1,0}-{2419,10192,0,1,0}-{2412,10200,0,1,0}-" + }, + { + "npc_id": "5498", + "loc_data": "{2412,3818,0,1,0}-{2412,3801,0,1,0}-{2392,3802,0,1,0}-" + }, + { + "npc_id": "5499", + "loc_data": "{2403,3807,0,1,4}-" + }, + { + "npc_id": "5500", + "loc_data": "{2395,3812,0,0,2}-" + }, + { + "npc_id": "5501", + "loc_data": "{2408,3816,0,1,3}-" + }, + { + "npc_id": "5502", + "loc_data": "{2409,3814,0,1,3}-" + }, + { + "npc_id": "5504", + "loc_data": "{2336,3799,0,0,4}-" + }, + { + "npc_id": "5505", + "loc_data": "{2334,3800,0,0,4}-" + }, + { + "npc_id": "5506", + "loc_data": "{2334,3798,0,0,4}-" + }, + { + "npc_id": "5507", + "loc_data": "{2311,3781,0,0,1}-" + }, + { + "npc_id": "5508", + "loc_data": "{2646,3710,0,0,4}-" + }, + { + "npc_id": "5509", + "loc_data": "{2336,3808,0,0,6}-" + }, + { + "npc_id": "5510", + "loc_data": "{2333,3804,0,1,1}-" + }, + { + "npc_id": "5511", + "loc_data": "{2327,3794,0,1,6}-" + }, + { + "npc_id": "5512", + "loc_data": "{2319,3795,0,1,3}-" + }, + { + "npc_id": "5513", + "loc_data": "{2352,3797,0,1,2}-" + }, + { + "npc_id": "5514", + "loc_data": "{2333,3795,1,1,4}-{2337,3831,0,1,0}-{2321,3833,0,1,0}-{2399,3852,0,1,0}-" + }, + { + "npc_id": "5515", + "loc_data": "{2330,3799,1,1,3}-{2327,3858,0,1,0}-{2321,3860,0,1,0}-{2343,3862,0,1,0}-" + }, + { + "npc_id": "5516", + "loc_data": "{2329,3808,1,1,4}-{2347,3890,0,1,0}-{2347,3890,0,1,0}-{2393,3861,0,1,0}-" + }, + { + "npc_id": "5517", + "loc_data": "{2328,3830,0,1,0}-{2335,3894,0,1,0}-{2403,3853,0,1,0}-{2405,3860,0,1,0}-" + }, + { + "npc_id": "5518", + "loc_data": "{2338,3798,0,0,1}-" + }, + { + "npc_id": "5519", + "loc_data": "{2338,3800,0,0,6}-" + }, + { + "npc_id": "5520", + "loc_data": "{2338,3810,0,0,3}-" + }, + { + "npc_id": "5521", + "loc_data": "{2324,3832,0,1,0}-{2350,3859,0,1,0}-{2350,3862,0,1,0}-{2352,3857,0,1,0}-{2375,3892,0,1,0}-{2388,3867,0,1,0}-{2384,3863,0,1,0}-{2387,3859,0,1,0}-" + }, + { + "npc_id": "5522", + "loc_data": "{2362,3893,0,1,0}-{2323,3857,0,1,0}-{2331,3860,0,1,0}-{2390,3861,0,1,0}-{2395,3859,0,1,0}-" + }, + { + "npc_id": "5523", + "loc_data": "{2367,3832,0,1,0}-{2319,3854,0,1,0}-{2324,3891,0,1,0}-{2406,3854,0,1,0}-{2405,3863,0,1,0}-{2400,3856,0,1,0}-" + }, + { + "npc_id": "5524", + "loc_data": "{2340,3830,0,1,0}-{2338,3860,0,1,0}-{2339,3895,0,1,0}-{2411,3886,0,1,0}-{2396,3854,0,1,0}-" + }, + { + "npc_id": "5529", + "loc_data": "{2323,3790,0,1,1}-{2323,3795,0,1,0}-{2322,3795,0,1,0}-{2317,3794,0,1,6}-{2317,3790,0,1,3}-{2321,3792,0,1,6}-" + }, + { + "npc_id": "5531", + "loc_data": "{3323,3138,1,1,6}-" + }, + { + "npc_id": "5532", + "loc_data": "{3320,3138,0,1,3}-" + }, + { + "npc_id": "5533", + "loc_data": "{2904,5460,0,1,4}-" + }, + { + "npc_id": "5534", + "loc_data": "{2900,5455,0,1,1}-" + }, + { + "npc_id": "5535", + "loc_data": "{2903,5449,0,1,4}-" + }, + { + "npc_id": "5536", + "loc_data": "{2905,5451,0,1,1}-" + }, + { + "npc_id": "5537", + "loc_data": "{2905,5457,0,1,4}-" + }, + { + "npc_id": "5538", + "loc_data": "{2912,5455,0,1,3}-" + }, + { + "npc_id": "5539", + "loc_data": "{2922,5464,0,1,1}-" + }, + { + "npc_id": "5540", + "loc_data": "{2924,5462,0,1,4}-" + }, + { + "npc_id": "5541", + "loc_data": "{2924,5458,0,1,4}-" + }, + { + "npc_id": "5542", + "loc_data": "{2930,5458,0,1,1}-" + }, + { + "npc_id": "5544", + "loc_data": "{2931,5469,0,1,3}-" + }, + { + "npc_id": "5545", + "loc_data": "{2925,5471,0,1,1}-" + }, + { + "npc_id": "5546", + "loc_data": "{2931,5475,0,1,1}-" + }, + { + "npc_id": "5547", + "loc_data": "{2907,5486,0,1,6}-" + }, + { + "npc_id": "5548", + "loc_data": "{2907,5492,0,1,6}-" + }, + { + "npc_id": "5549", + "loc_data": "{2910,5491,0,1,6}-" + }, + { + "npc_id": "5550", + "loc_data": "{2915,5483,0,1,1}-" + }, + { + "npc_id": "5551", + "loc_data": "{2922,5486,0,1,4}-" + }, + { + "npc_id": "5552", + "loc_data": "{2921,5493,0,1,4}-" + }, + { + "npc_id": "5553", + "loc_data": "{2899,5468,0,1,4}-" + }, + { + "npc_id": "5554", + "loc_data": "{2894,5470,0,1,3}-" + }, + { + "npc_id": "5555", + "loc_data": "{2898,5478,0,1,4}-" + }, + { + "npc_id": "5556", + "loc_data": "{2896,5482,0,1,3}-" + }, + { + "npc_id": "5557", + "loc_data": "{2894,5483,0,1,4}-" + }, + { + "npc_id": "5558", + "loc_data": "{2894,5485,0,1,3}-" + }, + { + "npc_id": "5562", + "loc_data": "{2913,5462,0,0,0}-{2900,5458,0,0,0}-{2908,5455,0,0,0}-{2910,5455,0,0,0}-{2913,5452,0,0,0}-{2914,5453,0,0,0}-" + }, + { + "npc_id": "5563", + "loc_data": "{2911,5474,0,0,0}-" + }, + { + "npc_id": "5574", + "loc_data": "{2836,3066,0,0,0}-" + }, + { + "npc_id": "5576", + "loc_data": "{3072,3336,0,0,0}-{3063,3348,0,0,0}-" + }, + { + "npc_id": "5608", + "loc_data": "{3786,2827,2,0,0}-" + }, + { + "npc_id": "5609", + "loc_data": "{3786,2824,1,0,0}-" + }, + { + "npc_id": "5610", + "loc_data": "{3785,2827,0,0,0}-" + }, + { + "npc_id": "5611", + "loc_data": "{3784,2824,0,0,0}-" + }, + { + "npc_id": "5614", + "loc_data": "{3779,2826,0,0,0}-{3789,2817,0,0,0}-{3794,2821,0,0,0}-{3808,2823,0,0,0}-{3812,2861,0,0,0}-{3813,2822,0,0,0}-{3818,2862,0,0,0}-{3819,2870,0,0,0}-{3821,2824,0,0,0}-{3831,2828,0,0,0}-" + }, + { + "npc_id": "5627", + "loc_data": "{3808,2845,0,0,0}-{3812,2846,0,0,0}-{3815,2844,0,0,3}-{3819,2839,0,0,0}-{3819,2851,0,0,0}-{3819,2838,1,0,0}-{3823,2836,1,0,0}-{3823,2846,1,0,0}-" + }, + { + "npc_id": "5628", + "loc_data": "{3810,2843,0,0,0}-{3813,2844,0,0,0}-{3821,2837,0,0,0}-{3820,2840,1,0,0}-{3820,2851,1,0,0}-{3823,2839,1,0,0}-" + }, + { + "npc_id": "5629", + "loc_data": "{3811,2826,0,0,0}-" + }, + { + "npc_id": "5631", + "loc_data": "{3827,2833,0,0,0}-" + }, + { + "npc_id": "5633", + "loc_data": "{3815,2859,0,0,0}-" + }, + { + "npc_id": "5634", + "loc_data": "{3793,2851,0,0,0}-" + }, + { + "npc_id": "5635", + "loc_data": "{3784,2843,0,0,0}-" + }, + { + "npc_id": "5637", + "loc_data": "{3803,2843,0,0,0}-" + }, + { + "npc_id": "5638", + "loc_data": "{3810,2833,0,0,0}-" + }, + { + "npc_id": "5639", + "loc_data": "{3824,2857,0,0,0}-" + }, + { + "npc_id": "5640", + "loc_data": "{3817,2824,0,0,0}-" + }, + { + "npc_id": "5641", + "loc_data": "{3799,2855,0,0,0}-" + }, + { + "npc_id": "5642", + "loc_data": "{3796,2858,0,0,0}-" + }, + { + "npc_id": "5644", + "loc_data": "{3795,2845,0,0,3}-" + }, + { + "npc_id": "5646", + "loc_data": "{3792,2830,0,0,0}-" + }, + { + "npc_id": "5647", + "loc_data": "{3803,2872,2,0,0}-" + }, + { + "npc_id": "5650", + "loc_data": "{3800,2835,0,0,0}-" + }, + { + "npc_id": "5656", + "loc_data": "{3794,2873,1,0,0}-" + }, + { + "npc_id": "5658", + "loc_data": "{3804,2873,1,0,0}-" + }, + { + "npc_id": "5659", + "loc_data": "{3805,2874,2,0,0}-" + }, + { + "npc_id": "5661", + "loc_data": "{3794,2874,3,0,0}-" + }, + { + "npc_id": "5665", + "loc_data": "{3803,2848,0,0,0}-" + }, + { + "npc_id": "5668", + "loc_data": "{3787,2824,0,0,0}-" + }, + { + "npc_id": "5669", + "loc_data": "{3787,2827,0,0,0}-" + }, + { + "npc_id": "5670", + "loc_data": "{3786,2827,1,0,0}-" + }, + { + "npc_id": "5671", + "loc_data": "{3788,2826,1,0,0}-" + }, + { + "npc_id": "5672", + "loc_data": "{3788,2825,2,0,0}-" + }, + { + "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}-" + }, + { + "npc_id": "5750", + "loc_data": "{3290,5544,0,1,4}-{3270,5552,0,1,1}-{3274,5547,0,1,7}-" + }, + { + "npc_id": "5752", + "loc_data": "{2711,5331,0,0,0}-" + }, + { + "npc_id": "5753", + "loc_data": "{2697,5332,0,0,0}-" + }, + { + "npc_id": "5755", + "loc_data": "{2723,5336,0,0,0}-" + }, + { + "npc_id": "5756", + "loc_data": "{2702,5352,1,0,0}-" + }, + { + "npc_id": "5757", + "loc_data": "{2743,5358,0,0,0}-" + }, + { + "npc_id": "5758", + "loc_data": "{2693,5332,0,0,0}-{2729,5329,0,0,0}-" + }, + { + "npc_id": "5761", + "loc_data": "{2725,5349,0,0,0}-" + }, + { + "npc_id": "5764", + "loc_data": "{2712,5342,0,0,0}-" + }, + { + "npc_id": "5765", + "loc_data": "{2694,5333,0,0,0}-" + }, + { + "npc_id": "5766", + "loc_data": "{2701,5352,1,0,0}-" + }, + { + "npc_id": "5767", + "loc_data": "{2695,5331,0,0,0}-{2719,5331,0,0,0}-" + }, + { + "npc_id": "5770", + "loc_data": "{2717,5359,0,0,0}-" + }, + { + "npc_id": "5771", + "loc_data": "{2722,5333,0,0,0}-" + }, + { + "npc_id": "5772", + "loc_data": "{2716,5359,0,0,0}-" + }, + { + "npc_id": "5776", + "loc_data": "{2699,5349,0,0,4}-" + }, + { + "npc_id": "5777", + "loc_data": "{2699,5348,0,0,4}-" + }, + { + "npc_id": "5780", + "loc_data": "{2722,5313,0,0,0}-" + }, + { + "npc_id": "5781", + "loc_data": "{2723,5321,0,0,0}-" + }, + { + "npc_id": "5782", + "loc_data": "{2742,5339,1,0,0}-" + }, + { + "npc_id": "5783", + "loc_data": "{2701,5348,0,0,0}-" + }, + { + "npc_id": "5785", + "loc_data": "{2726,5372,1,0,0}-" + }, + { + "npc_id": "5786", + "loc_data": "{2712,5369,1,0,0}-" + }, + { + "npc_id": "5787", + "loc_data": "{2704,5366,0,0,0}-" + }, + { + "npc_id": "5789", + "loc_data": "{2718,5313,0,0,0}-" + }, + { + "npc_id": "5790", + "loc_data": "{2715,5313,0,0,0}-" + }, + { + "npc_id": "5794", + "loc_data": "{2712,5321,0,0,0}-" + }, + { + "npc_id": "5796", + "loc_data": "{2711,5314,0,0,0}-" + }, + { + "npc_id": "5798", + "loc_data": "{2717,5315,0,1,2}-" + }, + { + "npc_id": "5799", + "loc_data": "{2730,5365,1,0,0}-" + }, + { + "npc_id": "5800", + "loc_data": "{2719,5335,0,1,1}-{2747,5373,0,1,3}-" + }, + { + "npc_id": "5801", + "loc_data": "{2730,5333,0,1,2}-" + }, + { + "npc_id": "5802", + "loc_data": "{2744,5353,1,0,0}-" + }, + { + "npc_id": "5803", + "loc_data": "{2741,5344,1,0,0}-" + }, + { + "npc_id": "5804", + "loc_data": "{2744,5333,1,0,0}-" + }, + { + "npc_id": "5805", + "loc_data": "{2743,5345,1,0,0}-" + }, + { + "npc_id": "5806", + "loc_data": "{2746,5333,1,0,0}-" + }, + { + "npc_id": "5807", + "loc_data": "{2741,5334,1,0,0}-" + }, + { + "npc_id": "5809", + "loc_data": "{2741,5341,1,0,0}-" + }, + { + "npc_id": "5811", + "loc_data": "{2744,5342,1,0,0}-" + }, + { + "npc_id": "5813", + "loc_data": "{2742,5338,1,0,0}-" + }, + { + "npc_id": "5815", + "loc_data": "{2743,5341,1,0,0}-" + }, + { + "npc_id": "5817", + "loc_data": "{2744,5338,1,0,0}-" + }, + { + "npc_id": "5819", + "loc_data": "{2744,5336,1,0,0}-" + }, + { + "npc_id": "5821", + "loc_data": "{2740,5334,1,0,0}-" + }, + { + "npc_id": "5827", + "loc_data": "{2699,5329,0,0,0}-{2699,5335,0,0,0}-{2747,5342,0,0,0}-" + }, + { + "npc_id": "5828", + "loc_data": "{2697,5316,0,0,0}-{3323,9606,0,0,0}-" + }, + { + "npc_id": "5832", + "loc_data": "{2730,5366,1,0,0}-" + }, + { + "npc_id": "5833", + "loc_data": "{3267,3334,0,1,3}-" + }, + { + "npc_id": "5834", + "loc_data": "{3208,3496,0,1,6}-" + }, + { + "npc_id": "5837", + "loc_data": "{3285,3468,0,0,4}-" + }, + { + "npc_id": "5839", + "loc_data": "{3183,5205,0,0,0}-" + }, + { + "npc_id": "5840", + "loc_data": "{3159,5211,0,0,0}-" + }, + { + "npc_id": "5841", + "loc_data": "{3176,5215,0,0,0}-" + }, + { + "npc_id": "5842", + "loc_data": "{3115,3475,0,1,1}-" + }, + { + "npc_id": "5843", + "loc_data": "{3117,3474,0,1,4}-" + }, + { + "npc_id": "5844", + "loc_data": "{3113,3473,0,1,7}-" + }, + { + "npc_id": "5845", + "loc_data": "{3115,3475,0,1,2}-" + }, + { + "npc_id": "5846", + "loc_data": "{3110,3471,0,0,2}-" + }, + { + "npc_id": "5847", + "loc_data": "{3115,3476,0,1,6}-" + }, + { + "npc_id": "5848", + "loc_data": "{3116,3475,0,1,1}-" + }, + { + "npc_id": "5849", + "loc_data": "{3112,3473,0,1,3}-" + }, + { + "npc_id": "5850", + "loc_data": "{3115,3476,0,1,4}-" + }, + { + "npc_id": "5851", + "loc_data": "{3119,3474,0,1,5}-" + }, + { + "npc_id": "5869", + "loc_data": "{2744,5344,0,0,0}-" + }, + { + "npc_id": "5880", + "loc_data": "{2439,5544,0,1,0}-{2485,5535,0,1,0}-" + }, + { + "npc_id": "5881", + "loc_data": "{2436,5540,0,1,0}-" + }, + { + "npc_id": "5882", + "loc_data": "{2437,5549,0,1,0}-{2484,5531,0,1,0}-" + }, + { + "npc_id": "5883", + "loc_data": "{2436,5526,0,1,0}-{2486,5525,0,1,0}-" + }, + { + "npc_id": "5884", + "loc_data": "{2439,5520,0,1,0}-" + }, + { + "npc_id": "5885", + "loc_data": "{2487,5538,0,1,0}-" + }, + { + "npc_id": "5887", + "loc_data": "{2737,5351,1,0,0}-" + }, + { + "npc_id": "5909", + "loc_data": "{3224,3402,0,1,4}-" + }, + { + "npc_id": "5910", + "loc_data": "{3230,3401,0,0,0}-{3284,3491,0,1,1}-" + }, + { + "npc_id": "5914", + "loc_data": "{3204,3419,0,0,6}-" + }, + { + "npc_id": "5915", + "loc_data": "{3255,3488,1,0,6}-" + }, + { + "npc_id": "5916", + "loc_data": "{3250,3429,0,1,5}-" + }, + { + "npc_id": "5917", + "loc_data": "{3176,3431,0,1,5}-{3200,3399,0,1,4}-{3262,3410,0,1,3}-{3261,3424,0,1,2}-" + }, + { + "npc_id": "5918", + "loc_data": "{3233,3394,0,0,7}-{3271,3399,0,1,3}-" + }, + { + "npc_id": "5919", + "loc_data": "{3175,3432,0,1,3}-{3175,3427,0,1,5}-{3172,3430,0,1,2}-{3175,3416,1,1,6}-{3175,3404,1,1,7}-{3205,3379,0,1,6}-{3211,3381,0,1,3}-{3244,3498,0,1,1}-{3244,3503,0,1,6}-{3247,3500,0,1,3}-" + }, + { + "npc_id": "5920", + "loc_data": "{3175,3421,0,1,0}-{3211,3378,0,1,0}-{3220,3461,0,1,3}-{3221,3464,0,1,4}-{3213,3466,0,1,1}-{3214,3461,0,1,5}-{3215,3464,0,1,0}-{3208,3467,0,1,6}-{3209,3460,0,1,4}-{3205,3463,0,1,4}-{3273,3430,0,1,0}-{3272,3429,0,1,1}-{3274,3427,0,1,3}-" + }, + { + "npc_id": "5923", + "loc_data": "{3224,3394,1,1,6}-" + }, + { + "npc_id": "5924", + "loc_data": "{3227,3396,0,1,1}-" + }, + { + "npc_id": "5925", + "loc_data": "{3220,3433,0,1,5}-" + }, + { + "npc_id": "5926", + "loc_data": "{3184,3386,0,1,4}-{3185,3391,0,1,5}-{3229,3392,0,1,6}-{3247,9775,0,1,3}-{3240,9770,0,1,4}-{3235,9766,0,1,3}-" + }, + { + "npc_id": "5927", + "loc_data": "{3250,9776,0,1,3}-" + }, + { + "npc_id": "5928", + "loc_data": "{3249,9772,0,1,5}-" + }, + { + "npc_id": "5929", + "loc_data": "{3245,9772,0,1,7}-{3239,9766,0,1,2}-" + }, + { + "npc_id": "5930", + "loc_data": "{3257,3454,2,0,0}-" + }, + { + "npc_id": "5932", + "loc_data": "{3253,3445,0,0,6}-" + }, + { + "npc_id": "5933", + "loc_data": "{3254,3444,0,0,1}-" + }, + { + "npc_id": "5934", + "loc_data": "{3256,3443,0,0,6}-" + }, + { + "npc_id": "5935", + "loc_data": "{3257,3442,0,0,1}-" + }, + { + "npc_id": "5936", + "loc_data": "{3266,3445,0,0,6}-" + }, + { + "npc_id": "5937", + "loc_data": "{3267,3444,0,0,1}-" + }, + { + "npc_id": "5938", + "loc_data": "{3253,3454,0,0,0}-" + }, + { + "npc_id": "5939", + "loc_data": "{3253,3453,1,1,0}-" + }, + { + "npc_id": "5940", + "loc_data": "{3262,3442,1,1,0}-" + }, + { + "npc_id": "5941", + "loc_data": "{3260,3447,0,0,0}-" + }, + { + "npc_id": "5942", + "loc_data": "{3295,3427,0,0,1}-" + }, + { + "npc_id": "5943", + "loc_data": "{3263,3441,0,0,0}-" + }, + { + "npc_id": "5944", + "loc_data": "{3263,3450,0,0,0}-{1746,4961,0,1,4}-{1761,4938,0,1,4}-{1761,4976,0,1,5}-" + }, + { + "npc_id": "5945", + "loc_data": "{1743,4959,0,1,4}-{1739,4969,0,1,4}-{1748,4941,0,1,4}-{1759,4979,0,1,6}-" + }, + { + "npc_id": "5946", + "loc_data": "{3261,3455,0,1,0}-{1758,4945,0,1,4}-{1757,4935,0,1,4}-{1746,4980,0,1,4}-" + }, + { + "npc_id": "5947", + "loc_data": "{3260,3452,0,1,0}-{3261,3451,2,1,0}-{1735,4950,0,1,4}-{1759,4948,0,1,4}-{1780,4960,0,1,4}-{1760,4974,0,1,5}-" + }, + { + "npc_id": "5952", + "loc_data": "{3326,3443,0,1,5}-" + }, + { + "npc_id": "5954", + "loc_data": "{3310,3425,0,1,5}-" + }, + { + "npc_id": "5956", + "loc_data": "{2659,2664,0,1,3}-" + }, + { + "npc_id": "5958", + "loc_data": "{3357,3415,0,0,3}-" + }, + { + "npc_id": "5959", + "loc_data": "{3352,3449,0,0,0}-" + }, + { + "npc_id": "5960", + "loc_data": "{3354,3449,0,0,0}-" + }, + { + "npc_id": "5961", + "loc_data": "{3352,3446,0,0,0}-" + }, + { + "npc_id": "5962", + "loc_data": "{3357,3446,0,0,0}-" + }, + { + "npc_id": "5963", + "loc_data": "{3363,3445,0,0,0}-" + }, + { + "npc_id": "5964", + "loc_data": "{3324,3445,0,1,7}-" + }, + { + "npc_id": "5965", + "loc_data": "{1759,4955,0,0,0}-" + }, + { + "npc_id": "5966", + "loc_data": "{1754,4960,0,1,4}-" + }, + { + "npc_id": "5967", + "loc_data": "{1735,4950,0,1,4}-{1758,4937,0,1,4}-{1756,4947,0,1,4}-{1771,4960,0,1,4}-{1779,4950,0,1,4}-{1759,4971,0,1,4}-{1773,4983,0,1,4}-" + }, + { + "npc_id": "5971", + "loc_data": "{1742,4964,0,0,0}-" + }, + { + "npc_id": "5972", + "loc_data": "{1781,4954,0,0,0}-" + }, + { + "npc_id": "5973", + "loc_data": "{1774,4964,0,0,0}-" + }, + { + "npc_id": "5974", + "loc_data": "{1774,4954,0,0,0}-" + }, + { + "npc_id": "5975", + "loc_data": "{1740,4979,0,0,0}-" + }, + { + "npc_id": "5976", + "loc_data": "{1742,4954,0,0,0}-" + }, + { + "npc_id": "5977", + "loc_data": "{1735,4964,0,0,0}-" + }, + { + "npc_id": "5978", + "loc_data": "{1752,4938,0,0,0}-" + }, + { + "npc_id": "5979", + "loc_data": "{1765,4979,0,0,0}-" + }, + { + "npc_id": "5980", + "loc_data": "{1775,4979,0,0,0}-" + }, + { + "npc_id": "5981", + "loc_data": "{1750,4979,0,0,0}-" + }, + { + "npc_id": "5982", + "loc_data": "{1735,4954,0,0,0}-" + }, + { + "npc_id": "5984", + "loc_data": "{3263,3452,0,1,0}-{1743,4950,0,1,4}-{1770,4935,0,1,4}-{1772,4962,0,1,4}-{1771,4957,0,1,4}-{1777,4976,0,1,6}-" + }, + { + "npc_id": "5987", + "loc_data": "{2891,3455,0,1,0}-" + }, + { + "npc_id": "5991", + "loc_data": "{2966,3466,0,0,0}-" + }, + { + "npc_id": "5997", + "loc_data": "{2862,3511,0,0,0}-" + }, + { + "npc_id": "5998", + "loc_data": "{2971,3473,0,0,0}-" + }, + { + "npc_id": "6026", + "loc_data": "{3489,3490,0,1,3}-" + }, + { + "npc_id": "6027", + "loc_data": "{3493,3472,0,1,4}-" + }, + { + "npc_id": "6028", + "loc_data": "{3497,3497,0,1,2}-" + }, + { + "npc_id": "6030", + "loc_data": "{3496,3476,0,1,2}-" + }, + { + "npc_id": "6031", + "loc_data": "{3511,3482,1,0,0}-" + }, + { + "npc_id": "6032", + "loc_data": "{3502,3487,0,1,1}-" + }, + { + "npc_id": "6033", + "loc_data": "{3484,3478,0,1,4}-" + }, + { + "npc_id": "6034", + "loc_data": "{3502,3493,0,1,3}-" + }, + { + "npc_id": "6035", + "loc_data": "{3486,3488,0,1,3}-" + }, + { + "npc_id": "6036", + "loc_data": "{3499,3474,1,0,0}-" + }, + { + "npc_id": "6037", + "loc_data": "{3499,3477,0,1,3}-" + }, + { + "npc_id": "6038", + "loc_data": "{3490,3472,1,0,0}-" + }, + { + "npc_id": "6039", + "loc_data": "{3483,3495,0,1,4}-" + }, + { + "npc_id": "6040", + "loc_data": "{3498,3472,1,0,0}-" + }, + { + "npc_id": "6041", + "loc_data": "{3479,3492,0,1,4}-" + }, + { + "npc_id": "6042", + "loc_data": "{3479,3498,0,1,4}-" + }, + { + "npc_id": "6044", + "loc_data": "{3505,3491,1,0,0}-" + }, + { + "npc_id": "6045", + "loc_data": "{3479,3498,1,0,0}-" + }, + { + "npc_id": "6046", + "loc_data": "{2835,3509,0,1,1}-{3306,5510,0,1,5}-{3309,5543,0,1,6}-" + }, + { + "npc_id": "6047", + "loc_data": "{2836,3495,0,1,0}-{2837,3499,0,1,0}-{2839,3502,0,1,0}-{2839,3506,0,1,0}-{2842,3504,0,1,0}-{2844,3507,0,1,0}-" + }, + { + "npc_id": "6050", + "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": "{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", + "loc_data": "{2761,3863,0,1,0}-{2764,3858,0,1,0}-{2769,3854,0,1,0}-{2769,3861,0,1,0}-" + }, + { + "npc_id": "6055", + "loc_data": "{2563,4348,0,1,0}-{2620,4348,0,1,0}-{2620,4291,0,1,0}-{2563,4291,0,1,0}-{2572,4339,0,1,0}-{2565,4327,0,1,0}-{2575,4315,0,1,0}-{2586,4306,0,1,0}-{2593,4304,0,1,0}-{2605,4312,0,1,0}-{2614,4330,0,1,0}-" + }, + { + "npc_id": "6056", + "loc_data": "{2565,4320,0,1,0}-{2592,4348,0,1,0}-{2596,4346,0,1,0}-{2576,4333,0,1,0}-{2576,4324,0,1,0}-{2612,4333,0,1,0}-{2617,4329,0,1,0}-{2608,4316,0,1,0}-{2593,4295,0,1,0}-{2587,4307,0,1,0}-" + }, + { + "npc_id": "6057", + "loc_data": "{2604,4348,0,1,0}-{2613,4346,0,1,0}-{2582,4347,0,1,0}-{2568,4333,0,1,0}-{2576,4321,0,1,0}-{2571,4302,0,1,0}-{2587,4296,0,1,0}-{2598,4301,0,1,0}-{2612,4303,0,1,0}-{2607,4321,0,1,0}-{2617,4325,0,1,0}-{2617,4346,0,1,0}-" + }, + { + "npc_id": "6058", + "loc_data": "{2585,4343,0,1,0}-{2599,4338,0,1,0}-{2612,4338,0,1,0}-{2573,4329,0,1,0}-{2572,4320,0,1,0}-{2575,4309,0,1,0}-{2587,4302,0,1,0}-{2606,4319,0,1,0}-{2608,4336,0,1,0}-" + }, + { + "npc_id": "6059", + "loc_data": "{2579,4340,0,1,0}-{2578,4323,0,1,0}-{2580,4302,0,1,0}-{2611,4324,0,1,0}-{2594,4338,0,1,0}-" + }, + { + "npc_id": "6060", + "loc_data": "{2589,4341,0,1,0}-{2569,4315,0,1,0}-{2591,4299,0,1,0}-{2611,4330,0,1,0}-" + }, + { + "npc_id": "6065", + "loc_data": "{2612,4348,0,1,0}-{2606,4340,0,1,0}-{2592,4333,0,1,0}-{2579,4336,0,1,0}-{2565,4340,0,1,0}-{2569,4329,0,1,0}-{2574,4311,0,1,0}-{2570,4304,0,1,0}-{2567,4294,0,1,0}-{2604,4305,0,1,0}-{2610,4293,0,1,0}-{2612,4320,0,1,0}-" + }, + { + "npc_id": "6066", + "loc_data": "{2601,4334,0,1,0}-{2586,4335,0,1,0}-{2572,4334,0,1,0}-{2574,4323,0,1,0}-{2579,4295,0,1,0}-{2588,4298,0,1,0}-{2600,4304,0,1,0}-{2612,4312,0,1,0}-{2607,4329,0,1,0}-{2616,4336,0,1,0}-" + }, + { + "npc_id": "6067", + "loc_data": "{2570,4341,0,0,0}-{2613,4298,0,0,0}-" + }, + { + "npc_id": "6070", + "loc_data": "{2589,4313,0,1,6}-" + }, + { + "npc_id": "6072", + "loc_data": "{2426,4442,0,0,0}-" + }, + { + "npc_id": "6073", + "loc_data": "{2594,4319,0,1,6}-{2424,4436,0,0,0}-" + }, + { + "npc_id": "6074", + "loc_data": "{2603,4306,0,1,4}-{2607,4312,0,1,7}-{2610,4319,0,1,6}-{2613,4326,0,1,5}-{2616,4333,0,1,0}-{2619,4340,0,1,6}-{2614,4344,0,1,3}-{2609,4347,0,1,4}-{2605,4347,0,1,4}-{2599,4345,0,1,3}-{2598,4342,0,1,3}-{2597,4339,0,1,3}-{2596,4336,0,1,3}-{2595,4333,0,1,3}-{2594,4330,0,1,4}-{2587,4330,0,1,3}-{2583,4332,0,1,4}-{2579,4335,0,1,3}-{2576,4339,0,1,4}-{2571,4342,0,1,1}-{2569,4332,0,1,4}-{2572,4326,0,1,6}-{2572,4297,0,1,1}-{2585,4291,0,1,3}-{2599,4297,0,1,4}-{2607,4300,0,1,1}-{2614,4301,0,1,6}-{2619,4305,0,1,0}-{2616,4320,0,1,6}-{2613,4327,0,1,0}-{2611,4335,0,1,6}-{2607,4336,0,1,6}-{2581,4322,0,1,1}-{2578,4315,0,1,6}-{2575,4308,0,1,6}-{2572,4301,0,1,4}-{2567,4295,0,1,3}-{2577,4291,0,1,3}-{2587,4298,0,1,3}-{2595,4307,0,1,3}-{2603,4314,0,1,3}-" + }, + { + "npc_id": "6085", + "loc_data": "{3046,3207,1,0,2}-" + }, + { + "npc_id": "6088", + "loc_data": "{2932,3253,0,1,4}-{2936,3249,0,1,2}-{2933,3244,0,1,4}-{2927,3247,0,1,5}-{2925,3256,0,1,0}-" + }, + { + "npc_id": "6089", + "loc_data": "{2932,3249,0,1,0}-{2929,3253,0,1,3}-{2938,3248,0,1,5}-" + }, + { + "npc_id": "6090", + "loc_data": "{2932,3245,0,1,0}-{2934,3246,0,1,3}-" + }, + { + "npc_id": "6101", + "loc_data": "{2932,9811,0,1,0}-" + }, + { + "npc_id": "6102", + "loc_data": "{1762,5329,0,1,1}-" + }, + { + "npc_id": "6104", + "loc_data": "{1751,5326,0,1,1}-" + }, + { + "npc_id": "6105", + "loc_data": "{1767,5355,0,0,6}-" + }, + { + "npc_id": "6108", + "loc_data": "{2854,3385,0,1,0}-" + }, + { + "npc_id": "6109", + "loc_data": "{2526,3215,0,1,6}-{2545,3231,0,1,4}-{2537,3236,0,1,4}-" + }, + { + "npc_id": "6110", + "loc_data": "{2532,3222,0,1,3}-{2533,3209,0,1,4}-" + }, + { + "npc_id": "6112", + "loc_data": "{3238,3247,0,1,0}-" + }, + { + "npc_id": "6113", + "loc_data": "{3232,3280,0,1,4}-{3222,3289,0,1,1}-" + }, + { + "npc_id": "6114", + "loc_data": "{3232,3283,0,1,1}-" + }, + { + "npc_id": "6115", + "loc_data": "{3033,3236,0,1,3}-{3041,3237,0,1,6}-{3035,3234,0,1,7}-{3028,3205,0,1,4}-{3019,3202,0,1,6}-{3026,3203,0,1,1}-" + }, + { + "npc_id": "6116", + "loc_data": "{3030,3244,0,1,0}-{3025,3203,0,1,3}-{3034,3204,0,1,4}-" + }, + { + "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}-" + }, + { + "npc_id": "6128", + "loc_data": "{2465,3183,0,0,4}-" + }, + { + "npc_id": "6129", + "loc_data": "{2470,3185,0,0,6}-" + }, + { + "npc_id": "6131", + "loc_data": "{1885,5026,0,0,6}-" + }, + { + "npc_id": "6132", + "loc_data": "{1883,5029,0,0,6}-" + }, + { + "npc_id": "6133", + "loc_data": "{1883,5026,0,0,6}-" + }, + { + "npc_id": "6134", + "loc_data": "{1890,5018,0,0,0}-" + }, + { + "npc_id": "6135", + "loc_data": "{3254,3427,0,1,1}-" + }, + { + "npc_id": "6136", + "loc_data": "{3077,3250,0,1,0}-" + }, + { + "npc_id": "6137", + "loc_data": "{2952,3381,0,1,0}-" + }, + { + "npc_id": "6138", + "loc_data": "{2665,3311,0,0,0}-" + }, + { + "npc_id": "6139", + "loc_data": "{2734,3484,0,1,1}-" + }, + { + "npc_id": "6140", + "loc_data": "{2644,2641,0,0,6}-" + }, + { + "npc_id": "6141", + "loc_data": "{2638,2656,0,0,0}-" + }, + { + "npc_id": "6166", + "loc_data": "{1904,4279,0,0,0}-" + }, + { + "npc_id": "6167", + "loc_data": "{1906,4277,0,0,0}-" + }, + { + "npc_id": "6168", + "loc_data": "{1904,4274,0,0,0}-" + }, + { + "npc_id": "6188", + "loc_data": "{1901,4271,0,0,0}-{1909,4271,0,0,0}-" + }, + { + "npc_id": "6189", + "loc_data": "{3016,3516,0,1,7}-" + }, + { + "npc_id": "6190", + "loc_data": "{1906,4270,0,1,0}-" + }, + { + "npc_id": "6191", + "loc_data": "{2739,3577,0,1,0}-{2739,3579,1,1,0}-" + }, + { + "npc_id": "6192", + "loc_data": "{2734,3575,0,1,0}-" + }, + { + "npc_id": "6193", + "loc_data": "{2748,3559,0,1,0}-" + }, + { + "npc_id": "6194", + "loc_data": "{2734,3581,1,1,0}-" + }, + { + "npc_id": "6195", + "loc_data": "{2739,3581,0,1,0}-" + }, + { + "npc_id": "6196", + "loc_data": "{2746,3581,1,1,0}-" + }, + { + "npc_id": "6197", + "loc_data": "{2742,3577,0,1,0}-" + }, + { + "npc_id": "6199", + "loc_data": "{2737,3466,0,1,0}-" + }, + { + "npc_id": "6200", + "loc_data": "{2947,3366,0,0,1}-{2949,3366,0,0,1}-{2946,3366,0,0,1}-{2948,3366,0,0,1}-{2945,3366,0,0,1}-{3010,3353,0,0,1}-{3011,3353,0,0,1}-{3012,3353,0,0,1}-{3013,3353,0,0,1}-{3014,3353,0,0,1}-{3015,3353,0,0,1}-" + }, + { + "npc_id": "6202", + "loc_data": "{2912,3749,0,0,0}-" + }, + { + "npc_id": "6203", + "loc_data": "{2924,5322,2,1,4}-" + }, + { + "npc_id": "6205", + "loc_data": "{2933,5329,2,1,1}-" + }, + { + "npc_id": "6210", + "loc_data": "{2837,5336,2,1,4}-{2876,5326,2,1,4}-{2838,5336,2,1,4}-{2898,5306,2,1,5}-{2927,5340,2,1,3}-{2884,5325,2,1,3}-{2886,5325,2,1,1}-{2919,5345,2,1,5}-" + }, + { + "npc_id": "6211", + "loc_data": "{2920,5353,2,1,4}-{2902,5357,2,1,4}-{2885,5315,2,1,4}-{2898,5313,2,1,2}-{2908,5354,2,1,2}-{2924,5354,2,1,6}-{2925,5352,2,1,3}-{2928,5355,2,1,6}-" + }, + { + "npc_id": "6212", + "loc_data": "{2836,5289,2,1,0}-{2869,5287,2,1,1}-{2858,5267,2,1,6}-{2867,5333,2,1,0}-{2877,5321,2,1,1}-{2872,5318,2,1,2}-{2836,5349,2,1,2}-{2921,5293,1,1,6}-{2894,5286,2,1,7}-{2920,5339,2,1,1}-{2888,5326,2,1,3}-{2896,5312,2,1,4}-{2894,5322,2,1,3}-" + }, + { + "npc_id": "6213", + "loc_data": "{2862,5290,2,1,1}-{2838,5347,2,1,6}-{2872,5318,2,1,4}-{2875,5313,2,1,4}-{2865,5327,2,1,4}-{2843,5352,2,1,4}-{2886,5293,2,1,5}-{2909,5297,2,1,0}-{2919,5293,1,1,4}-{2922,5295,1,1,0}-{2919,5293,1,1,5}-{2894,5353,2,1,0}-{2908,5346,2,1,6}-{2913,5355,2,1,3}-{2916,5345,2,1,1}-{2919,5349,2,1,2}-{2922,5356,2,1,1}-{2927,5354,2,1,6}-{2928,5350,2,1,4}-{2931,5357,2,1,3}-{2911,5338,2,1,6}-" + }, + { + "npc_id": "6214", + "loc_data": "{2862,5292,2,1,7}-{2861,5292,2,1,4}-{2864,5319,2,1,7}-{2828,5337,2,1,6}-{2878,5327,2,1,4}-{2853,5327,2,1,4}-{2854,5345,2,1,2}-{2828,5331,2,1,1}-{2919,5284,1,1,0}-{2895,5309,2,1,6}-{2881,5291,2,1,1}-{2924,5292,1,1,6}-{2918,5281,1,1,1}-{2918,5280,1,1,1}-{2884,5312,2,1,2}-{2883,5320,2,1,0}-{2892,5328,2,1,1}-" + }, + { + "npc_id": "6215", + "loc_data": "{2839,5275,2,1,5}-{2845,5286,2,1,0}-{2832,5288,2,1,6}-{2856,5349,2,1,1}-{2862,5334,2,1,0}-{2862,5337,2,1,4}-{2855,5352,2,1,1}-{2880,5293,2,1,4}-{2926,5345,2,1,7}-{2888,5361,2,1,5}-{2886,5313,2,1,3}-{2881,5320,2,1,3}-{2886,5325,2,1,2}-{2889,5322,2,1,4}-{2919,5359,2,1,0}-{2923,5357,2,1,6}-{2891,5353,2,1,1}-" + }, + { + "npc_id": "6216", + "loc_data": "{2865,5306,2,1,3}-{2867,5304,2,1,6}-{2871,5281,2,1,1}-{2864,5279,2,1,4}-{2871,5312,2,1,3}-{2888,5290,2,1,1}-" + }, + { + "npc_id": "6217", + "loc_data": "{2884,5307,2,1,0}-{2899,5304,2,1,3}-{2915,5270,0,1,5}-" + }, + { + "npc_id": "6218", + "loc_data": "{2856,5267,2,1,7}-{2861,5294,2,1,0}-{2836,5290,2,1,6}-{2858,5266,2,1,1}-{2859,5328,2,1,6}-{2911,5268,0,1,4}-{2916,5269,0,1,1}-{2906,5347,2,1,6}-{2913,5358,2,1,6}-{2887,5318,2,1,4}-{2888,5314,2,1,6}-{2931,5345,2,1,2}-{2903,5353,2,1,4}-{2907,5346,2,1,6}-{2921,5351,2,1,2}-{3044,5347,0,1,2}-{3041,5347,0,1,0}-{3031,5346,0,1,2}-{3023,5346,0,1,6}-{3038,5333,0,1,3}-{3048,5333,0,1,1}-{3054,5345,0,1,3}-{3043,5360,0,1,3}-{3030,5362,0,1,3}-{3040,5341,0,1,4}-" + }, + { + "npc_id": "6219", + "loc_data": "{2915,5340,2,1,6}-" + }, + { + "npc_id": "6220", + "loc_data": "{2846,5286,2,1,5}-{2848,5268,2,1,4}-{2931,5356,2,1,3}-{2937,5351,2,1,3}-" + }, + { + "npc_id": "6221", + "loc_data": "{2906,5354,2,1,7}-{2886,5354,2,1,5}-{2885,5352,2,1,2}-{2901,5362,2,1,1}-{2914,5344,2,1,3}-{2918,5343,2,1,2}-{2918,5361,2,1,0}-{2923,5345,2,1,3}-{2924,5352,2,1,4}-" + }, + { + "npc_id": "6222", + "loc_data": "{2831,5301,2,1,4}-" + }, + { + "npc_id": "6229", + "loc_data": "{2841,5285,2,1,3}-{2841,5289,2,1,1}-{2856,5264,2,1,5}-{2842,5282,2,1,6}-" + }, + { + "npc_id": "6230", + "loc_data": "{2843,5265,2,1,4}-{2851,5273,2,1,3}-{2846,5262,2,1,4}-" + }, + { + "npc_id": "6231", + "loc_data": "{2870,5265,2,1,0}-{2869,5266,2,1,7}-{2855,5273,2,1,2}-{2840,5263,2,1,3}-{2831,5285,2,1,4}-" + }, + { + "npc_id": "6232", + "loc_data": "{2872,5261,2,1,4}-{2863,5308,2,1,1}-{2872,5288,2,1,4}-" + }, + { + "npc_id": "6233", + "loc_data": "{2861,5261,2,1,0}-{2852,5264,2,1,0}-{2865,5263,2,1,6}-" + }, + { + "npc_id": "6234", + "loc_data": "{2839,5293,2,1,0}-{2842,5264,2,1,4}-{2862,5313,2,1,3}-" + }, + { + "npc_id": "6235", + "loc_data": "{2864,5270,2,1,3}-{2859,5303,2,1,4}-{2863,5293,2,1,7}-{2862,5286,2,1,1}-{2869,5314,2,1,3}-" + }, + { + "npc_id": "6236", + "loc_data": "{2854,5271,2,1,6}-{2872,5267,2,1,0}-{2858,5269,2,1,5}-" + }, + { + "npc_id": "6237", + "loc_data": "{2840,5290,2,1,6}-{2841,5269,2,1,7}-{2833,5292,2,1,3}-" + }, + { + "npc_id": "6238", + "loc_data": "{2874,5308,2,1,3}-{2851,5276,2,1,2}-{2864,5268,2,1,2}-" + }, + { + "npc_id": "6239", + "loc_data": "{2824,5216,2,1,7}-{2875,5298,2,1,4}-{2846,5288,2,1,0}-{2845,5281,2,1,1}-" + }, + { + "npc_id": "6240", + "loc_data": "{2837,5269,2,1,2}-{2860,5300,2,1,1}-{2852,5311,2,1,4}-{2879,5286,2,1,5}-" + }, + { + "npc_id": "6241", + "loc_data": "{2863,5272,2,1,6}-{2850,5269,2,1,7}-" + }, + { + "npc_id": "6242", + "loc_data": "{2836,5272,2,1,1}-" + }, + { + "npc_id": "6243", + "loc_data": "{2848,5275,2,1,0}-{2869,5304,2,1,4}-{2850,5292,2,1,1}-{2857,5300,2,1,3}-{2874,5287,2,1,4}-" + }, + { + "npc_id": "6244", + "loc_data": "{2847,5299,2,1,6}-{2844,5283,2,1,6}-" + }, + { + "npc_id": "6245", + "loc_data": "{2861,5299,2,1,4}-{2833,5290,2,1,1}-{2851,5262,2,1,0}-" + }, + { + "npc_id": "6246", + "loc_data": "{2855,5311,2,1,1}-{2872,5299,2,1,0}-{2856,5284,2,1,4}-{2872,5315,2,1,1}-" + }, + { + "npc_id": "6247", + "loc_data": "{2894,5265,0,1,2}-" + }, + { + "npc_id": "6254", + "loc_data": "{2917,5272,0,1,7}-{2898,5295,2,1,4}-{2881,5283,2,1,1}-{2883,5311,2,1,2}-{2894,5301,2,1,3}-{2882,5284,2,1,5}-{2890,5292,2,1,4}-{2896,5296,2,1,2}-{2905,5299,2,1,3}-{2905,5301,2,1,5}-{2921,5283,1,1,1}-{2923,5292,1,1,7}-{2906,5299,0,1,6}-{2910,5297,0,1,6}-{2915,5270,0,1,2}-{2910,5269,0,1,5}-" + }, + { + "npc_id": "6255", + "loc_data": "{2922,5290,1,1,5}-{2880,5304,2,1,1}-{2884,5311,2,1,2}-{2885,5311,2,1,2}-{2895,5310,2,1,1}-{2884,5280,2,1,3}-{2885,5293,2,1,6}-{2886,5291,2,1,6}-{2894,5285,2,1,3}-{2893,5286,2,1,7}-{2905,5292,2,1,4}-{2908,5297,2,1,4}-{2910,5297,2,1,6}-{2897,5308,2,1,4}-{2919,5299,1,1,1}-{2921,5292,1,1,5}-{2924,5288,1,1,0}-{2923,5287,1,1,1}-{2908,5300,0,1,6}-{2907,5294,0,1,6}-{2923,5293,1,1,5}-{2919,5298,1,1,1}-{2922,5291,1,1,6}-{2917,5285,1,1,0}-{2905,5295,0,1,6}-{2887,5312,2,1,1}-{2886,5313,2,1,0}-" + }, + { + "npc_id": "6256", + "loc_data": "{2922,5285,1,1,0}-{2886,5295,2,1,1}-{2891,5300,2,1,1}-{2889,5310,2,1,2}-{2887,5299,2,1,6}-{2891,5297,2,1,3}-{2884,5283,2,1,3}-{2885,5286,2,1,3}-{2886,5294,2,1,1}-{2896,5286,2,1,4}-{2908,5302,2,1,1}-{2919,5290,1,1,1}-{2919,5298,1,1,4}-{2918,5298,1,1,4}-{2911,5298,0,1,6}-{2919,5293,1,1,1}-{2917,5294,1,1,2}-{2923,5284,1,1,1}-{2916,5267,0,1,2}-" + }, + { + "npc_id": "6257", + "loc_data": "{2877,5293,2,1,5}-{2879,5329,2,1,5}-{2913,5267,0,1,6}-{2892,5291,2,1,4}-{2886,5311,2,1,0}-{2888,5305,2,1,3}-{2894,5285,2,1,0}-{2900,5287,2,1,0}-{2905,5302,2,1,1}-{2898,5307,2,1,6}-{2918,5292,1,1,3}-{2924,5286,1,1,0}-{2924,5292,1,1,1}-{2920,5297,1,1,3}-{2924,5285,1,1,1}-{2921,5289,1,1,6}-{2923,5294,1,1,0}-{2922,5285,1,1,3}-{2915,5273,0,1,7}-{2915,5271,0,1,4}-{2895,5313,2,1,6}-" + }, + { + "npc_id": "6258", + "loc_data": "{2918,5289,1,1,6}-{2884,5287,2,1,0}-{2895,5294,2,1,4}-{2897,5307,2,1,7}-{2918,5298,1,1,5}-{2917,5291,1,1,4}-{2916,5269,0,1,2}-" + }, + { + "npc_id": "6259", + "loc_data": "{2890,5304,2,1,7}-{2886,5299,2,1,3}-{2891,5297,2,1,5}-{2890,5291,2,1,3}-{2900,5286,2,1,7}-{2905,5303,2,1,1}-{2918,5282,1,1,6}-{2922,5295,1,1,2}-{2905,5297,0,1,6}-{2920,5291,1,1,5}-{2920,5290,1,1,6}-" + }, + { + "npc_id": "6260", + "loc_data": "{2870,5359,2,1,4}-" + }, + { + "npc_id": "6262", + "loc_data": "{2873,5356,0,1,1}-{2866,5358,2,1,6}-" + }, + { + "npc_id": "6264", + "loc_data": "{2870,5354,2,1,4}-" + }, + { + "npc_id": "6266", + "loc_data": "{2868,5362,2,1,3}-" + }, + { + "npc_id": "6268", + "loc_data": "{2856,5355,2,1,7}-{2838,5358,2,1,2}-{2841,5319,2,1,7}-{2856,5331,2,1,2}-{2854,5325,2,1,2}-{2873,5323,2,1,6}-{2860,5321,2,1,7}-{2868,5334,2,1,0}-{2856,5328,2,1,6}-{2855,5332,2,1,1}-{2845,5339,2,1,5}-{2831,5320,2,1,3}-{2847,5334,2,1,1}-{2858,5349,2,1,2}-{2829,5339,2,1,6}-{2856,5361,2,1,5}-{2839,5360,2,1,6}-" + }, + { + "npc_id": "6269", + "loc_data": "{2846,5351,2,1,3}-{2846,5339,2,1,2}-{2840,5327,2,1,1}-{2836,5345,2,1,7}-{2834,5328,2,1,2}-{2857,5359,2,1,4}-{2846,5357,2,1,3}-" + }, + { + "npc_id": "6270", + "loc_data": "{2845,5336,2,1,2}-{2848,5344,2,1,6}-{2842,5328,2,1,6}-{2844,5349,2,1,2}-{2836,5323,2,1,4}-{2832,5353,2,1,6}-{2848,5358,2,1,7}-" + }, + { + "npc_id": "6271", + "loc_data": "{2840,5330,2,1,0}-{2859,5328,2,1,6}-{2873,5317,2,1,3}-{2862,5332,2,1,2}-{2869,5332,2,1,6}-" + }, + { + "npc_id": "6272", + "loc_data": "{2838,5326,2,1,4}-{2870,5313,2,1,1}-{2865,5332,2,1,0}-{2838,5334,2,1,6}-{2837,5353,2,1,7}-" + }, + { + "npc_id": "6273", + "loc_data": "{2832,5324,2,1,7}-{2864,5325,2,1,2}-{2873,5325,2,1,7}-{2873,5324,2,1,7}-{2865,5336,2,1,6}-{2840,5333,2,1,6}-{2836,5336,2,1,4}-{2838,5354,2,1,6}-" + }, + { + "npc_id": "6274", + "loc_data": "{2872,5307,2,1,5}-{2858,5325,2,1,4}-{2873,5319,2,1,6}-{2867,5333,2,1,6}-{2866,5333,2,1,0}-{2839,5331,2,1,5}-{2837,5355,2,1,6}-" + }, + { + "npc_id": "6275", + "loc_data": "{2865,5340,2,1,1}-{2869,5326,2,1,7}-{2875,5318,2,1,1}-{2875,5314,2,1,1}-{2878,5313,2,1,3}-{2879,5322,2,1,3}-{2859,5317,2,1,6}-{2858,5319,2,1,4}-{2878,5320,2,1,0}-{2863,5337,2,1,7}-{2862,5338,2,1,7}-{2867,5341,2,1,6}-{2852,5312,2,1,2}-" + }, + { + "npc_id": "6276", + "loc_data": "{2856,5360,2,1,7}-{2854,5346,2,1,3}-{2845,5332,2,1,0}-{2845,5344,2,1,3}-{2848,5349,2,1,6}-{2837,5347,2,1,3}-{2835,5324,2,1,2}-{2835,5321,2,1,5}-{2858,5349,2,1,4}-{2830,5339,2,1,3}-{2828,5332,2,1,3}-{2846,5358,2,1,4}-{2855,5356,2,1,7}-" + }, + { + "npc_id": "6277", + "loc_data": "{2843,5341,2,1,3}-{2843,5331,2,1,3}-{2847,5341,2,1,5}-{2852,5350,2,1,1}-{2844,5341,2,1,6}-{2843,5347,2,1,1}-{2836,5325,2,1,6}-{2826,5335,2,1,2}-{2837,5351,2,1,4}-{2845,5353,2,1,3}-{2840,5361,2,1,3}-" + }, + { + "npc_id": "6278", + "loc_data": "{2845,5359,2,1,4}-{2830,5318,2,1,3}-{2848,5342,2,1,0}-{2846,5332,2,1,1}-{2843,5320,2,1,3}-{2838,5329,2,1,1}-{2855,5349,2,1,0}-{2837,5342,2,1,1}-{2827,5331,2,1,6}-{2830,5315,2,1,6}-{2845,5352,2,1,6}-{2839,5360,2,1,6}-{2851,5359,2,1,7}-{2860,5353,2,1,6}-{2792,5280,2,1,6}-" + }, + { + "npc_id": "6279", + "loc_data": "{2857,5315,2,1,6}-{2859,5318,2,1,5}-{2858,5329,2,1,3}-{2879,5326,2,1,4}-" + }, + { + "npc_id": "6280", + "loc_data": "{2869,5332,2,1,6}-{2871,5313,2,1,2}-{2879,5330,2,1,2}-{2877,5327,2,1,0}-" + }, + { + "npc_id": "6281", + "loc_data": "{2865,5316,2,1,2}-{2861,5333,2,1,2}-" + }, + { + "npc_id": "6282", + "loc_data": "{2874,5318,2,1,0}-{2859,5316,2,1,3}-{2878,5329,2,1,2}-{2858,5330,2,1,2}-" + }, + { + "npc_id": "6283", + "loc_data": "{2877,5326,2,1,2}-{2857,5328,2,1,3}-{2861,5315,2,1,7}-" + }, + { + "npc_id": "6346", + "loc_data": "{2799,3186,0,1,7}-{2797,3169,0,0,0}-{2793,3161,0,1,3}-" + }, + { + "npc_id": "6347", + "loc_data": "{2799,3167,0,0,0}-{2787,3188,0,1,1}-" + }, + { + "npc_id": "6348", + "loc_data": "{2793,3158,0,1,3}-{2798,3154,0,1,4}-{2796,3189,0,1,0}-" + }, + { + "npc_id": "6349", + "loc_data": "{2797,3186,0,1,6}-{2793,3161,0,1,0}-" + }, + { + "npc_id": "6350", + "loc_data": "{2798,3182,0,1,7}-{2793,3161,0,1,1}-" + }, + { + "npc_id": "6351", + "loc_data": "{3041,3951,0,1,0}-{3043,3958,0,1,0}-" + }, + { + "npc_id": "6352", + "loc_data": "{3039,3955,0,1,0}-{3043,3951,0,1,0}-" + }, + { + "npc_id": "6353", + "loc_data": "{3040,3955,0,1,0}-{3039,3950,0,1,0}-" + }, + { + "npc_id": "6354", + "loc_data": "{3041,3950,0,1,0}-{3041,3956,0,1,0}-" + }, + { + "npc_id": "6355", + "loc_data": "{3043,3950,0,1,0}-{3043,3956,0,1,0}-" + }, + { + "npc_id": "6356", + "loc_data": "{3041,3953,0,1,0}-" + }, + { + "npc_id": "6357", + "loc_data": "{3359,2969,0,1,3}-" + }, + { + "npc_id": "6362", + "loc_data": "{3270,4856,0,1,3}-" + }, + { + "npc_id": "6363", + "loc_data": "{3190,5523,0,1,3}-{3194,5530,0,1,3}-{3251,5455,0,1,4}-{3258,5456,0,1,6}-{3257,5451,0,1,6}-{3257,5464,0,1,2}-{3256,5473,0,1,2}-{3244,5469,0,1,1}-{3225,5485,0,1,5}-{3218,5482,0,1,7}-{3219,5486,0,1,4}-{3207,5461,0,1,6}-{3208,5458,0,1,3}-{3219,5528,0,1,2}-{3223,5520,0,1,3}-{3225,5511,0,1,0}-{3236,5511,0,1,6}-{3232,5523,0,1,0}-{3239,5530,0,1,4}-{3233,5546,0,1,3}-{3222,5539,0,1,5}-{3269,4829,0,1,3}-{3271,4823,0,1,2}-{3283,4809,0,1,3}-" + }, + { + "npc_id": "6364", + "loc_data": "{3188,5529,0,1,3}-{3194,5531,0,1,1}-{3188,5527,0,1,3}-{3222,5486,0,1,3}-{3221,5482,0,1,4}-{3212,5459,0,1,6}-{3210,5462,0,1,2}-{3234,5510,0,1,6}-{3271,4809,0,1,3}-" + }, + { + "npc_id": "6365", + "loc_data": "{3267,4817,0,1,4}-{3290,4803,0,1,3}-" + }, + { + "npc_id": "6367", + "loc_data": "{3207,5466,0,1,6}-{3223,5462,0,1,1}-{3237,5524,0,1,6}-{3229,5510,0,1,4}-{3276,4808,0,1,3}-{3291,4812,0,1,6}-" + }, + { + "npc_id": "6368", + "loc_data": "{3226,5458,0,1,3}-{3222,5470,0,1,6}-{3238,5541,0,1,1}-{3226,5540,0,1,7}-{3222,5522,0,1,7}-" + }, + { + "npc_id": "6369", + "loc_data": "{3268,4825,0,1,6}-{3268,4809,0,1,4}-{3288,4803,0,1,6}-{3298,4810,0,1,4}-" + }, + { + "npc_id": "6376", + "loc_data": "{2925,2976,0,1,1}-{2909,2978,0,1,5}-" + }, + { + "npc_id": "6377", + "loc_data": "{2920,2973,0,1,6}-{2919,2976,0,1,6}-{2910,2978,0,1,7}-" + }, + { + "npc_id": "6379", + "loc_data": "{2894,2995,0,0,0}-{2909,2970,0,0,0}-{3303,5485,0,1,1}-{3320,5494,0,1,4}-{3313,5490,0,1,4}-{3315,5484,0,1,3}-{3323,5471,0,1,6}-{3319,5471,0,1,4}-{3323,5477,0,1,0}-{3310,5484,0,1,4}-{3313,5488,0,1,6}-{3309,5481,0,1,1}-{3320,5495,0,1,0}-" + }, + { + "npc_id": "6380", + "loc_data": "{2883,2994,0,0,0}-{2882,2994,0,0,0}-{3319,5490,0,1,5}-{3298,5486,0,1,3}-{3312,5480,0,1,4}-{3315,5480,0,1,1}-{3308,5482,0,1,2}-{3321,5477,0,1,0}-{3317,5470,0,1,6}-{3324,5472,0,1,6}-{3311,5482,0,1,0}-" + }, + { + "npc_id": "6383", + "loc_data": "{2888,3001,0,0,0}-{2891,3002,0,0,0}-{2890,2998,0,0,0}-{2887,2994,0,0,0}-{2901,2991,0,0,0}-{2905,2990,0,0,0}-{2886,2989,0,0,0}-{2927,2979,0,0,0}-{2926,2973,0,0,0}-{2919,2973,0,0,0}-{2919,2976,0,0,0}-{2920,2979,0,0,0}-" + }, + { + "npc_id": "6386", + "loc_data": "{2683,3326,0,0,0}-" + }, + { + "npc_id": "6387", + "loc_data": "{3275,4808,0,1,3}-{3276,4808,0,1,4}-" + }, + { + "npc_id": "6388", + "loc_data": "{3369,2999,0,1,7}-{3368,2995,0,1,6}-{3362,2997,0,1,3}-{3361,2988,0,1,2}-{3367,2988,0,1,7}-{3379,2983,0,1,1}-" + }, + { + "npc_id": "6390", + "loc_data": "{1707,4814,0,0,6}-" + }, + { + "npc_id": "6394", + "loc_data": "{2703,5366,0,0,0}-" + }, + { + "npc_id": "6396", + "loc_data": "{2791,4279,0,0,0}-" + }, + { + "npc_id": "6405", + "loc_data": "{2799,4260,0,1,0}-" + }, + { + "npc_id": "6406", + "loc_data": "{2765,4267,0,1,0}-" + }, + { + "npc_id": "6407", + "loc_data": "{2802,4246,0,1,0}-" + }, + { + "npc_id": "6408", + "loc_data": "{2770,4238,0,1,0}-" + }, + { + "npc_id": "6409", + "loc_data": "{2787,4262,0,1,0}-{2792,4275,0,1,0}-" + }, + { + "npc_id": "6410", + "loc_data": "{2766,4257,0,1,0}-" + }, + { + "npc_id": "6416", + "loc_data": "{2801,4258,0,1,0}-" + }, + { + "npc_id": "6417", + "loc_data": "{2765,4264,0,1,0}-{2777,4260,0,1,0}-" + }, + { + "npc_id": "6418", + "loc_data": "{2799,4243,0,1,0}-" + }, + { + "npc_id": "6419", + "loc_data": "{2771,4237,0,1,0}-" + }, + { + "npc_id": "6420", + "loc_data": "{2796,4277,0,1,0}-" + }, + { + "npc_id": "6421", + "loc_data": "{2768,4254,0,1,0}-" + }, + { + "npc_id": "6427", + "loc_data": "{2802,4259,0,1,0}-" + }, + { + "npc_id": "6428", + "loc_data": "{2768,4268,0,1,0}-" + }, + { + "npc_id": "6429", + "loc_data": "{2776,4252,0,1,0}-{2805,4242,0,1,0}-" + }, + { + "npc_id": "6430", + "loc_data": "{2769,4236,0,1,0}-" + }, + { + "npc_id": "6431", + "loc_data": "{2792,4260,0,1,0}-{2796,4275,0,1,0}-" + }, + { + "npc_id": "6432", + "loc_data": "{2766,4255,0,1,0}-" + }, + { + "npc_id": "6438", + "loc_data": "{2790,4254,0,1,0}-{2800,4256,0,1,0}-" + }, + { + "npc_id": "6439", + "loc_data": "{2766,4265,0,1,0}-" + }, + { + "npc_id": "6440", + "loc_data": "{2789,4248,0,1,0}-{2802,4244,0,1,0}-" + }, + { + "npc_id": "6441", + "loc_data": "{2771,4241,0,1,0}-{2777,4252,0,1,0}-" + }, + { + "npc_id": "6442", + "loc_data": "{2795,4273,0,1,0}-" + }, + { + "npc_id": "6443", + "loc_data": "{2767,4252,0,1,0}-{2776,4250,0,1,0}-" + }, + { + "npc_id": "6449", + "loc_data": "{2792,4252,0,1,0}-{2800,4254,0,1,0}-" + }, + { + "npc_id": "6450", + "loc_data": "{2764,4266,0,1,0}-{2781,4261,0,1,0}-" + }, + { + "npc_id": "6451", + "loc_data": "{2802,4243,0,1,0}-" + }, + { + "npc_id": "6452", + "loc_data": "{2772,4235,0,1,0}-" + }, + { + "npc_id": "6453", + "loc_data": "{2797,4272,0,1,0}-" + }, + { + "npc_id": "6454", + "loc_data": "{2765,4253,0,1,0}-{2779,4256,0,1,0}-" + }, + { + "npc_id": "6458", + "loc_data": "{2784,4249,0,1,0}-" + }, + { + "npc_id": "6459", + "loc_data": "{2778,4264,0,1,0}-{2784,4252,0,1,0}-" + }, + { + "npc_id": "6460", + "loc_data": "{2783,4251,0,1,0}-{2789,4264,0,1,0}-{2801,4252,0,1,0}-" + }, + { + "npc_id": "6461", + "loc_data": "{2787,4250,0,1,0}-{2791,4249,0,1,0}-" + }, + { + "npc_id": "6462", + "loc_data": "{2785,4248,0,1,0}-{2786,4251,0,1,0}-{2801,4242,0,1,0}-" + }, + { + "npc_id": "6463", + "loc_data": "{2773,4238,0,1,0}-{2779,4248,0,1,0}-{2785,4250,0,1,0}-" + }, + { + "npc_id": "6464", + "loc_data": "{2796,4267,0,1,0}-" + }, + { + "npc_id": "6465", + "loc_data": "{2764,4251,0,1,0}-" + }, + { + "npc_id": "6482", + "loc_data": "{2801,4260,0,0,0}-" + }, + { + "npc_id": "6483", + "loc_data": "{2804,4246,0,0,0}-" + }, + { + "npc_id": "6484", + "loc_data": "{2795,4276,0,0,0}-" + }, + { + "npc_id": "6485", + "loc_data": "{2766,4250,0,0,0}-" + }, + { + "npc_id": "6486", + "loc_data": "{2774,4236,0,0,0}-" + }, + { + "npc_id": "6487", + "loc_data": "{2765,4268,0,0,0}-" + }, + { + "npc_id": "6488", + "loc_data": "{2784,4255,1,0,0}-" + }, + { + "npc_id": "6489", + "loc_data": "{2787,4266,0,0,0}-" + }, + { + "npc_id": "6498", + "loc_data": "{2796,4256,0,1,0}-" + }, + { + "npc_id": "6499", + "loc_data": "{2793,4247,0,1,0}-" + }, + { + "npc_id": "6500", + "loc_data": "{2793,4265,0,1,0}-" + }, + { + "npc_id": "6501", + "loc_data": "{2772,4256,0,1,0}-" + }, + { + "npc_id": "6502", + "loc_data": "{2775,4247,0,1,0}-" + }, + { + "npc_id": "6503", + "loc_data": "{2775,4265,0,1,0}-" + }, + { + "npc_id": "6521", + "loc_data": "{3161,3475,0,0,6}-" + }, + { + "npc_id": "6522", + "loc_data": "{3168,3475,0,0,6}-" + }, + { + "npc_id": "6523", + "loc_data": "{3173,3498,0,0,1}-" + }, + { + "npc_id": "6524", + "loc_data": "{3156,3481,0,0,1}-" + }, + { + "npc_id": "6525", + "loc_data": "{3175,3481,0,0,1}-" + }, + { + "npc_id": "6526", + "loc_data": "{3152,3490,0,0,1}-" + }, + { + "npc_id": "6527", + "loc_data": "{3154,3498,0,0,1}-" + }, + { + "npc_id": "6528", + "loc_data": "{3165,3491,0,0,1}-" + }, + { + "npc_id": "6529", + "loc_data": "{3164,3488,0,0,6}-" + }, + { + "npc_id": "6530", + "loc_data": "{3164,3491,0,0,1}-" + }, + { + "npc_id": "6531", + "loc_data": "{3165,3488,0,0,6}-" + }, + { + "npc_id": "6532", + "loc_data": "{3163,3489,0,0,3}-" + }, + { + "npc_id": "6533", + "loc_data": "{3166,3489,0,0,4}-" + }, + { + "npc_id": "6534", + "loc_data": "{3166,3490,0,0,4}-" + }, + { + "npc_id": "6535", + "loc_data": "{3163,3490,0,0,3}-" + }, + { + "npc_id": "6538", + "loc_data": "{3189,3690,0,0,3}-" + }, + { + "npc_id": "6731", + "loc_data": "{2655,5615,0,1,4}-" + }, + { + "npc_id": "6750", + "loc_data": "{2577,3078,0,1,3}-" + }, + { + "npc_id": "6753", + "loc_data": "{3381,2816,1,1,0}-{3061,4672,1,1,0}-" + }, + { + "npc_id": "6754", + "loc_data": "{3386,2824,1,1,0}-{3066,4680,1,1,0}-" + }, + { + "npc_id": "6755", + "loc_data": "{3382,2820,1,1,0}-{3062,4676,1,1,0}-" + }, + { + "npc_id": "6756", + "loc_data": "{3384,2822,1,1,0}-{3064,4678,1,1,0}-" + }, + { + "npc_id": "6761", + "loc_data": "{3424,2828,1,1,3}-{3430,2835,1,1,3}-{3436,2830,1,1,3}-" + }, + { + "npc_id": "6762", + "loc_data": "{3420,2829,1,1,3}-{3429,2827,1,1,3}-{3432,2832,1,1,3}-" + }, + { + "npc_id": "6763", + "loc_data": "{3427,2832,1,1,3}-{3432,2828,1,1,3}-{3435,2835,1,1,3}-" + }, + { + "npc_id": "6764", + "loc_data": "{3415,2840,1,1,0}-{3426,2852,1,1,0}-{3439,2857,1,1,0}-" + }, + { + "npc_id": "6765", + "loc_data": "{3418,2851,1,1,0}-{3432,2854,1,1,0}-" + }, + { + "npc_id": "6766", + "loc_data": "{3406,2837,1,1,0}-{3434,2848,1,1,0}-" + }, + { + "npc_id": "6767", + "loc_data": "{3409,2844,1,1,0}-{3421,2855,1,1,0}-" + }, + { + "npc_id": "6768", + "loc_data": "{3414,2844,1,1,0}-{3439,2851,1,1,0}-" + }, + { + "npc_id": "6773", + "loc_data": "{3358,9233,1,1,0}-{3363,9246,1,1,0}-{3369,9233,1,1,0}-{3373,9246,1,1,0}-{3377,9244,1,1,0}-{3381,9225,1,1,0}-{3389,9241,1,1,0}-{3391,9237,1,1,0}-{3391,9230,1,1,0}-{3439,2808,0,1,0}-" + }, + { + "npc_id": "6774", + "loc_data": "{3361,9238,1,1,0}-{3363,9241,1,1,0}-{3373,9241,1,1,0}-{3374,9225,1,1,0}-{3379,9230,1,1,0}-{3383,9232,1,1,0}-{3387,9229,1,1,0}-{3389,9236,1,1,0}-{3435,2808,0,1,2}-" + }, + { + "npc_id": "6776", + "loc_data": "{3441,2810,0,1,0}-{3445,2813,0,1,0}-" + }, + { + "npc_id": "6777", + "loc_data": "{3432,2810,0,1,4}-" + }, + { + "npc_id": "6778", + "loc_data": "{3440,2806,0,1,0}-" + }, + { + "npc_id": "6779", + "loc_data": "{3337,2762,0,1,0}-{3340,2767,0,1,0}-{3345,2763,0,1,0}-{3350,2766,0,1,0}-{3352,2761,0,1,0}-{3356,2769,0,1,0}-{3358,2758,0,1,0}-{3359,2763,0,1,0}-{3365,2769,0,1,0}-{3368,2764,0,1,0}-{3375,2762,0,1,0}-{3392,2759,0,1,2}-{3408,2763,0,1,7}-{3410,2786,0,1,2}-{3420,2778,0,1,6}-{3427,2792,0,1,6}-{3429,2802,0,1,4}-" + }, + { + "npc_id": "6780", + "loc_data": "{3366,9254,1,1,6}-{3398,9232,1,1,0}-{3409,9230,1,1,0}-" + }, + { + "npc_id": "6781", + "loc_data": "{3363,9231,1,1,0}-{3369,9254,1,1,6}-{3370,9226,1,1,0}-{3397,9225,1,1,0}-{3408,9226,1,1,0}-" + }, + { + "npc_id": "6785", + "loc_data": "{3442,9245,1,0,1}-" + }, + { + "npc_id": "6786", + "loc_data": "{3409,2812,1,0,0}-" + }, + { + "npc_id": "6787", + "loc_data": "{3408,2813,1,0,0}-" + }, + { + "npc_id": "6788", + "loc_data": "{3368,9263,1,0,6}-" + }, + { + "npc_id": "6891", + "loc_data": "{2598,3272,0,1,0}-" + }, + { + "npc_id": "6893", + "loc_data": "{2926,3435,0,1,2}-" + }, + { + "npc_id": "6894", + "loc_data": "{2928,3432,0,1,0}-" + }, + { + "npc_id": "6895", + "loc_data": "{2927,3431,0,1,0}-" + }, + { + "npc_id": "6896", + "loc_data": "{2573,3082,0,1,2}-" + }, + { + "npc_id": "6897", + "loc_data": "{2574,3083,0,1,0}-" + }, + { + "npc_id": "6917", + "loc_data": "{2916,3082,0,0,0}-{2917,3089,0,0,0}-{2912,3090,0,0,0}-" + }, + { + "npc_id": "6918", + "loc_data": "{2917,3088,0,1,4}-{2917,3092,0,1,4}-" + }, + { + "npc_id": "6921", + "loc_data": "{2954,3273,0,1,6}-{2974,3448,0,1,4}-{2971,3442,0,1,6}-{2968,3451,0,1,1}-{2967,3439,0,1,4}-{2965,3451,0,1,5}-" + }, + { + "npc_id": "6942", + "loc_data": "{2890,3100,0,1,4}-" + }, + { + "npc_id": "6944", + "loc_data": "{2855,3028,0,0,0}-{2841,3039,0,0,0}-{2838,3031,0,0,0}-{2836,3014,0,0,0}-{2839,3022,0,0,0}-" + }, + { + "npc_id": "6962", + "loc_data": "{2842,5222,0,1,6}-" + }, + { + "npc_id": "6971", + "loc_data": "{2926,3443,0,0,5}-" + }, + { + "npc_id": "6972", + "loc_data": "{2922,3482,0,1,0}-" + }, + { + "npc_id": "6973", + "loc_data": "{2893,3419,0,1,0}-" + }, + { + "npc_id": "6974", + "loc_data": "{2897,3419,0,1,0}-" + }, + { + "npc_id": "6975", + "loc_data": "{2928,3480,0,1,0}-{2932,3485,0,1,0}-" + }, + { + "npc_id": "6976", + "loc_data": "{2897,3432,1,1,0}-{2884,3418,0,1,0}-{2884,3434,0,1,0}-{2902,3449,0,1,0}-" + }, + { + "npc_id": "6977", + "loc_data": "{2890,3435,0,1,0}-" + }, + { + "npc_id": "6978", + "loc_data": "{2896,3437,0,1,0}-{2917,3446,0,1,0}-" + }, + { + "npc_id": "6979", + "loc_data": "{2888,3448,0,1,0}-" + }, + { + "npc_id": "6981", + "loc_data": "{2883,3431,0,1,0}-{2900,3449,0,1,0}-" + }, + { + "npc_id": "6982", + "loc_data": "{2896,3429,0,1,0}-{2918,3485,0,1,0}-" + }, + { + "npc_id": "6983", + "loc_data": "{2908,3441,0,1,0}-{2926,3488,0,1,0}-" + }, + { + "npc_id": "6985", + "loc_data": "{2894,3438,0,1,0}-{2913,3451,0,1,0}-" + }, + { + "npc_id": "6986", + "loc_data": "{2887,3441,0,1,0}-" + }, + { + "npc_id": "6987", + "loc_data": "{2894,3445,0,1,0}-" + }, + { + "npc_id": "6996", + "loc_data": "{3169,3266,0,0,3}-{3173,3274,0,0,3}-{3259,3206,0,0,3}-{3259,3204,0,0,3}-{2689,3598,0,0,4}-" + }, + { + "npc_id": "6997", + "loc_data": "{3002,3257,0,0,0}-{3007,3261,0,0,0}-{3058,3429,0,0,0}-{3059,3420,0,0,0}-{3059,3425,0,0,0}-{3049,3429,0,0,0}-" + }, + { + "npc_id": "7003", + "loc_data": "{2582,9895,0,1,7}-{3230,5493,0,1,1}-{3224,5497,0,1,5}-{3253,5535,0,1,7}-" + }, + { + "npc_id": "7004", + "loc_data": "{2581,9891,0,1,2}-{3225,5495,0,1,4}-{3256,5540,0,1,4}-{3247,5536,0,1,6}-" + }, + { + "npc_id": "7005", + "loc_data": "{2487,2924,0,1,0}-" + }, + { + "npc_id": "7009", + "loc_data": "{3282,3467,0,0,0}-" + }, + { + "npc_id": "7010", + "loc_data": "{2204,3229,0,1,3}-{2204,3229,0,1,3}-{2204,3229,0,1,3}-{2243,3200,0,1,3}-{2268,3239,0,1,3}-{2268,3239,0,1,3}-{2268,3239,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}-" + }, + { + "npc_id": "7011", + "loc_data": "{2243,3200,0,1,3}-{2268,3239,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}-" + }, + { + "npc_id": "7012", + "loc_data": "{2226,3143,0,1,3}-{2226,3143,0,1,3}-{2226,3143,0,1,3}-{2223,3146,0,1,3}-{2223,3146,0,1,3}-{2180,3201,0,1,3}-{2180,3201,0,1,3}-{2182,3204,0,1,3}-{2288,3147,0,1,3}-{2288,3147,0,1,3}-{2288,3147,0,1,3}-{2247,3187,0,1,3}-{2247,3187,0,1,3}-" + }, + { + "npc_id": "7014", + "loc_data": "{2226,3143,0,1,3}-{2180,3201,0,1,3}-{2288,3147,0,1,3}-{2247,3187,0,1,3}-" + }, + { + "npc_id": "7021", + "loc_data": "{2510,2822,0,1,3}-{2511,2823,0,1,4}-{2509,2825,0,1,6}-" + }, + { + "npc_id": "7022", + "loc_data": "{2503,2821,0,1,1}-{2500,2818,0,1,4}-{2503,2819,0,1,3}-" + }, + { + "npc_id": "7023", + "loc_data": "{2552,2821,0,1,4}-" + }, + { + "npc_id": "7024", + "loc_data": "{2523,2823,0,1,1}-" + }, + { + "npc_id": "7025", + "loc_data": "{2537,2820,0,1,1}-" + }, + { + "npc_id": "7026", + "loc_data": "{2543,2822,0,1,1}-" + }, + { + "npc_id": "7027", + "loc_data": "{2609,2863,0,1,0}-" + }, + { + "npc_id": "7028", + "loc_data": "{2610,2865,0,1,0}-" + }, + { + "npc_id": "7029", + "loc_data": "{2611,2862,0,1,0}-" + }, + { + "npc_id": "7030", + "loc_data": "{2611,2864,0,1,0}-" + }, + { + "npc_id": "7031", + "loc_data": "{2588,2801,0,1,1}-{2566,2820,0,1,5}-{2575,2818,0,1,4}-{2578,2819,0,1,4}-{2587,2819,0,1,7}-{2595,2821,0,1,4}-{2595,2826,0,1,0}-{2562,2819,0,1,6}-{2565,2828,0,1,0}-{2573,2825,0,1,1}-{2578,2820,0,1,6}-{2584,2822,0,1,4}-{2590,2831,0,1,4}-{2594,2823,0,1,4}-{2488,2768,0,1,1}-{2499,2820,0,1,4}-{2511,2819,0,1,3}-{2525,2820,0,1,0}-{2527,2818,0,1,3}-{2542,2819,0,1,4}-{2551,2817,0,1,3}-{2554,2819,0,1,3}-{2559,2819,0,1,4}-{2503,2824,0,1,3}-{2509,2822,0,1,4}-{2518,2825,0,1,7}-{2526,2822,0,1,1}-{2547,2822,0,1,4}-{2557,2826,0,1,4}-{2502,2823,0,1,4}-{2513,2820,0,1,7}-{2527,2819,0,1,4}-{2529,2818,0,1,6}-{2541,2822,0,1,3}-{2549,2817,0,1,2}-{2557,2818,0,1,2}-" + }, + { + "npc_id": "7032", + "loc_data": "{2539,2838,0,1,1}-" + }, + { + "npc_id": "7033", + "loc_data": "{2541,2837,0,1,1}-" + }, + { + "npc_id": "7034", + "loc_data": "{2536,2839,0,1,1}-" + }, + { + "npc_id": "7035", + "loc_data": "{2542,2839,0,1,1}-" + }, + { + "npc_id": "7036", + "loc_data": "{2538,2841,0,1,1}-" + }, + { + "npc_id": "7039", + "loc_data": "{2560,2822,0,1,1}-{2570,2823,0,1,1}-{2582,2824,0,1,6}-{2588,2828,0,1,4}-{2503,2824,0,1,4}-{2518,2824,0,1,4}-{2534,2821,0,1,1}-{2548,2823,0,1,4}-" + }, + { + "npc_id": "7040", + "loc_data": "{2562,2863,0,1,4}-{2617,5539,0,1,4}-" + }, + { + "npc_id": "7041", + "loc_data": "{2562,2863,0,1,4}-{2616,5540,0,1,4}-" + }, + { + "npc_id": "7042", + "loc_data": "{2605,5540,0,1,6}-{2551,2863,0,1,6}-" + }, + { + "npc_id": "7043", + "loc_data": "{2607,5539,0,1,6}-{2553,2862,0,1,6}-" + }, + { + "npc_id": "7044", + "loc_data": "{2467,2933,0,0,4}-{2470,2937,0,0,4}-{2445,2897,0,0,4}-{2444,2894,0,0,4}-" + }, + { + "npc_id": "7045", + "loc_data": "{2460,2922,0,0,4}-{2463,2928,0,0,4}-" + }, + { + "npc_id": "7046", + "loc_data": "{3364,3800,0,0,4}-{3368,3811,0,0,4}-{3347,3814,0,0,4}-{3363,3816,0,0,4}-{2460,2925,0,0,4}-" + }, + { + "npc_id": "7047", + "loc_data": "{2611,5513,0,0,1}-{2557,2836,0,1,1}-" + }, + { + "npc_id": "7048", + "loc_data": "{2599,5513,0,0,0}-{2545,2836,0,0,0}-" + }, + { + "npc_id": "7049", + "loc_data": "{2553,2839,0,0,4}-{2553,2836,0,0,4}-" + }, + { + "npc_id": "7051", + "loc_data": "{2547,2852,0,1,0}-" + }, + { + "npc_id": "7052", + "loc_data": "{2571,2855,0,1,3}-" + }, + { + "npc_id": "7054", + "loc_data": "{2560,2851,0,0,1}-" + }, + { + "npc_id": "7055", + "loc_data": "{2559,2851,0,0,1}-" + }, + { + "npc_id": "7056", + "loc_data": "{2560,2851,0,1,6}-{2613,5528,0,0,6}-" + }, + { + "npc_id": "7057", + "loc_data": "{2596,2845,0,1,0}-" + }, + { + "npc_id": "7062", + "loc_data": "{2571,2840,0,1,4}-" + }, + { + "npc_id": "7065", + "loc_data": "{2621,2857,0,1,3}-" + }, + { + "npc_id": "7066", + "loc_data": "{2619,2856,0,1,3}-" + }, + { + "npc_id": "7067", + "loc_data": "{2572,5519,0,0,6}-{2518,2842,0,1,0}-" + }, + { + "npc_id": "7068", + "loc_data": "{2585,5527,0,0,6}-{2531,2850,0,1,6}-" + }, + { + "npc_id": "7069", + "loc_data": "{2613,5535,0,0,6}-{2559,2858,0,1,0}-" + }, + { + "npc_id": "7070", + "loc_data": "{2581,2866,0,1,6}-" + }, + { + "npc_id": "7071", + "loc_data": "{2598,2864,0,1,6}-" + }, + { + "npc_id": "7072", + "loc_data": "{2517,2840,0,1,6}-" + }, + { + "npc_id": "7073", + "loc_data": "{2533,2848,0,1,6}-" + }, + { + "npc_id": "7074", + "loc_data": "{2556,2858,0,1,6}-" + }, + { + "npc_id": "7075", + "loc_data": "{2583,2865,0,1,6}-" + }, + { + "npc_id": "7076", + "loc_data": "{2596,2865,0,1,6}-" + }, + { + "npc_id": "7077", + "loc_data": "{2617,2856,0,1,3}-" + }, + { + "npc_id": "7078", + "loc_data": "{2451,2857,0,1,0}-{2453,2874,0,1,0}-{2475,2868,0,1,0}-{2477,2832,0,1,0}-{2475,2896,0,1,0}-{2476,2883,0,1,0}-{2502,2838,0,1,0}-" + }, + { + "npc_id": "7079", + "loc_data": "{2451,2849,0,1,0}-{2463,2872,0,1,0}-{2470,2853,0,1,0}-{2487,2837,0,1,0}-{2477,2903,0,1,0}-{2478,2907,0,1,0}-{2501,2851,0,1,0}-" + }, + { + "npc_id": "7080", + "loc_data": "{2562,5530,0,0,6}-{2456,2867,0,1,0}-{2457,2836,0,1,0}-{2484,2873,0,1,0}-{2487,2856,0,1,0}-{2480,2916,0,1,0}-{2492,2888,0,1,0}-{2508,2853,0,1,0}-" + }, + { + "npc_id": "7081", + "loc_data": "{2446,2869,0,1,0}-{2451,2828,0,1,0}-{2467,2860,0,1,0}-{2487,2844,0,1,0}-{2480,2892,0,1,0}-{2484,2917,0,1,0}-{2499,2860,0,1,0}-" + }, + { + "npc_id": "7082", + "loc_data": "{2569,5541,0,0,6}-{2440,2858,0,1,0}-{2459,2859,0,1,0}-{2471,2839,0,1,0}-{2482,2863,0,1,0}-{2483,2883,0,1,0}-{2491,2901,0,1,0}-{2516,2858,0,1,0}-" + }, + { + "npc_id": "7091", + "loc_data": "{2609,2863,0,0,0}-" + }, + { + "npc_id": "7092", + "loc_data": "{2610,2865,0,0,0}-" + }, + { + "npc_id": "7093", + "loc_data": "{2611,2862,0,0,0}-" + }, + { + "npc_id": "7094", + "loc_data": "{2611,2864,0,0,0}-" + }, + { + "npc_id": "7095", + "loc_data": "{2609,2863,0,0,0}-{2610,2862,0,0,0}-" + }, + { + "npc_id": "7096", + "loc_data": "{2610,2865,0,0,0}-{2605,2866,0,0,0}-" + }, + { + "npc_id": "7097", + "loc_data": "{2611,2862,0,0,0}-{2607,2867,0,0,0}-" + }, + { + "npc_id": "7098", + "loc_data": "{2611,2864,0,0,0}-{2608,2863,0,0,0}-" + }, + { + "npc_id": "7099", + "loc_data": "{2585,2844,0,1,3}-" + }, + { + "npc_id": "7100", + "loc_data": "{2586,2840,0,0,4}-" + }, + { + "npc_id": "7101", + "loc_data": "{2585,2842,0,1,1}-" + }, + { + "npc_id": "7102", + "loc_data": "{2576,2840,0,0,0}-{2576,2840,0,0,0}-" + }, + { + "npc_id": "7103", + "loc_data": "{2579,2843,0,0,0}-{2582,2843,0,0,0}-{2579,2843,0,0,0}-{2582,2843,0,0,0}-" + }, + { + "npc_id": "7104", + "loc_data": "{2603,5529,0,0,4}-{2549,2852,0,1,4}-" + }, + { + "npc_id": "7105", + "loc_data": "{2612,9484,0,1,1}-{2611,9484,0,1,0}-{2614,9484,0,1,6}-{2611,9486,0,1,1}-{3111,9929,0,1,7}-{3106,9941,0,1,1}-{3111,9935,0,1,4}-{3114,9931,0,1,1}-" + }, + { + "npc_id": "7106", + "loc_data": "{3131,9930,0,1,4}-" + }, + { + "npc_id": "7107", + "loc_data": "{3130,9928,0,1,0}-{3131,9931,0,1,4}-" + }, + { + "npc_id": "7109", + "loc_data": "{3127,9932,0,1,1}-" + }, + { + "npc_id": "7110", + "loc_data": "{3121,9929,0,1,4}-" + }, + { + "npc_id": "7112", + "loc_data": "{3130,9934,0,1,3}-" + }, + { + "npc_id": "7113", + "loc_data": "{3127,9932,0,1,4}-" + }, + { + "npc_id": "7114", + "loc_data": "{3132,9921,0,0,0}-{3132,9935,0,1,1}-" + }, + { + "npc_id": "7115", + "loc_data": "{3050,3259,0,0,0}-" + }, + { + "npc_id": "7121", + "loc_data": "{2189,3148,0,1,4}-" + }, + { + "npc_id": "7125", + "loc_data": "{1605,4702,0,1,0}-{1649,4730,0,1,0}-" + }, + { + "npc_id": "7126", + "loc_data": "{1605,4720,0,1,0}-{1658,4705,0,1,0}-{1633,4678,0,1,0}-" + }, + { + "npc_id": "7127", + "loc_data": "{1631,4730,0,1,0}-{1658,4687,0,1,0}-{1615,4678,0,1,0}-" + }, + { + "npc_id": "7128", + "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}-" + }, + { + "npc_id": "7129", + "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}-" + }, + { + "npc_id": "7130", + "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}-" + }, + { + "npc_id": "7131", + "loc_data": "{1634,4699,0,1,0}-" + }, + { + "npc_id": "7132", + "loc_data": "{1630,4705,0,0,0}-" + }, + { + "npc_id": "7138", + "loc_data": "{3190,5523,0,1,2}-{3195,5527,0,1,6}-{3245,5462,0,1,7}-{3257,5454,0,1,3}-{3255,5461,0,1,1}-{3251,5473,0,1,3}-{3213,5472,0,1,3}-{3210,5469,0,1,5}-{3221,5468,0,1,0}-{3223,5465,0,1,0}-{3224,5540,0,1,0}-{3240,5541,0,1,6}-{3239,5529,0,1,6}-{3233,5524,0,1,4}-{3237,5512,0,1,6}-{3225,5510,0,1,1}-{3222,5524,0,1,1}-{3225,5540,0,1,7}-{3218,5537,0,1,3}-" + }, + { + "npc_id": "7139", + "loc_data": "{3220,5463,0,1,7}-" + }, + { + "npc_id": "7142", + "loc_data": "{3082,4246,0,1,4}-" + }, + { + "npc_id": "7143", + "loc_data": "{3082,3459,0,0,0}-" + }, + { + "npc_id": "7144", + "loc_data": "{3085,4230,0,1,3}-" + }, + { + "npc_id": "7145", + "loc_data": "{3081,4252,0,1,7}-" + }, + { + "npc_id": "7146", + "loc_data": "{3078,4240,0,1,4}-" + }, + { + "npc_id": "7147", + "loc_data": "{3079,4245,0,1,4}-" + }, + { + "npc_id": "7148", + "loc_data": "{3077,4229,0,1,4}-" + }, + { + "npc_id": "7149", + "loc_data": "{3085,4235,0,1,4}-" + }, + { + "npc_id": "7150", + "loc_data": "{3085,4240,0,1,3}-" + }, + { + "npc_id": "7151", + "loc_data": "{3081,3457,0,0,4}-" + }, + { + "npc_id": "7152", + "loc_data": "{3081,3455,0,0,4}-" + }, + { + "npc_id": "7153", + "loc_data": "{3081,3453,0,0,4}-" + }, + { + "npc_id": "7154", + "loc_data": "{3079,3457,0,0,4}-" + }, + { + "npc_id": "7155", + "loc_data": "{3079,3455,0,0,4}-" + }, + { + "npc_id": "7156", + "loc_data": "{3077,3457,0,0,4}-" + }, + { + "npc_id": "7157", + "loc_data": "{3077,3453,0,0,4}-" + }, + { + "npc_id": "7158", + "loc_data": "{3080,3463,0,1,0}-{3079,3463,0,1,0}-{3144,4250,2,1,4}-{3143,4255,2,1,2}-{3140,4253,2,1,3}-{3145,4255,2,1,6}-{3149,4257,2,1,5}-{3148,4262,2,1,5}-{3142,4260,2,1,3}-{3144,4259,2,1,4}-{3145,4261,2,1,4}-{3146,4265,2,1,5}-{3143,4264,2,1,3}-{3144,4266,2,1,5}-{3141,4265,2,1,1}-{3144,4270,2,1,1}-{3142,4269,2,1,6}-{3157,4262,2,1,1}-{3164,4265,2,1,6}-{3162,4266,2,1,6}-{3165,4266,2,1,3}-{3164,4261,2,1,2}-{3162,4259,2,1,5}-{3162,4255,2,1,7}-{3165,4256,2,1,4}-{3163,4251,2,1,3}-{3165,4249,2,1,6}-{3162,4250,2,1,4}-{3163,4241,2,1,4}-{3165,4239,2,1,4}-{3165,4241,2,1,0}-{3160,4235,2,1,2}-{3165,4235,2,1,4}-{3165,4237,2,1,4}-{3162,4234,2,1,3}-{3164,4231,2,1,4}-{3166,4229,2,1,6}-{3162,4230,2,1,1}-{3167,4229,2,1,3}-{3184,4236,2,1,1}-{3153,4246,1,1,3}-{3159,4236,1,1,6}-{3163,4236,1,1,4}-{3163,4234,1,1,7}-{3146,4235,1,1,5}-{3144,4236,1,1,1}-{3143,4234,1,1,2}-{3145,4240,1,1,4}-{3148,4242,1,1,1}-{3145,4241,1,1,7}-{3142,4242,1,1,3}-{3146,4230,1,1,4}-" + }, + { + "npc_id": "7159", + "loc_data": "{3174,4238,2,1,6}-{3175,4243,2,1,6}-{3179,4236,2,1,4}-{3145,4272,1,1,3}-{3145,4276,1,1,3}-{3152,4271,1,1,6}-{3144,4264,1,1,4}-{3153,4266,1,1,1}-{3152,4261,1,1,4}-{3145,4259,1,1,6}-{3161,4259,1,1,0}-{3160,4265,1,1,0}-{3153,4246,1,1,3}-{3157,4248,1,1,6}-{3161,4247,1,1,4}-{3146,4247,1,1,6}-{3153,4233,1,1,4}-{3153,4240,1,1,6}-{3155,4238,1,1,5}-{3169,4267,3,1,3}-{3160,4265,3,1,6}-{3168,4250,3,1,4}-{3168,4244,3,1,6}-{3160,4239,3,1,1}-{3160,4228,3,1,3}-{3147,4225,3,1,3}-{3145,4242,3,1,5}-{3139,4249,3,1,6}-{3144,4253,3,1,3}-{3145,4266,3,1,0}-{3138,4266,3,1,3}-{3151,4272,3,1,0}-{3139,4272,3,1,0}-{3146,4231,3,1,0}-{3169,4270,3,1,0}-" + }, + { + "npc_id": "7160", + "loc_data": "{3175,4254,2,1,7}-{3176,4247,2,1,5}-{3174,4269,2,1,1}-{3190,4253,2,1,3}-{3192,4247,2,1,3}-{3189,4235,2,1,0}-{3152,4269,0,1,4}-{3160,4278,0,1,1}-{3160,4262,0,1,3}-{3149,4250,0,1,0}-{3158,4250,0,1,5}-{3161,4236,0,1,6}-{3172,4250,0,1,2}-{3171,4269,3,1,1}-{3157,4277,3,1,3}-{3150,4278,3,1,3}-{3160,4271,1,1,0}-{3163,4274,1,1,0}-{3167,4277,1,1,0}-{3173,4277,1,1,0}-{3175,4275,1,1,0}-{3160,4269,0,1,0}-{3173,4264,0,1,0}-{3190,4237,2,1,0}-{3154,4279,3,1,0}-" + }, + { + "npc_id": "7161", + "loc_data": "{3142,4230,2,1,6}-{3143,4231,2,1,3}-{3141,4235,2,1,4}-{3144,4241,2,1,4}-" + }, + { + "npc_id": "7162", + "loc_data": "{3144,4229,2,1,1}-{3143,4236,2,1,1}-{3145,4235,2,1,1}-{3148,4244,2,1,6}-" + }, + { + "npc_id": "7168", + "loc_data": "{2714,3309,0,1,1}-" + }, + { + "npc_id": "7228", + "loc_data": "{2862,3101,0,0,0}-{2879,3109,0,0,0}-{2874,3098,0,0,0}-{2882,3104,0,0,0}-{2882,3098,0,0,0}-" + }, + { + "npc_id": "7229", + "loc_data": "{2833,3088,0,0,0}-{2830,3085,0,0,0}-{2844,3089,0,0,0}-{2837,3084,0,0,0}-{2840,3076,0,0,0}-" + }, + { + "npc_id": "7230", + "loc_data": "{2840,3008,0,0,0}-{2887,3051,0,0,0}-{2890,3052,0,0,0}-{2894,3058,0,0,0}-{2897,3035,0,0,0}-" + }, + { + "npc_id": "7231", + "loc_data": "{2930,2961,0,0,0}-{2942,2964,0,0,0}-{2933,2964,0,0,0}-{2932,2967,0,0,0}-{2936,2973,0,0,0}-" + }, + { + "npc_id": "7232", + "loc_data": "{2802,3106,0,0,0}-{2807,3101,0,0,0}-{2806,3108,0,0,0}-{2808,3105,0,0,0}-{2814,3100,0,0,0}-" + }, + { + "npc_id": "7233", + "loc_data": "{2922,3070,0,0,0}-{2929,3075,0,0,0}-{2919,3080,0,0,0}-{2924,3080,0,0,0}-{2923,3081,0,0,0}-" + }, + { + "npc_id": "7234", + "loc_data": "{2900,3107,0,0,0}-{2903,3098,0,0,0}-{2908,3104,0,0,0}-{2905,3107,0,0,0}-{2904,3107,0,0,0}-" + }, + { + "npc_id": "7235", + "loc_data": "{2820,3065,0,0,0}-{2830,3059,0,0,0}-{2825,3057,0,0,0}-{2827,3050,0,0,0}-{2822,3053,0,0,0}-" + }, + { + "npc_id": "7236", + "loc_data": "{2799,3005,0,0,0}-{2801,2995,0,0,0}-{2804,3001,0,0,0}-{2803,2986,0,0,0}-{2811,2999,0,0,0}-" + }, + { + "npc_id": "7275", + "loc_data": "{2995,3252,0,0,0}-{2996,3259,0,0,0}-{2993,3250,0,0,0}-{3000,3258,0,0,0}-" + }, + { + "npc_id": "7276", + "loc_data": "{2994,3256,0,0,0}-{3002,3250,0,0,0}-{2992,3252,0,0,0}-{3000,3250,0,0,0}-{2728,3422,0,1,1}-{2718,3413,0,1,4}-{2729,3420,0,1,4}-{2727,3414,0,1,6}-{2714,3419,0,1,3}-{2719,3427,0,1,7}-" + }, + { + "npc_id": "7285", + "loc_data": "{2835,3107,0,0,0}-{2836,3101,0,0,0}-{2836,3110,0,0,0}-{2837,3112,0,0,0}-{2841,3107,0,0,0}-{2885,3018,0,0,0}-{2885,3019,0,0,0}-{2891,3018,0,0,0}-" + }, + { + "npc_id": "7286", + "loc_data": "{2931,3090,0,0,0}-{2925,3097,0,0,0}-{2936,3096,0,0,0}-{2924,3102,0,0,0}-{2933,3104,0,0,0}-" + }, + { + "npc_id": "7288", + "loc_data": "{2834,3045,0,0,0}-{2826,3048,0,0,0}-{2824,3043,0,0,0}-{2822,3045,0,0,0}-{2818,3049,0,0,0}-" + }, + { + "npc_id": "7289", + "loc_data": "{2882,3018,0,1,7}-{2887,3024,0,1,2}-" + }, + { + "npc_id": "7290", + "loc_data": "{2845,3088,0,1,6}-{2854,3085,0,1,1}-" + }, + { + "npc_id": "7292", + "loc_data": "{2863,3048,0,1,5}-{2868,3037,0,1,4}-" + }, + { + "npc_id": "7309", + "loc_data": "{2946,3280,0,0,0}-{3023,3410,0,0,0}-" + }, + { + "npc_id": "7310", + "loc_data": "{2946,3272,0,0,0}-{3015,3409,0,0,0}-" + }, + { + "npc_id": "7311", + "loc_data": "{2956,3273,0,0,0}-" + }, + { + "npc_id": "7312", + "loc_data": "{2950,3268,0,0,0}-" + }, + { + "npc_id": "7420", + "loc_data": "{3168,3334,0,0,3}-" + }, + { + "npc_id": "7421", + "loc_data": "{3163,3336,0,0,1}-" + }, + { + "npc_id": "7451", + "loc_data": "{2328,3170,0,1,6}-{2341,3179,0,1,4}-{2338,3164,0,1,4}-{2189,3181,0,1,2}-{2178,3186,0,1,7}-{2199,3182,0,1,0}-{2288,3149,0,1,2}-{2282,3144,0,1,3}-{2285,3144,0,1,2}-{2281,3139,0,1,1}-{2289,3152,0,1,6}-{2287,3155,0,1,1}-{2286,3146,0,1,1}-{2289,3156,0,0,5}-{2285,3136,0,1,3}-" + }, + { + "npc_id": "7600", + "loc_data": "{1714,5602,0,0,6}-" + }, + { + "npc_id": "7601", + "loc_data": "{1697,5605,0,0,6}-" + }, + { + "npc_id": "7602", + "loc_data": "{1693,5598,0,1,0}-" + }, + { + "npc_id": "7603", + "loc_data": "{1696,5600,0,1,0}-" + }, + { + "npc_id": "7604", + "loc_data": "{1699,5599,0,1,0}-" + }, + { + "npc_id": "7605", + "loc_data": "{1705,5599,0,0,1}-" + }, + { + "npc_id": "7636", + "loc_data": "{3618,9736,0,0,0}-{3620,9732,0,0,0}-{3624,9736,0,0,0}-{3625,9738,0,0,0}-{3618,9736,0,0,4}-{3620,9732,0,0,4}-{3624,9736,0,0,4}-{3625,9738,0,0,4}-" + }, + { + "npc_id": "7637", + "loc_data": "{3588,9768,0,0,0}-{3600,9768,0,0,0}-{3606,9780,0,0,0}-{3612,9729,0,0,0}-{3613,9764,0,0,0}-{3614,9740,0,0,0}-{3614,9750,0,0,0}-{3621,9748,0,0,0}-{3629,9737,0,0,0}-" + }, + { + "npc_id": "7639", + "loc_data": "{3592,9766,0,0,0}-{3603,9770,0,0,0}-{3605,9757,0,0,0}-{3611,9749,0,0,0}-{3611,9766,0,0,0}-{3616,9737,0,0,0}-{3616,9756,0,0,0}-{3616,9762,0,0,0}-{3617,9779,0,0,0}-{3621,9754,0,0,0}-" + }, + { + "npc_id": "7640", + "loc_data": "{3608,9780,0,1,3}-{3610,9777,0,1,1}-{3613,9772,0,1,4}-{3617,9748,0,1,7}-{3617,9758,0,1,1}-{3621,9751,0,1,1}-" + }, + { + "npc_id": "7641", + "loc_data": "{3607,9776,0,1,1}-{3610,9772,0,1,0}-{3614,9778,0,1,7}-{3615,9746,0,1,7}-{3618,9753,0,1,1}-{3619,9748,0,1,6}-" + }, + { + "npc_id": "7642", + "loc_data": "{3595,9742,0,0,0}-{3597,9751,0,0,0}-{3598,9736,0,0,0}-{3604,9746,0,0,0}-" + }, + { + "npc_id": "7643", + "loc_data": "{3587,9737,0,0,0}-{3592,9746,0,0,0}-{3600,9740,0,0,0}-{3605,9738,0,0,0}-" + }, + { + "npc_id": "7690", + "loc_data": "{3415,3489,2,0,0}-" + }, + { + "npc_id": "7707", + "loc_data": "{3440,9895,0,0,0}-" + }, + { + "npc_id": "7711", + "loc_data": "{3405,9902,0,0,0}-" + }, + { + "npc_id": "7716", + "loc_data": "{3005,3475,0,1,7}-{3003,3473,0,1,4}-{2997,3471,0,0,0}-{3004,3494,0,0,0}-{3005,3505,0,0,0}-" + }, + { + "npc_id": "7724", + "loc_data": "{3054,3503,0,1,0}-" + }, + { + "npc_id": "7726", + "loc_data": "{2996,3451,0,0,0}-" + }, + { + "npc_id": "7727", + "loc_data": "{3045,3483,0,0,0}-{3053,3492,0,0,4}-{3055,3491,0,0,0}-{3045,3496,0,0,0}-{3046,3490,1,0,0}-{3057,3497,1,0,3}-" + }, + { + "npc_id": "7728", + "loc_data": "{3009,3433,0,0,0}-" + }, + { + "npc_id": "7731", + "loc_data": "{3008,3466,0,0,0}-" + }, + { + "npc_id": "7732", + "loc_data": "{3009,3466,0,0,0}-" + }, + { + "npc_id": "7733", + "loc_data": "{3008,3479,0,0,0}-" + }, + { + "npc_id": "7734", + "loc_data": "{3009,3487,0,0,0}-" + }, + { + "npc_id": "7735", + "loc_data": "{2999,3469,0,0,0}-" + }, + { + "npc_id": "7744", + "loc_data": "{3149,3411,0,0,0}-" + }, + { + "npc_id": "7746", + "loc_data": "{4755,5116,0,1,7}-" + }, + { + "npc_id": "7747", + "loc_data": "{4751,5116,0,1,5}-" + }, + { + "npc_id": "7748", + "loc_data": "{4752,5114,0,1,5}-" + }, + { + "npc_id": "7749", + "loc_data": "{4739,5070,0,1,0}-" + }, + { + "npc_id": "7750", + "loc_data": "{4765,5064,0,1,7}-" + }, + { + "npc_id": "7751", + "loc_data": "{4778,5070,0,1,2}-" + }, + { + "npc_id": "7752", + "loc_data": "{4432,5096,0,1,0}-" + }, + { + "npc_id": "7753", + "loc_data": "{4438,5089,0,1,0}-{4428,5083,0,1,0}-{4437,5075,0,1,0}-" + }, + { + "npc_id": "7763", + "loc_data": "{4777,5080,0,1,2}-" + }, + { + "npc_id": "7765", + "loc_data": "{4776,5076,0,1,6}-" + }, + { + "npc_id": "7767", + "loc_data": "{2457,5139,0,1,6}-{2460,5140,0,1,0}-{2443,5147,0,1,3}-{2454,5155,0,1,5}-{2461,5125,0,1,3}-{2456,5124,0,1,4}-{2458,5129,0,1,4}-{2443,5145,0,1,1}-{2451,5148,0,1,1}-{2447,5143,0,1,4}-{2443,5138,0,1,4}-{4762,5145,0,1,2}-" + }, + { + "npc_id": "7768", + "loc_data": "{4762,5156,0,1,6}-" + }, + { + "npc_id": "7780", + "loc_data": "{3358,2993,0,1,0}-" + }, + { + "npc_id": "7786", + "loc_data": "{3348,9388,0,1,0}-{3350,9393,0,1,0}-{3350,9400,0,1,0}-{3350,9406,0,1,0}-{3351,9380,0,1,0}-{3352,9374,0,1,0}-{3354,9387,0,1,0}-{3358,9378,0,1,0}-{3358,9396,0,1,0}-{3358,9406,0,1,0}-{3365,9381,0,1,0}-{3365,9387,0,1,0}-{3365,9402,0,1,0}-{3369,9390,0,1,0}-{3350,9412,0,1,0}-{3351,9420,0,1,0}-{3356,9414,0,1,0}-{3358,9421,0,1,0}-{3364,9424,0,1,0}-{3365,9416,0,1,0}-" + }, + { + "npc_id": "7787", + "loc_data": "{3301,4413,0,1,0}-{3302,4394,0,1,0}-{3304,4397,0,1,0}-{3306,4410,0,1,0}-{3307,4383,0,1,0}-{3307,4397,0,1,0}-{3308,4407,0,1,0}-{3310,4383,0,1,0}-{3310,4386,0,1,0}-{3310,4398,0,1,0}-{3311,4407,0,1,0}-{3312,4380,0,1,0}-{3313,4401,0,1,0}-{3314,4411,0,1,0}-{3316,4376,0,1,0}-{3316,4382,0,1,0}-{3316,4387,0,1,0}-{3316,4397,0,1,0}-{3316,4401,0,1,0}-{3316,4405,0,1,0}-{3317,4391,0,1,0}-{3318,4412,0,1,0}-{3319,4373,0,1,0}-{3319,4407,0,1,0}-{3321,4385,0,1,0}-{3321,4398,0,1,0}-{3322,4412,0,1,0}-{3323,4402,0,1,0}-{3324,4394,0,1,0}-{3324,4397,0,1,0}-{3325,4410,0,1,0}-" + }, + { + "npc_id": "7801", + "loc_data": "{3283,4346,0,1,0}-{3308,4349,0,1,0}-{3293,4375,0,1,0}-{3303,4363,0,1,0}-" + }, + { + "npc_id": "7802", + "loc_data": "{3288,4350,0,1,0}-{3296,4340,0,1,0}-{3288,4361,0,1,0}-{3310,4355,0,1,0}-" + }, + { + "npc_id": "7803", + "loc_data": "{3297,4347,0,1,0}-{3315,4346,0,1,0}-{3282,4357,0,1,0}-{3303,4369,0,1,0}-" + }, + { + "npc_id": "7804", + "loc_data": "{3279,4350,0,1,0}-{3294,4353,0,1,0}-{3294,4366,0,1,0}-" + }, + { + "npc_id": "7891", + "loc_data": "{3207,3250,0,0,0}-{3208,3250,0,0,0}-{3209,3250,0,0,0}-" + }, + { + "npc_id": "7942", + "loc_data": "{3294,4937,0,0,6}-" + }, + { + "npc_id": "7959", + "loc_data": "{3188,3425,0,1,0}-" + }, + { + "npc_id": "7969", + "loc_data": "{3205,3240,0,0,0}-" + }, + { + "npc_id": "8000", + "loc_data": "{2987,3691,0,1,3}-" + }, + { + "npc_id": "8041", + "loc_data": "{2794,3100,0,1,0}-" + }, + { + "npc_id": "8065", + "loc_data": "{3399,3464,0,0,1}-" + }, + { + "npc_id": "8066", + "loc_data": "{3346,3513,0,0,6}-" + }, + { + "npc_id": "8067", + "loc_data": "{3280,3524,0,0,0}-" + }, + { + "npc_id": "8068", + "loc_data": "{3237,3525,0,0,0}-" + }, + { + "npc_id": "8069", + "loc_data": "{3171,3535,0,0,0}-" + }, + { + "npc_id": "8070", + "loc_data": "{3088,3515,0,0,0}-" + }, + { + "npc_id": "8071", + "loc_data": "{3035,3517,0,0,0}-" + }, + { + "npc_id": "8072", + "loc_data": "{2972,3515,0,0,0}-" + }, + { + "npc_id": "8073", + "loc_data": "{2941,3564,0,0,0}-" + }, + { + "npc_id": "8074", + "loc_data": "{2946,3621,0,0,0}-" + }, + { + "npc_id": "8075", + "loc_data": "{2938,3679,0,0,0}-" + }, + { + "npc_id": "8076", + "loc_data": "{2939,3772,0,0,0}-" + }, + { + "npc_id": "8082", + "loc_data": "{3552,5590,2,1,2}-" + }, + { + "npc_id": "8083", + "loc_data": "{3560,5602,0,1,3}-" + }, + { + "npc_id": "8084", + "loc_data": "{3541,5597,0,1,4}-" + }, + { + "npc_id": "8085", + "loc_data": "{3550,5598,0,1,3}-" + }, + { + "npc_id": "8111", + "loc_data": "{3273,3681,0,1,0}-" + }, + { + "npc_id": "8133", + "loc_data": "{2993,4380,2,1,0}-" + }, + { + "npc_id": "8149", + "loc_data": "{3244,9996,0,1,6}-{3229,10008,0,1,6}-{3222,10012,0,1,6}-{3205,10010,0,1,6}-{3212,10012,0,1,6}-{3205,10023,0,1,6}-{3210,10030,0,1,6}-{3212,10039,0,1,6}-{3213,10009,0,1,6}-{3243,9993,0,1,6}-{3218,10036,0,1,6}-{3218,10010,0,1,6}-" + }, + { + "npc_id": "8150", + "loc_data": "{3241,9997,0,1,6}-{3230,10011,0,1,6}-{3221,10012,0,1,6}-{3207,10006,0,1,6}-{3207,10017,0,1,6}-{3209,10031,0,1,6}-{3209,10019,0,1,6}-{3217,10012,0,1,6}-{3224,10012,0,1,6}-" + }, + { + "npc_id": "8151", + "loc_data": "{3241,9998,0,1,6}-{3223,10010,0,1,6}-{3208,10012,0,1,6}-{3209,10026,0,1,6}-{3214,10038,0,1,6}-{3211,10035,0,1,6}-{3214,10037,0,1,6}-{3240,9991,0,1,6}-{3231,10010,0,1,6}-{3210,10007,0,1,6}-{3222,10010,0,1,6}-" + }, + { + "npc_id": "8171", + "loc_data": "{3281,3403,0,1,6}-" + }, + { + "npc_id": "8227", + "loc_data": "{3135,3631,0,1,6}-" + }, + { + "npc_id": "8228", + "loc_data": "{3142,3635,0,1,0}-" + }, + { + "npc_id": "8229", + "loc_data": "{3128,3632,0,1,0}-{3135,3616,0,1,6}-" + }, + { + "npc_id": "8231", + "loc_data": "{3135,3627,0,1,0}-" + }, + { + "npc_id": "8233", + "loc_data": "{3133,3628,0,1,0}-" + }, + { + "npc_id": "8267", + "loc_data": "{2866,3546,0,1,1}-" + }, + { + "npc_id": "8273", + "loc_data": "{2931,3536,0,1,5}-" + }, + { + "npc_id": "8274", + "loc_data": "{3514,3513,0,1,6}-" + }, + { + "npc_id": "8275", + "loc_data": "{2869,2982,1,1,5}-" + }, + { + "npc_id": "8312", + "loc_data": "{3016,9974,1,1,0}-{3021,9940,1,1,0}-{3023,9940,1,1,0}-{3024,9959,1,1,0}-{3027,9960,1,1,0}-{3043,9967,1,1,0}-{3023,9992,1,1,0}-{3016,10021,2,1,0}-{3028,10013,2,1,0}-{3029,10028,2,1,0}-{3038,10005,2,1,0}-{3044,10001,2,1,0}-{3045,9993,2,1,0}-{3045,9999,2,1,0}-{3047,10000,2,1,0}-{3052,10009,2,1,0}-{3054,10001,2,1,0}-{3056,10006,2,1,0}-{3032,10090,1,1,0}-{3040,10096,1,1,0}-{3048,10095,1,1,0}-" + }, + { + "npc_id": "8316", + "loc_data": "{3017,9972,1,1,0}-{3025,9962,1,1,0}-{3030,9943,1,1,0}-{3033,9941,1,1,0}-{3034,9950,1,1,0}-{3035,9950,1,1,0}-{3036,9940,1,1,0}-{3038,9939,1,1,0}-{3045,9968,1,1,0}-{3058,9952,1,1,0}-{3063,9952,1,1,0}-{3025,9995,1,1,0}-{3027,10029,2,1,0}-{3051,10006,2,1,0}-{3052,10002,2,1,0}-{3054,10005,2,1,0}-{3027,10101,1,1,0}-{3039,10100,1,1,0}-{3054,10097,1,1,0}-" + }, + { + "npc_id": "8320", + "loc_data": "{3014,9971,1,1,0}-{3016,9969,1,1,0}-{3028,9942,1,1,0}-{3029,9943,1,1,0}-{3030,9951,1,1,0}-{3033,9950,1,1,0}-{3045,9965,1,1,0}-{3061,9953,1,1,0}-{3062,9953,1,1,0}-{3030,9995,1,1,0}-{3016,10023,2,1,0}-{3029,10014,2,1,0}-{3029,10030,2,1,0}-{3036,10005,2,1,0}-{3042,9994,2,1,0}-{3047,10005,2,1,0}-{3049,10002,2,1,0}-{3053,9998,2,1,0}-{3031,10100,1,1,0}-{3044,10098,1,1,0}-{3049,10102,1,1,0}-" + }, + { + "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}-{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", + "loc_data": "{3055,10103,1,1,0}-" + }, + { + "npc_id": "8349", + "loc_data": "{2589,5735,0,1,0}-{2589,5713,0,1,0}-{2610,5709,0,1,0}-{2613,5732,0,1,0}-" + }, + { + "npc_id": "8358", + "loc_data": "{2601,5710,0,1,0}-{2603,5737,0,1,0}-" + }, + { + "npc_id": "8380", + "loc_data": "{2907,3806,0,1,0}-" + }, + { + "npc_id": "8381", + "loc_data": "{2910,3805,0,1,0}-" + }, + { + "npc_id": "8382", + "loc_data": "{2912,3812,0,1,0}-{2923,3823,0,1,0}-" + }, + { + "npc_id": "8383", + "loc_data": "{2908,3809,0,1,0}-{2919,3823,0,1,0}-" + }, + { + "npc_id": "8384", + "loc_data": "{2922,3827,0,1,0}-" + }, + { + "npc_id": "8385", + "loc_data": "{2924,3825,0,1,0}-" + }, + { + "npc_id": "8386", + "loc_data": "{2921,3824,0,1,0}-" + }, + { + "npc_id": "8387", + "loc_data": "{2934,3786,0,1,0}-" + }, + { + "npc_id": "8388", + "loc_data": "{2935,3784,0,1,0}-{2940,3827,0,1,0}-" + }, + { + "npc_id": "8389", + "loc_data": "{2937,3785,0,1,0}-" + }, + { + "npc_id": "8390", + "loc_data": "{2935,3781,0,1,0}-{2939,3829,0,1,0}-" + }, + { + "npc_id": "8391", + "loc_data": "{2937,3780,0,1,0}-{2940,3833,0,1,0}-" + }, + { + "npc_id": "8392", + "loc_data": "{2938,3782,0,1,0}-{2938,3831,0,1,0}-" + }, + { + "npc_id": "8536", + "loc_data": "{2654,5600,0,1,3}-{2650,5600,0,0,3}-{2662,5593,0,0,3}-{2653,5590,0,0,3}-{2644,5592,0,0,3}-{2644,5601,0,0,3}-{2654,5604,0,0,3}-{2663,5606,0,0,3}-{2670,5597,0,0,3}-{2657,5589,0,0,3}-" + }, + { + "npc_id": "8545", + "loc_data": "{3122,3626,0,1,0}-" + }, + { + "npc_id": "8590", + "loc_data": "{2743,3444,0,1,0}-" + } +] \ No newline at end of file diff --git a/Server/data/configs/object_configs.json b/Server/data/configs/object_configs.json index 6c887b349..ae364fd37 100644 --- a/Server/data/configs/object_configs.json +++ b/Server/data/configs/object_configs.json @@ -16324,7 +16324,7 @@ "ids": "11629" }, { - "examine": "South to Falador :: West to Taverley :: East to Varrock.", + "examine": "This tells you which way is which.", "ids": "11630,11631,11632,11633" }, { @@ -16741,7 +16741,7 @@ }, { "examine": "Better not eat them!", - "ids": "12003" + "ids": "12003,12128" }, { "examine": "It's going to be a tight squeeze!", @@ -16881,7 +16881,7 @@ }, { "examine": "A wall jutting out into the path.", - "ids": "12127,12128,12129,12130" + "ids": "12127,12129,12130" }, { "examine": "Spooky.", @@ -20055,10 +20055,18 @@ "examine": "Home sweet home?", "ids": "15480" }, + { + "examine": "Baby bread.", + "ids": "15506,15507" + }, { "examine": "Home sweet home?", "ids": "15748" }, + { + "examine": "Danger - Possibly deadly creatures below!", + "ids": "16083,16151,16117" + }, { "examine": "Large urn.", "ids": "17362" @@ -20179,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" @@ -20191,6 +20211,10 @@ "examine": "I wonder what this spooky contains.", "ids": "37051" }, + { + "examine": "The mine has collapsed.", + "ids": "37634" + }, { "examine": "It really was this big!", "ids": "40043" diff --git a/Server/data/configs/ranged_weapon_configs.json b/Server/data/configs/ranged_weapon_configs.json index 03f8d60dd..335c9f031 100644 --- a/Server/data/configs/ranged_weapon_configs.json +++ b/Server/data/configs/ranged_weapon_configs.json @@ -15,7 +15,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,13280" + "ammunition": "877,878,6061,6062,879,9236" }, { "itemId": "800", @@ -294,7 +294,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,13280" + "ammunition": "877,878,6061,6062,879,9236" }, { "itemId": "839", @@ -1095,7 +1095,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "8882,877,878,6061,6062,879,9236" + "ammunition": "8882,877,878,6061,6062,879,9236,9139,9286,9293,9300,9140,9287,9294,9237,9145,9301,9292,9299,9306,9335,880,9238" }, { "itemId": "9174", @@ -1104,7 +1104,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,13280" + "ammunition": "877,878,6061,6062,879,9236" }, { "itemId": "9176", @@ -1113,7 +1113,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,9140,879,9139,9236,13280" + "ammunition": "877,878,6061,6062,879,9139,9236,9286,9293,9300,9335,9237" }, { "itemId": "9177", @@ -1122,7 +1122,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,9139,9140,9287,9294,9301,880,9238,9145,13280" + "ammunition": "877,878,6061,6062,879,9236,9139,9140,9287,9294,9301,880,9238,9145,9286,9293,9300,9292,9299,9306,9335,9237" }, { "itemId": "9179", @@ -1131,7 +1131,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9139,9140,9236,9141,9288,9295,9302,9336,9239,9145,13280" + "ammunition": "877,878,6061,6062,879,9139,9140,9236,9141,9288,9295,9302,9336,9239,9145,9286,9293,9300,9287,9294,9301,9292,9299,9306,9335,9237,880,9238" }, { "itemId": "9181", @@ -1140,7 +1140,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,9141,9288,9295,9302,9336,9239,13083,13084,13085,13086,9142,9289,9296,9303,9337,9240,9338,9145,9139,9140,9241,13280" + "ammunition": "877,878,6061,6062,879,9236,9141,9288,9295,9302,9336,9239,13083,13084,13085,13086,9142,9289,9296,9303,9337,9240,9338,9145,9139,9140,9241,9286,9293,9300,9140,9287,9294,9301,9292,9299,9306,9335,9237,880,9238,13083,13084,13085,13086" }, { "itemId": "9183", @@ -1149,7 +1149,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,9139,9141,9288,9295,9302,9336,9239,13083,13084,13085,13086,9142,9289,9296,9303,9337,9240,9338,9241,9145,9143,9290,9297,9304,9339,9242,9340,9243,13280" + "ammunition": "877,878,6061,6062,879,9236,9139,9141,9288,9295,9302,9336,9239,13083,13084,13085,13086,9142,9289,9296,9303,9337,9240,9338,9241,9145,9143,9290,9297,9304,9339,9242,9340,9243,9286,9293,9300,9140,9287,9294,9301,9292,9299,9306,9335,9237,880,9238,13083,13084,13085,13086" }, { "itemId": "9185", @@ -1158,7 +1158,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,9139,9141,9288,9295,9302,9336,9239,9236,9237,9238,9239,9240,9241,9242,9243,9244,9245,9145,13083,13084,13085,13086,9142,9289,9296,9303,9337,9240,9338,9241,9143,9290,9297,9304,9339,9242,9340,9243,9144,9291,9298,9305,9341,9244,9342,9245,9140,13280" + "ammunition": "877,878,6061,6062,879,9236,9139,9141,9288,9295,9302,9336,9239,9236,9237,9238,9239,9240,9241,9242,9243,9244,9245,9145,13083,13084,13085,13086,9142,9289,9296,9303,9337,9240,9338,9241,9143,9290,9297,9304,9339,9242,9340,9243,9144,9291,9298,9305,9341,9244,9342,9245,9140,13280,9139,9286,9293,9300,9287,9294,9301,9292,9299,9306,9335,9237,880,13083,13084,13085,13086" }, { "itemId": "9705", @@ -1293,7 +1293,7 @@ "weapon_type": "1", "animation": "4230", "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,9141,9288,9295,9302,9140,9336,9239,13083,13084,13085,13086,13280" + "ammunition": "877,878,6061,6062,879,9139,9140,9236,9141,9288,9295,9302,9336,9239,9145,9286,9293,9300,9287,9294,9301,9292,9299,9306,9335,9237,880,9238,13083,13084,13085,13086" }, { "itemId": "13405", diff --git a/Server/data/configs/shops.json b/Server/data/configs/shops.json index 714e32a73..2f285314c 100644 --- a/Server/data/configs/shops.json +++ b/Server/data/configs/shops.json @@ -177,7 +177,7 @@ "general_store": "true", "id": "21", "title": "Gunslik's Assorted Items", - "stock": "{1931,30,100}-{1935,30,100}-{1735,10,100}-{1925,10,100}-{1923,10,100}-{1887,10,100}-{590,10,100}-{1755,10,100}-{2347,10,100}-{550,10,100}-{9003,10,100}" + "stock": "{1935,10,100}-{1925,30,100}-{590,10,100}-{1755,10,100}-{2347,10,100}-{36,10,100}-{596,10,100}-{973,10,100}-{1059,10,100}-{229,300,100}-{233,10,100}-{954,10,100}" }, { "npcs": "1334", @@ -564,7 +564,7 @@ "general_store": "false", "id": "65", "title": "Dodgy Mikes Second-hand Clothing", - "stock": "{7114,10,100}" + "stock": "{7114,10,100}-{7122,10,100}-{7128,10,100}-{7134,10,100}-{7110,10,100}-{7126,10,100}-{7132,10,100}-{7138,10,100}-{7116,10,100}-{7124,10,100}-{7130,10,100}-{7112,10,100}-{7136,10,100}" }, { "npcs": "2161", @@ -2177,5 +2177,23 @@ "id": "255", "title": "Castle Wars Ticket Exchange", "stock": "{4068,1,100}-{4069,1,100}-{4070,1,100}-{4071,1,100}-{4072,1,100}-{4503,1,100}-{4504,1,100}-{4505,1,100}-{4506,1,100}-{4507,1,100}-{4508,1,100}-{4509,1,100}-{4510,1,100}-{4511,1,100}-{4512,1,100}-{4513,1,100}-{4514,1,100}-{4515,1,100}-{4516,1,100}" + }, + { + "npcs": "5111", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "256", + "title": "Leon's Prototype Crossbow", + "stock": "{10156,2,100}" + }, + { + "npcs": "2039", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "256", + "title": "Uglug's Stuffsies", + "stock": "{4844,100,10}-{10927,0,100}-{2862,100,100}-{1777,10,200}-{2876,0,500}-{2878,10,100}-{4850,0,100}-{946,5,100}-{4773,0,1000}-{4778,0,1000}-{4783,0,1000}-{4788,0,1500}-{4793,0,2000}-{4798,0,3000}-{4803,0,4000}-{4827,0,2000}" } ] \ No newline at end of file diff --git a/Server/data/configs/xteas.json b/Server/data/configs/xteas.json index 69528883f..5b8de21a7 100644 --- a/Server/data/configs/xteas.json +++ b/Server/data/configs/xteas.json @@ -1,17 +1,5 @@ { "xteas": [ - { - "regionId": "6230", - "keys": "0,0,0,0" - }, - { - "regionId": "6231", - "keys": "0,0,0,0" - }, - { - "regionId": "6232", - "keys": "0,0,0,0" - }, { "regionId": "6234", "keys": "-1591922206,-1429764510,-1087339341,1592935185" @@ -28,18 +16,6 @@ "regionId": "6483", "keys": "14881828,-6662814,58238456,146761213" }, - { - "regionId": "6484", - "keys": "0,0,0,0" - }, - { - "regionId": "6485", - "keys": "0,0,0,0" - }, - { - "regionId": "6486", - "keys": "0,0,0,0" - }, { "regionId": "6487", "keys": "-844708758,693908396,-1724237422,-2131620302" @@ -64,50 +40,10 @@ "regionId": "6726", "keys": "-3492110,65044555,1450623668,-483811536" }, - { - "regionId": "6732", - "keys": "0,0,0,0" - }, - { - "regionId": "6733", - "keys": "0,0,0,0" - }, - { - "regionId": "6734", - "keys": "0,0,0,0" - }, - { - "regionId": "6735", - "keys": "0,0,0,0" - }, - { - "regionId": "6736", - "keys": "0,0,0,0" - }, - { - "regionId": "6737", - "keys": "0,0,0,0" - }, - { - "regionId": "6738", - "keys": "0,0,0,0" - }, - { - "regionId": "6739", - "keys": "0,0,0,0" - }, - { - "regionId": "6740", - "keys": "0,0,0,0" - }, { "regionId": "6741", "keys": "-1634995791,710519356,-47126463,-1180416882" }, - { - "regionId": "6742", - "keys": "0,0,0,0" - }, { "regionId": "6743", "keys": "-1239303400,-1604922847,-1532369005,982307785" @@ -120,38 +56,14 @@ "regionId": "6745", "keys": "365190378,-1767339898,2011432030,276148826" }, - { - "regionId": "6746", - "keys": "0,0,0,0" - }, - { - "regionId": "6979", - "keys": "0,0,0,0" - }, - { - "regionId": "6980", - "keys": "0,0,0,0" - }, - { - "regionId": "6981", - "keys": "0,0,0,0" - }, { "regionId": "6985", "keys": "1193572731,-1288765743,-386862979,351599890" }, - { - "regionId": "6988", - "keys": "0,0,0,0" - }, { "regionId": "6989", "keys": "-891749173,-113432144,-1228601828,695082558" }, - { - "regionId": "6990", - "keys": "0,0,0,0" - }, { "regionId": "6991", "keys": "-1448188175,626198836,1985989165,-1062391664" @@ -172,46 +84,14 @@ "regionId": "6995", "keys": "-1227889203,-199624313,2143494494,531356102" }, - { - "regionId": "6996", - "keys": "0,0,0,0" - }, - { - "regionId": "6997", - "keys": "0,0,0,0" - }, - { - "regionId": "6998", - "keys": "0,0,0,0" - }, - { - "regionId": "6999", - "keys": "0,0,0,0" - }, - { - "regionId": "7000", - "keys": "0,0,0,0" - }, { "regionId": "7001", "keys": "170738852,2045755396,-1580637869,-1933392334" }, - { - "regionId": "7002", - "keys": "0,0,0,0" - }, - { - "regionId": "7235", - "keys": "0,0,0,0" - }, { "regionId": "7236", "keys": "570290255,-1513898184,-1269691884,782850050" }, - { - "regionId": "7237", - "keys": "0,0,0,0" - }, { "regionId": "7238", "keys": "-853120516,1697831503,-12188711,770411534" @@ -220,14 +100,6 @@ "regionId": "7244", "keys": "796665731,1335649913,758227393,585095755" }, - { - "regionId": "7245", - "keys": "0,0,0,0" - }, - { - "regionId": "7246", - "keys": "0,0,0,0" - }, { "regionId": "7247", "keys": "-122734679,-1029771956,1633683499,846700218" @@ -244,30 +116,10 @@ "regionId": "7250", "keys": "497663526,-1258315657,-785166329,-999527877" }, - { - "regionId": "7251", - "keys": "0,0,0,0" - }, - { - "regionId": "7252", - "keys": "0,0,0,0" - }, - { - "regionId": "7490", - "keys": "0,0,0,0" - }, - { - "regionId": "7491", - "keys": "0,0,0,0" - }, { "regionId": "7492", "keys": "417150556,1135530200,2139696777,-742314409" }, - { - "regionId": "7493", - "keys": "0,0,0,0" - }, { "regionId": "7494", "keys": "-1653750072,103282771,384178856,1122166637" @@ -276,14 +128,6 @@ "regionId": "7496", "keys": "-1890766195,-149577199,919306397,-954784877" }, - { - "regionId": "7497", - "keys": "0,0,0,0" - }, - { - "regionId": "7498", - "keys": "0,0,0,0" - }, { "regionId": "7499", "keys": "-2007696984,-1215288615,-1572330641,-2125382604" @@ -312,10 +156,6 @@ "regionId": "7505", "keys": "-1177354806,-1864641630,23736540,750869235" }, - { - "regionId": "7506", - "keys": "0,0,0,0" - }, { "regionId": "7507", "keys": "-1476331852,1093075535,-2003580146,734573751" @@ -332,6 +172,10 @@ "regionId": "7510", "keys": "2073894230,-553352720,274325814,-1461052837" }, + { + "regionId": "7511", + "keys": "14881828,-6662814,58238456,146761213" + }, { "regionId": "7513", "keys": "1066375195,548376104,-1771348528,1096433429" @@ -340,10 +184,6 @@ "regionId": "7745", "keys": "1212560091,-662504525,-1213494614,-1245629284" }, - { - "regionId": "7747", - "keys": "0,0,0,0" - }, { "regionId": "7748", "keys": "-850453392,-249468744,1635462867,100471784" @@ -352,10 +192,6 @@ "regionId": "7749", "keys": "369356966,-1038198212,282112206,802717032" }, - { - "regionId": "7750", - "keys": "0,0,0,0" - }, { "regionId": "7752", "keys": "1894158557,20416613,-1434347651,-1209846311" @@ -392,46 +228,14 @@ "regionId": "7760", "keys": "2042192998,743878512,-1804236758,-1160501924" }, - { - "regionId": "7761", - "keys": "0,0,0,0" - }, - { - "regionId": "7762", - "keys": "0,0,0,0" - }, { "regionId": "7763", "keys": "14881828,-6662814,58238456,146761213" }, - { - "regionId": "7764", - "keys": "0,0,0,0" - }, - { - "regionId": "7765", - "keys": "0,0,0,0" - }, { "regionId": "7769", "keys": "560280774,910095015,871073102,263015376" }, - { - "regionId": "7995", - "keys": "0,0,0,0" - }, - { - "regionId": "7996", - "keys": "0,0,0,0" - }, - { - "regionId": "7997", - "keys": "0,0,0,0" - }, - { - "regionId": "7998", - "keys": "0,0,0,0" - }, { "regionId": "8001", "keys": "690512367,1578449236,-1443501555,546253393" @@ -440,10 +244,6 @@ "regionId": "8002", "keys": "1597309277,469931771,1619035792,663887748" }, - { - "regionId": "8003", - "keys": "0,0,0,0" - }, { "regionId": "8004", "keys": "609107948,-755432209,-723012522,1175820911" @@ -488,26 +288,14 @@ "regionId": "8015", "keys": "305015825,-634097708,865786116,-1850059578" }, - { - "regionId": "8016", - "keys": "0,0,0,0" - }, { "regionId": "8017", "keys": "959968310,-363819769,-1402232275,-1678358818" }, - { - "regionId": "8018", - "keys": "0,0,0,0" - }, { "regionId": "8019", "keys": "-1512999074,1064201012,2143076355,-1937376726" }, - { - "regionId": "8020", - "keys": "-955989323,1284433026,690014510,2145912092" - }, { "regionId": "8021", "keys": "-297039986,-1011877372,-1763869337,-900998253" @@ -528,6 +316,14 @@ "regionId": "8241", "keys": "-320813650,-1654452876,-352291520,-290946896" }, + { + "regionId": "8242", + "keys": "347990635,-1017326068,-1600504696,1565374916" + }, + { + "regionId": "8243", + "keys": "-107199068,-843304946,910528064,-626516712" + }, { "regionId": "8251", "keys": "1142567601,1650263019,49659607,-899246535" @@ -580,10 +376,6 @@ "regionId": "8267", "keys": "-474856962,-1295973639,789657650,-1769058462" }, - { - "regionId": "8268", - "keys": "0,0,0,0" - }, { "regionId": "8269", "keys": "-497435078,2028988145,486156819,-1178938803" @@ -604,22 +396,6 @@ "regionId": "8273", "keys": "978831670,664328414,1943273766,-1823800887" }, - { - "regionId": "8274", - "keys": "0,0,0,0" - }, - { - "regionId": "8275", - "keys": "0,0,0,0" - }, - { - "regionId": "8276", - "keys": "0,0,0,0" - }, - { - "regionId": "8277", - "keys": "0,0,0,0" - }, { "regionId": "8280", "keys": "-1615836970,1947387796,2141672540,-333423400" @@ -644,10 +420,6 @@ "regionId": "8499", "keys": "1158999293,2131770333,-861122128,1149793504" }, - { - "regionId": "8500", - "keys": "0,0,0,0" - }, { "regionId": "8506", "keys": "-1227747958,1491928866,1293676120,2065870654" @@ -724,30 +496,14 @@ "regionId": "8530", "keys": "-1466808210,962381183,-1581244207,-457271917" }, - { - "regionId": "8531", - "keys": "0,0,0,0" - }, { "regionId": "8532", "keys": "1553260654,1996107908,-2124357970,1441816370" }, - { - "regionId": "8533", - "keys": "0,0,0,0" - }, { "regionId": "8534", "keys": "1712031920,299813553,484205392,-939092911" }, - { - "regionId": "8545", - "keys": "1322297584,105544016,-1148309508,-574484176" - }, - { - "regionId": "8751", - "keys": "0,0,0,0" - }, { "regionId": "8752", "keys": "238354066,1687454525,1494747147,1911377137" @@ -764,22 +520,6 @@ "regionId": "8755", "keys": "-1165798584,772244190,-402783978,541755309" }, - { - "regionId": "8756", - "keys": "0,0,0,0" - }, - { - "regionId": "8757", - "keys": "0,0,0,0" - }, - { - "regionId": "8759", - "keys": "0,0,0,0" - }, - { - "regionId": "8760", - "keys": "0,0,0,0" - }, { "regionId": "8761", "keys": "1760753095,-1427813077,266675077,-255782653" @@ -804,6 +544,14 @@ "regionId": "8766", "keys": "1244410566,-1397143615,-1848205088,-1435723900" }, + { + "regionId": "8767", + "keys": "-368125809,-1805271492,-400403765,697149536" + }, + { + "regionId": "8768", + "keys": "-517024281,-1363386333,227729042,-1950025355" + }, { "regionId": "8769", "keys": "2044453226,-2064473213,1306128347,784973817" @@ -840,10 +588,6 @@ "regionId": "8779", "keys": "-1680845199,-1149954104,-1177525481,-576614197" }, - { - "regionId": "8780", - "keys": "0,0,0,0" - }, { "regionId": "8781", "keys": "-2133721491,843228961,1706232103,-934369964" @@ -864,34 +608,14 @@ "regionId": "8785", "keys": "1543774040,-487517297,-1630373503,102220753" }, - { - "regionId": "8786", - "keys": "0,0,0,0" - }, { "regionId": "8787", "keys": "1791798624,-1711313280,166719404,-1487207913" }, - { - "regionId": "8788", - "keys": "0,0,0,0" - }, - { - "regionId": "8789", - "keys": "0,0,0,0" - }, { "regionId": "8790", "keys": "230755787,-1139640838,-21592892,49661984" }, - { - "regionId": "9006", - "keys": "0,0,0,0" - }, - { - "regionId": "9007", - "keys": "0,0,0,0" - }, { "regionId": "9008", "keys": "2084752401,-1394447522,2084945842,1474596929" @@ -912,18 +636,6 @@ "regionId": "9012", "keys": "1204696185,-486414605,-1016110497,134649113" }, - { - "regionId": "9013", - "keys": "0,0,0,0" - }, - { - "regionId": "9014", - "keys": "0,0,0,0" - }, - { - "regionId": "9015", - "keys": "0,0,0,0" - }, { "regionId": "9016", "keys": "-1274221467,730617286,-1448054569,427043430" @@ -948,6 +660,14 @@ "regionId": "9021", "keys": "-665917952,-432697139,-602494723,-2097936281" }, + { + "regionId": "9022", + "keys": "1910381678,352194237,836248,347278834" + }, + { + "regionId": "9024", + "keys": "-785865296,-520648067,-1207176889,-441641818" + }, { "regionId": "9025", "keys": "1812159970,1795526437,1172074678,289074337" @@ -976,34 +696,10 @@ "regionId": "9035", "keys": "1525685385,1372975156,-2010144170,-1594192581" }, - { - "regionId": "9036", - "keys": "0,0,0,0" - }, - { - "regionId": "9037", - "keys": "0,0,0,0" - }, { "regionId": "9038", "keys": "-473165893,2110570897,-1726275929,806180444" }, - { - "regionId": "9040", - "keys": "0,0,0,0" - }, - { - "regionId": "9041", - "keys": "0,0,0,0" - }, - { - "regionId": "9042", - "keys": "0,0,0,0" - }, - { - "regionId": "9043", - "keys": "0,0,0,0" - }, { "regionId": "9044", "keys": "1541894861,-2145670115,-138793212,-921922364" @@ -1028,18 +724,6 @@ "regionId": "9050", "keys": "-1725663967,1678055280,1100896300,706957861" }, - { - "regionId": "9109", - "keys": "0,0,0,0" - }, - { - "regionId": "9110", - "keys": "0,0,0,0" - }, - { - "regionId": "9111", - "keys": "0,0,0,0" - }, { "regionId": "9113", "keys": "-570298090,-864625293,-668545435,8389096" @@ -1048,18 +732,6 @@ "regionId": "9114", "keys": "-962790446,-1706163576,82969599,310163719" }, - { - "regionId": "9120", - "keys": "0,0,0,0" - }, - { - "regionId": "9121", - "keys": "0,0,0,0" - }, - { - "regionId": "9122", - "keys": "0,0,0,0" - }, { "regionId": "9262", "keys": "-1327237404,-257519940,-1398355053,-970581711" @@ -1125,21 +797,21 @@ "keys": "272801923,1797149371,-131972865,1265002008" }, { - "regionId": "9283", - "keys": "0,0,0,0" + "regionId": "9278", + "keys": "-1782196006,1021278723,887209787,-1016997291" }, { - "regionId": "9284", - "keys": "0,0,0,0" + "regionId": "9279", + "keys": "-841578670,318269186,-353500843,-932268603" + }, + { + "regionId": "9280", + "keys": "727010382,181752242,1033156911,1262921484" }, { "regionId": "9285", "keys": "1742241898,-1564206342,-460725435,1614032789" }, - { - "regionId": "9286", - "keys": "0,0,0,0" - }, { "regionId": "9287", "keys": "-272431907,639665846,-1633276264,1645937141" @@ -1156,14 +828,6 @@ "regionId": "9290", "keys": "702687456,43635181,-1983533012,-319469910" }, - { - "regionId": "9291", - "keys": "0,0,0,0" - }, - { - "regionId": "9292", - "keys": "0,0,0,0" - }, { "regionId": "9293", "keys": "-810831340,-1857428683,-892017185,249052010" @@ -1176,30 +840,14 @@ "regionId": "9295", "keys": "224580924,-858933614,-432602901,-1433710083" }, - { - "regionId": "9296", - "keys": "0,0,0,0" - }, { "regionId": "9297", "keys": "-1667110403,-655319123,-2124588845,-262291671" }, - { - "regionId": "9298", - "keys": "0,0,0,0" - }, - { - "regionId": "9299", - "keys": "0,0,0,0" - }, { "regionId": "9300", "keys": "644587156,-605721698,681391959,1688865408" }, - { - "regionId": "9301", - "keys": "0,0,0,0" - }, { "regionId": "9304", "keys": "14881828,-6662814,58238456,146761213" @@ -1216,10 +864,6 @@ "regionId": "9362", "keys": "-253025041,376209206,1663580032,1115549308" }, - { - "regionId": "9363", - "keys": "0,0,0,0" - }, { "regionId": "9364", "keys": "1350964684,-1141243546,-158288919,1853454502" @@ -1232,10 +876,6 @@ "regionId": "9366", "keys": "-1691974727,-1591730841,1506117200,822346809" }, - { - "regionId": "9367", - "keys": "0,0,0,0" - }, { "regionId": "9368", "keys": "-911562498,-1433720091,257702084,1652121807" @@ -1256,30 +896,10 @@ "regionId": "9372", "keys": "1005996231,-569567890,-1578994771,-1136932414" }, - { - "regionId": "9376", - "keys": "0,0,0,0" - }, { "regionId": "9377", "keys": "-1671261866,-750153286,1731026368,-1501269926" }, - { - "regionId": "9378", - "keys": "0,0,0,0" - }, - { - "regionId": "9515", - "keys": "0,0,0,0" - }, - { - "regionId": "9516", - "keys": "0,0,0,0" - }, - { - "regionId": "9517", - "keys": "0,0,0,0" - }, { "regionId": "9518", "keys": "-557639005,398566091,-133936467,-850662847" @@ -1344,6 +964,14 @@ "regionId": "9533", "keys": "1393307887,646464303,-1582337843,2048412434" }, + { + "regionId": "9534", + "keys": "-2072244370,1114739695,-1096608867,-1932870993" + }, + { + "regionId": "9535", + "keys": "-1107428972,531677783,-1575581308,1343828898" + }, { "regionId": "9536", "keys": "679931979,-1660801436,-1184954844,-1006016912" @@ -1360,10 +988,6 @@ "regionId": "9541", "keys": "1352006827,1096305726,805420370,-1426885864" }, - { - "regionId": "9542", - "keys": "0,0,0,0" - }, { "regionId": "9543", "keys": "1852273964,-104833248,-2069080639,-188650169" @@ -1384,10 +1008,6 @@ "regionId": "9547", "keys": "-1998382641,30700550,1548517068,-855708219" }, - { - "regionId": "9548", - "keys": "0,0,0,0" - }, { "regionId": "9549", "keys": "-1648136735,1402264941,639592027,348907048" @@ -1408,22 +1028,10 @@ "regionId": "9553", "keys": "-1873675730,-1930195432,759400017,-1395479956" }, - { - "regionId": "9554", - "keys": "0,0,0,0" - }, - { - "regionId": "9555", - "keys": "0,0,0,0" - }, { "regionId": "9556", "keys": "-300312374,-1152515846,-863452271,-1500855067" }, - { - "regionId": "9557", - "keys": "0,0,0,0" - }, { "regionId": "9558", "keys": "1245928204,-1892089406,-1321168410,-1108146443" @@ -1440,10 +1048,6 @@ "regionId": "9562", "keys": "-1047298777,1197811918,663745433,-2098029011" }, - { - "regionId": "9619", - "keys": "0,0,0,0" - }, { "regionId": "9620", "keys": "1941157140,-1630547377,-1114648519,87057467" @@ -1472,18 +1076,6 @@ "regionId": "9626", "keys": "-1411729186,1049744836,-1608961973,-1681145313" }, - { - "regionId": "9627", - "keys": "0,0,0,0" - }, - { - "regionId": "9629", - "keys": "0,0,0,0" - }, - { - "regionId": "9630", - "keys": "0,0,0,0" - }, { "regionId": "9631", "keys": "1266963397,269916041,734658458,725243574" @@ -1492,18 +1084,6 @@ "regionId": "9632", "keys": "1023657558,-2001662423,-46679010,2064543607" }, - { - "regionId": "9633", - "keys": "0,0,0,0" - }, - { - "regionId": "9634", - "keys": "0,0,0,0" - }, - { - "regionId": "9771", - "keys": "0,0,0,0" - }, { "regionId": "9772", "keys": "-1742165439,-1273697376,2055229018,901215817" @@ -1576,6 +1156,10 @@ "regionId": "9789", "keys": "-620749099,528872615,1976629038,667155751" }, + { + "regionId": "9791", + "keys": "-2105683947,1913166182,-1697741730,-25818496" + }, { "regionId": "9792", "keys": "1500471532,1729075639,-304984137,1117230148" @@ -1584,10 +1168,6 @@ "regionId": "9794", "keys": "1788083183,2097372895,1633476682,-589223611" }, - { - "regionId": "9795", - "keys": "0,0,0,0" - }, { "regionId": "9796", "keys": "517509106,-2019799796,1066150754,-676371780" @@ -1596,10 +1176,6 @@ "regionId": "9797", "keys": "672333395,-284721288,262558790,-344798491" }, - { - "regionId": "9798", - "keys": "0,0,0,0" - }, { "regionId": "9799", "keys": "181886191,758691911,810524169,1456599388" @@ -1632,10 +1208,6 @@ "regionId": "9806", "keys": "368995667,940146211,957819918,-761335591" }, - { - "regionId": "9807", - "keys": "0,0,0,0" - }, { "regionId": "9808", "keys": "308766586,-214665927,-1255727041,-1034716346" @@ -1656,10 +1228,6 @@ "regionId": "9812", "keys": "50571970,-1762921782,-1667424270,524742325" }, - { - "regionId": "9813", - "keys": "0,0,0,0" - }, { "regionId": "9814", "keys": "-34428628,1940609264,-1638642159,-295486604" @@ -1704,38 +1272,14 @@ "regionId": "9883", "keys": "472234846,1198089044,500187071,239458328" }, - { - "regionId": "9884", - "keys": "0,0,0,0" - }, - { - "regionId": "9885", - "keys": "0,0,0,0" - }, { "regionId": "9886", "keys": "1515209815,-1457146379,-146402808,-1454274033" }, - { - "regionId": "9887", - "keys": "0,0,0,0" - }, - { - "regionId": "9888", - "keys": "0,0,0,0" - }, - { - "regionId": "9889", - "keys": "0,0,0,0" - }, { "regionId": "10027", "keys": "1491811841,1724263915,-1397066186,1733646819" }, - { - "regionId": "10028", - "keys": "0,0,0,0" - }, { "regionId": "10029", "keys": "203145796,-773666188,-569831878,-1510455793" @@ -1760,10 +1304,6 @@ "regionId": "10034", "keys": "-1435464304,-836439389,-788586899,1579582706" }, - { - "regionId": "10035", - "keys": "0,0,0,0" - }, { "regionId": "10036", "keys": "689954852,-1377739344,-847433475,-1883927289" @@ -1804,26 +1344,18 @@ "regionId": "10045", "keys": "-1429464298,1580360509,-1936477065,-337842786" }, + { + "regionId": "10046", + "keys": "1579643227,-1819574910,1035634488,-1469908551" + }, + { + "regionId": "10047", + "keys": "1248461784,913422340,-928862457,-1858877253" + }, { "regionId": "10048", "keys": "-1457016886,349941309,-400267403,-1711584223" }, - { - "regionId": "10051", - "keys": "0,0,0,0" - }, - { - "regionId": "10052", - "keys": "0,0,0,0" - }, - { - "regionId": "10053", - "keys": "0,0,0,0" - }, - { - "regionId": "10054", - "keys": "0,0,0,0" - }, { "regionId": "10055", "keys": "-813287484,-1417786621,-1794468339,578992617" @@ -1844,10 +1376,6 @@ "regionId": "10059", "keys": "1185163869,-1047826729,-1461057337,139139001" }, - { - "regionId": "10060", - "keys": "0,0,0,0" - }, { "regionId": "10061", "keys": "1683880127,1851133137,-567572662,-1477799312" @@ -1856,10 +1384,6 @@ "regionId": "10062", "keys": "2024717410,-1807032706,1106507805,1426160484" }, - { - "regionId": "10063", - "keys": "0,0,0,0" - }, { "regionId": "10064", "keys": "1978019309,1433546802,-1989346624,-1611361553" @@ -1872,10 +1396,6 @@ "regionId": "10066", "keys": "105954939,-1741244981,-1491777272,207112205" }, - { - "regionId": "10067", - "keys": "0,0,0,0" - }, { "regionId": "10068", "keys": "-452138979,-1929816521,364996963,-1315825295" @@ -1952,26 +1472,14 @@ "regionId": "10140", "keys": "-104825447,-1092637213,-105814713,-2129567531" }, - { - "regionId": "10141", - "keys": "0,0,0,0" - }, { "regionId": "10142", "keys": "1872120942,-765842959,449655176,-1168971827" }, - { - "regionId": "10143", - "keys": "0,0,0,0" - }, { "regionId": "10144", "keys": "265530509,2033515489,-2022406749,-591072091" }, - { - "regionId": "10145", - "keys": "0,0,0,0" - }, { "regionId": "10280", "keys": "-1245893544,-118079793,-1624599660,-626968532" @@ -1988,10 +1496,6 @@ "regionId": "10283", "keys": "-2133080221,1327669620,173304076,-151662318" }, - { - "regionId": "10284", - "keys": "0,0,0,0" - }, { "regionId": "10285", "keys": "-1317120534,1321149083,-1700628824,1028203235" @@ -2016,10 +1520,6 @@ "regionId": "10290", "keys": "-587752684,-318735232,336724431,-1931735346" }, - { - "regionId": "10291", - "keys": "0,0,0,0" - }, { "regionId": "10292", "keys": "933507835,1135929795,-1932059890,1492191263" @@ -2108,10 +1608,6 @@ "regionId": "10315", "keys": "-1034094191,1810212216,618808107,1905492210" }, - { - "regionId": "10316", - "keys": "0,0,0,0" - }, { "regionId": "10317", "keys": "216403117,1910251124,144162918,224240414" @@ -2120,14 +1616,6 @@ "regionId": "10318", "keys": "1053988597,-2121917140,1730755899,1549334321" }, - { - "regionId": "10319", - "keys": "0,0,0,0" - }, - { - "regionId": "10320", - "keys": "0,0,0,0" - }, { "regionId": "10321", "keys": "1996825886,-1552231723,1721080145,-869420516" @@ -2136,18 +1624,10 @@ "regionId": "10322", "keys": "1773602876,-1410394076,-239593243,148286798" }, - { - "regionId": "10323", - "keys": "0,0,0,0" - }, { "regionId": "10324", "keys": "-1670587478,-401532783,1705875521,995683837" }, - { - "regionId": "10325", - "keys": "0,0,0,0" - }, { "regionId": "10326", "keys": "-375687378,472100528,-288941184,523344380" @@ -2208,26 +1688,10 @@ "regionId": "10396", "keys": "-1878072140,1648991920,1054624070,333445417" }, - { - "regionId": "10397", - "keys": "0,0,0,0" - }, - { - "regionId": "10398", - "keys": "0,0,0,0" - }, - { - "regionId": "10399", - "keys": "0,0,0,0" - }, { "regionId": "10400", "keys": "-1656816922,1318311812,-811481661,1625916625" }, - { - "regionId": "10401", - "keys": "0,0,0,0" - }, { "regionId": "10536", "keys": "349537117,1202512092,-698870718,-2083455397" @@ -2272,10 +1736,6 @@ "regionId": "10546", "keys": "192659093,263441429,29086759,670094037" }, - { - "regionId": "10547", - "keys": "0,0,0,0" - }, { "regionId": "10548", "keys": "-1157394711,-1306553144,2105373539,798383108" @@ -2332,10 +1792,6 @@ "regionId": "10564", "keys": "1279522133,380240348,604239496,1759916207" }, - { - "regionId": "10565", - "keys": "0,0,0,0" - }, { "regionId": "10566", "keys": "221004409,1368318650,-1237341956,25411913" @@ -2364,10 +1820,6 @@ "regionId": "10572", "keys": "-1375061166,-1411427241,-294467489,-2106748278" }, - { - "regionId": "10573", - "keys": "0,0,0,0" - }, { "regionId": "10574", "keys": "-1609693854,991245740,148787249,-1011766477" @@ -2376,10 +1828,6 @@ "regionId": "10575", "keys": "434036980,-780702800,-1042242727,-354464628" }, - { - "regionId": "10576", - "keys": "0,0,0,0" - }, { "regionId": "10577", "keys": "824897954,-1807466644,2079017570,1299975025" @@ -2393,12 +1841,8 @@ "keys": "-123618550,684067223,486273630,-1103816964" }, { - "regionId": "10580", - "keys": "0,0,0,0" - }, - { - "regionId": "10637", - "keys": "0,0,0,0" + "regionId": "10583", + "keys": "14881828,-6662814,58238456,146761213" }, { "regionId": "10638", @@ -2448,10 +1892,6 @@ "regionId": "10650", "keys": "-908578223,-911827513,1137573080,-419444146" }, - { - "regionId": "10651", - "keys": "0,0,0,0" - }, { "regionId": "10652", "keys": "1580722385,609745227,1407021351,1098485033" @@ -2460,22 +1900,6 @@ "regionId": "10653", "keys": "570189753,-1700559836,2108215671,-701775893" }, - { - "regionId": "10654", - "keys": "0,0,0,0" - }, - { - "regionId": "10655", - "keys": "0,0,0,0" - }, - { - "regionId": "10656", - "keys": "0,0,0,0" - }, - { - "regionId": "10657", - "keys": "0,0,0,0" - }, { "regionId": "10792", "keys": "-125671574,254943715,1343095705,-1965171670" @@ -2516,10 +1940,6 @@ "regionId": "10801", "keys": "850125851,1199386038,-1494136472,-1487577933" }, - { - "regionId": "10802", - "keys": "0,0,0,0" - }, { "regionId": "10803", "keys": "-1028081880,964144306,174845257,1906118817" @@ -2572,6 +1992,10 @@ "regionId": "10815", "keys": "2102925673,1133406202,-1885710196,165240951" }, + { + "regionId": "10816", + "keys": "-1909517714,218137505,744896894,831968418" + }, { "regionId": "10819", "keys": "-904401587,-2054970579,55120674,-1889562804" @@ -2612,10 +2036,6 @@ "regionId": "10828", "keys": "1665018619,-1607107877,-1997272567,1342325223" }, - { - "regionId": "10829", - "keys": "0,0,0,0" - }, { "regionId": "10830", "keys": "-608096960,58771202,1174291927,-1613596249" @@ -2636,18 +2056,10 @@ "regionId": "10834", "keys": "-1640569486,-512141704,-1179536382,720004469" }, - { - "regionId": "10835", - "keys": "0,0,0,0" - }, { "regionId": "10836", "keys": "1637505449,619992365,-1935536269,-1526242295" }, - { - "regionId": "10837", - "keys": "0,0,0,0" - }, { "regionId": "10838", "keys": "1678881362,-952862831,-1804688659,-1247653912" @@ -2744,10 +2156,6 @@ "regionId": "10911", "keys": "948965123,-545528654,-459191491,-860302360" }, - { - "regionId": "10912", - "keys": "0,0,0,0" - }, { "regionId": "11049", "keys": "-621090199,-1004888612,-749707219,2052444337" @@ -2872,30 +2280,14 @@ "regionId": "11083", "keys": "1255503855,-674022312,-807132804,-1287799121" }, - { - "regionId": "11084", - "keys": "0,0,0,0" - }, { "regionId": "11085", "keys": "327229160,-1433790638,1944433240,234034136" }, - { - "regionId": "11086", - "keys": "0,0,0,0" - }, - { - "regionId": "11087", - "keys": "0,0,0,0" - }, { "regionId": "11088", "keys": "-1943123304,1713176080,-1524844090,-445387865" }, - { - "regionId": "11089", - "keys": "0,0,0,0" - }, { "regionId": "11090", "keys": "2089067081,828176382,-1112393031,-1452165920" @@ -2904,10 +2296,6 @@ "regionId": "11091", "keys": "977844365,-245927723,99303766,-735341354" }, - { - "regionId": "11092", - "keys": "0,0,0,0" - }, { "regionId": "11093", "keys": "-379710910,-2088433559,1180928189,-54938349" @@ -2972,14 +2360,6 @@ "regionId": "11158", "keys": "160361538,733169435,1244326761,1681727551" }, - { - "regionId": "11159", - "keys": "0,0,0,0" - }, - { - "regionId": "11160", - "keys": "0,0,0,0" - }, { "regionId": "11161", "keys": "-1713920755,1291269906,320754968,741637543" @@ -3008,10 +2388,6 @@ "regionId": "11167", "keys": "-1735216162,2141149053,57787230,-1263840909" }, - { - "regionId": "11168", - "keys": "0,0,0,0" - }, { "regionId": "11305", "keys": "-1429346525,-118378130,1002549662,1383588995" @@ -3088,10 +2464,6 @@ "regionId": "11323", "keys": "-1710808226,725482732,1863997544,5983834" }, - { - "regionId": "11324", - "keys": "0,0,0,0" - }, { "regionId": "11326", "keys": "1862093150,1696245244,1320714102,1241887579" @@ -3108,30 +2480,14 @@ "regionId": "11332", "keys": "-936631483,-1093055133,-161202591,-1476364681" }, - { - "regionId": "11333", - "keys": "0,0,0,0" - }, - { - "regionId": "11334", - "keys": "0,0,0,0" - }, { "regionId": "11335", "keys": "-133492081,-915358326,-714649892,-2093709476" }, - { - "regionId": "11338", - "keys": "0,0,0,0" - }, { "regionId": "11339", "keys": "140118336,-1920268877,-1967576060,-1437209189" }, - { - "regionId": "11340", - "keys": "0,0,0,0" - }, { "regionId": "11341", "keys": "-1219163353,-1635920416,-2124217842,-1318908036" @@ -3152,14 +2508,6 @@ "regionId": "11347", "keys": "-122396964,-1996200666,-585000602,-863390951" }, - { - "regionId": "11348", - "keys": "0,0,0,0" - }, - { - "regionId": "11349", - "keys": "0,0,0,0" - }, { "regionId": "11350", "keys": "-1365557009,-1350738529,-1074800707,-446118748" @@ -3184,22 +2532,10 @@ "regionId": "11356", "keys": "2033204014,1416145849,-1721330996,2134394884" }, - { - "regionId": "11405", - "keys": "0,0,0,0" - }, { "regionId": "11406", "keys": "375301159,353365492,-940259891,-223647771" }, - { - "regionId": "11407", - "keys": "0,0,0,0" - }, - { - "regionId": "11408", - "keys": "0,0,0,0" - }, { "regionId": "11409", "keys": "1937092760,-1545935537,29954084,-1081016062" @@ -3208,10 +2544,6 @@ "regionId": "11410", "keys": "1509299526,-892391942,1485831814,1255407038" }, - { - "regionId": "11411", - "keys": "0,0,0,0" - }, { "regionId": "11412", "keys": "-1379772393,621580805,-316850307,1026773740" @@ -3316,10 +2648,6 @@ "regionId": "11572", "keys": "892185929,686162443,1793819023,-992991976" }, - { - "regionId": "11573", - "keys": "0,0,0,0" - }, { "regionId": "11574", "keys": "-1406155502,13961326,1071473330,1528558089" @@ -3336,22 +2664,10 @@ "regionId": "11577", "keys": "1283493672,1550135524,-1621670256,769418438" }, - { - "regionId": "11578", - "keys": "0,0,0,0" - }, { "regionId": "11579", "keys": "-719996638,-1831948234,494156672,1684753309" }, - { - "regionId": "11580", - "keys": "0,0,0,0" - }, - { - "regionId": "11581", - "keys": "0,0,0,0" - }, { "regionId": "11582", "keys": "602331327,-1036793753,1702673112,-1107214996" @@ -3376,18 +2692,10 @@ "regionId": "11589", "keys": "39678032,469576041,-1878694956,-1720799345" }, - { - "regionId": "11590", - "keys": "0,0,0,0" - }, { "regionId": "11591", "keys": "1300749474,-912770362,-1665451776,480747745" }, - { - "regionId": "11592", - "keys": "0,0,0,0" - }, { "regionId": "11593", "keys": "14881828,-6662814,58238456,146761213" @@ -3400,26 +2708,14 @@ "regionId": "11595", "keys": "-1497259084,-1208341848,874209282,-1950133061" }, - { - "regionId": "11596", - "keys": "0,0,0,0" - }, { "regionId": "11597", "keys": "-824244627,-1750674106,-2032396270,-451550546" }, - { - "regionId": "11598", - "keys": "0,0,0,0" - }, { "regionId": "11599", "keys": "-1124938025,110281395,-1121479364,68953473" }, - { - "regionId": "11600", - "keys": "0,0,0,0" - }, { "regionId": "11601", "keys": "722657934,1495340205,-437272583,-1331954618" @@ -3432,10 +2728,6 @@ "regionId": "11603", "keys": "-254635141,-1757221941,-2072225761,-752948578" }, - { - "regionId": "11604", - "keys": "0,0,0,0" - }, { "regionId": "11605", "keys": "57037316,1119657363,2040370510,801474589" @@ -3444,10 +2736,6 @@ "regionId": "11606", "keys": "-1790275662,-1050505330,-1496410655,679769180" }, - { - "regionId": "11607", - "keys": "0,0,0,0" - }, { "regionId": "11608", "keys": "248039728,1861834308,1335200688,317723667" @@ -3456,10 +2744,6 @@ "regionId": "11609", "keys": "-1581925228,-215021003,-1695323698,-1853661356" }, - { - "regionId": "11610", - "keys": "0,0,0,0" - }, { "regionId": "11612", "keys": "-1751954695,954770889,-337304771,986015401" @@ -3564,14 +2848,6 @@ "regionId": "11824", "keys": "-1116727390,-659788658,307002260,354360287" }, - { - "regionId": "11825", - "keys": "0,0,0,0" - }, - { - "regionId": "11826", - "keys": "0,0,0,0" - }, { "regionId": "11827", "keys": "379001889,1835230493,1861106702,338991847" @@ -3632,42 +2908,10 @@ "regionId": "11844", "keys": "46502425,-1301719099,-1597781617,1434487073" }, - { - "regionId": "11845", - "keys": "0,0,0,0" - }, - { - "regionId": "11846", - "keys": "0,0,0,0" - }, - { - "regionId": "11847", - "keys": "0,0,0,0" - }, - { - "regionId": "11848", - "keys": "0,0,0,0" - }, - { - "regionId": "11849", - "keys": "0,0,0,0" - }, - { - "regionId": "11850", - "keys": "0,0,0,0" - }, { "regionId": "11851", "keys": "-516358140,791692293,-1663509747,1243508195" }, - { - "regionId": "11852", - "keys": "0,0,0,0" - }, - { - "regionId": "11853", - "keys": "0,0,0,0" - }, { "regionId": "11854", "keys": "311566497,-486013229,1351929142,744327317" @@ -3676,30 +2920,10 @@ "regionId": "11855", "keys": "770787544,-1305958539,670684335,208100825" }, - { - "regionId": "11856", - "keys": "0,0,0,0" - }, { "regionId": "11857", "keys": "150488681,419659968,-1871963555,1031596369" }, - { - "regionId": "11858", - "keys": "0,0,0,0" - }, - { - "regionId": "11859", - "keys": "0,0,0,0" - }, - { - "regionId": "11860", - "keys": "0,0,0,0" - }, - { - "regionId": "11861", - "keys": "0,0,0,0" - }, { "regionId": "11862", "keys": "2071521092,797384499,364423664,-1286422461" @@ -3744,10 +2968,6 @@ "regionId": "11926", "keys": "-501586879,235710413,-1122707717,-354940885" }, - { - "regionId": "11927", - "keys": "0,0,0,0" - }, { "regionId": "11928", "keys": "-445378710,-788377286,1363332226,627739779" @@ -3760,18 +2980,6 @@ "regionId": "11930", "keys": "1436132147,-902725534,590285278,2018675988" }, - { - "regionId": "11931", - "keys": "0,0,0,0" - }, - { - "regionId": "11932", - "keys": "0,0,0,0" - }, - { - "regionId": "11933", - "keys": "0,0,0,0" - }, { "regionId": "11934", "keys": "-854127330,1609124159,-999989310,-734721734" @@ -3780,22 +2988,10 @@ "regionId": "11935", "keys": "-1453725493,-1575973068,1979334907,1130399929" }, - { - "regionId": "11936", - "keys": "0,0,0,0" - }, { "regionId": "11937", "keys": "-1518927827,1819850502,-992647105,299958470" }, - { - "regionId": "11938", - "keys": "0,0,0,0" - }, - { - "regionId": "12073", - "keys": "-882281631,257910959,1642484329,-1342807140" - }, { "regionId": "12076", "keys": "193939514,-115011236,-1406304015,-1626342231" @@ -3876,26 +3072,10 @@ "regionId": "12097", "keys": "-862465947,2109948185,583246321,-850147469" }, - { - "regionId": "12098", - "keys": "0,0,0,0" - }, - { - "regionId": "12099", - "keys": "0,0,0,0" - }, { "regionId": "12100", "keys": "1852910519,712916664,1670787779,1294835477" }, - { - "regionId": "12101", - "keys": "0,0,0,0" - }, - { - "regionId": "12102", - "keys": "14881828,-6662814,58238456,146761213" - }, { "regionId": "12105", "keys": "1153497516,-449169462,-635419798,-1471828315" @@ -3924,10 +3104,6 @@ "regionId": "12111", "keys": "-746445771,1759120362,1253848335,1433199232" }, - { - "regionId": "12112", - "keys": "0,0,0,0" - }, { "regionId": "12113", "keys": "-1749952613,1569535680,1900425482,-1993748284" @@ -3936,10 +3112,6 @@ "regionId": "12115", "keys": "2146362785,1996258091,-613445101,539756591" }, - { - "regionId": "12116", - "keys": "0,0,0,0" - }, { "regionId": "12117", "keys": "-97063310,1962833557,-849078816,-1560293275" @@ -3976,10 +3148,6 @@ "regionId": "12182", "keys": "-499997333,1910872683,-234510566,-361238419" }, - { - "regionId": "12183", - "keys": "0,0,0,0" - }, { "regionId": "12184", "keys": "-1993918550,1977224805,-1048356116,-1578331413" @@ -3992,10 +3160,6 @@ "regionId": "12186", "keys": "-259674875,-949495016,1835821953,1740556829" }, - { - "regionId": "12187", - "keys": "0,0,0,0" - }, { "regionId": "12188", "keys": "22801476,-1237575758,2139201694,-163382547" @@ -4004,26 +3168,10 @@ "regionId": "12189", "keys": "-626419958,-861781849,-400257621,465575053" }, - { - "regionId": "12191", - "keys": "0,0,0,0" - }, - { - "regionId": "12192", - "keys": "0,0,0,0" - }, { "regionId": "12193", "keys": "1013552068,-364826455,4137749,729786125" }, - { - "regionId": "12194", - "keys": "0,0,0,0" - }, - { - "regionId": "12332", - "keys": "0,0,0,0" - }, { "regionId": "12333", "keys": "372281878,-809369100,-1989039261,-980074402" @@ -4112,54 +3260,18 @@ "regionId": "12356", "keys": "-1201681208,106398334,-2123418385,1010672418" }, - { - "regionId": "12362", - "keys": "0,0,0,0" - }, - { - "regionId": "12363", - "keys": "0,0,0,0" - }, - { - "regionId": "12364", - "keys": "0,0,0,0" - }, - { - "regionId": "12365", - "keys": "0,0,0,0" - }, - { - "regionId": "12366", - "keys": "0,0,0,0" - }, { "regionId": "12367", "keys": "-2006617355,-1390093250,-1950318553,1842364433" }, - { - "regionId": "12368", - "keys": "0,0,0,0" - }, { "regionId": "12369", "keys": "-891954120,1710352781,-1883639556,-1461026236" }, - { - "regionId": "12372", - "keys": "0,0,0,0" - }, - { - "regionId": "12373", - "keys": "0,0,0,0" - }, { "regionId": "12374", "keys": "1620519366,-1309341299,68411383,1308746010" }, - { - "regionId": "12375", - "keys": "0,0,0,0" - }, { "regionId": "12376", "keys": "1918934265,1530066885,-1991367516,1783839145" @@ -4204,38 +3316,10 @@ "regionId": "12442", "keys": "1883167946,191149821,-1270401415,-126025128" }, - { - "regionId": "12443", - "keys": "0,0,0,0" - }, { "regionId": "12444", "keys": "1576354846,-1768236409,821965805,-861456742" }, - { - "regionId": "12447", - "keys": "0,0,0,0" - }, - { - "regionId": "12448", - "keys": "0,0,0,0" - }, - { - "regionId": "12449", - "keys": "0,0,0,0" - }, - { - "regionId": "12450", - "keys": "0,0,0,0" - }, - { - "regionId": "12587", - "keys": "0,0,0,0" - }, - { - "regionId": "12588", - "keys": "0,0,0,0" - }, { "regionId": "12589", "keys": "1981659006,1591583400,1958084245,667979721" @@ -4324,10 +3408,6 @@ "regionId": "12614", "keys": "1621048325,1179863793,357058137,-1896311701" }, - { - "regionId": "12615", - "keys": "0,0,0,0" - }, { "regionId": "12616", "keys": "1951523008,-2131232747,-381301931,2007660709" @@ -4340,26 +3420,14 @@ "regionId": "12619", "keys": "1922733696,773456556,-1923866275,-1305839916" }, - { - "regionId": "12620", - "keys": "0,0,0,0" - }, { "regionId": "12621", "keys": "1619333755,-1869146576,77302288,-937694202" }, - { - "regionId": "12622", - "keys": "0,0,0,0" - }, { "regionId": "12623", "keys": "-369583241,545715110,-134871454,1404138108" }, - { - "regionId": "12624", - "keys": "0,0,0,0" - }, { "regionId": "12625", "keys": "-260562230,1208275247,436993278,30028881" @@ -4380,18 +3448,6 @@ "regionId": "12631", "keys": "693308333,-269450306,-924437218,309489139" }, - { - "regionId": "12632", - "keys": "0,0,0,0" - }, - { - "regionId": "12633", - "keys": "0,0,0,0" - }, - { - "regionId": "12634", - "keys": "0,0,0,0" - }, { "regionId": "12688", "keys": "1861636222,1638403446,-649444098,2131977195" @@ -4408,10 +3464,6 @@ "regionId": "12691", "keys": "-998717750,-178246764,973015517,-1732934591" }, - { - "regionId": "12692", - "keys": "0,0,0,0" - }, { "regionId": "12693", "keys": "1339198001,799729854,-750687990,1703141753" @@ -4420,10 +3472,6 @@ "regionId": "12694", "keys": "631068451,-510806123,2026757696,207332608" }, - { - "regionId": "12695", - "keys": "0,0,0,0" - }, { "regionId": "12696", "keys": "690970369,-466418236,-666391415,1091047140" @@ -4444,10 +3492,6 @@ "regionId": "12700", "keys": "-1802430094,1220107446,-1897971895,-1437873968" }, - { - "regionId": "12842", - "keys": "0,0,0,0" - }, { "regionId": "12843", "keys": "-203885401,22803731,234561826,140244374" @@ -4476,22 +3520,6 @@ "regionId": "12849", "keys": "183396243,-461663209,-212681663,-266126212" }, - { - "regionId": "12850", - "keys": "0,0,0,0" - }, - { - "regionId": "12851", - "keys": "0,0,0,0" - }, - { - "regionId": "12852", - "keys": "0,0,0,0" - }, - { - "regionId": "12853", - "keys": "0,0,0,0" - }, { "regionId": "12854", "keys": "-648789394,672092351,713515997,1153393095" @@ -4541,24 +3569,12 @@ "keys": "1462732309,395803116,803023666,-1139113006" }, { - "regionId": "12874", - "keys": "0,0,0,0" + "regionId": "12869", + "keys": "14881828,-6662814,58238456,146761213" }, { - "regionId": "12875", - "keys": "0,0,0,0" - }, - { - "regionId": "12876", - "keys": "0,0,0,0" - }, - { - "regionId": "12877", - "keys": "0,0,0,0" - }, - { - "regionId": "12878", - "keys": "0,0,0,0" + "regionId": "12870", + "keys": "14881828,-6662814,58238456,146761213" }, { "regionId": "12879", @@ -4636,10 +3652,6 @@ "regionId": "12956", "keys": "-1383918319,-1190517730,-1037058957,534994622" }, - { - "regionId": "13098", - "keys": "0,0,0,0" - }, { "regionId": "13099", "keys": "919393388,-1011543647,454982828,-809340931" @@ -4664,14 +3676,6 @@ "regionId": "13104", "keys": "-421493992,1381068261,1710426242,2142500778" }, - { - "regionId": "13105", - "keys": "0,0,0,0" - }, - { - "regionId": "13106", - "keys": "0,0,0,0" - }, { "regionId": "13107", "keys": "-1161498786,-880845643,1588242926,2124040528" @@ -4732,34 +3736,18 @@ "regionId": "13126", "keys": "1732039952,536833573,-81136437,1031070663" }, - { - "regionId": "13130", - "keys": "0,0,0,0" - }, { "regionId": "13131", "keys": "-1234145533,479347649,1835828804,1685593501" }, - { - "regionId": "13132", - "keys": "0,0,0,0" - }, { "regionId": "13133", "keys": "-1026003062,917176839,-84576515,-1037395862" }, - { - "regionId": "13134", - "keys": "0,0,0,0" - }, { "regionId": "13135", "keys": "425723212,580084357,1788364975,1608414630" }, - { - "regionId": "13136", - "keys": "0,0,0,0" - }, { "regionId": "13138", "keys": "14881828,-6662814,58238456,146761213" @@ -4800,10 +3788,6 @@ "regionId": "13203", "keys": "-1342258113,-2106603709,395365404,-973258459" }, - { - "regionId": "13204", - "keys": "0,0,0,0" - }, { "regionId": "13205", "keys": "1250202258,-2009534633,1981493488,10068968" @@ -4812,14 +3796,6 @@ "regionId": "13206", "keys": "1456157358,1036496685,-1478577110,-1039552689" }, - { - "regionId": "13207", - "keys": "0,0,0,0" - }, - { - "regionId": "13208", - "keys": "0,0,0,0" - }, { "regionId": "13209", "keys": "-441828435,-1834839387,-1652863357,1161715526" @@ -4928,30 +3904,10 @@ "regionId": "13387", "keys": "50769406,1445159687,1657343198,-1977600563" }, - { - "regionId": "13388", - "keys": "0,0,0,0" - }, - { - "regionId": "13389", - "keys": "0,0,0,0" - }, { "regionId": "13393", "keys": "14881828,-6662814,58238456,146761213" }, - { - "regionId": "13397", - "keys": "0,0,0,0" - }, - { - "regionId": "13398", - "keys": "0,0,0,0" - }, - { - "regionId": "13399", - "keys": "0,0,0,0" - }, { "regionId": "13456", "keys": "-476100316,-1682296770,133855524,112315923" @@ -4996,10 +3952,6 @@ "regionId": "13466", "keys": "-460228398,-76181685,2062962515,1167974062" }, - { - "regionId": "13467", - "keys": "0,0,0,0" - }, { "regionId": "13610", "keys": "-1259340500,2094890289,-1255134464,1578867778" @@ -5048,10 +4000,6 @@ "regionId": "13621", "keys": "-1291167236,-1205011126,-1949699235,1530879202" }, - { - "regionId": "13622", - "keys": "0,0,0,0" - }, { "regionId": "13623", "keys": "-1700397822,-1427942931,1638091118,438962861" @@ -5096,10 +4044,6 @@ "regionId": "13643", "keys": "1113875885,-236852397,193142585,-854043613" }, - { - "regionId": "13644", - "keys": "0,0,0,0" - }, { "regionId": "13650", "keys": "-1281992532,-252178835,-207196406,32900092" @@ -5112,10 +4056,6 @@ "regionId": "13713", "keys": "676853493,-1887443315,-998811191,1947183065" }, - { - "regionId": "13715", - "keys": "0,0,0,0" - }, { "regionId": "13716", "keys": "-913580283,340446282,893998558,589419388" @@ -5140,10 +4080,6 @@ "regionId": "13721", "keys": "-1188425330,1307846097,888707126,-1843934128" }, - { - "regionId": "13722", - "keys": "0,0,0,0" - }, { "regionId": "13723", "keys": "-310574621,110640497,1685108265,-1931806505" @@ -5160,18 +4096,6 @@ "regionId": "13868", "keys": "255205616,-1585903009,-1371243067,-264891948" }, - { - "regionId": "13869", - "keys": "0,0,0,0" - }, - { - "regionId": "13870", - "keys": "0,0,0,0" - }, - { - "regionId": "13871", - "keys": "0,0,0,0" - }, { "regionId": "13872", "keys": "-865041276,1374390842,-909885942,30088570" @@ -5196,10 +4120,6 @@ "regionId": "13877", "keys": "1498185652,2064367906,152992439,585271079" }, - { - "regionId": "13878", - "keys": "0,0,0,0" - }, { "regionId": "13879", "keys": "-2101514338,-1139447054,51825149,-512907347" @@ -5220,18 +4140,6 @@ "regionId": "13899", "keys": "-2117805741,-268051413,-733527930,471010998" }, - { - "regionId": "13900", - "keys": "0,0,0,0" - }, - { - "regionId": "13901", - "keys": "0,0,0,0" - }, - { - "regionId": "13902", - "keys": "0,0,0,0" - }, { "regionId": "13905", "keys": "14881828,-6662814,58238456,146761213" @@ -5240,18 +4148,10 @@ "regionId": "13968", "keys": "-620062336,-1110235330,-1095431209,-470716307" }, - { - "regionId": "13971", - "keys": "0,0,0,0" - }, { "regionId": "13972", "keys": "1266294685,1139088638,1514900183,-1389022839" }, - { - "regionId": "13973", - "keys": "0,0,0,0" - }, { "regionId": "13974", "keys": "1384510929,1857691725,-293821029,-1219099456" @@ -5280,10 +4180,6 @@ "regionId": "13980", "keys": "-12957944,-743456598,2041601862,618819565" }, - { - "regionId": "14127", - "keys": "0,0,0,0" - }, { "regionId": "14128", "keys": "1907787432,-241593923,1152767085,-541422403" @@ -5320,26 +4216,10 @@ "regionId": "14136", "keys": "-1104533465,1792124817,-1564247177,79706641" }, - { - "regionId": "14154", - "keys": "0,0,0,0" - }, - { - "regionId": "14155", - "keys": "0,0,0,0" - }, - { - "regionId": "14156", - "keys": "0,0,0,0" - }, { "regionId": "14157", "keys": "-852479713,1901063521,-1097484894,1013793379" }, - { - "regionId": "14158", - "keys": "0,0,0,0" - }, { "regionId": "14161", "keys": "14881828,-6662814,58238456,146761213" @@ -5348,18 +4228,6 @@ "regionId": "14167", "keys": "-1398455,-469478596,190482870,-1233173597" }, - { - "regionId": "14227", - "keys": "0,0,0,0" - }, - { - "regionId": "14228", - "keys": "0,0,0,0" - }, - { - "regionId": "14229", - "keys": "0,0,0,0" - }, { "regionId": "14230", "keys": "-453901412,-2074594443,-809419406,1774073603" @@ -5388,6 +4256,10 @@ "regionId": "14236", "keys": "816141215,-2114312227,1578950035,1471842311" }, + { + "regionId": "14380", + "keys": "1663518625,-107913441,-1625821936,330353351" + }, { "regionId": "14381", "keys": "-534048527,1614741928,-469012711,1064350100" @@ -5420,10 +4292,6 @@ "regionId": "14388", "keys": "2048238173,2003642395,1658667134,411519881" }, - { - "regionId": "14389", - "keys": "0,0,0,0" - }, { "regionId": "14390", "keys": "-1631704360,1281079278,-1549067757,-2034268623" @@ -5436,18 +4304,6 @@ "regionId": "14392", "keys": "993603906,-1477196579,593064486,166369140" }, - { - "regionId": "14412", - "keys": "0,0,0,0" - }, - { - "regionId": "14413", - "keys": "0,0,0,0" - }, - { - "regionId": "14414", - "keys": "0,0,0,0" - }, { "regionId": "14486", "keys": "-131982139,-1189975142,-1667401999,-1794787704" @@ -5456,14 +4312,6 @@ "regionId": "14487", "keys": "-330478679,11506767,395036194,-971774384" }, - { - "regionId": "14488", - "keys": "0,0,0,0" - }, - { - "regionId": "14489", - "keys": "0,0,0,0" - }, { "regionId": "14490", "keys": "1578682567,-1175551739,170259358,-1972709096" @@ -5476,6 +4324,10 @@ "regionId": "14492", "keys": "420310205,1004898846,889537686,13961361" }, + { + "regionId": "14636", + "keys": "-1554034534,-545576842,-416921234,1887723532" + }, { "regionId": "14637", "keys": "112774068,-1867270219,-442076728,-1436438131" @@ -5496,10 +4348,6 @@ "regionId": "14641", "keys": "-310201697,1724559662,90126688,-664432561" }, - { - "regionId": "14645", - "keys": "0,0,0,0" - }, { "regionId": "14646", "keys": "-2069357113,-2117603296,1620573021,-239596308" @@ -5512,10 +4360,6 @@ "regionId": "14648", "keys": "-1757352130,-1996516705,263017190,1647356258" }, - { - "regionId": "14745", - "keys": "0,0,0,0" - }, { "regionId": "14746", "keys": "2081288932,-143900986,-1978370486,-544919360" @@ -5548,10 +4392,6 @@ "regionId": "14896", "keys": "548765086,801664009,-1716752945,-1492954302" }, - { - "regionId": "14901", - "keys": "0,0,0,0" - }, { "regionId": "14902", "keys": "1158948283,-12013143,1242958376,-1265402384" @@ -5572,26 +4412,10 @@ "regionId": "14995", "keys": "958328498,239876404,608758325,933929091" }, - { - "regionId": "15001", - "keys": "0,0,0,0" - }, - { - "regionId": "15002", - "keys": "0,0,0,0" - }, - { - "regionId": "15003", - "keys": "0,0,0,0" - }, { "regionId": "15147", "keys": "-423978185,359589519,280220972,-1608470374" }, - { - "regionId": "15148", - "keys": "0,0,0,0" - }, { "regionId": "15149", "keys": "-1200628316,15132614,-465260385,-1525542932" diff --git a/Server/src/main/content/data/EnchantedJewellery.kt b/Server/src/main/content/data/EnchantedJewellery.kt index a913e9a88..cb94545e5 100644 --- a/Server/src/main/content/data/EnchantedJewellery.kt +++ b/Server/src/main/content/data/EnchantedJewellery.kt @@ -2,8 +2,11 @@ package content.data import content.global.skill.magic.TeleportMethod import content.global.skill.slayer.SlayerManager.Companion.getInstance +import content.global.skill.slayer.SlayerUtils +import core.ServerConstants import core.api.* import core.game.event.TeleportEvent +import core.game.interaction.QueueStrength import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager import core.game.node.item.Item @@ -17,8 +20,8 @@ import org.rs09.consts.Sounds import java.util.* /** - * Represents an enchanted jewellery. - * @author Vexia & downthecrop + * Represents a piece of enchanted jewellery. + * @author Vexia, downthecrop, Player Name */ enum class EnchantedJewellery( @@ -186,6 +189,13 @@ enum class EnchantedJewellery( Items.RING_OF_WEALTH2_14642, Items.RING_OF_WEALTH1_14640, Items.RING_OF_WEALTH_14638 + ), + RING_OF_LIFE(arrayOf(), + arrayOf( + Location.create(ServerConstants.HOME_LOCATION) + ), + true, + Items.RING_OF_LIFE_2570 ); val isCrumble: Boolean = crumble @@ -199,7 +209,7 @@ enum class EnchantedJewellery( constructor(options: Array, locations: Array, vararg ids: Int) : this(options, locations, false, *ids) /** - * Method used to teleport the player to the desired location. + * Method used when the player "Use"s the jewellery piece. * @param player the player. * @param item the used jewellery item. * @param buttonID the button id. @@ -212,39 +222,52 @@ enum class EnchantedJewellery( } return } + attemptTeleport(player, item, buttonID, isEquipped) + } + + /** + * Method used to actually teleport the player to the desired location. + * @param player the player. + * @param item the used jewellery item. + * @param buttonID the button id. + * @param isEquipped If the player is operating. + */ + fun attemptTeleport(player: Player, item: Item, buttonID: Int, isEquipped: Boolean): Boolean { val itemIndex = getItemIndex(item) val nextJewellery = Item(getNext(itemIndex)) - if (canTeleport(player, nextJewellery)) { - Pulser.submit(object : Pulse(0) { - private var count = 0 - private var location = getLocation(buttonID) - override fun pulse(): Boolean { - when (count) { - 0 -> { - lock(player,4) - visualize(player, ANIMATION, GRAPHICS) - playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) - player.impactHandler.disabledTicks = 4 - closeInterface(player) - } - 3 -> { - teleport(player,location) - resetAnimator(player) - if (isLastItemIndex(itemIndex)) { - if (isCrumble) crumbleJewellery(player, item, isEquipped) - } else { - replaceJewellery(player, item, nextJewellery, isEquipped) - } - unlock(player) - player.dispatch(TeleportEvent(TeleportManager.TeleportType.NORMAL, TeleportMethod.JEWELRY, item, location)) - return true - } - } - count += 1 - return false - } - }) + if (!canTeleport(player, nextJewellery)) { + return false } + Pulser.submit(object : Pulse(0) { + private var count = 0 + private var location = getLocation(buttonID) + override fun pulse(): Boolean { + when (count) { + 0 -> { + lock(player,4) + visualize(player, ANIMATION, GRAPHICS) + playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + player.impactHandler.disabledTicks = 4 + closeInterface(player) + } + 3 -> { + teleport(player,location) + resetAnimator(player) + if (isLastItemIndex(itemIndex)) { + if (isCrumble) crumbleJewellery(player, item, isEquipped) + } else { + replaceJewellery(player, item, nextJewellery, isEquipped) + } + unlock(player) + player.dispatch(TeleportEvent(TeleportManager.TeleportType.NORMAL, TeleportMethod.JEWELRY, item, location)) + return true + } + } + count += 1 + return false + } + }) + return true } private fun replaceJewellery(player: Player, item: Item, nextJewellery: Item, isEquipped: Boolean) { @@ -262,8 +285,11 @@ enum class EnchantedJewellery( removeItem(player, item) } if (isSlayerRing(item)) { - addItem(player, Items.ENCHANTED_GEM_4155) - sendMessage(player, "Your Ring of Slaying reverts back into a regular enchanted gem.") + queueScript(player, 1, QueueStrength.SOFT) { + addItemOrDrop(player, Items.ENCHANTED_GEM_4155) + sendMessage(player, "Your Ring of Slaying reverts back into a regular enchanted gem.") + return@queueScript stopExecuting(player) + } } } @@ -275,11 +301,11 @@ enum class EnchantedJewellery( val slayerManager = getInstance(player) if (!slayerManager.hasTask()) { sendNPCDialogue(player, slayerManager.master!!.npc, "You need something new to hunt. Come and " + - "see me When you can and I'll give you a new task.", core.game.dialogue.FacialExpression.HALF_GUILTY) + "see me when you can and I'll give you a new task.", core.game.dialogue.FacialExpression.HALF_GUILTY) return } sendNPCDialogue(player, slayerManager.master!!.npc, "You're currently " + - "assigned to kill ${getSlayerTaskName(player).lowercase(Locale.getDefault())}'s; " + + "assigned to kill ${SlayerUtils.pluralise(getSlayerTaskName(player))} " + "only ${getSlayerTaskKillsRemaining(player)} more to go.", core.game.dialogue.FacialExpression.FRIENDLY) // Slayer tracker UI setVarp(player, 2502, slayerManager.flags.taskFlags shr 4) diff --git a/Server/src/main/content/data/GodBook.java b/Server/src/main/content/data/GodBook.java index 314e61773..92029b479 100644 --- a/Server/src/main/content/data/GodBook.java +++ b/Server/src/main/content/data/GodBook.java @@ -100,7 +100,7 @@ public enum GodBook { * @param page the page. */ public void insertPage(Player player, Item book, Item page) { - if (!hasRequirement(player, "Horror from the Deep")) + if (!hasRequirement(player, Quests.HORROR_FROM_THE_DEEP)) return; if (hasPage(player, book, page)) { player.sendMessage("The book already has that page."); diff --git a/Server/src/main/content/data/Quests.kt b/Server/src/main/content/data/Quests.kt new file mode 100644 index 000000000..190cf2093 --- /dev/null +++ b/Server/src/main/content/data/Quests.kt @@ -0,0 +1,158 @@ +package content.data + +enum class Quests(val questName: String) { + MYTHS_OF_THE_WHITE_LANDS("Myths of the White Lands"), + BLACK_KNIGHTS_FORTRESS("Black Knights' Fortress"), + COOKS_ASSISTANT("Cook's Assistant"), + DEMON_SLAYER("Demon Slayer"), + DORICS_QUEST("Doric's Quest"), + DRAGON_SLAYER("Dragon Slayer"), + ERNEST_THE_CHICKEN("Ernest the Chicken"), + GOBLIN_DIPLOMACY("Goblin Diplomacy"), + IMP_CATCHER("Imp Catcher"), + THE_KNIGHTS_SWORD("The Knight's Sword"), + PIRATES_TREASURE("Pirate's Treasure"), + PRINCE_ALI_RESCUE("Prince Ali Rescue"), + THE_RESTLESS_GHOST("The Restless Ghost"), + ROMEO_JULIET("Romeo & Juliet"), + RUNE_MYSTERIES("Rune Mysteries"), + SHEEP_SHEARER("Sheep Shearer"), + SHIELD_OF_ARRAV("Shield of Arrav"), + VAMPIRE_SLAYER("Vampire Slayer"), + WITCHS_POTION("Witch's Potion"), + ANIMAL_MAGNETISM("Animal Magnetism"), + BETWEEN_A_ROCK("Between a Rock..."), + BIG_CHOMPY_BIRD_HUNTING("Big Chompy Bird Hunting"), + BIOHAZARD("Biohazard"), + CABIN_FEVER("Cabin Fever"), + CLOCK_TOWER("Clock Tower"), + CONTACT("Contact!"), + ZOGRE_FLESH_EATERS("Zogre Flesh Eaters"), + CREATURE_OF_FENKENSTRAIN("Creature of Fenkenstrain"), + DARKNESS_OF_HALLOWVALE("Darkness of Hallowvale"), + DEATH_TO_THE_DORGESHUUN("Death to the Dorgeshuun"), + DEATH_PLATEAU("Death Plateau"), + DESERT_TREASURE("Desert Treasure"), + DEVIOUS_MINDS("Devious Minds"), + THE_DIG_SITE("The Dig Site"), + DRUIDIC_RITUAL("Druidic Ritual"), + DWARF_CANNON("Dwarf Cannon"), + EADGARS_RUSE("Eadgar's Ruse"), + EAGLES_PEAK("Eagles' Peak"), + ELEMENTAL_WORKSHOP_I("Elemental Workshop I"), + ELEMENTAL_WORKSHOP_II("Elemental Workshop II"), + ENAKHRAS_LAMENT("Enakhra's Lament"), + ENLIGHTENED_JOURNEY("Enlightened Journey"), + THE_EYES_OF_GLOUPHRIE("The Eyes of Glouphrie"), + FAIRYTALE_I_GROWING_PAINS("Fairytale I - Growing Pains"), + FAIRYTALE_II_CURE_A_QUEEN("Fairytale II - Cure a Queen"), + FAMILY_CREST("Family Crest"), + THE_FEUD("The Feud"), + FIGHT_ARENA("Fight Arena"), + FISHING_CONTEST("Fishing Contest"), + FORGETTABLE_TALE("Forgettable Tale..."), + THE_FREMENNIK_TRIALS("The Fremennik Trials"), + WATERFALL_QUEST("Waterfall Quest"), + GARDEN_OF_TRANQUILITY("Garden of Tranquility"), + GERTRUDES_CAT("Gertrude's Cat"), + GHOSTS_AHOY("Ghosts Ahoy"), + THE_GIANT_DWARF("The Giant Dwarf"), + THE_GOLEM("The Golem"), + THE_GRAND_TREE("The Grand Tree"), + THE_HAND_IN_THE_SAND("The Hand in the Sand"), + HAUNTED_MINE("Haunted Mine"), + HAZEEL_CULT("Hazeel Cult"), + HEROES_QUEST("Heroes' Quest"), + HOLY_GRAIL("Holy Grail"), + HORROR_FROM_THE_DEEP("Horror from the Deep"), + ICTHLARINS_LITTLE_HELPER("Icthlarin's Little Helper"), + IN_AID_OF_THE_MYREQUE("In Aid of the Myreque"), + IN_SEARCH_OF_THE_MYREQUE("In Search of the Myreque"), + JUNGLE_POTION("Jungle Potion"), + LEGENDS_QUEST("Legend's Quest"), + LOST_CITY("Lost City"), + THE_LOST_TRIBE("The Lost Tribe"), + LUNAR_DIPLOMACY("Lunar Diplomacy"), + MAKING_HISTORY("Making History"), + MERLINS_CRYSTAL("Merlin's Crystal"), + MONKEY_MADNESS("Monkey Madness"), + MONKS_FRIEND("Monk's Friend"), + MOUNTAIN_DAUGHTER("Mountain Daughter"), + MOURNINGS_END_PART_I("Mourning's End Part I"), + MOURNINGS_END_PART_II("Mourning's End Part II"), + MURDER_MYSTERY("Murder Mystery"), + MY_ARMS_BIG_ADVENTURE("My Arm's Big Adventure"), + NATURE_SPIRIT("Nature Spirit"), + OBSERVATORY_QUEST("Observatory Quest"), + ONE_SMALL_FAVOUR("One Small Favour"), + PLAGUE_CITY("Plague City"), + PRIEST_IN_PERIL("Priest in Peril"), + RAG_AND_BONE_MAN("Rag and Bone Man"), + RATCATCHERS("Ratcatchers"), + RECIPE_FOR_DISASTER("Recipe for Disaster"), + RECRUITMENT_DRIVE("Recruitment Drive"), + REGICIDE("Regicide"), + ROVING_ELVES("Roving Elves"), + ROYAL_TROUBLE("Royal Trouble"), + RUM_DEAL("Rum Deal"), + SCORPION_CATCHER("Scorpion Catcher"), + SEA_SLUG("Sea Slug"), + THE_SLUG_MENACE("The Slug Menace"), + SHADES_OF_MORTTON("Shades of Mort'ton"), + SHADOW_OF_THE_STORM("Shadow of the Storm"), + SHEEP_HERDER("Sheep Herder"), + SHILO_VILLAGE("Shilo Village"), + A_SOULS_BANE("A Soul's Bane"), + SPIRITS_OF_THE_ELID("Spirits of the Elid"), + SWAN_SONG("Swan Song"), + TAI_BWO_WANNAI_TRIO("Tai Bwo Wannai Trio"), + A_TAIL_OF_TWO_CATS("A Tail of Two Cats"), + TEARS_OF_GUTHIX("Tears of Guthix"), + TEMPLE_OF_IKOV("Temple of Ikov"), + THRONE_OF_MISCELLANIA("Throne of Miscellania"), + THE_TOURIST_TRAP("The Tourist Trap"), + WITCHS_HOUSE("Witch's House"), + TREE_GNOME_VILLAGE("Tree Gnome Village"), + TRIBAL_TOTEM("Tribal Totem"), + TROLL_ROMANCE("Troll Romance"), + TROLL_STRONGHOLD("Troll Stronghold"), + UNDERGROUND_PASS("Underground Pass"), + WANTED("Wanted!"), + WATCHTOWER("Watchtower"), + COLD_WAR("Cold War"), + THE_FREMENNIK_ISLES("The Fremennik Isles"), + TOWER_OF_LIFE("Tower of Life"), + THE_GREAT_BRAIN_ROBBERY("The Great Brain Robbery"), + WHAT_LIES_BELOW("What Lies Below"), + OLAFS_QUEST("Olaf's Quest"), + ANOTHER_SLICE_OF_HAM("Another Slice of H.A.M"), + DREAM_MENTOR("Dream Mentor"), + GRIM_TALES("Grim Tales"), + KINGS_RANSOM("King's Ransom"), + THE_PATH_OF_GLOUPHRIE("The Path of Glouphrie"), + BACK_TO_MY_ROOTS("Back to my Roots"), + LAND_OF_THE_GOBLINS("Land of the Goblins"), + DEALING_WITH_SCABARAS("Dealing with Scabaras"), + WOLF_WHISTLE("Wolf Whistle"), + AS_A_FIRST_RESORT("As a First Resort..."), + CATAPULT_CONSTRUCTION("Catapult Construction"), + KENNITHS_CONCERNS("Kennith's Concerns"), + LEGACY_OF_SEERGAZE("Legacy of Seergaze"), + PERILS_OF_ICE_MOUNTAIN("Perils of Ice Mountain"), + TOKTZ_KET_DILL("TokTz-Ket-Dill"), + SMOKING_KILLS("Smoking Kills"), + ROCKING_OUT("Rocking Out"), + SPIRIT_OF_SUMMER("Spirit of Summer"), + MEETING_HISTORY("Meeting History"), + ALL_FIRED_UP("All Fired Up"), + SUMMERS_END("Summer's End"), + DEFENDER_OF_VARROCK("Defender of Varrock"), + SWEPT_AWAY("Swept Away"), + WHILE_GUTHIX_SLEEPS("While Guthix Sleeps"), + IN_PYRE_NEED("In Pyre Need"), + TEST_QUEST("Test Quest"); + + override fun toString(): String { + return questName + } +} diff --git a/Server/src/main/content/data/RepairItem.java b/Server/src/main/content/data/RepairItem.java deleted file mode 100644 index 3ab25a4d5..000000000 --- a/Server/src/main/content/data/RepairItem.java +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/Server/src/main/content/data/RepairItem.kt b/Server/src/main/content/data/RepairItem.kt new file mode 100644 index 000000000..4d89a7194 --- /dev/null +++ b/Server/src/main/content/data/RepairItem.kt @@ -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 = 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 } + } +} diff --git a/Server/src/main/content/data/consumables/Consumables.java b/Server/src/main/content/data/consumables/Consumables.java index 7fc446f1c..58143afc0 100644 --- a/Server/src/main/content/data/consumables/Consumables.java +++ b/Server/src/main/content/data/consumables/Consumables.java @@ -30,36 +30,36 @@ public enum Consumables { HERRING(new Food(new int[] {347}, new HealingEffect(5))), MACKEREL(new Food(new int[] {355}, new HealingEffect(6))), ROAST_BIRD_MEAT(new Food(new int[] {9980}, new HealingEffect(6))), - THIN_SNAIL(new Food(new int[] {3369}, new HealingEffect(5))), + THIN_SNAIL(new Food(new int[] {3369}, new RandomHealthEffect(5, 7))), TROUT(new Food(new int[] {333}, new HealingEffect(7))), - SPIDER_ON_STICK(new Food(new int[] {6297, 6305}, new HealingEffect(7))), + SPIDER_ON_STICK(new Food(new int[] {6297, 6305}, new RandomHealthEffect(7, 11))), SPIDER_ON_SHAFT(new Food(new int[] {6299}, new HealingEffect(7))), ROAST_RABBIT(new Food(new int[] {7223}, new HealingEffect(7))), - LEAN_SNAIL(new Food(new int[] {3371}, new HealingEffect(8))), + LEAN_SNAIL(new Food(new int[] {3371}, new RandomHealthEffect(6, 8))), COD(new Food(new int[] {339}, new HealingEffect(7))), PIKE(new Food(new int[] {351}, new HealingEffect(8))), ROAST_BEAST_MEAT(new Food(new int[] {9988}, new HealingEffect(8))), COOKED_CRAB_MEAT(new Food(new int[] {7521, 7523, 7524, 7525, 7526}, new HealingEffect(2))), - FAT_SNAIL(new Food(new int[] {3373}, new HealingEffect(9))), + FAT_SNAIL(new Food(new int[] {3373}, new RandomHealthEffect(7, 9))), SALMON(new Food(new int[] {329}, new HealingEffect(9))), - SLIMY_EEL(new Food(new int[] {3381}, new HealingEffect(6))), + SLIMY_EEL(new Food(new int[] {3381}, new RandomHealthEffect(6, 10))), TUNA(new Food(new int[] {361}, new HealingEffect(10))), COOKED_KARAMBWAN(new Food(new int[] {3144}, new HealingEffect(18)), true), COOKED_CHOMPY(new Food(new int[] {2878}, new HealingEffect(10))), RAINBOW_FISH(new Food(new int[] {10136}, new HealingEffect(11))), - CAVE_EEL(new Food(new int[] {5003}, new HealingEffect(7))), + CAVE_EEL(new Food(new int[] {5003}, new RandomHealthEffect(8, 12))), LOBSTER(new Food(new int[] {379}, new HealingEffect(12))), COOKED_JUBBLY(new Food(new int[] {7568}, new HealingEffect(15))), BASS(new Food(new int[] {365}, new HealingEffect(13))), SWORDFISH(new Food(new int[] {373}, new HealingEffect(14))), - LAVA_EEL(new Food(new int[] {2149}, new HealingEffect(14))), + LAVA_EEL(new Food(new int[] {2149}, new HealingEffect(11))), MONKFISH(new Food(new int[] {7946}, new HealingEffect(16))), SHARK(new Food(new int[] {385}, new HealingEffect(20))), SEA_TURTLE(new Food(new int[] {397}, new HealingEffect(21))), MANTA_RAY(new Food(new int[] {391}, new HealingEffect(22))), KARAMBWANJI(new Food(new int[] {3151}, new HealingEffect(3))), STUFFED_SNAKE(new Food(new int[] {7579}, new HealingEffect(20), "You eat the stuffed snake-it's quite a meal! It tastes like chicken.")), - CRAYFISH(new Food(new int[] {13433}, new HealingEffect(2))), + CRAYFISH(new Food(new int[] {13433}, new HealingEffect(1))), GIANT_FROG_LEGS(new Food(new int [] {4517}, new HealingEffect(6))), /** Breads */ @@ -111,7 +111,7 @@ public enum Consumables { /** Vegetables */ POTATO(new Food(new int[] {1942}, new HealingEffect(1), "You eat the potato. Yuck!")), - BAKED_POTATO(new Food(new int[] {6701}, new HealingEffect(2))), + BAKED_POTATO(new Food(new int[] {6701}, new HealingEffect(4))), SPICY_SAUCE(new Food(new int[] {7072, 1923}, new HealingEffect(2))), CHILLI_CON_CARNE(new Food(new int[] {7062, 1923}, new HealingEffect(5))), SCRAMBLED_EGG(new Food(new int[] {7078, 1923}, new HealingEffect(5))), @@ -128,8 +128,8 @@ public enum Consumables { MUSHROOM_POTATO(new Food(new int[] {7058}, new HealingEffect(20))), TUNA_AND_CORN(new Food(new int[] {7068, 1923}, new HealingEffect(13))), TUNA_POTATO(new Food(new int[] {7060}, new HealingEffect(22))), - ONION(new Food(new int[] {1957}, new HealingEffect(2), "It's always sad to see a grown man/woman cry.")), - CABBAGE(new Food(new int[] {1965}, new HealingEffect(2), "You eat the cabbage. Yuck!")), + ONION(new Food(new int[] {1957}, new HealingEffect(1), "It's always sad to see a grown man/woman cry.")), + CABBAGE(new Food(new int[] {1965}, new HealingEffect(1), "You eat the cabbage. Yuck!")), DRAYNOR_CABBAGE(new Food(new int[] {1967}, new DraynorCabbageEffect(), "You eat the cabbage.", "It seems to taste nicer than normal.")), EVIL_TURNIP(new Food(new int[] {12134, 12136, 12138}, new HealingEffect(6))), SPINACH_ROLL(new Food(new int[] {1969}, new HealingEffect(2))), @@ -147,7 +147,7 @@ public enum Consumables { ORANGE(new Food(new int[] {2108}, new HealingEffect(2))), ORANGE_CHUNKS(new Food(new int[] {2110}, new HealingEffect(2))), ORANGE_SLICES(new Food(new int[] {2112}, new HealingEffect(2))), - PAPAYA_FRUIT(new Food(new int[] {5972}, new HealingEffect(2))), + PAPAYA_FRUIT(new Food(new int[] {5972}, new MultiEffect(new EnergyEffect(5), new HealingEffect(8)))), TENTI_PINEAPPLE(new FakeConsumable(1851, new String[] {"Try using a knife to slice it into pieces."})), PINEAPPLE(new FakeConsumable(2114, new String[] {"Try using a knife to slice it into pieces."})), PINEAPPLE_CHUNKS(new Food(new int[] {2116}, new HealingEffect(2))), @@ -169,8 +169,8 @@ public enum Consumables { STRANGE_FRUIT(new Food(new int[] {464}, new MultiEffect(new RemoveTimerEffect("poison"), new EnergyEffect(30)))), /** Gnome Cooking */ - TOAD_CRUNCHIES(new Food(new int[] {2217}, new HealingEffect(12))), - PREMADE_TD_CRUNCH(new Food(new int[] {2243}, new HealingEffect(12))), + TOAD_CRUNCHIES(new Food(new int[] {2217}, new HealingEffect(8))), + PREMADE_TD_CRUNCH(new Food(new int[] {2243}, new HealingEffect(8))), SPICY_CRUNCHIES(new Food(new int[] {2213}, new HealingEffect(7))), PREMADE_SY_CRUNCH(new Food(new int[] {2241}, new HealingEffect(7))), WORM_CRUNCHIES(new Food(new int[] {2205}, new HealingEffect(8))), @@ -272,11 +272,11 @@ public enum Consumables { BRAINDEATH_RUM(new Drink(new int[] {7157}, new MultiEffect(new SkillEffect(Skills.DEFENCE, 0, -0.1), new SkillEffect(Skills.ATTACK, 0, -0.05), new SkillEffect(Skills.PRAYER, 0, -0.05), new SkillEffect(Skills.RANGE, 0, -0.05), new SkillEffect(Skills.MAGIC, 0, -0.05), new SkillEffect(Skills.HERBLORE, 0, -0.05), new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.MINING, 1, 0)), "With a sense of impending doom you drink the 'rum'. You try very hard not to die.")), RUM_TROUBLE_BREWING_RED(new Drink(new int[] {8940, 8940}, new TroubleBrewingRumEffect("Oh gods! It tastes like burning!"), new Animation(9605))), RUM_TROUBLE_BREWING_BLUE(new Drink(new int[] {8941, 8941}, new TroubleBrewingRumEffect("My Liver! My Liver is melting!"), new Animation(9604))), - VODKA(new Drink(new int[] {2015}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -3, 0)))), + VODKA(new Drink(new int[] {2015}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.ATTACK, -4, 0), new SkillEffect(Skills.STRENGTH, 4, 0)))), GIN(new Drink(new int[] {2019}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 1, 0), new SkillEffect(Skills.ATTACK, 4, 0), new RandomHealthEffect(3, 4)))), BRANDY(new Drink(new int[] {2021}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.ATTACK, 4, 0)))), WHISKY(new Drink(new int[] {2017}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), - BOTTLE_OF_WINE(new Drink(new int[] {2015, 7921}, new MultiEffect(new HealingEffect(14), new SkillEffect(Skills.ATTACK, -3, 0)))), + BOTTLE_OF_WINE(new Drink(new int[] {7919, 7921}, new MultiEffect(new HealingEffect(14), new SkillEffect(Skills.ATTACK, -3, 0)))), /** Wine */ JUG_OF_WINE(new Drink(new int[] {1993, 1935}, new MultiEffect(new HealingEffect(11), new SkillEffect(Skills.ATTACK, -2, 0)))), @@ -287,7 +287,7 @@ public enum Consumables { 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, 1980}, new NettleTeaEffect())), + NETTLE_TEA(new Drink(new int[] {4239, 1923}, new NettleTeaEffect())), NETTLE_TEA_MILKY(new Drink(new int[] {4240, 1980}, new NettleTeaEffect())), 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))), @@ -307,7 +307,7 @@ public enum Consumables { OOMLIE_WRAP(new Food(new int[] {Items.COOKED_OOMLIE_WRAP_2343}, new MultiEffect(new HealingEffect(14), new AchievementEffect(DiaryType.KARAMJA, 2, 2)))), ROE(new Food(new int[]{11324}, new HealingEffect(3))), EQUA_LEAVES(new Food(new int[]{2128}, new HealingEffect(1))), - CHOC_ICE(new Food(new int[]{6794}, new HealingEffect(6))), + CHOC_ICE(new Food(new int[]{6794}, new HealingEffect(7))), EDIBLE_SEAWEED(new Food(new int[] {403}, new HealingEffect(4))), FROG_SPAWN(new Food(new int[] {5004}, new RandomHealthEffect(3, 7), "You eat the frogspawn. Yuck.")), @@ -319,44 +319,44 @@ public enum Consumables { STRENGTH(new Potion(new int[] {113, 115, 117, 119}, new SkillEffect(Skills.STRENGTH, 3, 0.1))), ATTACK(new Potion(new int[] {2428, 121, 123, 125}, new SkillEffect(Skills.ATTACK, 3, 0.1))), DEFENCE(new Potion(new int[] {2432, 133, 135, 137}, new SkillEffect(Skills.DEFENCE, 3, 0.1))), - RANGING(new Potion(new int[] {2444, 169, 171, 173}, new SkillEffect(Skills.RANGE, 3, 0.1))), - MAGIC(new Potion(new int[] {3040, 3042, 3044, 3046}, new SkillEffect(Skills.MAGIC, 3, 0.1))), - SUPER_STRENGTH(new Potion(new int[] {2440, 157, 159, 161}, new SkillEffect(Skills.STRENGTH, 3, 0.2))), - SUPER_ATTACK(new Potion(new int[] {2436, 145, 147, 149}, new SkillEffect(Skills.ATTACK, 3, 0.2))), - SUPER_DEFENCE(new Potion(new int[] {2442, 163, 165, 167}, new SkillEffect(Skills.DEFENCE, 3, 0.2))), + RANGING(new Potion(new int[] {2444, 169, 171, 173}, new SkillEffect(Skills.RANGE, 4, 0.1))), + MAGIC(new Potion(new int[] {3040, 3042, 3044, 3046}, new SkillEffect(Skills.MAGIC, 4, 0))), + SUPER_STRENGTH(new Potion(new int[] {2440, 157, 159, 161}, new SkillEffect(Skills.STRENGTH, 5, 0.15))), + SUPER_ATTACK(new Potion(new int[] {2436, 145, 147, 149}, new SkillEffect(Skills.ATTACK, 5, 0.15))), + SUPER_DEFENCE(new Potion(new int[] {2442, 163, 165, 167}, new SkillEffect(Skills.DEFENCE, 5, 0.15))), ANTIPOISON(new Potion(new int[] {2446, 175, 177, 179}, new AddTimerEffect("poison:immunity", secondsToTicks(90)))), ANTIPOISON_(new Potion(new int[] {5943, 5945, 5947, 5949}, new AddTimerEffect("poison:immunity", minutesToTicks(9)))), ANTIPOISON__(new Potion(new int[] {5952, 5954, 5956, 5958}, new AddTimerEffect("poison:immunity", minutesToTicks(12)))), SUPER_ANTIP(new Potion(new int[] {2448, 181, 183, 185}, new AddTimerEffect("poison:immunity", minutesToTicks(6)))), - RELICYM(new Potion(new int[] {4842, 4844, 4846, 4848}, new MultiEffect(new SetAttributeEffect("disease:immunity", 300), new RemoveTimerEffect("disease")))), + RELICYM(new Potion(new int[] {4842, 4844, 4846, 4848}, new CureDiseaseEffect())), AGILITY(new Potion(new int[] {3032, 3034, 3036, 3038}, new SkillEffect(Skills.AGILITY, 3, 0))), HUNTER(new Potion(new int[] {9998, 10000, 10002, 10004}, new SkillEffect(Skills.HUNTER, 3, 0))), RESTORE(new Potion(new int[] {2430, 127, 129, 131}, new RestoreEffect(10, 0.3))), - SARA_BREW(new Potion(new int[] {6685, 6687, 6689, 6691}, new MultiEffect(new PercentHeal(2, .15), new SkillEffect(Skills.ATTACK, 0, -0.10), new SkillEffect(Skills.STRENGTH, 0, -0.10), new SkillEffect(Skills.MAGIC, 0, -0.10), new SkillEffect(Skills.RANGE, 0, -0.10), new SkillEffect(Skills.DEFENCE, 2, 0.2)))), + SARA_BREW(new Potion(new int[] {6685, 6687, 6689, 6691}, new MultiEffect(new PercentHeal(0, .15), new SkillEffect(Skills.ATTACK, 0, -0.10), new SkillEffect(Skills.STRENGTH, 0, -0.10), new SkillEffect(Skills.MAGIC, 0, -0.10), new SkillEffect(Skills.RANGE, 0, -0.10), new SkillEffect(Skills.DEFENCE, 0, 0.25)))), SUMMONING(new Potion(new int[] {12140, 12142, 12144, 12146}, new MultiEffect(new RestoreSummoningSpecial(), new SummoningEffect(7, 0.25)))), COMBAT(new Potion(new int[] {9739, 9741, 9743, 9745}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, .1), new SkillEffect(Skills.ATTACK, 3, .1)))), - ENERGY(new Potion(new int[] {3008, 3010, 3012, 3014}, new MultiEffect(new EnergyEffect(10), new HealingEffect(3)))), + ENERGY(new Potion(new int[] {3008, 3010, 3012, 3014}, new EnergyEffect(10))), FISHING(new Potion(new int[] {2438, 151, 153, 155}, new SkillEffect(Skills.FISHING, 3, 0))), PRAYER(new Potion(new int[] {2434, 139, 141, 143}, new PrayerEffect(7, 0.25))), - SUPER_RESTO(new Potion(new int[] {3024, 3026, 3028, 3030}, new MultiEffect(new RestoreEffect(8, 0.25), new PrayerEffect(8, 0.25), new SummoningEffect(8, 0.25)))), - ZAMMY_BREW(new Potion(new int[] {2450, 189, 191, 193}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.15), new SkillEffect(Skills.STRENGTH, 0, 0.25), new SkillEffect(Skills.DEFENCE, 0, -0.1), new RandomPrayerEffect(0, 10)))), - ANTIFIRE(new Potion(new int[] {2452, 2454, 2456, 2458}, new SetAttributeEffect("fire:immune", 600, true))), + SUPER_RESTO(new Potion(new int[] {3024, 3026, 3028, 3030}, new RestoreEffect(8, 0.25, true))), + ZAMMY_BREW(new Potion(new int[] {2450, 189, 191, 193}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.25), new SkillEffect(Skills.STRENGTH, 0, 0.15), new SkillEffect(Skills.DEFENCE, 0, -0.1)))), + ANTIFIRE(new Potion(new int[] {2452, 2454, 2456, 2458}, new AddTimerEffect("dragonfire:immunity", 600, true))), GUTH_REST(new Potion(new int[] {4417, 4419, 4421, 4423}, new MultiEffect(new RemoveTimerEffect("poison"), new EnergyEffect(5), new HealingEffect(5)))), - MAGIC_ESS(new Potion(new int[] {11491, 11489}, new SkillEffect(Skills.MAGIC,3,0))), - SANFEW(new Potion(new int[] {10925, 10927, 10929, 10931}, new MultiEffect(new RestoreEffect(8,0.25), new PrayerEffect(8,0.25), new RemoveTimerEffect("poison"), new RemoveTimerEffect("disease")))), + MAGIC_ESS(new Potion(new int[] {9021, 9022, 9023, 9024}, new SkillEffect(Skills.MAGIC,3,0))), + SANFEW(new Potion(new int[] {10925, 10927, 10929, 10931}, new MultiEffect(new RestoreEffect(8,0.25, true), new AddTimerEffect("poison:immunity", secondsToTicks(90)), new RemoveTimerEffect("disease")))), SUPER_ENERGY(new Potion(new int[] {3016, 3018, 3020, 3022}, new EnergyEffect(20))), BLAMISH_OIL(new FakeConsumable(1582, new String[] {"You know... I'd really rather not."})), /** Barbarian Mixes */ PRAYERMIX(new BarbarianMix(new int[] {11465, 11467}, new MultiEffect(new PrayerEffect(7, 0.25), new HealingEffect(6)))), - ZAMMY_MIX(new BarbarianMix(new int[] {11521, 11523}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.15), new SkillEffect(Skills.STRENGTH, 0, 0.25), new SkillEffect(Skills.DEFENCE, 0, -0.1), new RandomPrayerEffect(0, 10)))), + ZAMMY_MIX(new BarbarianMix(new int[] {11521, 11523}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.15), new SkillEffect(Skills.STRENGTH, 0, 0.25), new SkillEffect(Skills.DEFENCE, 0, -0.1)))), ATT_MIX(new BarbarianMix(new int[] {11429, 11431}, new MultiEffect(new SkillEffect(Skills.ATTACK, 3, 0.1), new HealingEffect(3)))), ANTIP_MIX(new BarbarianMix(new int[] {11433, 11435}, new MultiEffect(new AddTimerEffect("poison:immunity", secondsToTicks(90)), new HealingEffect(3)))), - RELIC_MIX(new BarbarianMix(new int[] {11437, 11439}, new MultiEffect(new RemoveTimerEffect("disease"), new SetAttributeEffect("disease:immunity", 300), new HealingEffect(3)))), + RELIC_MIX(new BarbarianMix(new int[] {11437, 11439}, new MultiEffect(new CureDiseaseEffect(), new HealingEffect(3)))), STR_MIX(new BarbarianMix(new int[] {11443, 11441}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0.1), new HealingEffect(3)))), RESTO_MIX(new BarbarianMix(new int[] {11449, 11451}, new MultiEffect(new RestoreEffect(10, 0.3), new HealingEffect(3)))), SUPER_RESTO_MIX(new BarbarianMix(new int [] {11493, 11495}, new MultiEffect(new RestoreEffect(8,0.25), new PrayerEffect(8, 0.25), new SummoningEffect(8, 0.25), new HealingEffect(6)))), - ENERGY_MIX(new BarbarianMix(new int[] {11453, 11455}, new MultiEffect(new EnergyEffect(10), new HealingEffect(6)))), + ENERGY_MIX(new BarbarianMix(new int[] {11453, 11455}, new MultiEffect(new EnergyEffect(10), new HealingEffect(3)))), DEF_MIX(new BarbarianMix(new int[] {11457, 11459}, new MultiEffect(new SkillEffect(Skills.DEFENCE, 3, 0.1), new HealingEffect(6)))), AGIL_MIX(new BarbarianMix(new int[] {11461, 11463}, new MultiEffect(new SkillEffect(Skills.AGILITY, 3, 0), new HealingEffect(6)))), COMBAT_MIX(new BarbarianMix(new int[] {11445, 11447}, new MultiEffect(new SkillEffect(Skills.ATTACK, 3, 0.1), new SkillEffect(Skills.STRENGTH, 3, 0.1), new HealingEffect(6)))), @@ -365,8 +365,13 @@ public enum Consumables { SUPER_ENERGY_MIX(new BarbarianMix(new int[] {11481, 11483}, new MultiEffect(new EnergyEffect(20), new HealingEffect(6)))), HUNTING_MIX(new BarbarianMix(new int[] {11517, 11519}, new MultiEffect(new SkillEffect(Skills.HUNTER, 3, 0), new HealingEffect(6)))), SUPER_STR_MIX(new BarbarianMix(new int[] {11485, 11487}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 5, 0.15), new HealingEffect(6)))), - ANTIDOTE_PLUS_MIX(new BarbarianMix(new int[] {11501, 11503}, new MultiEffect(new AddTimerEffect("poison:immunity", minutesToTicks(9)), new RandomHealthEffect(3, 7)))), - ANTIP_SUPERMIX(new BarbarianMix(new int[] {11473, 11475}, new MultiEffect(new AddTimerEffect("poison:immunity", minutesToTicks(6)), new RandomHealthEffect(3, 7)))), + ANTIDOTE_PLUS_MIX(new BarbarianMix(new int[] {11501, 11503}, new MultiEffect(new AddTimerEffect("poison:immunity", minutesToTicks(9)), new HealingEffect(6)))), + ANTIP_SUPERMIX(new BarbarianMix(new int[] {11473, 11475}, new MultiEffect(new AddTimerEffect("poison:immunity", minutesToTicks(6)), new HealingEffect(6)))), + ANTIFIRE_MIX(new BarbarianMix(new int[] {11505, 11507}, new MultiEffect(new AddTimerEffect("dragonfire:immunity", 600, true), new HealingEffect(6)))), + MAGIC_ESS_MIX(new BarbarianMix(new int[] {11489, 11491}, new MultiEffect(new SkillEffect(Skills.MAGIC, 3, 0), new HealingEffect(6)))), + SUPER_DEF_MIX(new BarbarianMix(new int[] {11497, 11499}, new MultiEffect(new SkillEffect(Skills.DEFENCE, 5, 0.15), new HealingEffect(6)))), + RANGING_MIX(new BarbarianMix(new int[] {11509, 11511}, new MultiEffect(new SkillEffect(Skills.RANGE, 4, 0.1), new HealingEffect(6)))), + MAGIC_MIX(new BarbarianMix(new int[] {11513, 11515}, new MultiEffect(new SkillEffect(Skills.MAGIC, 4, 0), new HealingEffect(6)))), /** Stealing creation potions */ SC_PRAYER(new Potion(new int[] {14207, 14209, 14211, 14213, 14215}, new PrayerEffect(7, 0.25))), diff --git a/Server/src/main/content/data/consumables/effects/CureDiseaseEffect.kt b/Server/src/main/content/data/consumables/effects/CureDiseaseEffect.kt new file mode 100644 index 000000000..4c63c6e99 --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/CureDiseaseEffect.kt @@ -0,0 +1,21 @@ +package content.data.consumables.effects + +import core.api.* +import core.game.consumable.ConsumableEffect +import core.game.node.entity.player.Player +import core.game.system.timer.impl.Disease + +class CureDiseaseEffect () : ConsumableEffect() { + override fun activate (p: Player) { + val existingTimer = getTimer(p) + if (existingTimer != null) { + existingTimer.hitsLeft -= 9 + if (existingTimer.hitsLeft <= 0) { + sendMessage(p, "The disease has been cured.") + removeTimer(p) + }else{ + sendMessage(p,"You feel slightly better.") + } + } + } +} diff --git a/Server/src/main/content/data/consumables/effects/RestoreEffect.java b/Server/src/main/content/data/consumables/effects/RestoreEffect.java index 3b57acabf..ba9b3ea8d 100644 --- a/Server/src/main/content/data/consumables/effects/RestoreEffect.java +++ b/Server/src/main/content/data/consumables/effects/RestoreEffect.java @@ -6,21 +6,37 @@ import core.game.node.entity.skill.Skills; public class RestoreEffect extends ConsumableEffect { double base,bonus; + boolean all_skills; // Except for hitpoints public RestoreEffect(double base, double bonus){ this.base = base; this.bonus = bonus; + this.all_skills = false; + } + + public RestoreEffect(double base, double bonus, boolean all_skills){ + this.base = base; + this.bonus = bonus; + this.all_skills = all_skills; } final int[] SKILLS = new int[] { Skills.DEFENCE, Skills.ATTACK, Skills.STRENGTH, Skills.MAGIC, Skills.RANGE }; + final int[] ALL_SKILLS = new int[]{ + Skills.ATTACK,Skills.DEFENCE, Skills.STRENGTH,Skills.RANGE,Skills.PRAYER,Skills.MAGIC, Skills.COOKING, + Skills.WOODCUTTING,Skills.FLETCHING,Skills.FISHING,Skills.FIREMAKING,Skills.CRAFTING,Skills.SMITHING, + Skills.MINING,Skills.HERBLORE,Skills.AGILITY,Skills.THIEVING,Skills.SLAYER,Skills.FARMING, + Skills.RUNECRAFTING,Skills.HUNTER,Skills.CONSTRUCTION,Skills.SUMMONING }; @Override public void activate(Player p) { Skills sk = p.getSkills(); - for(int skill : SKILLS){ + int[] skills = this.all_skills ? ALL_SKILLS : SKILLS; + for(int skill : skills){ int statL = sk.getStaticLevel(skill); + int boost = (int) (base + (statL * bonus)); int curL = sk.getLevel(skill); if(curL < statL){ - int boost = (int) (base + (statL * bonus)); p.getSkills().updateLevel(skill, boost, statL); } + if (skill == Skills.PRAYER) + p.getSkills().incrementPrayerPoints(boost); } } } diff --git a/Server/src/main/content/data/skill/SkillingPets.java b/Server/src/main/content/data/skill/SkillingPets.java deleted file mode 100644 index 4ea03edd9..000000000 --- a/Server/src/main/content/data/skill/SkillingPets.java +++ /dev/null @@ -1,103 +0,0 @@ -package content.data.skill; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.repository.Repository; - -/** - * Represents the skilling pets obtained randomly. - * @author Empathy - * - */ -public enum SkillingPets { - - BABY_RED_CHINCHOMPA(new Item(14823), "Baby Chinchompa", Skills.HUNTER), - BABY_GREY_CHINCHOMPA(new Item(14824), "Baby Chinchompa", Skills.HUNTER), - BEAVER(new Item(14821), "Beaver", Skills.WOODCUTTING), - GOLEM(new Item(14822), "Rock Golem", Skills.MINING), - HERON(new Item(14827), "Heron", Skills.FISHING); - - /** - * The pet item drop. - */ - private final Item pet; - - /** - * The name. - */ - private final String name; - - /** - * The skill. - */ - private final int skill; - - /** - * Constructs a new {@code SkillingPets} object. - * @param skill The skill id. - * @param pet The pet item. - */ - SkillingPets(Item pet, String name, int skill) { - this.pet = pet; - this.name = name; - this.skill = skill; - } - - /** - * Checks the pet drop. - * @param player The player. - * @param pet The pet drop to check. - */ - public static void checkPetDrop(Player player, SkillingPets pet) { - if (pet == null) { - return; - } - int defaultChance = 15000; - int newChance = (defaultChance / player.getSkills().getStaticLevel(pet.getSkill()) * 55); - int outOf = (newChance > defaultChance ? defaultChance : newChance); - int getChance = outOf; - if (getChance != 1) { - return; - } - if (player.hasItem(pet.getPet())) { - return; - } - if (player.getFamiliarManager().hasFamiliar() && player.getInventory().isFull()) { - return; - } - if (player.getFamiliarManager().hasFamiliar()) { - if (player.getFamiliarManager().getFamiliar().getName().equalsIgnoreCase(pet.getName())) { - return; - } - player.getInventory().add(pet.getPet()); - player.sendNotificationMessage("You feel something weird sneaking into your backpack."); - } else { - player.getFamiliarManager().summon(pet.getPet(), true); - player.sendNotificationMessage("You have a funny feeling like you're being followed."); - } - Repository.sendNews(player.getUsername() + " has found a " + pet.getPet().getName() + "!"); - } - - - /** - * @return the pet - */ - public Item getPet() { - return pet; - } - - /** - * @return the pet name. - */ - public String getName() { - return name; - } - - /** - * @return the skill. - */ - public int getSkill() { - return skill; - } -} diff --git a/Server/src/main/content/global/activity/cchallange/ChampionChallengeListener.kt b/Server/src/main/content/global/activity/cchallange/ChampionChallengeListener.kt index 8cea6d806..1b9147172 100644 --- a/Server/src/main/content/global/activity/cchallange/ChampionChallengeListener.kt +++ b/Server/src/main/content/global/activity/cchallange/ChampionChallengeListener.kt @@ -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,28 +93,28 @@ 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" ) private val PORTCULLIS = Scenery.PORTCULLIS_10553 - private val LADDER = Scenery.LADDER_10554 private val CHAMPION_STATUE_CLOSED = Scenery.CHAMPION_STATUE_10556 private val CHAMPION_STATUE_OPEN = Scenery.CHAMPION_STATUE_10557 private val TRAPDOOR_CLOSED = Scenery.TRAPDOOR_10558 @@ -124,6 +124,13 @@ class ChampionChallengeListener : InteractionListener, MapArea { private val ARENA_ZONE = 12696 override fun defineListeners() { + // Champion's Guild Basement Ladder to Main Floor + addClimbDest(Location(3190, 9758, 0), Location(3190, 3356, 0)) + // Champion Statue Ladder to Arena + addClimbDest(Location(3184, 9758, 0), Location(3182, 9758, 0)) + // Arena Ladder to Champion's Guild Basement + addClimbDest(Location(3183, 9758, 0), Location(3185, 9758, 0)) + on(LARXUS, IntType.NPC, "talk-to") { player, _ -> openDialogue(player, LarxusDialogue(false)) return@on true @@ -134,18 +141,18 @@ class ChampionChallengeListener : InteractionListener, MapArea { return@on true } - on(TRAPDOOR_CLOSED, IntType.SCENERY, "open") { _, node -> - replaceScenery(node.asScenery(), TRAPDOOR_OPEN, 100, node.location) - return@on true - } - onUseWith(IntType.NPC, ChampionScrollsDropHandler.SCROLLS, NPCs.LARXUS_3050) { player, _, _ -> openDialogue(player, LarxusDialogue(true)) return@onUseWith true } + on(TRAPDOOR_CLOSED, IntType.SCENERY, "open") { _, node -> + replaceScenery(node.asScenery(), TRAPDOOR_OPEN, 100, node.location) + return@on true + } + on(TRAPDOOR_OPEN, IntType.SCENERY, "close") { _, node -> - replaceScenery(node.asScenery(), TRAPDOOR_CLOSED, 100, node.location) + replaceScenery(node.asScenery(), TRAPDOOR_CLOSED, -1, node.location) return@on true } @@ -154,15 +161,6 @@ class ChampionChallengeListener : InteractionListener, MapArea { return@on true } - on(LADDER, IntType.SCENERY, "climb-up") { player, _ -> - teleport(player, Location.create(3185, 9758, 0)) - return@on true - } - on(CHAMPION_STATUE_OPEN, IntType.SCENERY, "climb-down") { player, _ -> - teleport(player, Location.create(3182, 9758, 0)) - return@on true - } - on(PORTCULLIS, IntType.SCENERY, "open") { player, node -> if (player.getAttribute("championsarena:start", false) == false) { sendNPCDialogue(player, NPCs.LARXUS_3050, "You need to arrange a challenge with me before you enter the arena.") @@ -229,4 +227,4 @@ class ChampionChallengeListener : InteractionListener, MapArea { ZoneRestriction.RANDOM_EVENTS ) } -} +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/ChampionScrollsDropHandler.kt b/Server/src/main/content/global/activity/cchallange/ChampionScrollsDropHandler.kt index 02b3b2b0e..bfbe78a76 100644 --- a/Server/src/main/content/global/activity/cchallange/ChampionScrollsDropHandler.kt +++ b/Server/src/main/content/global/activity/cchallange/ChampionScrollsDropHandler.kt @@ -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 diff --git a/Server/src/main/content/global/activity/cchallange/LarxusDialogue.kt b/Server/src/main/content/global/activity/cchallange/LarxusDialogue.kt index 6ec919bd7..3157b6b50 100644 --- a/Server/src/main/content/global/activity/cchallange/LarxusDialogue.kt +++ b/Server/src/main/content/global/activity/cchallange/LarxusDialogue.kt @@ -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 } } } } diff --git a/Server/src/main/content/global/activity/cchallange/npc/ImpChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/ImpChampionNPC.kt index 4a81fc066..1c131efc9 100644 --- a/Server/src/main/content/global/activity/cchallange/npc/ImpChampionNPC.kt +++ b/Server/src/main/content/global/activity/cchallange/npc/ImpChampionNPC.kt @@ -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::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.") } } } diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt index 9663b16fa..9432ea009 100644 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt @@ -11,12 +11,13 @@ import core.ServerStore.Companion.getString import content.global.bots.ShootingStarBot import core.game.world.repository.Repository +import core.tools.RandomFunction /** * Represents a shooting star object (Only ever initialized once) (ideally) * @author Ceikry */ -class ShootingStar(var level: ShootingStarType = ShootingStarType.values().random()){ +class ShootingStar(var level: ShootingStarType = ShootingStarType.values().random()) { val crash_locations = mapOf( "East of Dark Wizards' Tower" to Location.create(2925, 3339, 0), // East of Dark Wizards' Tower "Crafting Guild" to Location.create(2940, 3280, 0), // Crafting Guild Mine @@ -26,12 +27,12 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando "Brimhaven mining site" to Location.create(2743, 3143, 0), // Brimhaven mining site "South Crandor mining site" to Location.create(2822, 3239, 0), // South Crandor mining site (requires Dragon Slayer) "Karamja mining site" to Location.create(2854, 3032, 0), // Karamja mining site - "Shilo Village mining site" to Location.create(2826, 2997, 0), // Shilo Village mining site/Gem rocks + "Shilo Village mining site" to Location.create(2826, 2997, 0), // Shilo Village mining site/Gem rocks (requires Shilo Village prereqs) "Relleka mining site" to Location.create(2682, 3700, 0), // Rellekka mining site - "Jatizso mine" to Location.create(2393, 3815, 0), //Jatiszo mining site (requires Fremennik Trials) - //"Lunar Isle mine" to Location.create(2140, 3939, 0), // Lunar Isle mine (requires Lunar Diplomacy?) - //"Miscellania coal mine" to Location.create(2529, 3887, 0), // Miscellania coal mine (requires Fremennik Trials) - //"Neitiznot runite mine" to Location.create(2376, 3835, 0), // Near the Neitiznot runite mine (requires Fremennik Trials) currently inaccessible as bridge does not work + "Jatizso mine" to Location.create(2393, 3815, 0), //Jatiszo mining site (requires Fremennik Isles prereqs) + "Lunar Isle mine" to Location.create(2140, 3939, 0), // Lunar Isle mine (requires Lunar Diplomacy prereqs) + "Miscellania coal mine" to Location.create(2529, 3887, 0), // Miscellania coal mine (requires The Fremennik Trials) + //"Neitiznot runite mine" to Location.create(2376, 3835, 0), // Near the Neitiznot runite mine (requires Fremennik Isles prereqs) currently inaccessible as bridge does not work "Ardougne mining site" to Location.create(2600, 3232, 0), // Ardougne mining site (Monastery) "Ardougne eastern mine" to Location.create(2706, 3334, 0), // Ardougne mining site (Legends Guild) "Kandarin Coal trucks" to Location.create(2589, 3485, 0), // Kandarin Coal trucks @@ -48,11 +49,11 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando "South-west Varrock mine" to Location.create(3176, 3362, 0), // South-west Varrock mine / Champion's Guild mine "Varrock east bank" to Location.create(3259, 3407, 0), // Varrock east bank / Rune shop "Lumbridge Swamp south-east mine" to Location.create(3227, 3150, 0), // Lumbridge Swamp south-east mine - //"Burgh de Rott bank" to Location.create(3500, 3219, 0), // Burgh de Rott bank (requires Quest to enter) + //"Burgh de Rott bank" to Location.create(3500, 3219, 0), // Burgh de Rott bank (requires quest to enter) "Canifis Bank" to Location.create(3504, 3487, 0), // Canifis bank - "Mos Le'Harmless bank" to Location.create(3687, 2969, 0), // Mos Le'Harmless bank (requires Quest to enter but is currently accessible for Slayer) + "Mos Le'Harmless bank" to Location.create(3687, 2969, 0), // Mos Le'Harmless bank (requires quest to enter but is currently accessible for Slayer) "Gnome stronghold Bank" to Location.create(2460, 3432, 0), // Gnome stronghold bank - "Lletya bank" to Location.create(2329, 3163, 0), // Lletya bank (requires Roving Elves?) + "Lletya bank" to Location.create(2329, 3163, 0), // Lletya bank (requires MEP1 prereqs) "Piscatoris mining site" to Location.create(2336, 3636, 0), // Piscatoris mining site "North Edgeville mining site" to Location.create(3101, 3569, 0), // Wilderness Steel mine / Zamorak mage mine "Southern wilderness mine" to Location.create(3025, 3591, 0), // Wilderness skeleton mine @@ -79,22 +80,15 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando * Degrades a ShootingStar (or removes the starObject and spawns a Star Sprite if it's the last star) */ fun degrade() { - if(level.ordinal == 0){ - selfBots.filter { it.isMining() }.forEach { it.sleep() } - SceneryBuilder.remove(starObject) - isSpawned = false - starSprite.location = starObject.location - starSprite.init() - spriteSpawned = true - ShootingStarPlugin.getStoreFile().clear() + if(level.ordinal == 0) { + spawnSprite() return } level = getNextType() maxDust = level.totalStardust dustLeft = level.totalStardust - ShootingStarPlugin.getStoreFile()["level"] = level.ordinal - ShootingStarPlugin.getStoreFile()["isDiscovered"] = isDiscovered + ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft val newStar = Scenery(level.objectId, starObject.location) SceneryBuilder.replace(starObject, newStar) @@ -110,7 +104,6 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando */ fun fire() { SceneryBuilder.remove(starObject) - rebuildVars() clearSprite() SceneryBuilder.add(starObject) if(!isSpawned) { @@ -123,37 +116,54 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando } isSpawned = true Repository.sendNews("A shooting star level ${level.ordinal + 1} just crashed near ${location}!") + ShootingStarPlugin.getStoreFile()["level"] = level.ordinal + ShootingStarPlugin.getStoreFile()["location"] = location + ShootingStarPlugin.getStoreFile()["isDiscovered"] = isDiscovered + ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft } /** * Rebuilds some of the variables with new information. */ fun rebuildVars(){ - if(firstStar && ShootingStarPlugin.getStoreFile().isNotEmpty()){ - level = ShootingStarType.values()[ShootingStarPlugin.getStoreFile().getInt("level")] - location = ShootingStarPlugin.getStoreFile().getString("location") - isDiscovered = ShootingStarPlugin.getStoreFile().getBoolean("isDiscovered") - } else { - level = ShootingStarType.values().random() - location = crash_locations.entries.random().key - isDiscovered = false + // Defaults + var levelOrd = RandomFunction.random(9) + level = ShootingStarType.values()[levelOrd] + location = crash_locations.entries.random().key + isDiscovered = false + dustLeft = level.totalStardust + ticks = 0 + spriteSpawned = false + + if (firstStar && ShootingStarPlugin.getStoreFile().isNotEmpty()) { + // Replace default with stored values, if any + levelOrd = ShootingStarPlugin.getStoreFile().getInt("level", levelOrd) + level = ShootingStarType.values()[levelOrd] + location = ShootingStarPlugin.getStoreFile().getString("location", location) + isDiscovered = ShootingStarPlugin.getStoreFile().getBoolean("isDiscovered", false) + dustLeft = ShootingStarPlugin.getStoreFile().getInt("dustLeft", dustLeft) + ticks = ShootingStarPlugin.getStoreFile().getInt("ticks", ticks) + spriteSpawned = ShootingStarPlugin.getStoreFile().getBoolean("spriteSpawned", false) } maxDust = level.totalStardust - dustLeft = level.totalStardust starObject = Scenery(level.objectId, crash_locations.get(location)) + } - ShootingStarPlugin.getStoreFile()["level"] = level.ordinal - ShootingStarPlugin.getStoreFile()["location"] = location - ShootingStarPlugin.getStoreFile()["isDiscovered"] = false - - ticks = 0 - firstStar = false + fun spawnSprite() { + selfBots.filter { it.isMining() }.forEach { it.sleep() } + SceneryBuilder.remove(starObject) + isSpawned = false + starSprite.location = starObject.location + starSprite.init() + spriteSpawned = true + ShootingStarPlugin.getStoreFile()["spriteSpawned"] = spriteSpawned } fun clearSprite() { starSprite.clear() spriteSpawned = false + ShootingStarPlugin.getStoreFile()["spriteSpawned"] = spriteSpawned } /** @@ -161,6 +171,7 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando */ fun decDust() { if(--dustLeft <= 0) degrade() + ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft } /** diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt index 74360b3d2..5c5ecf033 100644 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt @@ -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. */ @@ -46,14 +50,14 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin //checks if the star has been discovered and if not, awards the bonus xp. Xp can be awarded regardless of mining level as per the wiki. if (!star.isDiscovered && !player.isArtificial) { val bonusXp = 75 * player.skills.getStaticLevel(Skills.MINING) - player.incrementAttribute("/save:shooting-star:bonus-xp", bonusXp) + rewardXP(player, Skills.MINING, bonusXp.toDouble()) Repository.sendNews(player.username + " is the discoverer of the crashed star near " + star.location + "!") - player.sendMessage("You have ${player.skills.experienceMutiplier * player.getAttribute("shooting-star:bonus-xp", 0).toDouble()} bonus xp towards mining stardust.") ShootingStarPlugin.submitScoreBoard(player) star.isDiscovered = true + 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 @@ -87,6 +91,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin val bonusXp = player.getAttribute("shooting-star:bonus-xp", 0).toDouble() var xp = star.level.exp.toDouble() if(bonusXp > 0) { + // legacy: shooting star bonus xp used to be handed out in an inauthentic way; see GL !2092 val delta = Math.min(bonusXp, xp) player.incrementAttribute("/save:shooting-star:bonus-xp", (-delta).toInt()) xp += delta @@ -99,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) } @@ -129,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.") } } diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt index cf141ca56..1e993e1bb 100644 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt @@ -12,11 +12,11 @@ import core.ServerStore.Companion.getBoolean import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType -import core.tools.SystemLogger import core.game.system.command.Privilege import core.game.world.GameWorld import core.tools.Log import core.tools.secondsToTicks +import content.data.Quests class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Commands, StartupListener { override fun login(player: Player) { @@ -27,14 +27,30 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com override fun tick() { ++star.ticks + // Check if the current star sprite should expire val maxDelay = tickDelay + (tickDelay / 3) if(star.ticks > maxDelay && star.spriteSpawned){ star.clearSprite() } - if ((star.ticks >= tickDelay && !star.spriteSpawned) || (!star.isSpawned && !star.spriteSpawned)) { + if (star.firstStar && !star.isSpawned && !star.spriteSpawned) { + // Apparently, the server has only just booted + star.rebuildVars() + if (star.spriteSpawned) { + star.spawnSprite() + } else { + star.fire() + } + star.firstStar = false + } + + // Check if it's time to fire a new one + if (star.ticks >= tickDelay && !star.spriteSpawned) { + star.rebuildVars() star.fire() } + + getStoreFile()["ticks"] = star.ticks } override fun defineListeners() { @@ -66,35 +82,52 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com return@on true } - val condition: (Player) -> Boolean = when(star.location.toLowerCase()){ - "canifis bank" -> { p -> hasRequirement(p, "Priest in Peril") } - //"Burgh de Rott bank" -> { p -> p.questRepository.isComplete("Priest in Peril")} // for now, require this as it is in Morytania. remove when appropriate quests added - Crash - "crafting guild" -> {p -> hasLevelStat(p, Skills.CRAFTING, 40) } - "lletya bank" -> {p -> hasRequirement(p, "Mourning's End Part I") } - "jatizso mine" -> {p -> hasRequirement(player, "Fremennik Trials") } - "south crandor mining site" -> {p -> hasRequirement(p, "Dragon Slayer") } - "shilo village mining site" -> {p -> hasRequirement(p, "Shilo Village") } - "mos le'harmless bank" -> {p -> hasRequirement(p, "Cabin Fever") } - else -> {_ -> true} + class RingDialogue(val star: ShootingStar) : DialogueFile() { + val shouldWarn = when (star.location) { + "North Edgeville mining site", + "Southern wilderness mine", + "Wilderness hobgoblin mine", + "Pirates' Hideout mine", + "Lava Maze mining site", + "Mage Arena bank" -> true + else -> false + } + + override fun handle(componentID: Int, buttonID: Int) { + fun teleportToStar(player: Player) { + val condition: (p: Player) -> Boolean = when (star.location.toLowerCase()) { + "canifis bank" -> {p -> requireQuest(p, Quests.PRIEST_IN_PERIL, "to access this.")} + //"burgh de rott bank" -> {p -> hasRequirement(p, Quests.IN_AID_OF_THE_MYREQUE)} //disabled: crash + "crafting guild" -> {p -> hasLevelStat(p, Skills.CRAFTING, 40)} + "lletya bank" -> {p -> hasRequirement(p, Quests.MOURNINGS_END_PART_I)} + "jatizso mine" -> {p -> hasRequirement(p, Quests.THE_FREMENNIK_ISLES)} + "south crandor mining site" -> {p -> hasRequirement(p, Quests.DRAGON_SLAYER)} + "shilo village mining site" -> {p -> hasRequirement(p, Quests.SHILO_VILLAGE)} + "mos le'harmless bank" -> {p -> hasRequirement(p, Quests.CABIN_FEVER)} //needs to be updated to check for completion when the quest releases; https://runescape.wiki/w/Mos_Le%27Harmless?oldid=913025 + "lunar isle mine" -> {p -> hasRequirement(p, Quests.LUNAR_DIPLOMACY)} + "miscellania coal mine" -> {p -> requireQuest(p, Quests.THE_FREMENNIK_TRIALS, "to access this.")} + //"neitiznot runite mine" -> {p -> hasRequirement(p, Quests.THE_FREMENNIK_ISLES)} //disabled: currently not reachable + else -> {_ -> true} + } + if (!condition.invoke(player)) { + sendDialogue(player,"Magical forces prevent your teleportation.") + } else if (teleport(player, star.crash_locations[star.location]!!.transform(0, -1, 0), TeleportManager.TeleportType.MINIGAME)) { + getRingStoreFile()[player.username.toLowerCase()] = true + } + } + when (stage) { + 0 -> dialogue(if (star.spriteSpawned) "The star sprite has already been freed." else "The star sprite is still trapped.").also { if (shouldWarn) stage++ else stage += 2 } + 1 -> dialogue("WARNING: The star is located in the wilderness.").also { stage++ } + 2 -> player.dialogueInterpreter.sendOptions("Teleport to the star?", "Yes", "No").also { stage++ } + 3 -> when (buttonID) { + 1 -> end().also { teleportToStar(player) } + 2 -> end() + } + } + } } - if(!condition.invoke(player)){ - sendDialogue(player, "Magical forces prevent your teleportation.") - return@on true - } - - val shouldWarn = when(star.location){ - "North Edgeville mining site", - "Southern wilderness mine", - "Wilderness hobgoblin mine", - "Pirates' Hideout mine", - "Lava Maze mining site", - "Mage Arena bank" -> true - else -> false - } - - openDialogue(player, RingDialogue(shouldWarn, star)) - + openDialogue(player, RingDialogue(star)) return@on true } } @@ -105,6 +138,7 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com } define("submit", Privilege.ADMIN) { _, _ -> + star.rebuildVars() star.fire() } @@ -119,26 +153,6 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com private data class ScoreboardEntry(val player: String, val time: Int) - private class RingDialogue(val shouldWarn: Boolean, val star: ShootingStar) : DialogueFile(){ - override fun handle(componentID: Int, buttonID: Int) { - when (stage) { - 0 -> dialogue(if (star.spriteSpawned) "The star sprite has already been freed." else "The star sprite is still trapped.").also { if (shouldWarn) stage++ else stage += 2 } - 1 -> dialogue("WARNING: The star is located in the wilderness.").also { stage++ } - 2 -> player!!.dialogueInterpreter.sendOptions("Teleport to the star?", "Yes", "No").also { stage++ } - 3 -> when (buttonID) { - 1 -> teleport(player!!, star).also { end() } - 2 -> end() - } - } - } - - fun teleport(player: Player, star: ShootingStar){ - if (teleport(player, star.crash_locations[star.location]!!.transform(0, -1, 0), TeleportManager.TeleportType.MINIGAME)) { - getRingStoreFile()[player.username.toLowerCase()] = true - } - } - } - companion object { private val star = ShootingStar() private val tickDelay = if(GameWorld.settings?.isDevMode == true) 200 else 25000 diff --git a/Server/src/main/content/global/ame/KidnapHelper.kt b/Server/src/main/content/global/ame/KidnapHelper.kt new file mode 100644 index 000000000..716fb6277 --- /dev/null +++ b/Server/src/main/content/global/ame/KidnapHelper.kt @@ -0,0 +1,58 @@ +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(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(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) + } + } +} + +fun returnPlayer(player: Player) { + player.locks.unlockTeleport() + val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION) + teleport(player, destination!!) + unlock(player) + removeAttributes(player, "/save:original-loc", "kidnapped-by-random") +} diff --git a/Server/src/main/content/global/ame/RandomEventNPC.kt b/Server/src/main/content/global/ame/RandomEventNPC.kt index c0aba00af..b4da55bb3 100644 --- a/Server/src/main/content/global/ame/RandomEventNPC.kt +++ b/Server/src/main/content/global/ame/RandomEventNPC.kt @@ -1,11 +1,18 @@ package content.global.ame -import content.global.ame.events.MysteriousOldManNPC +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 +import core.game.node.entity.combat.CombatStyle import core.game.node.entity.impl.PulseType import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.agg.AggressiveBehavior +import core.game.node.entity.npc.agg.AggressiveHandler import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.world.map.Location @@ -13,10 +20,13 @@ 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.api.utils.WeightBasedTable +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 import kotlin.random.Random import kotlin.reflect.full.createInstance @@ -36,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 } @@ -66,7 +77,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { if (!player.getAttribute("random:pause", false)) { ticksLeft-- } - if (!pulseManager.hasPulseRunning() && !finalized) { follow() } @@ -87,10 +97,14 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { location = spawnLocation player.setAttribute("re-npc", this) super.init() + super.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() { + override fun canSelectTarget(entity: Entity, target: Entity): Boolean { + return target == player + } + }) } open fun onTimeUp() { - noteAndTeleport() terminate() } @@ -118,4 +132,38 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { } abstract fun talkTo(npc: NPC) -} \ No newline at end of file + + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { + if (entity != player) { + if (entity is Player) { + sendMessage(entity, "It isn't interested in fighting you.") //TODO authentic message + } + return false + } + return super.isAttackable(entity, style, message) + } + + fun idForCombatLevel(ids: List, player: Player): Int { + val index = min(ids.size, ceil(player.properties.currentCombatLevel / 20.0).toInt()) - 1 + return ids[index] + } + + fun sayLine(npc: NPC, phrases: Array, 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) + } + } + } +} diff --git a/Server/src/main/content/global/ame/RandomEvents.kt b/Server/src/main/content/global/ame/RandomEvents.kt index 7e07d744a..db5ad9b2e 100644 --- a/Server/src/main/content/global/ame/RandomEvents.kt +++ b/Server/src/main/content/global/ame/RandomEvents.kt @@ -1,23 +1,28 @@ package content.global.ame import org.rs09.consts.Items -import content.global.ame.events.MysteriousOldManNPC +import content.global.ame.events.surpriseexam.MysteriousOldManNPC import content.global.ame.events.certer.CerterNPC -import content.global.ame.events.drilldemon.SeargentDamienNPC +import content.global.ame.events.drilldemon.SergeantDamienNPC import content.global.ame.events.drunkendwarf.DrunkenDwarfNPC import content.global.ame.events.evilbob.EvilBobNPC import content.global.ame.events.evilchicken.EvilChickenNPC import content.global.ame.events.freakyforester.FreakyForesterNPC +import content.global.ame.events.maze.MazeNPC import content.global.ame.events.genie.GenieNPC +import content.global.ame.events.candlelight.PiousPeteNPC +import content.global.ame.events.pillory.PilloryNPC import content.global.ame.events.rickturpentine.RickTurpentineNPC import content.global.ame.events.rivertroll.RiverTrollRENPC import content.global.ame.events.rockgolem.RockGolemRENPC +import content.global.ame.events.quizmaster.QuizMasterNPC import content.global.ame.events.sandwichlady.SandwichLadyRENPC import content.global.ame.events.shade.ShadeRENPC import content.global.ame.events.strangeplant.StrangePlantNPC import content.global.ame.events.swarm.SwarmNPC import content.global.ame.events.treespirit.TreeSpiritRENPC import content.global.ame.events.zombie.ZombieRENPC +import core.ServerConstants import core.api.utils.WeightBasedTable import core.api.utils.WeightedItem @@ -26,6 +31,7 @@ import core.game.node.entity.skill.Skills enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = null, val skillIds: IntArray = intArrayOf(), val type: String = "") { SANDWICH_LADY(npc = SandwichLadyRENPC()), GENIE(npc = GenieNPC()), + CANDLELIGHT(npc = PiousPeteNPC(), skillIds = intArrayOf(Skills.PRAYER)), CERTER(npc = CerterNPC(), loot = WeightBasedTable.create( WeightedItem(Items.UNCUT_SAPPHIRE_1623,1,1,3.4), WeightedItem(Items.KEBAB_1971,1,1,1.7), @@ -43,7 +49,8 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985,1,1,0.1), WeightedItem(Items.LOOP_HALF_OF_A_KEY_987,1,1,0.1) )), - DRILL_DEMON(npc = SeargentDamienNPC()), + MAZE(npc = MazeNPC()), + DRILL_DEMON(npc = SergeantDamienNPC()), EVIL_CHICKEN(npc = EvilChickenNPC()), STRANGE_PLANT(npc = StrangePlantNPC()), SWARM(npc = SwarmNPC()), @@ -52,7 +59,9 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n RICK_TURPENTINE(npc = RickTurpentineNPC(), loot = CERTER.loot), SURPRISE_EXAM(npc = MysteriousOldManNPC(), type = "sexam"), FREAKY_FORESTER(npc = FreakyForesterNPC(), skillIds = intArrayOf(Skills.WOODCUTTING)), + PILLORY(npc = PilloryNPC(), skillIds = intArrayOf(Skills.THIEVING)), TREE_SPIRIT(npc = TreeSpiritRENPC(), skillIds = intArrayOf(Skills.WOODCUTTING)), + QUIZ_MASTER(npc = QuizMasterNPC()), RIVER_TROLL(RiverTrollRENPC(), skillIds = intArrayOf(Skills.FISHING)), ROCK_GOLEM(RockGolemRENPC(), skillIds = intArrayOf(Skills.MINING)), SHADE(ShadeRENPC(), skillIds = intArrayOf(Skills.PRAYER)), @@ -76,6 +85,9 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n private fun populateMappings() { for (event in values()) { + if (!ServerConstants.INAUTHENTIC_CANDLELIGHT_RANDOM && event == CANDLELIGHT) { + continue + } for (id in event.skillIds) { val list = skillMap[id] ?: ArrayList().also { skillMap[id] = it } list.add (event) @@ -85,5 +97,4 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n } } } - } diff --git a/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt b/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt new file mode 100644 index 000000000..952bd0289 --- /dev/null +++ b/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt @@ -0,0 +1,20 @@ +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 +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.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 { + val xprate = super.getXpMultiplier(self, attacker) + return if (getAttribute(self, "spawned-by-ame", false)) xprate / 16.0 else xprate + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/MysteriousOldManDialogue.kt b/Server/src/main/content/global/ame/events/MysteriousOldManDialogue.kt deleted file mode 100644 index 628a3c8f0..000000000 --- a/Server/src/main/content/global/ame/events/MysteriousOldManDialogue.kt +++ /dev/null @@ -1,41 +0,0 @@ -package content.global.ame.events - -import core.game.node.entity.player.Player -import content.global.ame.events.supriseexam.SurpriseExamUtils -import core.game.dialogue.DialogueFile -import core.game.system.timer.impl.AntiMacro - -class MysteriousOldManDialogue(val type: String) : DialogueFile() { - - val CHOICE_STAGE = 50000 - - override fun handle(componentID: Int, buttonID: Int) { - - if(type == "sexam" && stage < CHOICE_STAGE){ - npc("Would you like to come do a surprise exam?") - stage = CHOICE_STAGE - } - - else if(stage >= CHOICE_STAGE){ - when(stage) { - CHOICE_STAGE -> options("Yeah, sure!", "No, thanks.").also { stage++ } - CHOICE_STAGE.substage(1) -> when(buttonID){ - 1 -> { - end() - teleport(player!!,type) - AntiMacro.terminateEventNpc(player!!) - } - 2 -> { - end() - AntiMacro.terminateEventNpc(player!!) - } - } - } - } - } - fun teleport(player: Player,type: String){ - when(type){ - "sexam" -> SurpriseExamUtils.teleport(player) - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/MysteriousOldManNPC.kt b/Server/src/main/content/global/ame/events/MysteriousOldManNPC.kt deleted file mode 100644 index 3ed7289b1..000000000 --- a/Server/src/main/content/global/ame/events/MysteriousOldManNPC.kt +++ /dev/null @@ -1,31 +0,0 @@ -package content.global.ame.events - -import content.global.ame.RandomEventNPC -import core.game.node.entity.npc.NPC -import core.tools.RandomFunction -import org.rs09.consts.NPCs -import core.api.utils.WeightBasedTable - -class MysteriousOldManNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { - override fun init() { - super.init() - sayLine() - } - - override fun tick() { - super.tick() - if(RandomFunction.random(1,10) == 5) sayLine() - } - - fun sayLine() { - when(type){ - "sexam" -> sendChat("Surprise exam, ${player.username.capitalize()}!") - } - } - - override fun talkTo(npc: NPC) { - when(type){ - "sexam" -> player.dialogueInterpreter.open(MysteriousOldManDialogue("sexam"),this.asNpc()) - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/candlelight/CandlelightInterface.kt b/Server/src/main/content/global/ame/events/candlelight/CandlelightInterface.kt new file mode 100644 index 000000000..249f9f194 --- /dev/null +++ b/Server/src/main/content/global/ame/events/candlelight/CandlelightInterface.kt @@ -0,0 +1,164 @@ +package content.global.ame.events.candlelight + +import core.api.* +import core.api.utils.PlayerCamera +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import org.rs09.consts.Scenery + +/** + * Candlelight Interface CANDLELIGHT_178 + * + */ +class CandlelightInterface : InterfaceListener, InteractionListener, MapArea { + companion object { + const val CANDLELIGHT_INTERFACE = 178 + const val CANDLELIGHT_RETURN_LOC = "/save:original-loc" + const val CANDLELIGHT_CANDLE_ARRAY = "/save:candlelight:candle-array" + const val CANDLELIGHT_CAMERA_AT = "candlelight:camera-at" + + val CANDLE_LOC_ARRAY = arrayOf( + Location(1967, 4997), + Location(1968, 4998), + Location(1967, 4999), + Location(1968, 5000), + Location(1967, 5001), + Location(1968, 5002), + Location(1967, 5003), + Location(1968, 5004), + Location(1967, 5005), + Location(1968, 5006), + Location(1967, 5007), + ) + + fun initCandlelight(player: Player) { + val candleArray = intArrayOf(0,0,0,0,0,0,2,2,2,2,2) + candleArray.shuffle() + setAttribute(player, CANDLELIGHT_CANDLE_ARRAY, candleArray) + for (candleIndex in 0..10) { + setVarbit(player, 1771 + candleIndex, candleArray[candleIndex]) + } + } + + fun areCandlesLit(player: Player): Boolean { + val candleArray = getAttribute(player, CANDLELIGHT_CANDLE_ARRAY, intArrayOf(0,0,0,0,0,0,0,0,0,0,0)) + for (candle in candleArray) { + if (candle == 0) { + return false + } + } + return true + } + + fun lightCandle(player: Player) { + var currentCamLoc = getAttribute(player, CANDLELIGHT_CAMERA_AT, Location(1968, 5002)) + val candleIndex = CANDLE_LOC_ARRAY.indexOf(currentCamLoc) + val varbit = candleIndex + 1771 // Essentially all varbits are 1771 .. 1881 + + if (candleIndex != -1) { + val candleArray = getAttribute(player, CANDLELIGHT_CANDLE_ARRAY, intArrayOf(0,0,0,0,0,0,0,0,0,0,0)) + if (candleArray[candleIndex] == 0) { + candleArray[candleIndex] = 1 + setAttribute(player, CANDLELIGHT_CANDLE_ARRAY, candleArray) + setVarbit(player, varbit, 1) + sendMessage(player, "You light the candle.") + } else if (candleArray[candleIndex] == 1) { + sendMessage(player, "This candle is already lit.") + } else { + sendMessage(player, "This candle is too short to light.") + } + } else { + sendMessage(player, "There is nothing to light here.") + } + } + + fun moveCamera(player: Player, direction: Direction, firstTime: Boolean = false) { + var currentCamLoc = getAttribute(player, CANDLELIGHT_CAMERA_AT, Location(1968, 5002)) + when(direction) { + Direction.NORTH -> currentCamLoc = currentCamLoc.transform(Direction.NORTH) + Direction.SOUTH -> currentCamLoc = currentCamLoc.transform(Direction.SOUTH) + Direction.EAST -> currentCamLoc = currentCamLoc.transform(Direction.EAST) + Direction.WEST -> currentCamLoc = currentCamLoc.transform(Direction.WEST) + else -> {} + } + if (currentCamLoc.x < 1967) { currentCamLoc.x = 1967 } + if (currentCamLoc.x > 1968) { currentCamLoc.x = 1968 } + if (currentCamLoc.y < 4997) { currentCamLoc.y = 4997 } + if (currentCamLoc.y > 5007) { currentCamLoc.y = 5007 } + setAttribute(player, CANDLELIGHT_CAMERA_AT, currentCamLoc) + PlayerCamera(player).rotateTo(currentCamLoc.x - 30, currentCamLoc.y,0,200) // height is kind of a relative value? + PlayerCamera(player).panTo(currentCamLoc.x + 2, currentCamLoc.y,350, if(firstTime) 400 else 10) + } + } + + override fun defineInterfaceListeners() { + on(CANDLELIGHT_INTERFACE){ player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + 1 -> moveCamera(player, Direction.WEST) + 2 -> moveCamera(player, Direction.EAST) + 3 -> lightCandle(player)/* Light */ + 4 -> moveCamera(player, Direction.SOUTH) + 5 -> moveCamera(player, Direction.NORTH) + 9 -> closeInterface(player) + } + return@on true + } + + onOpen(CANDLELIGHT_INTERFACE){ player, component -> + // Move camera + return@onOpen true + } + + onClose(CANDLELIGHT_INTERFACE){ player, component -> + PlayerCamera(player).reset() + // Reset camera + return@onClose true + } + } + + + override fun defineListeners() { + on((11364 .. 11394).toIntArray(), SCENERY, "light") { player, node -> + setAttribute(player, CANDLELIGHT_CAMERA_AT, Location(node.location.x, node.location.y)) + moveCamera(player, Direction.NORTH_WEST, true) + openInterface(player, CANDLELIGHT_INTERFACE) + return@on true + } + } + + override fun defineDestinationOverrides() { + setDest(SCENERY, (11364 .. 11394).toIntArray(),"light"){ player, node -> + return@setDest Location(1970, node.location.y) + } + } + + + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders.forRegion(7758)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + initCandlelight(entity) + entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.interfaceManager.restoreTabs() + } + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/candlelight/PiousPeteDialogue.kt b/Server/src/main/content/global/ame/events/candlelight/PiousPeteDialogue.kt new file mode 100644 index 000000000..9eb9b7ede --- /dev/null +++ b/Server/src/main/content/global/ame/events/candlelight/PiousPeteDialogue.kt @@ -0,0 +1,94 @@ +package content.global.ame.events.candlelight + +import content.global.ame.RandomEvents +import content.global.ame.events.pillory.PilloryInterface +import content.global.ame.returnPlayer +import core.api.* +import core.game.dialogue.* +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +// iface 178 + +@Initializable +class PiousPeteDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return PiousPeteDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PiousPeteDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.PIOUS_PETE_3207, NPCs._6564) + } +} + +class PiousPeteDialogueFile : DialogueLabeller() { + + override fun addConversation() { + npc(ChatAnim.THINKING, "Have you lit all the tall candles?") + exec { player, npc -> + if (CandlelightInterface.areCandlesLit(player)) { + loadLabel(player, "yeslit") + } else { + loadLabel(player, "nolit") + } + } + + label("nolit") + player(ChatAnim.HALF_GUILTY, "Sorry, not yet.") + npc(ChatAnim.SAD, "Please help me in lighting the candles. I just need you to light all the tall candles, but not the short ones.") + line("Click on the pillars to open an interface", "to move around and light the candles.") + + label("yeslit") + player(ChatAnim.FRIENDLY, "Yes, the tall ones are all lit.") + npc(ChatAnim.HAPPY, "Thank you my brother! I will now return you where you came from with a parting gift.") + npc(ChatAnim.FRIENDLY, "Take care brother!") + exec { player, npc -> + queueScript(player, 0, 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 -> { + returnPlayer(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + closeInterface(player) + return@queueScript delayScript(player, 3) + } + 2 -> { + val loot = RandomEvents.CERTER.loot!!.roll(player)[0] + addItemOrDrop(player, loot.id, loot.amount) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + } +} +class PiousPeteStartingDialogueFile : DialogueLabeller() { + + override fun addConversation() { + npc("I'm sorry to drag you away from your tasks, but I need a little help with something.") + player(ChatAnim.THINKING,"How can I help?") + npc("This is a chapel dedicated to our lord, Saradomin, and I'm tasked with maintaining this chapel.") + npc(ChatAnim.SAD,"My task is to light the chapel candles, but I couldn't reach them myself and I kept getting dazzled by the light whenever I tried.") + npc("So I need your help in lighting the candles. I need you to light all the tall candles, but not the short ones.") + npc(ChatAnim.FRIENDLY, "Once all the tall candles are all lit, come back and see me, and I will reward you for your work.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt b/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt new file mode 100644 index 000000000..5231b6f9c --- /dev/null +++ b/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt @@ -0,0 +1,26 @@ +package content.global.ame.events.candlelight + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import org.rs09.consts.NPCs + +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}! I need a little help with something.") + face(player) + kidnapPlayer(this, player, Location(1972, 5002, 0)) { player, _ -> + CandlelightInterface.initCandlelight(player) + openDialogue(player, PiousPeteStartingDialogueFile(), NPC(NPCs.PIOUS_PETE_3207)) + } + } + + override fun talkTo(npc: NPC) { + player.dialogueInterpreter.open(PiousPeteDialogueFile(),npc) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/certer/CerterNPC.kt b/Server/src/main/content/global/ame/events/certer/CerterNPC.kt index daf3bc822..20b172404 100644 --- a/Server/src/main/content/global/ame/events/certer/CerterNPC.kt +++ b/Server/src/main/content/global/ame/events/certer/CerterNPC.kt @@ -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 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() + } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonListeners.kt b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonListeners.kt index cb270f02f..2287de37d 100644 --- a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonListeners.kt +++ b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonListeners.kt @@ -79,7 +79,7 @@ class DrillDemonListeners : InteractionListener, MapArea { } override fun getRestrictions(): Array { - return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS) + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP) } override fun areaEnter(entity: Entity) { @@ -90,4 +90,4 @@ class DrillDemonListeners : InteractionListener, MapArea { setComponentVisibility(entity.asPlayer(), 746, 12, true) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt index a85bde6a3..8c90bd77f 100644 --- a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt +++ b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt @@ -1,9 +1,9 @@ package content.global.ame.events.drilldemon +import content.global.ame.returnPlayer import core.api.* import core.game.interaction.QueueStrength import core.game.node.entity.player.Player -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 @@ -11,7 +11,6 @@ import org.rs09.consts.NPCs object DrillDemonUtils { val DD_KEY_TASK = "/save:drilldemon:task" - val DD_KEY_RETURN_LOC = "/save:drilldemon:original-loc" val DD_SIGN_VARP = 531 val DD_SIGN_JOG = 0 val DD_SIGN_SITUP = 1 @@ -22,14 +21,6 @@ object DrillDemonUtils { val DD_AREA = ZoneBorders(3158, 4817, 3168, 4823) val DD_NPC = NPCs.SERGEANT_DAMIEN_2790 - fun teleport(player: Player) { - setAttribute(player, DD_KEY_RETURN_LOC, player.location) - teleport(player, Location.create(3163, 4819, 0)) - 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() @@ -63,13 +54,8 @@ object DrillDemonUtils { } fun cleanup(player: Player) { - player.locks.unlockTeleport() - unlock(player) - teleport(player, getAttribute(player, DD_KEY_RETURN_LOC, Location.create(3222, 3218, 0))) - removeAttribute(player, DD_KEY_RETURN_LOC) - removeAttribute(player, DD_KEY_TASK) - removeAttribute(player, DD_CORRECT_OFFSET) - removeAttribute(player, DD_CORRECT_COUNTER) + returnPlayer(player) + removeAttributes(player, DD_KEY_TASK, DD_CORRECT_OFFSET, DD_CORRECT_COUNTER) player.interfaceManager.openDefaultTabs() setComponentVisibility(player, 548, 69, false) setComponentVisibility(player, 746, 12, false) @@ -98,6 +84,5 @@ object DrillDemonUtils { } return@queueScript stopExecuting(player) } - } } diff --git a/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienNPC.kt b/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienNPC.kt deleted file mode 100644 index f767b0554..000000000 --- a/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienNPC.kt +++ /dev/null @@ -1,38 +0,0 @@ -package content.global.ame.events.drilldemon - -import core.game.node.entity.npc.NPC -import org.rs09.consts.NPCs -import content.global.ame.RandomEventNPC -import core.api.* -import core.api.utils.WeightBasedTable - -import core.game.interaction.QueueStrength -import core.game.system.timer.impl.AntiMacro -import core.tools.secondsToTicks - -class SeargentDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) { - - override fun init() { - super.init() - sendChat(player.username.capitalize() + "! 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) - } - } - } - - override fun talkTo(npc: NPC) { - openDialogue(player, SeargentDamienDialogue(), npc) - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt b/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt new file mode 100644 index 000000000..2a3ec7f51 --- /dev/null +++ b/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt @@ -0,0 +1,28 @@ +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.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!") + 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) + } + } + + override fun talkTo(npc: NPC) { + openDialogue(player, SeargentDamienDialogue(), npc) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt index 1485dd8c4..f4a6842ff 100644 --- a/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt +++ b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt @@ -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 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) + } + } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobDialogue.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobDialogue.kt index ce67a8fc4..f8d132a7f 100644 --- a/Server/src/main/content/global/ame/events/evilbob/EvilBobDialogue.kt +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobDialogue.kt @@ -22,10 +22,8 @@ class EvilBobDialogue(val rewardDialogue: Boolean = false, val rewardXpSkill: In } else if (getAttribute(player!!, EvilBobUtils.eventComplete, false)) { sendDialogue(player!!, "Evil Bob appears to be sleeping, best not to wake him up.").also { stage = END_DIALOGUE } } else if (removeItem(player!!, Items.RAW_FISHLIKE_THING_6200)) { - setAttribute(player!!, EvilBobUtils.fishCaught, false) playerl(FacialExpression.NEUTRAL, "Here, I've brought you some fish.").also { stage = 500 } } else if (removeItem(player!!, Items.RAW_FISHLIKE_THING_6204)) { - setAttribute(player!!, EvilBobUtils.fishCaught, false) setAttribute(player!!, EvilBobUtils.attentive, true) setAttribute(player!!, EvilBobUtils.attentiveNewSpot, true) playerl(FacialExpression.NEUTRAL, "Here, I've brought you some fish.").also { stage = 600 } diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobListeners.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobListeners.kt index 64f2c54fa..f2a3e9134 100644 --- a/Server/src/main/content/global/ame/events/evilbob/EvilBobListeners.kt +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobListeners.kt @@ -1,5 +1,6 @@ package content.global.ame.events.evilbob +import content.global.ame.returnPlayer import core.ServerConstants import core.api.* import core.game.dialogue.FacialExpression @@ -8,8 +9,6 @@ import core.game.interaction.InteractionListener import core.game.interaction.QueueStrength import core.game.node.entity.Entity import core.game.node.entity.player.link.emote.Emotes -import core.game.node.entity.skill.Skills -import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneRestriction @@ -37,7 +36,8 @@ class EvilBobListeners : InteractionListener, MapArea { sendNPCDialogue(player, NPCs.SERVANT_2481, "You'll need a fishing net. There are plenty scattered around the beach.", FacialExpression.SAD) } else if (freeSlots(player) == 0) { sendDialogue(player, "You don't have enough space in your inventory.") - } else if (getAttribute(player, EvilBobUtils.fishCaught, false)) { + } else if (inInventory(player, Items.FISHLIKE_THING_6202) || inInventory(player, Items.FISHLIKE_THING_6206) || + inInventory(player, Items.RAW_FISHLIKE_THING_6200) || inInventory(player, Items.RAW_FISHLIKE_THING_6204)) { sendNPCDialogue(player, NPCs.SERVANT_2481, "You've already got a fish. Come over here to uncook it, then serve it to Evil Bob.", FacialExpression.SAD) } else { lock(player, 6) @@ -68,7 +68,6 @@ class EvilBobListeners : InteractionListener, MapArea { } sendItemDialogue(player, Items.FISHLIKE_THING_6202, "You catch a... what is this?? Is this a fish?? And it's cooked already??") resetAnimator(player) - setAttribute(player, EvilBobUtils.fishCaught, true) } } return@on true @@ -121,7 +120,7 @@ class EvilBobListeners : InteractionListener, MapArea { } 3 -> { sendMessage(player, "Welcome back to ${ServerConstants.SERVER_NAME}.") - teleport(player, getAttribute(player, EvilBobUtils.prevLocation, Location.create(3222, 3219, 0))) + returnPlayer(player) EvilBobUtils.reward(player) EvilBobUtils.cleanup(player) resetAnimator(player) @@ -139,7 +138,7 @@ class EvilBobListeners : InteractionListener, MapArea { } override fun getRestrictions(): Array { - return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS) + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP) } override fun areaEnter(entity: Entity) { diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt index fc0fe2826..4995b6b41 100644 --- a/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt @@ -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) } } diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt index 9d5f4957d..58f8e5aee 100644 --- a/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt @@ -3,7 +3,6 @@ package content.global.ame.events.evilbob import core.api.* import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills -import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics @@ -13,10 +12,8 @@ import org.rs09.consts.NPCs import org.rs09.consts.Scenery object EvilBobUtils { - const val prevLocation = "/save:evilbob:prevlocation" const val eventComplete = "/save:evilbob:eventcomplete" const val assignedFishingZone = "/save:evilbob:fishingzone" - const val fishCaught = "evilbob:fishcaught" const val attentive = "/save:evilbob:attentive" const val servantHelpDialogueSeen = "/save:evilbob:servantdialogeseen" const val attentiveNewSpot = "/save:evilbob:attentivenewspot" @@ -52,15 +49,8 @@ object EvilBobUtils { } } - fun teleport(player: Player) { - setAttribute(player, prevLocation, player.location) - player.properties.teleportLocation = Location.create(3419, 4776, 0) - } - fun cleanup(player: Player) { - player.locks.unlockTeleport() - player.properties.teleportLocation = getAttribute(player, prevLocation, null) - removeAttributes(player, assignedFishingZone, eventComplete, prevLocation, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen) + removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen) removeAll(player, Items.FISHLIKE_THING_6202) removeAll(player, Items.FISHLIKE_THING_6202, Container.BANK) removeAll(player, Items.FISHLIKE_THING_6206) diff --git a/Server/src/main/content/global/ame/events/evilbob/ServantCutscene.kt b/Server/src/main/content/global/ame/events/evilbob/ServantCutscene.kt index db54e0db9..4dfc72698 100644 --- a/Server/src/main/content/global/ame/events/evilbob/ServantCutscene.kt +++ b/Server/src/main/content/global/ame/events/evilbob/ServantCutscene.kt @@ -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) } } - } } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt b/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt index ac26b914b..5b065f567 100644 --- a/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt +++ b/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt @@ -9,9 +9,8 @@ import core.tools.RandomFunction import org.rs09.consts.Items import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable -import java.lang.Integer.max -val ids = 2463..2468 +val ids = (2463..2468).toList() class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(2463) { val phrases = arrayOf("Bwuk","Bwuk bwuk bwuk","Flee from me, @name!","Begone, @name!","Bwaaaauuuk bwuk bwuk","MUAHAHAHAHAAA!") @@ -19,8 +18,7 @@ class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventN override fun init() { super.init() - val index = max(0, (player.properties.combatLevel / 20) - 1) - val id = ids.toList()[index] + val id = idForCombatLevel(ids, player) this.transform(id) this.attack(player) sendChat(phrases.random().replace("@name",player.username.capitalize())) diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakListeners.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakListeners.kt index e2d36acc0..8754a8d08 100644 --- a/Server/src/main/content/global/ame/events/freakyforester/FreakListeners.kt +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakListeners.kt @@ -57,10 +57,10 @@ class FreakListeners : InteractionListener, MapArea { } override fun getRestrictions(): Array { - return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS) + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP) } override fun areaEnter(entity: Entity) { entity.locks.lockTeleport(1000000) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt index cd76dc23d..84e491f89 100644 --- a/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt @@ -1,16 +1,15 @@ package content.global.ame.events.freakyforester +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.world.map.Location import core.game.world.map.zone.ZoneBorders import core.tools.RandomFunction object FreakUtils{ const val freakNpc = NPCs.FREAKY_FORESTER_2458 - const val freakPreviousLoc = "/save:freakyf:location" const val freakTask = "/save:freakyf:task" const val freakComplete = "/save:freakyf:complete" const val pheasantKilled = "freakyf:killed" @@ -26,15 +25,9 @@ object FreakUtils{ player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc) } - fun teleport(player: Player) { - setAttribute(player, freakPreviousLoc, player.location) - teleport(player, Location.create(2599, 4777 ,0)) - } - fun cleanup(player: Player) { - player.locks.unlockTeleport() - player.properties.teleportLocation = getAttribute(player,freakPreviousLoc,null) - removeAttributes(player, freakPreviousLoc, freakTask, freakComplete, pheasantKilled) + returnPlayer(player) + removeAttributes(player, freakTask, freakComplete, pheasantKilled) removeAll(player, Items.RAW_PHEASANT_6178) removeAll(player, Items.RAW_PHEASANT_6178, Container.BANK) removeAll(player, Items.RAW_PHEASANT_6179) diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt index 4b0366263..1ef52dde6 100644 --- a/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt @@ -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) } } diff --git a/Server/src/main/content/global/ame/events/genie/GenieNPC.kt b/Server/src/main/content/global/ame/events/genie/GenieNPC.kt index 6f3cf39a1..b4700a9a5 100644 --- a/Server/src/main/content/global/ame/events/genie/GenieNPC.kt +++ b/Server/src/main/content/global/ame/events/genie/GenieNPC.kt @@ -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 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() + } } diff --git a/Server/src/main/content/global/ame/events/maze/MazeInterface.kt b/Server/src/main/content/global/ame/events/maze/MazeInterface.kt new file mode 100644 index 000000000..d42743118 --- /dev/null +++ b/Server/src/main/content/global/ame/events/maze/MazeInterface.kt @@ -0,0 +1,276 @@ +package content.global.ame.events.maze + +import content.global.ame.returnPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +import core.game.event.EventHook +import core.game.event.TickEvent +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.GameWorld.Pulser +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.* + +class MazeInterface : InteractionListener, EventHook, MapArea { + companion object { + const val MAZE_TIMER_INTERFACE = Components.MAZETIMER_209 + const val MAZE_TIMER_VARP = 531 // Interface 209 child 2 config: [531, 0] + const val MAZE_ATTRIBUTE_TICKS_LEFT = "maze:percent-ticks-left" + const val MAZE_ATTRIBUTE_CHESTS_OPEN = "/save:maze:chests-opened" + + val STARTING_POINTS = arrayOf( + Location(2928, 4553, 0), + Location(2917, 4553, 0), + Location(2908, 4555, 0), + Location(2891, 4589, 0), + Location(2891, 4595, 0), + Location(2891, 4595, 0), + Location(2926, 4597, 0), + Location(2931, 4597, 0), + // There's 2 more, but there isn't a door for them... + ) + + val REWARD_ITEM = intArrayOf( + Items.COINS_995, + Items.FEATHER_314, + Items.IRON_ARROW_884, + Items.CHAOS_RUNE_562, + Items.STEEL_ARROW_886, + Items.DEATH_RUNE_560, + Items.COAL_454, + Items.NATURE_RUNE_561, + Items.MITHRIL_ORE_448 + ) + + val ITEM_DIVISOR = arrayOf( + 1.0, + 2.0, + 3.0, + 9.0, + 12.0, + 18.0, + 45.0, + 162.0, + 180.0, + ) + + val CHEST_REWARDS = WeightBasedTable.create( + WeightedItem(Items.AIR_RUNE_556,15,15,1.0), + WeightedItem(Items.WATER_RUNE_555,10,10,1.0), + WeightedItem(Items.EARTH_RUNE_557,10,10,1.0), + WeightedItem(Items.FIRE_RUNE_554,10,10,1.0), + WeightedItem(Items.BRONZE_ARROW_882,20,20,1.0), + WeightedItem(Items.BRONZE_BOLTS_877,10,10,1.0), + WeightedItem(Items.IRON_ARROW_884,15,15,1.0), + WeightedItem(Items.ATTACK_POTION2_123,1,1,1.0), + WeightedItem(Items.STRENGTH_POTION2_117,1,1,1.0), + WeightedItem(Items.DEFENCE_POTION2_135,1,1,1.0), + ) + + fun initMaze(player: Player) { + setAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 300) + setVarp(player, MAZE_TIMER_VARP, (getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0) / 3),false) + openOverlay(player, MAZE_TIMER_INTERFACE) + sendMessage(player, "You need to reach the maze center, then you'll be returned to where you were.") + sendNPCDialogue(player, NPCs.MYSTERIOUS_OLD_MAN_410, "You need to reach the maze center, then you'll be returned to where you were.") + } + + fun calculateLoot(player: Player) { + val randomNumber = (0..8).random() + val totalLevel = player.getSkills().totalLevel.toDouble() + val rewardPotential = getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0).toDouble() / 300.0 + val itemDivisor = ITEM_DIVISOR[randomNumber] + val itemQuantity = (totalLevel * rewardPotential * 3.33) / itemDivisor + // sendMessage(player, "Maze reward calculation: $totalLevel * $rewardPotential * 3.33 / $itemDivisor = $itemQuantity") + if (itemQuantity.toInt() > 0) { + addItemOrDrop(player, REWARD_ITEM[randomNumber], itemQuantity.toInt()) + } + } + + /** + * Chest Location to rotation mapping. + * This is needed as it is impossible to obtain the underlying chest scenery for the rotation. + * 0: Facing North, 1: Facing East, 2: Facing South, 3: Facing West + */ + val chestLocationRotationMap = mapOf( + Location(2930, 4595, 0).toString() to 2, + Location(2924, 4572, 0).toString() to 2, + Location(2925, 4573, 0).toString() to 0, + Location(2900, 4578, 0).toString() to 2, + Location(2901, 4560, 0).toString() to 1, + Location(2890, 4599, 0).toString() to 2, + Location(2896, 4591, 0).toString() to 2, + Location(2895, 4592, 0).toString() to 1, + Location(2901, 4560, 0).toString() to 3, + Location(2918, 4590, 0).toString() to 1, + Location(2917, 4590, 0).toString() to 3, + ) + + /** + * Chest Interaction workaround + * + * The issue here is that the walls(3626) of the Maze are overlapping some(not all) chest sceneries. + * + * The types for the wallScenery: + * Type 0 - flat panel | + * Type 2 - right angle panel with rotation 0:r 1:7 2:> 3:L + * Type 3 - corner post . for angle edges of walls + */ + fun overrideScenery(wallScenery: core.game.node.scenery.Scenery, chestSceneryId: Int): core.game.node.scenery.Scenery { + if (wallScenery.id == chestSceneryId) { + replaceScenery(wallScenery, Scenery.CHEST_3636, 30) + wallScenery.isActive = true + return wallScenery // Return the chest scenery as the wallScenery isn't there. + } + + addScenery(Scenery.CHEST_3636, wallScenery.location, chestLocationRotationMap[wallScenery.location.toString()] ?: 0, 10) + addScenery(wallScenery) + // replaceScenery(newChestScenery, Scenery.CHEST_3636, 3) // didn't work for an underlying scenery + // I did a world pulse since everyone will get to see the chest open. + Pulser.submit(object : Pulse(30) { + override fun pulse(): Boolean { + addScenery(Scenery.CHEST_3635, wallScenery.location, chestLocationRotationMap[wallScenery.location.toString()] ?: 0, 10) + addScenery(wallScenery) + return true + } + }) + // Return the chest scenery to replace PacketProcessor so that MISMATCH will not happen. + return core.game.node.scenery.Scenery( + chestSceneryId, + wallScenery.location, + chestLocationRotationMap[wallScenery.location.toString()] ?: 0 + ) + } + } + + override fun defineListeners() { + + // This somehow doesn't trigger as the scenery.id != objId (3626 != 3635) + on(Scenery.CHEST_3635, IntType.SCENERY, "open") { player, node -> + if (getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0) > 0 && getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0) < 10) { + animate(player, 536) + // val actualScenery = RegionManager.getObject(node.location.z, node.location.x, node.location.y, 3626) + val tableRoll = CHEST_REWARDS.roll() + addItemOrBank(player, tableRoll[0].id, tableRoll[0].amount) + when (tableRoll[0].id){ + Items.AIR_RUNE_556 -> sendItemDialogue(player, Items.AIR_RUNE_556, "You've found some air runes!") + Items.WATER_RUNE_555 -> sendItemDialogue(player, Items.WATER_RUNE_555, "You've found some water runes!") + Items.EARTH_RUNE_557 -> sendItemDialogue(player, Items.EARTH_RUNE_557, "You've found some earth runes!") + Items.FIRE_RUNE_554 -> sendItemDialogue(player, Items.FIRE_RUNE_554, "You've found some fire runes!") + Items.BRONZE_ARROW_882 -> sendItemDialogue(player, Items.BRONZE_ARROW_882, "You've found some bronze arrows!") + Items.BRONZE_BOLTS_877 -> sendItemDialogue(player, Items.BRONZE_BOLTS_877, "You've found some bronze bolts!") + Items.IRON_ARROW_884 -> sendItemDialogue(player, Items.IRON_ARROW_884, "You've found some iron arrows!") + Items.ATTACK_POTION2_123 -> sendItemDialogue(player, Items.ATTACK_POTION2_123, "You've found an attack potion!") + Items.STRENGTH_POTION2_117 -> sendItemDialogue(player, Items.STRENGTH_POTION2_117, "You've found a strength potion!") + Items.DEFENCE_POTION2_135 -> sendItemDialogue(player, Items.DEFENCE_POTION2_135, "You've found a defence potion!") + } + setAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0)) + } else { + sendMessage(player,"You find nothing of interest.") + } + return@on true + } + + on(Scenery.CHEST_3636, SCENERY, "search") { player, node -> + sendMessage(player,"You find nothing of interest.") + return@on true + } + + on(Scenery.WALL_3626, IntType.SCENERY, "open") { player, node -> + sendMessage(player, "That bit doesn't open.") // 0xBrLo9woIY + return@on true + } + + on(Scenery.WALL_3628, IntType.SCENERY, "open") { player, node -> + // Door opening workaround + // Ignore 3629(WALL_3629) and 3630(WALL_3630) in handleAutowalkDoor ignoreSecondDoor + DoorActionHandler.handleAutowalkDoor(player, node as core.game.node.scenery.Scenery) + return@on true + } + + on(Scenery.STRANGE_SHRINE_3634, IntType.SCENERY, "touch") { player, node -> + player.unhook(this) + lock(player, 12) + closeOverlay(player) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendGraphics(Graphics(86, 0, 3), player.location) + animate(player,862) + return@queueScript delayScript(player, 6) + } + 1 -> { + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 2 -> { + returnPlayer(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + closeOverlay(player) + return@queueScript delayScript(player, 1) + } + 3 -> { + calculateLoot(player) + removeAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT) + removeAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + + return@on true + } + } + + override fun process(entity: Entity, event: TickEvent) { + if (entity is Player) { + if (getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) > 0) { + setAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) - 1) + } + setVarp(entity, MAZE_TIMER_VARP, (getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) / 3), false) + } + } + + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(11591)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + sendMessage(entity, "Head for the center of the maze.") + entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12) + openOverlay(entity, MAZE_TIMER_INTERFACE) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.interfaceManager.restoreTabs() + closeOverlay(entity) + entity.unhook(this) + } + } + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + entity.hook(Event.Tick, this) + } + } + +} diff --git a/Server/src/main/content/global/ame/events/maze/MazeNPC.kt b/Server/src/main/content/global/ame/events/maze/MazeNPC.kt new file mode 100644 index 000000000..a51f0b044 --- /dev/null +++ b/Server/src/main/content/global/ame/events/maze/MazeNPC.kt @@ -0,0 +1,32 @@ +package content.global.ame.events.maze + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +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) + // 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) { + sendMessage(player, "He isn't interested in talking to you.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/pillory/PilloryInterface.kt b/Server/src/main/content/global/ame/events/pillory/PilloryInterface.kt new file mode 100644 index 000000000..f48fc76f4 --- /dev/null +++ b/Server/src/main/content/global/ame/events/pillory/PilloryInterface.kt @@ -0,0 +1,219 @@ +package content.global.ame.events.pillory + +import content.global.ame.RandomEvents +import content.global.ame.returnPlayer +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.interaction.InterfaceListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +/** + * Pillory Unlocking Interface PILLORY_189 + * + * https://www.youtube.com/watch?v=caWn7pE2mkE + * https://www.youtube.com/watch?v=TMVR5cZZwZ0 + * https://www.youtube.com/watch?v=Ym9LCDP-Q74 + * https://www.youtube.com/watch?v=_vn0QZTtI6U (Failure) + * https://www.youtube.com/watch?v=zmXDikQIua4 + * + * Child IDs + * 4 - Rotating Lock Model + * 5 6 7 - Swinging Keys Models + * 8 9 10 - Buttons for the Swinging Keys Models + * 11 12 13 14 15 16 - Padlocks at the Top + * 17 18 19 20 21 22 - Padlocks stars? Model 15272, Anim 4135 + * + * Model IDs + * Using the amazeballs ::listifmodels + * 9749, 9750, 9751, 9752 - Swinging Keys Models + * 9753, 9754, 9755, 9756 - Rotating Lock Models + * 9757 9758 locked unlock + */ +class PilloryInterface : InterfaceListener, InteractionListener, MapArea { + companion object { + const val PILLORY_LOCK_INTERFACE = 189 + const val PILLORY_ATTRIBUTE_EVENT_KEYS = "pillory:event-keys" + const val PILLORY_ATTRIBUTE_EVENT_LOCK = "pillory:event-lock" + const val PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT = "/save:pillory:target-correct" + const val PILLORY_ATTRIBUTE_CORRECT_COUNTER = "/save:pillory:num-correct" + + val LOCATIONS = arrayOf( + // Varrock Cages + Location(3226, 3407, 0), + Location(3228, 3407, 0), + Location(3230, 3407, 0), + // Seers Village Cages + Location(2681, 3489, 0), + Location(2683, 3489, 0), + Location(2685, 3489, 0), + // Yannile Cages + Location(2604, 3105, 0), + Location(2606, 3105, 0), + Location(2608, 3105, 0), + ) + + fun initPillory(player: Player) { + setAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 3) + setAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.") + } + + fun randomPillory(player: Player) { + // Shuffle all 4 kinds of keys in, pick 3 for the keys, pick 1 from the 3 as the lock. + val keys = (0..3).toIntArray().let{ keys -> keys.shuffle(); return@let keys } + val lock = intArrayOf(keys[1], keys[2], keys[3]).random() // Last 3 as there are 4 keys. key[0] is fallback. + + setAttribute(player, PILLORY_ATTRIBUTE_EVENT_KEYS, keys) + setAttribute(player, PILLORY_ATTRIBUTE_EVENT_LOCK, lock) + + player.packetDispatch.sendModelOnInterface(9753 + lock, PILLORY_LOCK_INTERFACE, 4, 0) + player.packetDispatch.sendModelOnInterface(9749 + keys[1], PILLORY_LOCK_INTERFACE, 5, 0) + player.packetDispatch.sendModelOnInterface(9749 + keys[2], PILLORY_LOCK_INTERFACE, 6, 0) + player.packetDispatch.sendModelOnInterface(9749 + keys[3], PILLORY_LOCK_INTERFACE, 7, 0) + + val numberToGetCorrect = getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 3) + val correctCount = getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + for (i in 1.. 6) { + // Set if lock is red or green. + if (i <= correctCount) { + player.packetDispatch.sendModelOnInterface(9758, PILLORY_LOCK_INTERFACE, 10 + i, 0) + } else { + player.packetDispatch.sendModelOnInterface(9757, PILLORY_LOCK_INTERFACE, 10 + i, 0) + } + // Set if hide or show lock. + player.packetDispatch.sendInterfaceConfig(PILLORY_LOCK_INTERFACE, 10 + i, i > numberToGetCorrect) + } + } + + fun selectedKey(player: Player, buttonID: Int) { + val keys = getAttribute(player, PILLORY_ATTRIBUTE_EVENT_KEYS, intArrayOf(0, 0, 0)) + val lock = getAttribute(player, PILLORY_ATTRIBUTE_EVENT_LOCK, -1) + if (keys[buttonID] == lock) { + // CORRECT ANSWER + setAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + 1) + if (getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 3) <= getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, -1)) { + player.dialogueInterpreter.sendPlainMessage(true, "", "You've escaped!") + sendMessage(player, "You've escaped!") + removeAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT) + removeAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER) + closeInterface(player) + queueScript(player, 0, 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 -> { + val loot = RandomEvents.CERTER.loot!!.roll(player)[0] + addItemOrDrop(player, loot.id, loot.amount) + returnPlayer(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + closeInterface(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return + } + randomPillory(player) + player.dialogueInterpreter.sendPlainMessage( + true, + "", + "Correct!", + "" + getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + " down, " + + (getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 3) - getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0)) + " to go!") + // Animation for the star, but it doesn't work. + player.packetDispatch.sendInterfaceConfig(PILLORY_LOCK_INTERFACE, 16 + getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 1), false) + sendAnimationOnInterface(player, 4135, PILLORY_LOCK_INTERFACE, 16 + getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 1)) + } else { + // WRONG ANSWER + player.dialogueInterpreter.close() + player.dialogueInterpreter.sendDialogues(NPCs.TRAMP_2794 , FacialExpression.OLD_ANGRY1, "Bah, that's not right.","Use the key that matches the hole", "in the spinning lock.") + if (getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 0) < 6) { + setAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 0) + 1) + } + setAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + closeInterface(player) + } + } + } + + override fun defineInterfaceListeners() { + on(PILLORY_LOCK_INTERFACE){ player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + 8 -> selectedKey(player, 1) + 9 -> selectedKey(player, 2) + 10 -> selectedKey(player, 3) + } + return@on true + } + + onOpen(PILLORY_LOCK_INTERFACE){ player, component -> + return@onOpen true + } + } + + override fun defineListeners() { + on(Scenery.CAGE_6836, IntType.SCENERY, "unlock") { player, node -> + if (player.location in LOCATIONS) { // When you aren't inside. + randomPillory(player) + openInterface(player, PILLORY_LOCK_INTERFACE) + player.dialogueInterpreter.sendPlainMessage(true, "", "Pick the swinging key that matches the", "hole in the spinning lock.") + } else { + sendMessage(player, "You can't unlock the pillory, you'll let all the prisoners out!") + } + return@on true + } + } + + override fun defineAreaBorders(): Array { + return arrayOf( + // Varrock Cages + ZoneBorders(3226, 3407, 3226, 3407), + ZoneBorders(3228, 3407, 3228, 3407), + ZoneBorders(3230, 3407, 3230, 3407), + // Seers Village Cages + ZoneBorders(2681, 3489, 2681, 3489), + ZoneBorders(2683, 3489, 2683, 3489), + ZoneBorders(2685, 3489, 2685, 3489), + // Yannile Cages + ZoneBorders(2604, 3105, 2604, 3105), + ZoneBorders(2606, 3105, 2606, 3105), + ZoneBorders(2608, 3105, 2608, 3105), + ) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.interfaceManager.restoreTabs() + } + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt b/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt new file mode 100644 index 000000000..3917b035c --- /dev/null +++ b/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt @@ -0,0 +1,25 @@ +package content.global.ame.events.pillory + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.component.Component.setUnclosable +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +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) + 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) { + sendMessage(player, "He isn't interested in talking to you.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterBorders.kt b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterBorders.kt new file mode 100644 index 000000000..860d4d7dc --- /dev/null +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterBorders.kt @@ -0,0 +1,37 @@ +package content.global.ame.events.quizmaster + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import org.rs09.consts.NPCs + +class QuizMasterBorders : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(7754)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12) + face(entity, Location(1952, 4768, 1)) + animate(entity,2378) + openDialogue(entity, QuizMasterDialogueFile(), NPC(NPCs.QUIZ_MASTER_2477)) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.interfaceManager.restoreTabs() + //closeOverlay(entity) + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt new file mode 100644 index 000000000..4bb2ff79d --- /dev/null +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt @@ -0,0 +1,152 @@ +package content.global.ame.events.quizmaster + +import content.global.ame.returnPlayer +import core.ServerConstants +import core.api.* +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +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 org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class QuizMasterDialogueFile : DialogueLabeller() { + companion object { + const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191 + const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct" + const val QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER = "quizmaster:random-answer" + + /* + // Golden Models: + 8828 ADAMANT_BATTLEAXE_1371 + 8829 SALMON_329 + 8830 TROUT_333 + 8831 NECKLACE + 8832 WOODEN_SHIELD_1171 + 8833 BRONZE_MED_HELM_1139 + 8834 RING + 8835 SECATEURS_5329 + 8836 BRONZE_SWORD_1277 + 8837 GARDENING_TROWEL_5325 + */ + val sets = arrayOf( + intArrayOf(8828, 8829, 8829), + intArrayOf(8831, 8837, 8835), + intArrayOf(8830, 8832, 8833), + intArrayOf(8835, 8834, 8831), + intArrayOf(8837, 8836, 8828), + ) + + fun randomQuestion(player: Player): Int { + val randomSet = intArrayOf(*sets.random()) + val answer = intArrayOf(*randomSet)[0] + randomSet.shuffle() + val correctButton = randomSet.indexOf(answer) + 2 // buttons are 3,4,5 + + player.packetDispatch.sendModelOnInterface(randomSet[0], QUIZMASTER_INTERFACE, 6, 512) + player.packetDispatch.sendModelOnInterface(randomSet[1], QUIZMASTER_INTERFACE, 7, 512) + player.packetDispatch.sendModelOnInterface(randomSet[2], QUIZMASTER_INTERFACE, 8, 512) + player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 6, 512,0,0) + player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 7, 512,0,0) + player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 8, 512,0,0) + + return correctButton + } + + // Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas. + val tableRoll = WeightBasedTable.create( + WeightedItem(Items.LAMP_2528, 1, 1, 1.0, false), + WeightedItem(Items.CABBAGE_1965, 1, 1, 1.0, false), + WeightedItem(Items.DIAMOND_1601, 1, 1, 1.0, false), + WeightedItem(Items.BUCKET_1925, 1, 1, 1.0, false), + WeightedItem(Items.FLIER_956, 1, 1, 1.0, false), + WeightedItem(Items.OLD_BOOT_685, 1, 1, 1.0, false), + WeightedItem(Items.BODY_RUNE_559, 1, 1, 1.0, false), + WeightedItem(Items.ONION_1957, 1, 1, 1.0, false), + WeightedItem(Items.MITHRIL_SCIMITAR_1329, 1, 1, 1.0, false), + WeightedItem(Items.CASKET_405, 1, 1, 1.0, false), + WeightedItem(Items.STEEL_PLATEBODY_1119, 1, 1, 1.0, false), + WeightedItem(Items.NATURE_RUNE_561, 20, 20, 1.0, false), + ) + } + + + 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}:", "O D D O N E O U T", 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:", "${player?.username}!", "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 { + goto("right") + } + } 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,"CONGRATULATIONS!", "You are a WINNER!", "Please choose your PRIZE!", 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") + } +} diff --git a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt new file mode 100644 index 000000000..444fc1f1b --- /dev/null +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt @@ -0,0 +1,39 @@ +package content.global.ame.events.quizmaster + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import org.rs09.consts.NPCs + +/** + * Quiz Master NPC: + * + * https://www.youtube.com/watch?v=EFAWSiPTfcM + * https://www.youtube.com/watch?v=caWn7pE2mkE + * https://www.youtube.com/watch?v=Bc1gAov2o4w + * https://www.youtube.com/watch?v=oHU8-MUarxE + * https://www.youtube.com/watch?v=wvjYiF4v9tI + * https://www.youtube.com/watch?v=dC6rlSnXEfw + */ +class QuizMasterNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.QUIZ_MASTER_2477) { + override fun init() { + super.init() + sendChat("Hey ${player.username}! It's your lucky day!") + 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. + } + } + + override fun talkTo(npc: NPC) { + openDialogue(player, QuizMasterDialogueFile(), this.asNpc()) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt b/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt index b69ec7abe..dff28d7e9 100644 --- a/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt +++ b/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt @@ -4,17 +4,14 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable -import java.lang.Integer.max - -val ids = 391..396 +val ids = (391..396).toList() class RiverTrollRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(391){ override fun talkTo(npc: NPC) {} override fun init() { super.init() - val index = max(0, (player.properties.combatLevel / 20) - 1) - val id = ids.toList()[index] + val id = idForCombatLevel(ids, player) this.transform(id) this.attack(player) sendChat("Fishies be mine, leave dem fishies!") diff --git a/Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt b/Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt new file mode 100644 index 000000000..bd397a5b9 --- /dev/null +++ b/Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt @@ -0,0 +1,25 @@ +package content.global.ame.events.rockgolem + +import core.game.node.entity.Entity +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.combat.CombatSwingHandler +import core.game.node.entity.combat.MultiSwingHandler +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import org.rs09.consts.NPCs + +class RockGolemBehavior() : NPCBehavior( + NPCs.ROCK_GOLEM_413, NPCs.ROCK_GOLEM_414, NPCs.ROCK_GOLEM_415, NPCs.ROCK_GOLEM_416, NPCs.ROCK_GOLEM_417, NPCs.ROCK_GOLEM_418 +) { + val rangeHandler = SwitchAttack(CombatStyle.RANGE) + val meleeHandler = SwitchAttack(CombatStyle.MELEE) + val combatHandler = MultiSwingHandler(rangeHandler, meleeHandler) + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + return combatHandler + } + + override fun getXpMultiplier(self: NPC, attacker: Entity): Double { + return super.getXpMultiplier(self, attacker) / 16.0 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt b/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt index 82fda9bf4..eb3399657 100644 --- a/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt +++ b/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt @@ -4,17 +4,14 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable -import kotlin.math.max - -val ids = 413..418 +val ids = (413..418).toList() class RockGolemRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(413){ override fun talkTo(npc: NPC) {} override fun init() { super.init() - val index = max(0,(player.properties.combatLevel / 20) - 1) - val id = ids.toList()[index] + val id = idForCombatLevel(ids, player) this.transform(id) this.attack(player) sendChat("Raarrrgghh! Flee human!") diff --git a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt index 1c5a14956..99e0a0e68 100644 --- a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt @@ -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 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) } } diff --git a/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt b/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt index 4d991afdf..60a0f57ae 100644 --- a/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt +++ b/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt @@ -4,15 +4,13 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable -import kotlin.math.* class ShadeRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(425){ val ids = (425..430).toList() override fun talkTo(npc: NPC) {} override fun init() { super.init() - val index = max(0, min(ids.size, (player.properties.combatLevel / 20) - 1)) - val id = ids[index] + val id = idForCombatLevel(ids, player) this.transform(id) this.attack(player) sendChat("Leave this place!") diff --git a/Server/src/main/content/global/ame/events/strangeplant/StrangePlantBehavior.kt b/Server/src/main/content/global/ame/events/strangeplant/StrangePlantBehavior.kt index cd80ad3d9..0491e01bc 100644 --- a/Server/src/main/content/global/ame/events/strangeplant/StrangePlantBehavior.kt +++ b/Server/src/main/content/global/ame/events/strangeplant/StrangePlantBehavior.kt @@ -32,4 +32,8 @@ class StrangePlantBehavior() : NPCBehavior(NPCs.STRANGE_PLANT_408) { override fun onDeathStarted(self: NPC, killer: Entity) { AntiMacro.terminateEventNpc(killer.asPlayer()) } -} \ No newline at end of file + + override fun getXpMultiplier(self: NPC, attacker: Entity): Double { + return super.getXpMultiplier(self, attacker) / 16.0 + } +} diff --git a/Server/src/main/content/global/ame/events/supriseexam/MordautDialogue.kt b/Server/src/main/content/global/ame/events/surpriseexam/MordautDialogue.kt similarity index 98% rename from Server/src/main/content/global/ame/events/supriseexam/MordautDialogue.kt rename to Server/src/main/content/global/ame/events/surpriseexam/MordautDialogue.kt index ca0e61c3f..d3bd04af0 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/MordautDialogue.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/MordautDialogue.kt @@ -1,4 +1,4 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam import core.game.component.Component import core.game.dialogue.FacialExpression diff --git a/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt b/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt new file mode 100644 index 000000000..2a3896836 --- /dev/null +++ b/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt @@ -0,0 +1,24 @@ +package content.global.ame.events.surpriseexam + +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.node.entity.npc.NPC +import core.game.world.map.Location + +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}!") + face(player) + kidnapPlayer(this, player, Location(1886, 5025, 0)) { _, _ -> + /* nothing needed */ + } + } + + override fun talkTo(npc: NPC) { + sendMessage(player, "He isn't interested in talking to you.") + } +} diff --git a/Server/src/main/content/global/ame/events/supriseexam/SEDoorDialogue.kt b/Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt similarity index 90% rename from Server/src/main/content/global/ame/events/supriseexam/SEDoorDialogue.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt index 3fffea71b..221f4b4fe 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SEDoorDialogue.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt @@ -1,4 +1,4 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE diff --git a/Server/src/main/content/global/ame/events/supriseexam/SEPatternInterface.kt b/Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt similarity index 96% rename from Server/src/main/content/global/ame/events/supriseexam/SEPatternInterface.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt index e445c5145..3ba12a8c0 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SEPatternInterface.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt @@ -1,4 +1,4 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam import core.game.node.entity.npc.NPC import org.rs09.consts.Components diff --git a/Server/src/main/content/global/ame/events/supriseexam/SupriseExamListeners.kt b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt similarity index 58% rename from Server/src/main/content/global/ame/events/supriseexam/SupriseExamListeners.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt index 6629982fa..ad19ffbda 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SupriseExamListeners.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt @@ -1,28 +1,29 @@ -package content.global.ame.events.supriseexam +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 import core.game.interaction.InteractionListener import core.game.interaction.IntType import content.global.handlers.iface.ExperienceInterface +import core.api.MapArea +import core.api.removeItem +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction -class SupriseExamListeners : InteractionListener { - val MORDAUT = NPCs.MR_MORDAUT_6117 - val BOOK_OF_KNOWLEDGE = Items.BOOK_OF_KNOWLEDGE_11640 +class SupriseExamListeners : InteractionListener, MapArea { override fun defineListeners() { - on(MORDAUT, IntType.NPC, "talk-to"){ player, node -> + on(NPCs.MR_MORDAUT_6117, IntType.NPC, "talk-to") { player, node -> player.faceLocation(Location.create(1886, 5024, 0)) - val examComplete = player.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT,0) == 3 - player.dialogueInterpreter.open(MordautDialogue(examComplete),node.asNpc()) + val examComplete = player.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT, 0) == 3 + player.dialogueInterpreter.open(MordautDialogue(examComplete), node.asNpc()) return@on true } - on(SurpriseExamUtils.SE_DOORS, IntType.SCENERY, "open"){ player, node -> + on(SurpriseExamUtils.SE_DOORS, IntType.SCENERY, "open") { player, node -> val correctDoor = player.getAttribute(SurpriseExamUtils.SE_DOOR_KEY,-1) if(correctDoor == -1){ @@ -39,9 +40,9 @@ class SupriseExamListeners : InteractionListener { return@on true } - on(BOOK_OF_KNOWLEDGE, IntType.ITEM, "read"){ player, _ -> - player.setAttribute("caller"){skill: Int,p: Player -> - if(p.inventory.remove(Item(BOOK_OF_KNOWLEDGE))) { + on(Items.BOOK_OF_KNOWLEDGE_11640, IntType.ITEM, "read") { player, _ -> + player.setAttribute("caller") { skill: Int, p: Player -> + if (removeItem(p, Items.BOOK_OF_KNOWLEDGE_11640)) { val level = p.skills.getStaticLevel(skill) val experience = level * 15.0 p.skills.addExperience(skill, experience) @@ -54,8 +55,16 @@ class SupriseExamListeners : InteractionListener { } override fun defineDestinationOverrides() { - setDest(IntType.NPC,MORDAUT){ _, _ -> + setDest(IntType.NPC, NPCs.MR_MORDAUT_6117) { _, _ -> return@setDest Location.create(1886, 5025, 0) } } -} \ No newline at end of file + + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders.forRegion(7502)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } +} diff --git a/Server/src/main/content/global/ame/events/supriseexam/SurpriseExamUtils.kt b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt similarity index 68% rename from Server/src/main/content/global/ame/events/supriseexam/SurpriseExamUtils.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt index d3905d358..e1cbdd5a8 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SurpriseExamUtils.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt @@ -1,21 +1,15 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam +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.node.item.GroundItemManager -import core.game.node.item.Item import core.game.system.task.Pulse -import core.game.world.map.Location import org.rs09.consts.Components import org.rs09.consts.Items -import core.ServerConstants object SurpriseExamUtils { - - val SE_KEY_LOC = "supexam:loc" val SE_KEY_INDEX = "supexam:index" - val SE_LOGOUT_KEY = "suprise_exam" val SE_DOOR_KEY = "supexam:door" val INTER_PATTERN_CHILDS = intArrayOf(6,7,8) val INTER_OPTION_CHILDS = intArrayOf(10,11,12,13) @@ -29,28 +23,12 @@ object SurpriseExamUtils { intArrayOf(Items.FLY_FISHING_ROD_309,Items.BARBARIAN_ROD_11323,Items.SMALL_FISHING_NET_303,Items.HARPOON_311) ) - fun teleport(player: Player){ - player.setAttribute(SE_KEY_LOC,player.location) - - registerLogoutListener(player, SE_LOGOUT_KEY){p -> - p.location = getAttribute(p, SE_KEY_LOC, ServerConstants.HOME_LOCATION) - } - - player.properties.teleportLocation = Location.create(1886, 5025, 0) - } - fun cleanup(player: Player){ - player.properties.teleportLocation = player.getAttribute(SE_KEY_LOC,null) - clearLogoutListener(player, SE_LOGOUT_KEY) - player.removeAttribute(SE_KEY_LOC) - player.removeAttribute(SE_KEY_INDEX) - player.removeAttribute(SE_KEY_CORRECT) + returnPlayer(player) + removeAttributes(player, SE_KEY_INDEX, SE_KEY_CORRECT) player.pulseManager.run(object : Pulse(2){ override fun pulse(): Boolean { - val reward = Item(Items.BOOK_OF_KNOWLEDGE_11640) - if(!player.inventory.add(reward)){ - GroundItemManager.create(reward,player) - } + addItemOrDrop(player, Items.BOOK_OF_KNOWLEDGE_11640) return true } }, PulseType.CUSTOM_1) diff --git a/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt b/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt index 2e4fb3436..3b0033972 100644 --- a/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt +++ b/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt @@ -4,17 +4,14 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable -import kotlin.math.max - -val ids = 438..443 +val ids = (438..443).toList() class TreeSpiritRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(438){ override fun talkTo(npc: NPC) {} override fun init() { super.init() - val index = max(0,(player.properties.combatLevel / 20) - 1) - val id = ids.toList()[index] + val id = idForCombatLevel(ids, player) this.transform(id) this.attack(player) sendChat("Leave these woods and never return!") diff --git a/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt b/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt index 230d85ee8..6b43d54b4 100644 --- a/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt +++ b/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt @@ -4,15 +4,13 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable -import kotlin.math.* class ZombieRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(419){ val ids = (419..424).toList() override fun talkTo(npc: NPC) {} override fun init() { super.init() - val index = max(0, min(ids.size, (player.properties.combatLevel / 20) - 1)) - val id = ids[index] + val id = idForCombatLevel(ids, player) this.transform(id) this.attack(player) sendChat("Brainsssss!") diff --git a/Server/src/main/content/global/bots/Adventurer.kt b/Server/src/main/content/global/bots/Adventurer.kt index a6c69e8e1..916503945 100644 --- a/Server/src/main/content/global/bots/Adventurer.kt +++ b/Server/src/main/content/global/bots/Adventurer.kt @@ -12,15 +12,16 @@ import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.map.zone.ZoneBorders import core.game.world.update.flag.* -import core.tools.RandomFunction import org.json.simple.JSONArray import org.json.simple.JSONObject import core.ServerConstants +import core.api.log import core.game.bots.AIRepository import core.game.bots.CombatBotAssembler import core.game.bots.Script import core.game.interaction.IntType import core.game.interaction.InteractionListeners +import core.tools.Log import java.io.File import java.io.FileReader import java.time.LocalDateTime @@ -40,21 +41,27 @@ import kotlin.random.Random * @author Ceikry */ -//Adventure Bots v4.0.0 -Expansion Edition- +// Adventure Bots v1.1.0 : Expansion Edition (Previously v4.0.0) +// Super Grand Exchange Update class Adventurer(val style: CombatStyle): Script() { var city: Location = lumbridge - var ticks = 0 - var freshspawn = true - var sold = false - private val geloc: Location = if (Random.nextBoolean()){ - Location.create(3165, 3487, 0) - }else{ - Location.create(3164, 3492, 0) - } + var poiloc: Location = karamja + var geSocialLoc: Location = getRandomGESocialLocation() + var geClerkLoc: Location = getRandomGELocation() + var geClerksloc: Location = neGEClerk + var freshspawn: Boolean = true + var sold: Boolean = false + var poi: Boolean = false + + val chance: Int = if (cityLocationsGE.contains(city)) 3500 else 3000 + var ticks: Int = 0 var counter: Int = 0 - var random: Int = (5..30).random() + val waitTotal: Int = 8 + var returnToAdventure: Int = 0 + var geWait: Int = 0 + var geLongWait: Int = 0 val type = when(style){ CombatStyle.MELEE -> CombatBotAssembler.Type.MELEE @@ -74,23 +81,81 @@ class Adventurer(val style: CombatStyle): Script() { } override fun toString(): String { - return "${bot.name} is an Adventurer bot at ${bot.location}! State: $state - City: $city" + return "${bot.username} is an Adventurer bot " + + "at ${bot.location}! " + + "State: $state - " + + "City: $city - " + + "Ticks: $ticks - " + + "Freshspawn: $freshspawn - " + + "Sold: $sold - " + + "Counter: $counter" } var state = State.START - fun getRandomCity(): Location{ + private fun getRandomCity(): Location{ return cities.random() } - fun getRandomPoi(): Location{ + private fun getRandomPoi(): Location{ return pois.random() } + private fun getRandomGESocialLocation(): Location{ + return socialLocationsGE.random() + } + + private fun getRandomGELocation(): Location { + return cityLocationsGE.random() + } + + private fun randomNumberFromOne(maxInt: Int): Int { + return Random.nextInt(0, maxInt) + } + + private fun otherPlayersNearby(): Boolean { + val localPlayers = RegionManager.getLocalPlayers(bot) + val otherPlayers = localPlayers.filter { it.name != bot.name } + return otherPlayers.isNotEmpty() + } + + private fun checkNearBank() { + if(bankMap[city] == null){ + scriptAPI.teleport(getRandomCity().also { city = it }) + } else { + if(bankMap[city]?.insideBorder(bot) == true){ + state = State.FIND_BANK + } else { + bankMap[city]?.let { scriptAPI.walkTo(it.randomLoc) } + } + } + } + + private fun checkCounter(maxCounter: Int) { + if (counter++ >= maxCounter) { + state = State.TELEPORTING + } + } + + private fun teleportToRandomCity() { + city = getRandomCity() + when (city) { + neGEClerk -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,-3,2,0,1,0)) } + swGEClerk -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,-2,3,-1,0,0)) } + nwGEBanker -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,-2,0,-3,2,0)) } + seGEBanker -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,0,2,-2,3,0)) } + else -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,-1,1,-1,1,0)) } + } + } + + val resources = listOf( + "Rocks","Tree","Oak","Willow", + "Maple tree","Yew","Magic tree", + "Teak","Mahogany") + //TODO: Optimise and adjust how bots handle picking up ground items further. fun immerse() { - if (counter++ == 180) {state = State.TELEPORTING - } + if (counter++ >= Random.nextInt(150,300)) { state = State.TELEPORTING } val items = AIRepository.groundItems[bot] if (Random.nextBoolean()) { if (items.isNullOrEmpty()) { @@ -98,33 +163,13 @@ class Adventurer(val style: CombatStyle): Script() { state = State.LOOT_DELAY } if (bot.inventory.isFull) { - if(bankMap[city] == null){ - scriptAPI.teleport(getRandomCity().also { city = it }) - } else { - if(bankMap[city]?.insideBorder(bot) == true){ - state = State.FIND_BANK - } else { - scriptAPI.walkTo(bankMap[city]?.randomLoc ?: Location(0, 0, 0)) - } - } + checkNearBank() } } else { if (bot.inventory.isFull){ - if(bankMap[city] == null){ - scriptAPI.teleport(getRandomCity().also { city = it }) - } else { - if(bankMap[city]?.insideBorder(bot) == true){ - state = State.FIND_BANK - } else { - scriptAPI.walkTo(bankMap[city]?.randomLoc ?: Location(0, 0, 0)) - } - } + checkNearBank() } else { - val resources = listOf( - "Rocks","Tree","Oak","Willow", - "Maple tree","Yew","Magic tree", - "Teak","Mahogany") val resource = scriptAPI.getNearestNodeFromList(resources,true) if(resource != null){ if(resource.name.contains("ocks")) InteractionListeners.run(resource.id, @@ -137,18 +182,17 @@ class Adventurer(val style: CombatStyle): Script() { } fun refresh() { +// log(this::class.java, Log.WARN, "${bot.username} refreshed from $state at $city with $ticks and $counter counter.") scriptAPI.teleport(lumbridge) - freshspawn = true state = State.START } - var poi = false - var poiloc = karamja - //Adventure Bots Actual Code STARTS HERE!!! + // 100 ticks = 60 seconds override fun tick() { ticks++ - if (ticks++ >= 800) { + // Hard refresh + if (ticks >= 1000) { ticks = 0 refresh() return @@ -186,49 +230,42 @@ class Adventurer(val style: CombatStyle): Script() { } return } else { - state = State.EXPLORE + state = State.ADVENTURE } } State.START -> { if (freshspawn) { freshspawn = false - scriptAPI.randomWalkTo(lumbridge, 20) + scriptAPI.randomWalkTo(lumbridge, randomNumberFromOne(25)) } else { - city = getRandomCity() state = State.TELEPORTING } } State.TELEPORTING -> { + if (freshspawn){ freshspawn = false } + teleportToRandomCity() + poi = false + sold = false ticks = 0 counter = 0 - if (bot.location != city) { - poi = false - scriptAPI.teleport(city) - } else { - poi = false - state = State.EXPLORE - } + state = State.ADVENTURE + return } - State.EXPLORE -> { - if (counter++ == 350) { - state = State.TELEPORTING - } - - val chance = if (city == ge || city == ge2) 5000 else 2500 - if (RandomFunction.random(chance) <= 10) { - val nearbyPlayers = RegionManager.getLocalPlayers(bot) - if (nearbyPlayers.isNotEmpty()) { + State.ADVENTURE -> { + checkCounter(800) + if (randomNumberFromOne(chance) <= 10) { + if (otherPlayersNearby()) { ticks = 0 dialogue() } } - if (RandomFunction.random(1000) <= 150 && !poi) { - val roamDistance = if (city != ge && city != ge2) 225 else 7 - if ((city == ge || city == ge2) && RandomFunction.random(100) < 90) { + if (!poi && randomNumberFromOne(1000) <= 75) { + val roamDistance = if (!cityLocationsGE.contains(city)) 225 else randomNumberFromOne(5) + if (cityLocationsGE.contains(city) && randomNumberFromOne(100) < 90) { if (!bot.bank.isEmpty) { state = State.FIND_GE } @@ -238,33 +275,46 @@ class Adventurer(val style: CombatStyle): Script() { return } - if (RandomFunction.random(1000) <= 50 && poi){ + if (poi && randomNumberFromOne(1000) <= 100){ + immerse() + return + } + + if (poi && randomNumberFromOne(1000) <= 25){ + dialogue() + } + + if (poi && randomNumberFromOne(1000) <= 50){ val roamDistancePoi = when(poiloc){ - teakfarm, crawlinghands -> 5 - treegnome -> 50 - isafdar -> 40 - eaglespeek -> 40 - keldagrimout -> 30 - teak1 -> 30 - miningguild -> 5 - magics, coal -> 7 gemrocks, chaosnpc, chaosnpc2 -> 1 + magics, coalTrucks -> 7 + miningguild, teakfarm, crawlinghands -> 5 + varLumberYard -> 20 + keldagrimout, teak1 -> 30 + eaglespeek, isafdar -> 40 + treegnome -> 50 else -> 60 } scriptAPI.randomWalkTo(poiloc,roamDistancePoi) return } - if (RandomFunction.random(1000) <= 75) { - if (city != ge && city != ge2) { + if (randomNumberFromOne(1000) <= 75) { + if (!cityLocationsGE.contains(city)) { + ticks = 0 immerse() return - } else { - return + } else if (randomNumberFromOne(chance) <= 55 && otherPlayersNearby()) { + ticks = 0 + dialogue() } } - if (RandomFunction.random(20000) <= 60 && !poi) { + if (cityLocationsGE.contains(city) && randomNumberFromOne(1000) <= 50) { + state = State.IDLE_GE + } + + if (!poi && randomNumberFromOne(1000) <= 5) { poiloc = getRandomPoi() city = teak1 poi = true @@ -272,26 +322,24 @@ class Adventurer(val style: CombatStyle): Script() { return } - if ((city == ge || city == ge2) && RandomFunction.random(1000) >= 999) { - ticks = 0 - city = getRandomCity() + if (cityLocationsGE.contains(city) && randomNumberFromOne(1000) <= 100) { state = State.TELEPORTING - } - - if (city == ge || city == ge2) { return } - if (city == teak1 && counter++ >= 240){ - city = getRandomCity() - state = State.TELEPORTING + if (cityLocationsGE.contains(city)) { + return } - if (counter++ >= 240 && RandomFunction.random(100) >= 10) { + if (poi && randomNumberFromOne(1000) <= 20){ + state = State.TELEPORTING + return + } + + if (counter++ >= 750 && randomNumberFromOne(100) <= 50) { +// log(this::class.java, Log.FINE, "${bot.username} has moved on to a different city at $ticks ticks and $counter counter.") city = getRandomCity() - if (RandomFunction.random(100) % 2 == 0) { - counter = 0 - ticks = 0 + if (randomNumberFromOne(100) % 2 == 0) { state = State.TELEPORTING } else { if (citygroupA.contains(city)) { @@ -309,122 +357,122 @@ class Adventurer(val style: CombatStyle): Script() { return } - State.GE -> { - var ge = false - if (counter++ == 180) { - state = State.TELEPORTING - } - if (!sold) { - if (counter++ >= 15) { - sold = true - ge = true + State.IDLE_GE -> { + returnToAdventure = Random.nextInt(350, 750) + if (counter++ >= returnToAdventure){ + if (randomNumberFromOne(100) <= 25){ + ticks = 0 + counter = 0 + poiloc = getRandomPoi() + city = teak1 + poi = true + scriptAPI.teleport(poiloc) + state = State.ADVENTURE + return + } else { counter = 0 ticks = 0 - scriptAPI.sellAllOnGeAdv() + state = State.TELEPORTING + return + } + } + if (cityLocationsGE.contains(city)){ + if (randomNumberFromOne(1000) <= 5) { + ticks = 0 + geSocialLoc = scriptAPI.randomizeLocationInRanges(getRandomGESocialLocation(),-1,1,-1,1,0) + } else if (randomNumberFromOne(1000) <= 10) { + ticks = 0 + scriptAPI.randomWalkTo(geSocialLoc, randomNumberFromOne(5)) + return + } + if (randomNumberFromOne(1000) <= 5 && otherPlayersNearby()){ + ticks = 0 + dialogue() + } else if (randomNumberFromOne(1000) <= 250){ return } - } else if (ge && sold) { - ge = false - city = getRandomCity() - state = State.TELEPORTING } return } State.FIND_GE -> { - if (counter++ == 180) { - state = State.TELEPORTING - } sold = false val ge: Scenery? = scriptAPI.getNearestNode("Desk", true) as Scenery? - if (ge == null || bot.bank.isEmpty) state = State.EXPLORE + if (ge == null || bot.bank.isEmpty) state = State.ADVENTURE class GEPulse : MovementPulse(bot, ge, DestinationFlag.OBJECT) { override fun pulse(): Boolean { bot.faceLocation(ge?.location) - state = State.GE - return true + return true.also { state = State.GE } } } + if (ge == null || bot.bank.isEmpty) state = State.ADVENTURE if (ge != null && !bot.bank.isEmpty) { - counter = 0 - scriptAPI.randomWalkTo(geloc, 3) - GameWorld.Pulser.submit(GEPulse()) + if (randomNumberFromOne(1000) <= 25 && otherPlayersNearby()){ + dialogue() + scriptAPI.randomWalkTo(geSocialLoc, randomNumberFromOne(5)) + } else if (randomNumberFromOne(500) <= 50) { + GameWorld.Pulser.submit(GEPulse()) + } } + checkCounter(500) + return + } + + State.GE -> { + geClerksloc = clerkLocationsGe.random() + geWait = Random.nextInt(35, 100) + geLongWait = Random.nextInt(350, 750) + if (!sold) { + if (randomNumberFromOne(500) <= 25){ scriptAPI.randomWalkTo(geClerksloc, randomNumberFromOne(4))} + if (counter++ >= geWait) { + scriptAPI.randomWalkTo(geClerksloc, randomNumberFromOne(1)) + sold = true + counter = 0 + ticks = 0 + scriptAPI.sellAllOnGeAdv() + state = State.TELEPORTING + return + } + } else if (counter++ >= geLongWait) { + state = State.TELEPORTING + return + } + checkCounter(1000) return } State.FIND_BANK -> { - if (counter++ == 300) { - state = State.TELEPORTING - } val bank: Scenery? = scriptAPI.getNearestNode("Bank booth", true) as Scenery? - if (badedge.insideBorder(bot) || bot.location == badedge2 || bot.location == badedge3 || bot.location == badedge4) { - bot.randomWalk(5, 5) - } - if (bank == null) state = State.EXPLORE - class BankingPulse : MovementPulse(bot, bank, DestinationFlag.OBJECT) { - override fun pulse(): Boolean { - bot.faceLocation(bank?.location) - state = State.IDLE_BANKS - return true - } - } - if (bank != null) { - bot.pulseManager.run(BankingPulse()) + if (bank == null) { state = State.TELEPORTING } + if (bank != null && randomNumberFromOne(100) <= 5) { + scriptAPI.depositAtBank() + } else if (bank != null && randomNumberFromOne(100) <= 5){ + scriptAPI.randomWalkTo(bank.location,3) } + checkCounter(500) return } - State.IDLE_BANKS -> { - if (counter++ == 300) { - state = State.TELEPORTING - } - if (RandomFunction.random(1000) < 100) { - for (item in bot.inventory.toArray()) { - item ?: continue - when (item.id) { - 1359, 590, 1271, 995 -> continue - } - bot.bank.add(item) - bot.inventory.remove(item) - } - counter = 0 - state = State.EXPLORE - } - return - } State.FIND_CITY -> { - if (counter++ >= 600 || (city == ge || city == ge2)) { - counter = 0 + if (counter++ >= 500 || cityLocationsGE.contains(city)){ scriptAPI.teleport(getRandomCity().also { city = it }) - state = State.EXPLORE + state = State.ADVENTURE } if (bot.location.equals(city)) { - state = State.EXPLORE + state = State.ADVENTURE } else { - scriptAPI.randomWalkTo(city, 5) + scriptAPI.randomWalkTo(city, randomNumberFromOne(10)) } + checkCounter(600) return } - State.IDLE_CITY -> { - if (counter++ == 300) { - state = State.TELEPORTING - } - var random = (120..300).random() - if (counter++ == random && RandomFunction.random(1000) % 33 == 0) { - counter = 0 - state = State.EXPLORE - } - return - } } } fun dialogue() { - val localPlayer = RegionManager.getLocalPlayers(bot).random() val until = 1225 - dateCode val lineStd = dialogue.getLines("standard").rand() var lineAlt = "" @@ -455,20 +503,29 @@ class Adventurer(val style: CombatStyle): Script() { dateCode == 404 -> lineAlt = dialogue.getLines("easter").rand() } - val chat = if (lineAlt.isNotEmpty() && Random.nextBoolean()) { lineAlt } else { lineStd } - .replace("@name", localPlayer.username) - .replace("@timer", until.toString()) - - scriptAPI.sendChat(chat) + var localPlayers = RegionManager.getLocalPlayers(bot) + if (localPlayers.isNotEmpty()) { + val localPlayer = localPlayers + .filter { it.name != bot.name } + .randomOrNull() + if (localPlayer != null) { + val chat = if (lineAlt.isNotEmpty() && Random.nextBoolean()) { lineAlt } else { lineStd } + .replace("@name", localPlayer.username) + .replace("@timer", until.toString()) + scriptAPI.sendChat(chat) + } else { + val chat = if (lineAlt.isNotEmpty() && Random.nextBoolean()) { lineAlt } else { lineStd } + scriptAPI.sendChat(chat) + } + } } enum class State{ START, - EXPLORE, + ADVENTURE, FIND_BANK, - IDLE_BANKS, FIND_CITY, - IDLE_CITY, + IDLE_GE, GE, TELEPORTING, LOOT, @@ -489,44 +546,94 @@ class Adventurer(val style: CombatStyle): Script() { } companion object { - val badedge = ZoneBorders(3094, 3494, 3096, 3497) - val badedge2 = Location.create(3094, 3492, 0) - val badedge3 = Location.create(3094, 3490, 0) - val badedge4 = Location.create(3094, 3494, 0) - + // Start Cities val yanille: Location = Location.create(2615, 3104, 0) val ardougne: Location = Location.create(2662, 3304, 0) val seers: Location = Location.create(2726, 3485, 0) val edgeville: Location = Location.create(3088, 3486, 0) - val ge: Location = Location.create(3168, 3487, 0) - val ge2: Location = Location.create(3161, 3493, 0) val catherby: Location = Location.create(2809, 3435, 0) val falador: Location = Location.create(2965, 3380, 0) val varrock: Location = Location.create(3213, 3428, 0) val draynor: Location = Location.create(3080, 3250, 0) val rimmington: Location = Location.create(2977, 3239, 0) val lumbridge: Location = Location.create(3222, 3219, 0) - val karamja = Location.create(2849, 3033, 0) - val alkharid = Location.create(3297, 3219, 0) - val feldiphills = Location.create(2535, 2919, 0) - val isafdar = Location.create(2241, 3217, 0) - val eaglespeek = Location.create(2333, 3579, 0) - val canafis = Location.create(3492, 3485, 0) - val treegnome = Location.create(2437, 3441, 0) - val teak1 = Location.create(2334, 3048, 0) - val teakfarm = Location.create(2825, 3085, 0) - val keldagrimout = Location.create(2724,3692,0) - val miningguild = Location.create(3046,9740,0) - val magics = Location.create(2285,3146,0) - val coal = Location.create(2581,3481,0) - val crawlinghands = Location.create(3422,3548,0) - val gemrocks = Location.create(2825,2997,0) - val chaosnpc = Location.create(2612,9484,0) - val chaosnpc2 = Location.create(2580,9501,0) - val taverly = Location.create(2909, 3436, 0) + val karamja: Location = Location.create(2849, 3033, 0) + val alkharid: Location = Location.create(3297, 3219, 0) + + // Start POI + val feldiphills: Location = Location.create(2535, 2919, 0) + val isafdar: Location = Location.create(2241, 3217, 0) + val eaglespeek: Location = Location.create(2333, 3579, 0) + val canafis: Location = Location.create(3492, 3485, 0) + val treegnome: Location = Location.create(2437, 3441, 0) + val teak1: Location = Location.create(2334, 3048, 0) + val teakfarm: Location = Location.create(2825, 3085, 0) + val keldagrimout: Location = Location.create(2724,3692, 0) + val miningguild: Location = Location.create(3046,9740, 0) + val magics: Location = Location.create(2285,3146, 0) + val coalTrucks: Location = Location.create(2581,3481, 0) + val crawlinghands: Location = Location.create(3422,3548, 0) + val gemrocks: Location = Location.create(2825,2997, 0) + val chaosnpc: Location = Location.create(2612,9484, 0) + val chaosnpc2: Location = Location.create(2586, 9501, 0) + val varLumberYard: Location = Location.create(3289, 3482, 0) + val taverly: Location = Location.create(2909, 3436, 0) + + val swGEClerk: Location = Location.create(3164, 3487, 0) + val neGEClerk: Location = Location.create(3165, 3492, 0) + val nwGEBanker: Location = Location.create(3162, 3490, 0) + val seGEBanker: Location = Location.create(3167, 3489, 0) + + val badedge = ZoneBorders(3094, 3494, 3096, 3497) + val badedge2: Location = Location.create(3094, 3492, 0) + val badedge3: Location = Location.create(3094, 3490, 0) + val badedge4: Location = Location.create(3094, 3494, 0) + var citygroupA = listOf(falador, varrock, draynor, rimmington, lumbridge, edgeville) var citygroupB = listOf(yanille, ardougne, seers, catherby) + val cities = listOf( + swGEClerk, neGEClerk, nwGEBanker, seGEBanker, + yanille, ardougne, seers, catherby, + falador, varrock, draynor, rimmington, + lumbridge, edgeville + ) + + val pois = listOf( + karamja, karamja, alkharid, + alkharid, feldiphills, feldiphills, + isafdar, eaglespeek, eaglespeek, + canafis, treegnome, treegnome, + teak1, teakfarm, keldagrimout, + miningguild, coalTrucks, crawlinghands, + magics, gemrocks, chaosnpc, chaosnpc, + chaosnpc2, taverly, + varLumberYard) + + val cityLocationsGE = listOf(swGEClerk, neGEClerk, nwGEBanker, seGEBanker) + + val socialLocationsGE = listOf( + Location.create(3158, 3483, 0), + Location.create(3165, 3480, 0), + Location.create(3172, 3483, 0), + Location.create(3174, 3489, 0), + Location.create(3171, 3497, 0), + Location.create(3164, 3499, 0), + Location.create(3157, 3497, 0), + Location.create(3155, 3489, 0), + Location.create(3167, 3492, 0), + Location.create(3162, 3492, 0), + Location.create(3162, 3487, 0), + Location.create(3167, 3487, 0) + ) + + val clerkLocationsGe = listOf( + Location.create(3165, 3492, 0), + Location.create(3164, 3492, 0), + Location.create(3164, 3487, 0), + Location.create(3165, 3487, 0) + ) + var bankMap = mapOf( falador to ZoneBorders(2950, 3374, 2943, 3368), varrock to ZoneBorders(3182, 3435, 3189, 3446), @@ -538,22 +645,10 @@ class Adventurer(val style: CombatStyle): Script() { catherby to ZoneBorders(2807, 3438, 2811, 3441) ) - val cities = listOf(yanille, ardougne, seers, catherby, falador, varrock, - draynor, rimmington, lumbridge, ge, ge2, edgeville) - - val pois = listOf( - karamja, karamja, alkharid, - alkharid, feldiphills, feldiphills, - isafdar, eaglespeek, eaglespeek, - canafis, treegnome, treegnome, - teak1, teakfarm, keldagrimout, - miningguild, coal, crawlinghands, - magics, gemrocks, chaosnpc, chaosnpc, - chaosnpc2, taverly) - private val whiteWolfMountainTop = Location(2850, 3496, 0) private val catherbyToTopOfWhiteWolf = arrayOf(Location(2856, 3442, 0), Location(2848, 3455, 0), Location(2848, 3471, 0), Location(2848, 3487, 0)) private val tavleryToTopOfWhiteWolf = arrayOf(Location(2872, 3425, 0), Location(2863, 3440, 0), Location(2863, 3459, 0), Location(2854, 3475, 0), Location(2859, 3488, 0)) + val common_stuck_locations = mapOf( // South of Tavlery dungeon ZoneBorders(2878, 3386, 2884, 3395) to { it: Adventurer -> diff --git a/Server/src/main/content/global/bots/CannonballSmelter.kt b/Server/src/main/content/global/bots/CannonballSmelter.kt index ca6406937..8ac77f7e1 100644 --- a/Server/src/main/content/global/bots/CannonballSmelter.kt +++ b/Server/src/main/content/global/bots/CannonballSmelter.kt @@ -7,8 +7,6 @@ import core.api.* import core.game.bots.* import core.game.ge.GrandExchange import core.game.interaction.DestinationFlag -import core.game.interaction.IntType -import core.game.interaction.InteractionListeners import core.game.interaction.MovementPulse import core.game.node.Node import core.game.node.entity.skill.Skills @@ -16,6 +14,7 @@ import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items +import content.data.Quests @PlayerCompatible @ScriptName("Falador Cannonball Smelter") @@ -273,6 +272,6 @@ class CannonballSmelter : Script() { skills.put(Skills.HITPOINTS,99) skills.put(Skills.DEFENCE,99) skills.put(Skills.SMITHING,35) - quests.add("Dwarf Cannon") + quests.add(Quests.DWARF_CANNON) } } diff --git a/Server/src/main/content/global/bots/DraynorFisher.kt b/Server/src/main/content/global/bots/DraynorFisher.kt index e8434f906..79a9bee8c 100644 --- a/Server/src/main/content/global/bots/DraynorFisher.kt +++ b/Server/src/main/content/global/bots/DraynorFisher.kt @@ -8,6 +8,8 @@ import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items import core.game.bots.SkillingBotAssembler import core.game.bots.Script +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners class DraynorFisher : Script() { val fishingZone = ZoneBorders(3085, 3223,3089, 3233) @@ -22,7 +24,11 @@ class DraynorFisher : Script() { scriptAPI.walkTo(fishingZone.randomLoc) else { val spot = scriptAPI.getNearestNode(316,false) - spot?.interaction?.handle(bot,spot.interaction[0]) ?: scriptAPI.walkTo(fishingZone.randomLoc) + if (spot != null) { + InteractionListeners.run(spot.id, IntType.NPC,"net",bot,spot) + } else { + scriptAPI.walkTo(fishingZone.randomLoc) + } if(bot.inventory.getMaximumAdd(Item(4151)) < 5) state = State.BANKING } diff --git a/Server/src/main/content/global/bots/FletchingBankstander.kt b/Server/src/main/content/global/bots/FletchingBankstander.kt index 201dd6f00..dd60a4e3c 100644 --- a/Server/src/main/content/global/bots/FletchingBankstander.kt +++ b/Server/src/main/content/global/bots/FletchingBankstander.kt @@ -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 } diff --git a/Server/src/main/content/global/bots/NonBankingMiner.kt b/Server/src/main/content/global/bots/NonBankingMiner.kt index 0d99c11d3..909d0a41e 100644 --- a/Server/src/main/content/global/bots/NonBankingMiner.kt +++ b/Server/src/main/content/global/bots/NonBankingMiner.kt @@ -17,7 +17,7 @@ class NonBankingMiner : Script() { } //checks if the bot has tin ore in his inventory and drops it if he does if(bot.inventory.containsAtLeastOneItem(Items.TIN_ORE_438)){ - produceGroundItem(null,438,1,bot.location) + produceGroundItem(bot,438,1,bot.location) bot.inventory.remove(Item(Items.TIN_ORE_438,1)) } //The following is to prevent lucky bots from breaking by having a full inventory of gems diff --git a/Server/src/main/content/global/bots/VarrockEssenceMiner.kt b/Server/src/main/content/global/bots/VarrockEssenceMiner.kt index 39ce02c55..38a9e6b5a 100644 --- a/Server/src/main/content/global/bots/VarrockEssenceMiner.kt +++ b/Server/src/main/content/global/bots/VarrockEssenceMiner.kt @@ -1,10 +1,13 @@ package content.global.bots +import content.data.Quests import core.game.bots.* import core.game.interaction.DestinationFlag import core.game.interaction.IntType import core.game.interaction.InteractionListeners import core.game.interaction.MovementPulse +import core.game.node.entity.skill.Skills +import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items @@ -24,6 +27,11 @@ class VarrockEssenceMiner : Script(){ when(state){ State.TO_ESSENCE -> { bot.interfaceManager.close() + if (bot.bank.getAmount(Items.PURE_ESSENCE_7936) > 500) { + state = State.TELE_GE + return + } + if(!auburyZone.insideBorder(bot)) scriptAPI.walkTo(auburyZone.randomLoc) else { @@ -99,7 +107,6 @@ class VarrockEssenceMiner : Script(){ scriptAPI.sellOnGE(Items.PURE_ESSENCE_7936) state = State.TO_ESSENCE } - } } @@ -118,4 +125,10 @@ class VarrockEssenceMiner : Script(){ script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR,bot.startLocation) return script } + + init { + quests.add(Quests.RUNE_MYSTERIES) + inventory.add(Item(Items.ADAMANT_PICKAXE_1271)) + skills[Skills.MINING] = 31 + } } diff --git a/Server/src/main/content/global/dialogue/BankerDialogue.kt b/Server/src/main/content/global/dialogue/BankerDialogue.kt deleted file mode 100644 index e913ce4f0..000000000 --- a/Server/src/main/content/global/dialogue/BankerDialogue.kt +++ /dev/null @@ -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 -} diff --git a/Server/src/main/content/global/dialogue/GardenerDialoguePlugin.kt b/Server/src/main/content/global/dialogue/GardenerDialoguePlugin.kt index 3fa655c5d..6c192972c 100644 --- a/Server/src/main/content/global/dialogue/GardenerDialoguePlugin.kt +++ b/Server/src/main/content/global/dialogue/GardenerDialoguePlugin.kt @@ -3,160 +3,227 @@ package content.global.dialogue import content.global.skill.farming.FarmerPayOptionDialogue import content.global.skill.farming.Farmers import content.global.skill.farming.FarmingPatch -import core.game.node.entity.npc.NPC +import content.global.skill.farming.PatchType +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE @Initializable class GardenerDialoguePlugin(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return GardenerDialoguePlugin(player) - } - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - options("Would you look after my crops for me?","Can you sell me something?") - return true - } - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> when(buttonId){ - 1 -> player("Would you look after my crops for me?").also { stage = 10 } - 2 -> player("Can you sell me something?").also { stage = 30 } + val patches = Farmers.forId(npc.id)!!.patches + when (stage) { + // TODO: Can fruit trees be chopped down by the gardener too? + START_DIALOGUE -> { + val patch = patches[0].getPatchFor(player) + showTopics( + IfTopic( + FacialExpression.ASKING, + "Would you chop my tree down for me?", + 1000, + patch.patch.type == PatchType.TREE_PATCH && patch.plantable != null && patch.isGrown() + ), + IfTopic( + FacialExpression.ASKING, + "Would you look after my crops for me?", + 10, + !(patch.patch.type == PatchType.TREE_PATCH && patch.plantable != null && patch.isGrown()) + ), + Topic(FacialExpression.ASKING, "Can you give me any farming advice?", 2000), + Topic(FacialExpression.ASKING, "Can you sell me something?", 30), + Topic(FacialExpression.NEUTRAL, "That's all, thanks.", END_DIALOGUE) + ) } - 10 -> npc("I might. Which one were you thinking of?").also { stage++ } - 11 -> when(npc.id){ - Farmers.ELSTAN.id, Farmers.LYRA.id -> options("The north-western allotment.","The south-eastern allotment.").also { stage = 15 } - Farmers.DANTAERA.id, Farmers.KRAGEN.id -> options("The north allotment.","The south allotment.").also { stage = 15 } - else -> player("Uh, that one.").also { stage++ } + 10 -> { + if (patches.size > 1) { + npc("I might. Which one were you thinking of?").also { stage = 20 } + } else { + openPayGardenerDialogue(player, patches[0]) + } } - 12 -> npc("Oh, right. My bad.").also { stage++ } - 13 -> checkPatch(player,Farmers.forId(npc.id)!!.patches[0]) - - 15 -> when(buttonId){ - 1 -> checkPatch(player,Farmers.forId(npc.id)!!.patches[0]) - 2 -> checkPatch(player,Farmers.forId(npc.id)!!.patches[1]) + 20 -> when (npc.id) { + Farmers.ELSTAN.id, Farmers.LYRA.id -> showTopics( + Topic(FacialExpression.NEUTRAL, "The north-western allotment.", 21), + Topic(FacialExpression.NEUTRAL, "The south-eastern allotment.", 22) + ) + Farmers.DANTAERA.id, Farmers.KRAGEN.id -> showTopics( + Topic(FacialExpression.NEUTRAL, "The northern allotment.", 21), + Topic(FacialExpression.NEUTRAL, "The southern allotment.", 22) + ) } + 21 -> openPayGardenerDialogue(player, patches[0]) + 22 -> openPayGardenerDialogue(player, patches[1]) - 30 -> npc("That depends on whether I have it to sell.","What is it that you're looking for?").also { stage++ } - 31 -> options("Some plant cure.","A bucket of compost.","A rake.","(See more items)").also { stage = 32 } - 32 -> when(buttonId){ - 1 -> player("Some plant cure.").also { stage = 100 } - 2 -> player("A bucket of compost.").also { stage = 200 } - 3 -> player("A rake.").also { stage = 300 } - 4 -> options("A watering can.","A gardening trowel.","A seed dibber.","(See previous items)").also { stage++ } - } - 33 -> when(buttonId){ - 1 -> player("A watering can.").also { stage = 400 } - 2 -> player("A gardening trowel.").also { stage = 500 } - 3 -> player("A seed dibber.").also { stage = 600 } - 4 -> options("Some plant cure.","A bucket of compost.","A rake.","(See more items)").also { stage = 32 } - } + 30 -> npc(FacialExpression.NEUTRAL, "That depends on whether I have it to sell. What is it", "that you're looking for?").also { stage++ } + 31 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Some plant cure.", 100), + Topic(FacialExpression.NEUTRAL, "A bucket of compost.", 200), + Topic(FacialExpression.NEUTRAL, "A rake.", 300), + Topic("(See more items)", 32, true) + ) + 32 -> showTopics( + Topic(FacialExpression.NEUTRAL, "A watering can.", 400), + Topic(FacialExpression.NEUTRAL, "A gardening trowel.", 500), + Topic(FacialExpression.NEUTRAL, "A seed dibber.", 600), + Topic("(See previous items)", 31, true), + Topic(FacialExpression.NEUTRAL, "Forget it.", 40, true) + ) - 100 -> npc("Plant cure, eh? I might have some put aside for myself.","Tell you what, I'll sell you some plant cure","for 25 gp if you like.").also { stage++ } - 101 -> options("Yes, that sounds like a fair price.","No thanks, I can get that much cheaper.").also { stage++ } - 102 -> when(buttonId){ + 40 -> player("Forget it, you don't have anything I need.").also { stage = END_DIALOGUE } + + 100 -> npc("Plant cure, eh? I might have some put aside for myself.", "Tell you what. I'll sell you some plant cure for 25 gp if", "you like.").also { stage++ } + 101 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ } + 102 -> when (buttonId) { 1 -> { - player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE } - if(player.inventory.remove(Item(995,25))){ - player.inventory.add(Item(Items.PLANT_CURE_6036)) + player(FacialExpression.HAPPY, "Yes, that sounds like a fair price.").also { stage = END_DIALOGUE } + if (removeItem(player, Item(Items.COINS_995, 25))) { + addItemOrDrop(player, Items.PLANT_CURE_6036) } else { - player.sendMessage("You need 25 gp to pay for that.") + sendMessage(player, "You need 25 gp to pay for that.") } } - 2 -> end() + 2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE } } - 200 -> npc("A bucket of compost, eh? I might have one spare...","tell you what, I'll sell it to you for 35 gp if you like.").also { stage++ } - 201 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ } - 202 -> when(buttonId){ + 200 -> npc("A bucket of compost, eh? I might have one spare...", "tell you what, I'll sell it to you for 35 gp if you like.").also { stage++ } + 201 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ } + 202 -> when (buttonId) { 1 -> { player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE } - if(player.inventory.remove(Item(995,35))){ - player.inventory.add(Item(Items.COMPOST_6032)) + if (removeItem(player, Item(Items.COINS_995, 35))) { + addItemOrDrop(player, Items.COMPOST_6032) } else { - player.sendMessage("You need 35 gp to pay for that.") + sendMessage(player, "You need 35 gp to pay for that.") } } - 2 -> end() + 2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE } } - 300 -> npc("A rake, eh? I might have one spare...","tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ } - 301 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ } - 302 -> when(buttonId){ + 300 -> npc("A rake, eh? I might have one spare...", "tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ } + 301 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ } + 302 -> when (buttonId) { 1 -> { player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE } - if(player.inventory.remove(Item(995,15))){ - player.inventory.add(Item(Items.RAKE_5341)) + if (removeItem(player, Item(Items.COINS_995, 15))) { + addItemOrDrop(player, Items.RAKE_5341) } else { - player.sendMessage("You need 15 gp to pay for that.") + sendMessage(player, "You need 15 gp to pay for that.") } } - 2 -> end() + 2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE } } - 400 -> npc("A watering can, eh? I might have one spare...","tell you what, I'll sell it to you for 25 gp if you like.").also { stage++ } - 401 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ } + 400 -> npc("A watering can, eh? I might have one spare...", "tell you what, I'll sell it to you for 25 gp if you like.").also { stage++ } + 401 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ } 402 -> when(buttonId){ 1 -> { player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE } - if(player.inventory.remove(Item(995,25))){ - player.inventory.add(Item(Items.WATERING_CAN8_5340)) + if (removeItem(player, Item(Items.COINS_995, 25))) { + addItemOrDrop(player, Items.WATERING_CAN8_5340) } else { - player.sendMessage("You need 25 gp to pay for that.") + sendMessage(player, "You need 25 gp to pay for that.") } } - 2 -> end() + 2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE } } - 500 -> npc("A gardening trowel, eh? I might have one spare...","tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ } - 501 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ } - 502 -> when(buttonId){ + 500 -> npc("A gardening trowel, eh? I might have one spare...", "tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ } + 501 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ } + 502 -> when (buttonId) { 1 -> { player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE } - if(player.inventory.remove(Item(995,15))){ - player.inventory.add(Item(Items.GARDENING_TROWEL_5325)) + if (removeItem(player, Item(Items.COINS_995, 15))) { + addItemOrDrop(player, Items.GARDENING_TROWEL_5325) } else { - player.sendMessage("You need 15 gp to pay for that.") + sendMessage(player, "You need 15 gp to pay for that.") } } - 2 -> end() + 2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE } } - 600 -> npc("A seed dibber, eh? I might have one spare...","tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ } - 601 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ } - 602 -> when(buttonId){ + 600 -> npc("A seed dibber, eh? I might have one spare...", "tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ } + 601 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ } + 602 -> when (buttonId) { 1 -> { player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE } - if(player.inventory.remove(Item(995,15))){ - player.inventory.add(Item(Items.SEED_DIBBER_5343)) + if (removeItem(player, Item(Items.COINS_995, 15))) { + addItemOrDrop(player, Items.SEED_DIBBER_5343) } else { - player.sendMessage("You need 15 gp to pay for that.") + sendMessage(player, "You need 15 gp to pay for that.") } } - 2 -> end() + 2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE } + } + + // Note: This dialogue changes slightly in April 2009, and significantly in December 2009 + 1000 -> npc(FacialExpression.THINKING, "Why? You look like you could chop it down yourself!").also { stage++ } + 1001 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, you're right - I'll do it myself.", END_DIALOGUE), + Topic(FacialExpression.NEUTRAL, "I can't be bothered - I'd rather pay you to do it.", 1020) + ) + + 1020 -> npc(FacialExpression.NEUTRAL, "Well, it's a lot of hard work - if you pay me 200 GP", "I'll chop it down for you.").also { stage++ } + 1021 -> { + if (inInventory(player, Items.COINS_995, 200)) { + showTopics( + Topic(FacialExpression.NEUTRAL, "Here's 200GP - chop my tree down please.", 1022), + Topic(FacialExpression.NEUTRAL, "I don't want to pay that much, sorry.", END_DIALOGUE) + ) + } else { + player("I don't have that much money on me.").also { stage = END_DIALOGUE } // not authentic + } + } + 1022 -> { + end() + if (removeItem(player, Item(Items.COINS_995, 200))) { + patches[0].getPatchFor(player).clear() + } + } + + 2000 -> { + val advice = arrayOf( + "There are four main Farming areas - Elstan looks after an area south of Falador, Dantaera has one to the north of Catherby, Kragen has one near Ardougne, and Lyra looks after a place in north Morytania.", + "If you want to grow fruit trees you could try a few places: Catherby and Brimhaven have a couple of fruit tree patches, and I hear that the gnomes are big on that sort of thing.", + "Bittercap mushrooms can only be grown in a special patch in Morytania, near the Mort Myre swamp. There the ground is especially dank and suited to growing poisonous fungi.", + "There is a special patch for growing Belladonna - I believe it's somewhere near Draynor Manor, where the ground is a tad 'unblessed'.", + + "Don't just throw away your weeds after you've raked a patch - put them in a compost bin and make some compost.", + "Applying compost to a patch will not only reduce the chance that your crops will get diseased, but you will also grow more crops to harvest.", + "Supercompost is far better than normal compost, but more expensive to make. You need to rot the right type of item; show me an item, and I'll tell you if it's super-compostable or not.", + + "Tree seeds must be grown in a plantpot of soil into a sapling, and then transferred to a tree patch to continue growing to adulthood.", + "You don't have to buy all your plantpots you know, you can make them yourself on a pottery wheel. If you're a good enough ${if (player!!.isMale) "craftsman" else "craftswoman"}, that is.", + "You can fill plantpots with soil from Farming patches, if you have a gardening trowel.", + + "Vegetables, hops and flowers are far more likely to grow healthily if you water them periodically.", + "The only way to cure a bush or tree of disease is to prune away the diseased leaves with a pair of secateurs. For all other crops I would just apply some plant-cure.", + "If you need to be rid of your fruit trees for any reason, all you have to do is chop them down and then dig up the stump.", + + "You can put up to ten potatoes, cabbages or onions in vegetable sacks, although you can't have a mix in the same sack.", + "You can put up to five tomatoes, strawberries, apples, bananas or oranges into a fruit basket, although you can't have a mix in the same basket.", + "If you want to make your own sacks and baskets you'll need to use the loom that's near the Farming shop in Falador. If you're a good enough ${if (player!!.isMale) "craftsman" else "craftswoman"}, that is.", + "You can buy all the farming tools from farming shops, which can be found close to the allotments.", + + "Hops are good for brewing ales. I believe there's a brewery up in Keldagrim somewhere, and I've heard rumours that a place called Phasmatys used to be good for that type of thing. 'Fore they all died, of course.", + ) + npcl(FacialExpression.NEUTRAL, advice.random()).also { stage = START_DIALOGUE } } } return true } - fun checkPatch(player: Player,fPatch: FarmingPatch){ - if(fPatch.getPatchFor(player).isWeedy()){ - npc("You don't have anything planted in that patch.","Plant something and I might agree to look after it for you.").also { stage = END_DIALOGUE } - } else if(fPatch.getPatchFor(player).isGrown()){ - npc("That patch is already fully grown!","I don't know what you want me to do with it!").also { stage = END_DIALOGUE } - } else if(fPatch.getPatchFor(player).protectionPaid) { - npc("Are you alright? You've already", "paid me for that.").also { stage = END_DIALOGUE } - } else { - end() - player.dialogueInterpreter.open(FarmerPayOptionDialogue(fPatch.getPatchFor(player)),npc) - } + fun openPayGardenerDialogue(player: Player, fPatch: FarmingPatch) { + end() + openDialogue(player, FarmerPayOptionDialogue(fPatch.getPatchFor(player)), npc) } override fun getIds(): IntArray { diff --git a/Server/src/main/content/global/dialogue/ManDialoguePlugin.java b/Server/src/main/content/global/dialogue/ManDialoguePlugin.java index 36785504e..aeff88be1 100644 --- a/Server/src/main/content/global/dialogue/ManDialoguePlugin.java +++ b/Server/src/main/content/global/dialogue/ManDialoguePlugin.java @@ -22,7 +22,7 @@ public class ManDialoguePlugin extends DialoguePlugin { /** * The NPC ids that use this dialogue plugin. */ - private static final int[] NPC_IDS = {1, 2, 3, 4, 5, 6, 16, 24, 25, 170, 351, 352, 353, 354, 359, 360, 361, 362, 363, 726, 727, 728, 729, 730, 1086, 2675, 2776, 3224, 3225, 3227, 5923, 5924,}; + private static final int[] NPC_IDS = {1, 2, 3, 4, 5, 6, 16, 24, 25, 170, 1086, 2675, 2776, 3224, 3225, 3227, 5923, 5924,}; public ManDialoguePlugin() { } diff --git a/Server/src/main/content/global/handlers/iface/BookInterface.kt b/Server/src/main/content/global/handlers/iface/BookInterface.kt index 070b83ebd..508b8b16d 100644 --- a/Server/src/main/content/global/handlers/iface/BookInterface.kt +++ b/Server/src/main/content/global/handlers/iface/BookInterface.kt @@ -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); } } diff --git a/Server/src/main/content/global/handlers/iface/ClanInterfacePlugin.java b/Server/src/main/content/global/handlers/iface/ClanInterfacePlugin.java deleted file mode 100644 index 1ad3857e8..000000000 --- a/Server/src/main/content/global/handlers/iface/ClanInterfacePlugin.java +++ /dev/null @@ -1,155 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -import core.game.system.communication.ClanRank; -import core.game.system.communication.ClanRepository; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.StringUtils; -import kotlin.Unit; - -import static core.api.ContentAPIKt.sendInputDialogue; -import static core.api.ContentAPIKt.setInterfaceText; - -/** - * Represents the plugin used to handle the clan interfaces. - * @author Vexia - */ -@Initializable -public final class ClanInterfacePlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(590, this); - ComponentDefinition.put(589, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (component.getId()) { - case 589: - switch (button) { - case 9: - if (player.getInterfaceManager().getComponent(590) != null) { - player.getPacketDispatch().sendMessage("Please close the interface you have open before using 'Clan Setup'"); - return true; - } - ClanRepository.openSettings(player); - return true; - case 14: - player.getDetails().getCommunication().toggleLootshare(player); - return true; - } - break; - case 590: - final ClanRepository clan = ClanRepository.get(player.getName(), true); - switch (button) { - case 23: - if (opcode == 155) { - clan.setJoinRequirement(ClanRank.NONE); - } else { - clan.setJoinRequirement(getRank(opcode)); - } - player.getDetails().getCommunication().setJoinRequirement(clan.getJoinRequirement()); - MSPacketRepository.setClanSetting(player, 0, clan.getJoinRequirement()); - player.getPacketDispatch().sendString(clan.getJoinRequirement().getInfo(), 590, 23); - break; - case 24: - clan.setMessageRequirement(getRank(opcode)); - player.getDetails().getCommunication().setMessageRequirement(clan.getMessageRequirement()); - MSPacketRepository.setClanSetting(player, 1, clan.getMessageRequirement()); - player.getPacketDispatch().sendString(clan.getMessageRequirement().getInfo(), 590, 24); - break; - case 25: - clan.setKickRequirement(getRank(opcode)); - player.getDetails().getCommunication().setKickRequirement(clan.getKickRequirement()); - MSPacketRepository.setClanSetting(player, 2, clan.getKickRequirement()); - player.getPacketDispatch().sendString(clan.getKickRequirement().getInfo(), 590, 25); - clan.update(); - break; - case 26: - if (opcode == 230) { - clan.setLootRequirement(ClanRank.ADMINISTRATOR); - } else { - clan.setLootRequirement(getRank(opcode)); - } - player.getDetails().getCommunication().setLootRequirement(clan.getLootRequirement()); - MSPacketRepository.setClanSetting(player, 3, clan.getLootRequirement()); - player.getPacketDispatch().sendString(clan.getLootRequirement().getInfo(), 590, 26); - break; - case 22: - switch (opcode) { - case 196: - clan.setName("Chat disabled"); - player.getCommunication().setClanName(""); - player.getPacketDispatch().sendString(clan.getName(), 590, 22); - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendClanRename(player, ""); - break; - } - clan.clean(true); - break; - default: - sendInputDialogue(player, false, "Enter clan prefix:", (value) -> { - String name = StringUtils.formatDisplayName((String) value); - setInterfaceText(player, name, 590, 22); - if(WorldCommunicator.isEnabled()){ - MSPacketRepository.sendClanRename(player, name); - clan.setName(name); - return Unit.INSTANCE; - } - if (clan.getName().equals("Chat disabled")) { - player.getPacketDispatch().sendMessage("Your clan channel has now been enabled!"); - player.getPacketDispatch().sendMessage("Join your channel by clicking 'Join Chat' and typing: " + player.getUsername()); - } - clan.setName(name); - player.getCommunication().setClanName(name); - clan.update(); - return Unit.INSTANCE; - }); - break; - } - break; - } - break; - } - return true; - } - - /** - * Gets the value to set. - * @param opcode the opcode. - * @return the value. - */ - public static ClanRank getRank(int opcode) { - switch (opcode) { - case 155: - return ClanRank.NONE; - case 196: - return ClanRank.FRIEND; - case 124: - return ClanRank.RECRUIT; - case 199: - return ClanRank.CORPORAL; - case 234: - return ClanRank.SERGEANT; - case 168: - return ClanRank.LIEUTENANT; - case 166: - return ClanRank.CAPTAIN; - case 64: - return ClanRank.GENERAL; - case 53: - return ClanRank.OWNER; - } - return ClanRank.NONE; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/CrystalKeyChestPlugin.java b/Server/src/main/content/global/handlers/iface/CrystalKeyChestPlugin.java deleted file mode 100644 index 852eb4997..000000000 --- a/Server/src/main/content/global/handlers/iface/CrystalKeyChestPlugin.java +++ /dev/null @@ -1,50 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -@Initializable -public class CrystalKeyChestPlugin extends ComponentPlugin { - - private final Integer CHEST_INTERFACE = 501; - - private Player player; - - public CrystalKeyChestPlugin() { - /* - * Empty - */ - } - - public CrystalKeyChestPlugin(Player player) { - this.player = player; - } - - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(CHEST_INTERFACE, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - return false; - } - - public void constructInterface(Player player) { - Integer[] hiddenChildren = new Integer[] { 3, 4, 5, 6, 7}; - Component component = new Component(CHEST_INTERFACE); - for (int i = 3; i < hiddenChildren.length; i++) { - player.getPacketDispatch().sendInterfaceConfig(CHEST_INTERFACE, i, true); - } - player.getPacketDispatch().sendItemOnInterface(989, 1, CHEST_INTERFACE, hiddenChildren[2]); - player.getInterfaceManager().open(component); - this.player = player; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/DeathInterface.kt b/Server/src/main/content/global/handlers/iface/DeathInterface.kt new file mode 100644 index 000000000..3b0dae31f --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/DeathInterface.kt @@ -0,0 +1,17 @@ +package content.global.handlers.iface + +import core.api.closeInterface +import core.game.interaction.InterfaceListener +import org.rs09.consts.Components + +class DeathInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.AIDE_DEATH_153) { player, _, _, buttonID, _, _ -> + if (buttonID == 1) { + player.savedData.globalData.setDisableDeathScreen(true) + closeInterface(player) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/DeathInterfacePlugin.java b/Server/src/main/content/global/handlers/iface/DeathInterfacePlugin.java deleted file mode 100644 index 5b57c3078..000000000 --- a/Server/src/main/content/global/handlers/iface/DeathInterfacePlugin.java +++ /dev/null @@ -1,32 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the death interface. - * @author Vexia - */ -@Initializable -public final class DeathInterfacePlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(153).setPlugin(this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - if (button == 1) { - player.getSavedData().getGlobalData().setDisableDeathScreen(true); - player.getInterfaceManager().close(); - } - return true; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/EllisDialogue.java b/Server/src/main/content/global/handlers/iface/EllisDialogue.java deleted file mode 100644 index 0a28d3495..000000000 --- a/Server/src/main/content/global/handlers/iface/EllisDialogue.java +++ /dev/null @@ -1,136 +0,0 @@ -package content.global.handlers.iface; - -import core.game.dialogue.DialoguePlugin; -import content.global.skill.crafting.TanningProduct; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the ellis dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class EllisDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code EllisDialogue} {@code Object}. - */ - public EllisDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code EllisDialogue} {@code Object}. - * @param player the player. - */ - public EllisDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new EllisDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Greetings friend I am a manufacturer of leather."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player.getInventory().refresh(); - Item items[] = player.getInventory().toArray(); - for (int i = 0; i < items.length; i++) { - if (items[i] == null) { - continue; - } - if (TanningProduct.forItemId(items[i].getId()) != null) { - npc("I see you have brought me some hides.", "Would you like me to tan them for you?"); - stage = 100; - return true; - } - } - options("Can I buy some leather?", "Leather is rather weak stuff."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - player("Can I buy some leather then?"); - stage = 10; - break; - case 2: - player("Leather is rather weak stuff."); - stage = 3000; - break; - } - break; - case 10: - npc("I make leather from animal hides. Bring me some cowhides", "and one gold coin per hide, and I'll tan them into soft", "leather for you."); - stage = 2000; - break; - case 2000: - end(); - break; - case 3000: - npc("Normal leather may be quite weak, but it's very cheap - I", "make it from cowhides for only 1 gp per hide - and it's so", "easy to craft that anyone can work with it."); - stage = 3001; - break; - case 3001: - npc("Alternatively you could try hard leather. It's not so easy", "to craft, but I only charge 3 gp per cowhide to prepare it,", "and it makes much sturdier armour."); - stage = 3002; - break; - case 3002: - player("Thanks; I'll bear it in mind."); - stage = 3003; - break; - case 3003: - end(); - break; - case 100: - options("Yes please.", "No thanks."); - stage = 101; - break; - case 101: - switch (buttonId) { - case 1: - player("Yes please."); - stage = 210; - break; - case 2: - player("No thanks."); - stage = 200; - break; - } - break; - case 210: - end(); - TanningProduct.open(player, 2824); - break; - case 200: - npc("Very well, sir, as you wish."); - stage = 201; - break; - case 201: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2824 }; - } -} diff --git a/Server/src/main/content/global/handlers/iface/EmoteTabInterface.java b/Server/src/main/content/global/handlers/iface/EmoteTabInterface.java deleted file mode 100644 index 7506f5fcb..000000000 --- a/Server/src/main/content/global/handlers/iface/EmoteTabInterface.java +++ /dev/null @@ -1,30 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.emote.Emotes; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the emote tab interface. - * @author Vexia - * - */ -@Initializable -public final class EmoteTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(464, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - Emotes.handle(player, button); - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/EquipmentInterface.java b/Server/src/main/content/global/handlers/iface/EquipmentInterface.java deleted file mode 100644 index f8cfa2660..000000000 --- a/Server/src/main/content/global/handlers/iface/EquipmentInterface.java +++ /dev/null @@ -1,231 +0,0 @@ -package content.global.handlers.iface; - -import content.global.skill.summoning.familiar.BurdenBeast; -import core.api.ContentAPIKt; -import core.cache.def.impl.ItemDefinition; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.container.Container; -import core.game.container.ContainerEvent; -import core.game.container.ContainerListener; -import core.game.container.access.InterfaceContainer; -import core.game.container.impl.EquipmentContainer; -import core.game.interaction.OptionHandler; -import core.game.node.entity.combat.DeathTask; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.net.packet.PacketRepository; -import core.net.packet.context.ContainerContext; -import core.net.packet.out.ContainerPacket; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.global.action.EquipHandler; -import core.game.interaction.IntType; -import core.game.interaction.InteractionListeners; -import core.game.world.GameWorld; -import core.tools.Log; - -/** - * Represents the equipment interface. - * @author Emperor - * - */ -@Initializable -public final class EquipmentInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(102, this); - ComponentDefinition.put(387, this); - ComponentDefinition.put(667, this); - ComponentDefinition.put(670, this); - return this; - } - - @Override - public boolean handle(final Player p, Component component, int opcode, int button, final int slot, final int itemId) { - if (component.getId() == 667) { - if (button != 14) { - return false; - } - switch (opcode) { - case 155: - p.getPulseManager().clear(); - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - EquipHandler.unequip(p, slot, itemId); - return true; - } - }); - return true; - case 9: - p.sendMessage(p.getEquipment().get(slot).getDefinition().getExamine()); - return true; - case 196: - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - operate(p, slot, itemId); - return true; - } - }); - return true; - } - return false; - } - else if (component.getId() == 670) { - switch (opcode) { - case 155: - p.getPulseManager().clear(); - final Item item = p.getInventory().get(slot); - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - if (item == null) return true; - InteractionListeners.run(item.getId(), IntType.ITEM,"equip",p, item); - return true; - } - }); - return true; - case 9: - p.sendMessage(p.getInventory().get(slot).getDefinition().getExamine()); - return true; - } - } - switch (opcode) { - case 206: - if (button != 28) { - return false; - } - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - operate(p, slot, itemId); - return true; - } - }); - return true; - default: - switch (button) { - case 52: - if (p.getInterfaceManager().isOpened() && p.getInterfaceManager().getOpened().getId() == 102) { - return true; - } - - // (Highlight white items are auto destroyed on death Enum#616 (Items kept on death interface) TODO: Parse server sided - // SCRIPT 118 - Items kept on death interface CS - // ARG 0: Safe location check Takes: 0 Safe Area/2 in POH/3 in Castle Wars/4 in Trouble Brewing/5 in Barbass - int zoneType = p.getZoneMonitor().getType(); - // ARG 1: Amount of items kept on death Takes: 0/1/3/4 - Container[] itemArray = DeathTask.getContainers(p); - Container kept = itemArray[0]; - int amtKeptOnDeath = kept.itemCount(); - if (amtKeptOnDeath > 4 && zoneType == 0) { - ContentAPIKt.log(this.getClass(), Log.ERR, "Items kept on death interface should not contain more than 4 items when not in a safe zone!"); - } - // ARG 2: Item kept on death slot 0 - int slot0 = kept.getId(0); - // ARG 3: Item kept on death slot 1 - int slot1 = kept.getId(1); - // ARG 4: Item kept on death slot 2 - int slot2 = kept.getId(2); - // ARG 5: Item kept on death slot 3 - int slot3 = kept.getId(3); - // ARG 6: Player skulled Takes: 0 not skulled/1 skulled - int skulled = p.getSkullManager().isSkulled() ? 1 : 0; - // ARG 7: Player has summoning creature out Takes: 0 not out/1 Creature summoned - int hasBoB; - if (p.getFamiliarManager().hasFamiliar()) { - if (p.getFamiliarManager().getFamiliar().isBurdenBeast()) { - hasBoB = ((BurdenBeast) p.getFamiliarManager().getFamiliar()).getContainer().isEmpty() ? 0 : 1; - } else { - hasBoB = 0; - } - } else { - hasBoB = 0; - } - // ARG 8: String for effect: - // if (arg1 == 0) arg8 + " This reduces the items you keep from three to zero!" - // if (arg1 == 1) arg8 + " This reduces the items you keep from three to zero!" + "
" + "
" + "However, you also have the " + "" + "Protect Items" + "" + " prayer active, which saves you one extra item!"); - Object[] params = new Object[] { hasBoB, skulled, slot3, slot2, slot1, slot0, amtKeptOnDeath, zoneType, "You are skulled." }; - p.getPacketDispatch().sendRunScript(118, "siiooooii", params); - - p.getInterfaceManager().openComponent(102); - break; - case 28: - if (opcode == 81) { - p.getPulseManager().clear(); - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - EquipHandler.unequip(p, slot, itemId); - return true; - } - }); - return true; - } - break; - case 55: - if (p.getInterfaceManager().isOpened() && p.getInterfaceManager().getOpened().getId() == 667) { - return true; - } - final ContainerListener listener = new ContainerListener() { - @Override - public void update(Container c, ContainerEvent e) { - PacketRepository.send(ContainerPacket.class, new ContainerContext(p, -1, -1, 98, e.getItems(), false, e.getSlots())); - } - - @Override - public void refresh(Container c) { - PacketRepository.send(ContainerPacket.class, new ContainerContext(p, -1, -1, 98, c, false)); - } - }; - p.getInterfaceManager().openComponent(667).setCloseEvent((player, c) -> { - player.removeAttribute("equip_stats_open"); - player.getInterfaceManager().closeSingleTab(); - player.getInventory().getListeners().remove(listener); - return true; - }); - p.setAttribute("equip_stats_open", true); - EquipmentContainer.update(p); - p.getInterfaceManager().openSingleTab(new Component(670)); - InterfaceContainer.generateItems(p, p.getInventory().toArray(), new String[] { "Equip" }, 670, 0, 7, 4, 93); - p.getInventory().getListeners().add(listener); - p.getInventory().refresh(); - ItemDefinition.statsUpdate(p); - p.getPacketDispatch().sendIfaceSettings(1278, 14, 667, 0, 13); - break; - } - } - return true; - } - - /** - * Operates an item. - * @param player The player. - * @param slot The container slot. - * @param itemId The item id. - */ - public void operate(Player player, int slot, int itemId) { - if (slot < 0 || slot > 13) { - return; - } - Item item = player.getEquipment().get(slot); - if (item == null) { - return; - } - if(InteractionListeners.run(item.getId(),IntType.ITEM,"operate",player,item)){ - return; - } - OptionHandler handler = item.getOperateHandler(); - if (handler != null && handler.handle(player, item, "operate")) { - return; - } - player.getPacketDispatch().sendMessage("You can't operate that."); - } - -} diff --git a/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt b/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt index be4d2599e..c7003ea83 100644 --- a/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt +++ b/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt @@ -10,6 +10,7 @@ import core.plugin.Initializable import core.plugin.Plugin import core.tools.Log import org.rs09.consts.Sounds +import content.data.Quests /** * Represents the experience interface. @@ -78,15 +79,15 @@ class ExperienceInterface() : ComponentPlugin() { } private fun checkHerblore(player: Player): Boolean{ - return (player.questRepository.isComplete("Druidic Ritual")) + return (player.questRepository.isComplete(Quests.DRUIDIC_RITUAL)) } private fun checkSummoning(player: Player): Boolean{ - return player.questRepository.isComplete("Wolf Whistle") + return player.questRepository.isComplete(Quests.WOLF_WHISTLE) } private fun checkRunecrafting(player: Player): Boolean{ - return player.questRepository.isComplete("Rune Mysteries") + return player.questRepository.isComplete(Quests.RUNE_MYSTERIES) } companion object { diff --git a/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt b/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt index 70a005ec4..69bba13d7 100644 --- a/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt +++ b/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt @@ -2,109 +2,128 @@ package content.global.handlers.iface import core.api.* import core.game.event.FairyRingDialEvent -import core.game.component.Component +import core.game.interaction.InterfaceListener import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager import core.game.system.task.Pulse +import core.game.world.GameWorld import core.game.world.map.Location import core.game.world.map.RegionManager import core.tools.RandomFunction -import core.game.interaction.InterfaceListener -import core.game.world.GameWorld - -val RING_1 = arrayOf('a','d','c','b') -val RING_2 = arrayOf('i','l','k','j') -val RING_3 = arrayOf('p','s','r','q') +import content.data.Quests /** * Handles the fairy ring interface * @author Ceikry */ class FairyRingInterface : InterfaceListener { - - val RINGS = 734 - val TRAVEL_LOG = 735 + companion object { + const val RINGS_IFACE = 734 + const val LOG_IFACE_ID = 735 + const val VARP_F_RING = 816 + const val VB_LOG_SORT_ORDER = 4618 + const val VB_RING_1 = 2341 + const val VB_RING_2 = 2342 + const val VB_RING_3 = 2343 + val RING_1 = arrayOf('a','d','c','b') + val RING_2 = arrayOf('i','l','k','j') + val RING_3 = arrayOf('p','s','r','q') + } override fun defineInterfaceListeners() { - onOpen(RINGS){player, _ -> - player.interfaceManager.openSingleTab(Component(TRAVEL_LOG)) - player.setAttribute("fr:ring1", 0) - player.setAttribute("fr:ring2", 0) - player.setAttribute("fr:ring3", 0) - FairyRing.drawLog(player) + onOpen(RINGS_IFACE){ player, _ -> + openSingleTab(player, LOG_IFACE_ID) + saveVarp(player, VARP_F_RING) return@onOpen true } - onClose(RINGS){player, _ -> - closeTabInterface(player) - player.removeAttribute("fr:ring1") - player.removeAttribute("fr:ring2") - player.removeAttribute("fr:ring3") - setVarp(player, 816, 0) + onOpen(LOG_IFACE_ID){ player, _ -> + drawLog(player) + return@onOpen true + } + + onClose(RINGS_IFACE){ player, _ -> closeTabInterface(player) return@onClose true } - on(RINGS){player, _, _, buttonID, _, _ -> - if(player.getAttribute("fr:time",0L) > System.currentTimeMillis()) return@on true - var delayIncrementer = 1750L + on(RINGS_IFACE){ player, _, _, buttonID, _, _ -> when(buttonID){ - 23 -> delayIncrementer += increment(player,1) - 25 -> delayIncrementer += increment(player,2) - 27 -> delayIncrementer += increment(player,3) + 23 -> increment(player,1) + 25 -> increment(player,2) + 27 -> increment(player,3) 24 -> decrement(player,1) 26 -> decrement(player,2) 28 -> decrement(player,3) 21 -> confirm(player) } - player.setAttribute("fr:time",System.currentTimeMillis() + delayIncrementer) return@on true } - on(TRAVEL_LOG,12){player, _, _, _, _, _ -> + on(LOG_IFACE_ID,12){ player, _, _, _, _, _ -> toggleSortOrder(player) return@on true } } - private fun toggleSortOrder(player: Player): Long{ - val ring1index = player.getAttribute("fr:ring1",0) - var toSet = player.getAttribute("fr:sortorder",true) - toSet = !toSet - player.setAttribute("fr:sortorder",toSet) - if(toSet) { - setVarp(player, 816, ring1index) - player.setAttribute("fr:ring2",0) - player.setAttribute("fr:ring3",0) + /** + * Draws the travel log interface + * Currently, the visited logs is a bool array in globalData. Someone should migrate this to prefs or something at some point. + * On transmit of Varp 816, which all used varbits are part of here, the CS2 is invoked to populate the codes in the log and sort them correctly. + * @param player The player to draw the interface for + */ + private fun drawLog (player: Player) + { + for (i in FairyRing.values().indices) { + if (!player.savedData.globalData.hasTravelLog(i)) { + continue + } + val ring = FairyRing.values()[i] + if (ring.childId == -1) { + continue + } + setInterfaceText(player, "
${ring.tip}", LOG_IFACE_ID, ring.childId) } - return -1750L } - fun increment(player: Player,ring: Int): Long{ - val curIndex = player.getAttribute("fr:ring$ring",0) - var nextIndex = 0 - if(curIndex == 3) nextIndex = 0 - else if(curIndex == 1) nextIndex = 3 - else if(curIndex == 2) nextIndex = 2 - else nextIndex = curIndex + 1 - player.setAttribute("fr:ring$ring",nextIndex) - return if (curIndex == 1) 1750L else 0L + private fun toggleSortOrder(player: Player) { + val curSort = getVarbit(player, VB_LOG_SORT_ORDER) == 0 + setVarbit(player, VB_LOG_SORT_ORDER, if (curSort) 1 else 0) + drawLog(player) + } + + fun increment(player: Player,ring: Int) { + val vbit = when(ring) { + 1 -> VB_RING_1 + 2 -> VB_RING_2 + 3 -> VB_RING_3 + else -> return + } + val curIndex = getVarbit(player, vbit) + val nextIndex: Int = if(curIndex == 3) 0 + else curIndex + 1 + setVarbit(player, vbit, nextIndex) } fun decrement(player: Player,ring: Int){ - val curIndex = player.getAttribute("fr:ring$ring",0) - var nextIndex = 0 - if(curIndex == 0) nextIndex = 3 - else nextIndex = curIndex - 1 - player.setAttribute("fr:ring$ring",nextIndex) + val vbit = when(ring) { + 1 -> VB_RING_1 + 2 -> VB_RING_2 + 3 -> VB_RING_3 + else -> return + } + val curIndex = getVarbit(player, vbit) + val nextIndex: Int = if(curIndex == 0) 3 + else curIndex - 1 + setVarbit(player, vbit, nextIndex) } private fun confirm(player: Player){ - val ring1index = player.getAttribute("fr:ring1",0) - val ring2index = player.getAttribute("fr:ring2",0) - val ring3index = player.getAttribute("fr:ring3",0) + val ring1index = getVarbit(player, VB_RING_1) + val ring2index = getVarbit(player, VB_RING_2) + val ring3index = getVarbit(player, VB_RING_3) val code = "${RING_1[ring1index]}${RING_2[ring2index]}${RING_3[ring3index]}" val ring: FairyRing? = try { FairyRing.valueOf(code.uppercase()) @@ -160,7 +179,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int AKS(Location.create(2571, 2956, 0), "Feldip Hills: Jungle Hunter area", 25), ALQ(Location.create(3597, 3495, 0), "Morytania: Haunted Woods east of Canifis", 27) { override fun checkAccess(player: Player) : Boolean { - return requireQuest(player, "Priest in Peril", "to use this ring.") + return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.") } }, ALS(Location.create(2644, 3495, 0), "Kandarin: McGrubor's Wood", 29), @@ -172,7 +191,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int BKQ(Location.create(3041, 4532, 0), "Other realms: Enchanted Valley", 39), BKR(Location.create(3469, 3431, 0), "Morytania: Mort Myre, south of Canifis", 40) { override fun checkAccess(player: Player) : Boolean { - return requireQuest(player, "Priest in Peril", "to use this ring.") + return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.") } }, BLP(Location.create(2437, 5126, 0), "Dungeons: TzHaar area", 42), @@ -180,7 +199,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int BLR(Location.create(2740, 3351, 0), "Kandarin: Legends' Guild", 44), CIP(Location.create(2513, 3884, 0), "Islands: Miscellania", 46) { override fun checkAccess(player: Player): Boolean { - return requireQuest(player, "Fremennik Trials", "to use this ring.") + return requireQuest(player, Quests.THE_FREMENNIK_TRIALS, "to use this ring.") } }, CIQ(Location.create(2528, 3127, 0), "Kandarin: North-west of Yanille", 47), @@ -189,7 +208,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int CKR(Location.create(2801, 3003, 0), "Karamja: South of Tai Bwo Wannai Village", 56), CKS(Location.create(3447, 3470, 0), "Morytania: Canifis", 57) { override fun checkAccess(player: Player) : Boolean { - return requireQuest(player, "Priest in Peril", "to use this ring.") + return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.") } }, CLP(Location.create(3082, 3206, 0), "Islands: South of Draynor Village", 58), @@ -208,23 +227,4 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int open fun checkAccess(player: Player) : Boolean { return true } - - companion object { - /** - * Draws the travel log. - * @param player the player. - */ - fun drawLog(player: Player) { - for (i in FairyRing.values().indices) { - if (!player.savedData.globalData.hasTravelLog(i)) { - continue - } - val ring = FairyRing.values()[i] - if (ring.childId == -1) { - continue - } - setInterfaceText(player, "
${ring.tip}", 735, ring.childId) - } - } - } } diff --git a/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt b/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt index c395840e6..e9025bad9 100644 --- a/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt +++ b/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt @@ -162,9 +162,10 @@ class FurClothingInterface : ComponentPlugin(){ return } - removeItem(player, requiredFur, Container.INVENTORY) - removeItem(player, coins, Container.INVENTORY) - addItem(player, clothing.product.id, amount) + if (removeItem(player, requiredFur, Container.INVENTORY) && + removeItem(player, coins, Container.INVENTORY)) { + addItem(player, clothing.product.id, amount) + } } override fun newInstance(arg: Any?): Plugin { diff --git a/Server/src/main/content/global/handlers/iface/GameInterface.java b/Server/src/main/content/global/handlers/iface/GameInterface.java deleted file mode 100644 index 0c8754f29..000000000 --- a/Server/src/main/content/global/handlers/iface/GameInterface.java +++ /dev/null @@ -1,185 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.node.entity.combat.equipment.WeaponInterface; -import core.game.node.entity.combat.equipment.WeaponInterface.WeaponInterfaces; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.Rights; -import core.game.world.GameWorld; -import core.plugin.Plugin; - -/** - * Represents the component plugin used for the game interface. - * @author Vexia - * - */ -@Initializable -public final class GameInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(740, this); - return this; - } - - @Override - public boolean handle(final Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (component.getId()) { - case 740: - switch (button){ - case 3: - player.getInterfaceManager().closeChatbox(); - break; - } - return true; - case 746: - switch (button){ - case 12: - player.getPacketDispatch().sendString("When you have finished playing " + GameWorld.getSettings().getName() + ", always use the button below to logout safely. ", 182, 0); - break; - case 49: - player.getPacketDispatch().sendString("Friends List - " + GameWorld.getSettings().getName() + " " + GameWorld.getSettings().getWorldId(), 550, 3); - break; - case 110: - configureWorldMap(player); - break; - } - return true; - case 548: - if (button >= 38 && button<= 44 || button >= 20 && button <= 26) { - player.getInterfaceManager().setCurrentTabIndex(getTabIndex(button)); - } - //Interface buttons that advance the Tutorial Island stages - switch (button) { - case 21://Friends Tab - player.getPacketDispatch().sendString("Friends List -" + GameWorld.getSettings().getName() + " " + GameWorld.getSettings().getWorldId(), 550, 3); - break; - case 22://Ignore Tab - break; - case 24://Settings Tab - break; - case 25://Emotes Tab - break; - case 26://Music Tab - break; - case 38://Attack Tab - if (player.getExtension(WeaponInterface.class) == WeaponInterfaces.STAFF) { - final Component c = new Component(WeaponInterfaces.STAFF.getInterfaceId()); - player.getInterfaceManager().openTab(0, c); - final WeaponInterface inter = player.getExtension(WeaponInterface.class); - inter.updateInterface(); - } - break; - case 39://Skill Tab - break; - case 40://Quest Tab - /*if (GameWorld.isEconomyWorld()) { - player.getQuestRepository().syncronizeTab(player); - } else { - player.getSavedData().getSpawnData().drawStatsTab(player); - }*/ - - player.getQuestRepository().syncronizeTab(player); - break; - case 41://Inventory Tab - player.getInventory().refresh(); - break; - case 42://Worn Equipment Tab - break; - case 43://Prayer Tab - break; - case 44://Magic Tab - break; - case 66://World map - case 110: - configureWorldMap(player); - break; - case 69://Logout - player.getPacketDispatch().sendString("When you have finished playing " + GameWorld.getSettings().getName() + ", always use the button below to logout safely. ", 182, 0); - break; - } - return true; - case 750: - switch (opcode) { - case 155: - switch (button) { - case 1: - player.getSettings().toggleRun(); - break; - } - break; - } - return true; - case 751: - switch (opcode) { - case 155: - switch (button) { - case 27: - openReport(player); - break; - } - break; - } - return true; - } - return true; - } - - /** - * World map - * Thanks, Snickerize! - */ - private void configureWorldMap(Player player) { - if (player.inCombat()) { - player.getPacketDispatch().sendMessage("It wouldn't be very wise opening the world map during combat."); - return; - } - if(player.getLocks().isInteractionLocked() || player.getLocks().isMovementLocked()){ - player.getPacketDispatch().sendMessage("You can't do this right now."); - return; - } - player.getInterfaceManager().openWindowsPane(new Component(755)); - int posHash = (player.getLocation().getZ() << 28) | (player.getLocation().getX() << 14) | player.getLocation().getY(); - player.getPacketDispatch().sendScriptConfigs(622, posHash, "", 0); - player.getPacketDispatch().sendScriptConfigs(674, posHash, "", 0); - } - - /** - * Method used to open the report interface. - * @param player the player. - */ - public static void openReport(final Player player) { - player.getInterfaceManager().open(new Component(553)).setCloseEvent((player1, c) -> { - player1.getPacketDispatch().sendRunScript(80, ""); - player1.getPacketDispatch().sendRunScript(137, ""); - return true; - }); - player.getPacketDispatch().sendRunScript(508, ""); - if (player.getDetails().getRights() != Rights.REGULAR_PLAYER) { - for (int i = 0; i < 18; i++) { - player.getPacketDispatch().sendInterfaceConfig(553, i, false); - } - } - } - - /** - * Gets the tab index. - * @param button The button id. - * @return The tab index. - */ - private static int getTabIndex(int button) { - int tabIndex = button - 38; - if (button < 27) { - tabIndex = (button - 20) + 7; - } - return tabIndex; - } - - /** - * Configures the world map for a player. - * @param player The player. - */ -} diff --git a/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt b/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt index b1750ae63..d9058006e 100644 --- a/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt +++ b/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt @@ -152,11 +152,11 @@ class HairDresserInterface : ComponentPlugin(){ player.setAttribute("beard-setting",false) if(!pl.getAttribute("hairdresser-paid",false)){ val original_hair = player.getAttribute("original-hair",0) - val original_beard = player.getAttribute("original_beard",-1) - val original_color = player.getAttribute("original_color",0) + val original_beard = player.getAttribute("original-beard",-1) + val original_color = player.getAttribute("original-color",0) pl.appearance.hair.changeLook(original_hair) pl.appearance.hair.changeColor(original_color) - if(original_beard != -1) { + if (original_beard != -1) { pl.appearance.beard.changeLook(original_beard) } pl.appearance.sync() @@ -174,7 +174,7 @@ class HairDresserInterface : ComponentPlugin(){ when(button){ 199 -> player.setAttribute("beard-setting",false) 200 -> player.setAttribute("beard-setting",true) - 68,196,274 -> pay(player) + 196,274,68,100 -> pay(player) else -> when(component?.id){ 592 -> { //Female if(femaleColorButtonRange.contains(button)){ diff --git a/Server/src/main/content/global/handlers/iface/LoginInterface.kt b/Server/src/main/content/global/handlers/iface/LoginInterface.kt new file mode 100644 index 000000000..e2464c176 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/LoginInterface.kt @@ -0,0 +1,34 @@ +package content.global.handlers.iface + +import core.api.closeInterface +import core.api.runTask +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.info.login.LoginConfiguration +import org.rs09.consts.Components + +class LoginInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.WELCOME_SCREEN_378) { player, _, _, buttonID, _, _ -> + val playButton = 140 + val creditButton = 145 + val discordButton = 204 + + when (buttonID) { + playButton -> { + player.locks.lock("login", 2) + closeInterface(player) + runTask(player, 1, 0) { + LoginConfiguration.configureGameWorld(player) + } + } + creditButton -> return@on true + discordButton -> return@on true + } + return@on true + } + + onClose(Components.WELCOME_SCREEN_378) { player, _ -> + return@onClose player.locks.isLocked("login") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/LoginInterfacePlugin.java b/Server/src/main/content/global/handlers/iface/LoginInterfacePlugin.java deleted file mode 100644 index f91a3000e..000000000 --- a/Server/src/main/content/global/handlers/iface/LoginInterfacePlugin.java +++ /dev/null @@ -1,51 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.login.LoginConfiguration; -import core.game.system.task.Pulse; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used for the login interface. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LoginInterfacePlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(378, this); - return null; - } - - @Override - public boolean handle(final Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (button) { - case 140: - if (player.getLocks().isLocked("login")) { - return true; - } - player.getLocks().lock("login", 2); - player.getInterfaceManager().close(); - player.getPulseManager().run(new Pulse(1) { - @Override - public boolean pulse() { - LoginConfiguration.configureGameWorld(player); - return true; - } - }); - break; - case 145://credits - break; - case 204://message centre - break; - } - return true; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/LogoutInterface.java b/Server/src/main/content/global/handlers/iface/LogoutInterface.java deleted file mode 100644 index d3a7c9c1a..000000000 --- a/Server/src/main/content/global/handlers/iface/LogoutInterface.java +++ /dev/null @@ -1,42 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.world.GameWorld; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.world.repository.Repository; - -/** - * Represents the interface used to logout of the game. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LogoutInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(182, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - if (!player.getZoneMonitor().canLogout()) { - return true; - } - if (player.inCombat()) { - player.getPacketDispatch().sendMessage("You can't log out until 10 seconds after the end of combat."); - return true; - } - if (player.isTeleporting()) { - player.sendMessage("Please finish your teleport before logging out."); - return true; - } - Repository.getDisconnectionQueue().add(player); - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/MagicBookInterface.java b/Server/src/main/content/global/handlers/iface/MagicBookInterface.java deleted file mode 100644 index af5c87baa..000000000 --- a/Server/src/main/content/global/handlers/iface/MagicBookInterface.java +++ /dev/null @@ -1,50 +0,0 @@ -package content.global.handlers.iface; - -import content.global.skill.magic.SpellListener; -import content.global.skill.magic.SpellListeners; -import content.global.skill.magic.SpellUtils; -import core.game.event.SpellCastEvent; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.node.entity.combat.spell.MagicSpell; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.world.GameWorld; -import core.plugin.Plugin; - -/** - * Represents the magic book interface handling of non-combat spells. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MagicBookInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(192, this); - ComponentDefinition.put(193, this); - ComponentDefinition.put(430, this); - return this; - } - - @Override - public boolean handle(final Player player, Component component, int opcode, int button, int slot, int itemId) { - if (GameWorld.getTicks() < player.getAttribute("magic:delay", -1)) { - return true; - } - - SpellBook spellBook = component.getId() == 192 - ? SpellBook.MODERN - : component.getId() == 193 - ? SpellBook.ANCIENT - : SpellBook.LUNAR; - - SpellListeners.run(button, SpellListener.NONE, SpellUtils.getBookFromInterface(component.getId()),player,null); - boolean result = MagicSpell.castSpell(player, spellBook, button, player); - - return result; - } -} diff --git a/Server/src/main/content/global/handlers/iface/MainGameInterface.kt b/Server/src/main/content/global/handlers/iface/MainGameInterface.kt index 243337f4e..e7f4edb05 100644 --- a/Server/src/main/content/global/handlers/iface/MainGameInterface.kt +++ b/Server/src/main/content/global/handlers/iface/MainGameInterface.kt @@ -31,32 +31,13 @@ class MainGameInterface : InterfaceListener { } on(TOPLEVEL_FS){player, _, _, buttonID, _, _ -> - when (buttonID) { - 12 -> setInterfaceText(player, - "When you have finished playing " + settings!!.name + ", always use the button below to logout safely. ", - 182, - 0 - ) - 49 -> setInterfaceText(player, - "Friends List - " + settings!!.name + " " + settings!!.worldId, - 550, - 3 - ) - 110 -> configureWorldMap(player) - } + if (buttonID == 110) + configureWorldMap(player) return@on true } - on(TOPLEVEL){player, _, _, buttonID, _, _ -> when (buttonID) { - 21 -> { - player.packetDispatch.sendString( - "Friends List -" + settings!!.name + " " + settings!!.worldId, - 550, - 3 - ) - } 38 -> { if (player.getExtension(WeaponInterface::class.java) === WeaponInterfaces.STAFF) { val c = Component(WeaponInterfaces.STAFF.interfaceId) @@ -67,12 +48,7 @@ class MainGameInterface : InterfaceListener { } 40 -> player.questRepository.syncronizeTab(player) 41 -> player.inventory.refresh() - 66, 110 -> configureWorldMap(player) - 69 -> player.packetDispatch.sendString( - "When you have finished playing " + settings!!.name + ", always use the button below to logout safely. ", - 182, - 0 - ) + 66 -> configureWorldMap(player) } return@on true } @@ -95,11 +71,11 @@ class MainGameInterface : InterfaceListener { private fun configureWorldMap(player: Player) { if (player.inCombat()) { - player.packetDispatch.sendMessage("It wouldn't be very wise opening the world map during combat.") + sendMessage(player, "It wouldn't be very wise opening the world map during combat.") return } if (player.locks.isInteractionLocked || player.locks.isMovementLocked) { - player.packetDispatch.sendMessage("You can't do this right now.") + sendMessage(player, "You can't do this right now.") return } player.interfaceManager.close() diff --git a/Server/src/main/content/global/handlers/iface/MusicTabInterface.java b/Server/src/main/content/global/handlers/iface/MusicTabInterface.java deleted file mode 100644 index 579607fe5..000000000 --- a/Server/src/main/content/global/handlers/iface/MusicTabInterface.java +++ /dev/null @@ -1,57 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.Rights; -import core.game.node.entity.player.link.music.MusicEntry; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the interface tab buttons. - * @author Emperor - */ -@Initializable -public final class MusicTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(187, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (opcode) { - case 155: - switch (button) { - case 11: - player.getMusicPlayer().toggleLooping(); - return true; - case 1: - MusicEntry entry = player.getMusicPlayer().getUnlocked().get(slot); - if (entry == null) { - if(player.getRights().equals(Rights.ADMINISTRATOR)){ - for(MusicEntry ent : MusicEntry.getSongs().values()){ - if(ent.getIndex() == slot){ - player.getMusicPlayer().unlock(ent.getId()); - break; - } - } - } else { - player.getPacketDispatch().sendMessage("You have not unlocked this piece of music yet!"); - } - return true; - } - player.getMusicPlayer().setPlaying(false); - player.getMusicPlayer().play(entry); - return true; - } - break; - } - return false; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/MysticStaffEnchantInterface.kt b/Server/src/main/content/global/handlers/iface/MysticStaffEnchantInterface.kt new file mode 100644 index 000000000..6d9748d09 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/MysticStaffEnchantInterface.kt @@ -0,0 +1,57 @@ +package content.global.handlers.iface + +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.interaction.InterfaceListener +import core.game.node.item.Item +import core.tools.StringUtils +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class MysticStaffEnchantInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(INTERFACE_332) { player, _, _, buttonID, _, _ -> + val staff = buttonMap[buttonID] ?: return@on true + + if (!inInventory(player, staff.basicID)) { + sendMessage(player, "You don't have a${if (StringUtils.isPlusN(getItemName(staff.basicID))) "n" else ""} ${getItemName(staff.basicID)} to enchant.") + return@on true + } + + closeInterface(player) + + if (!inInventory(player, Items.COINS_995, 40000)) { + sendNPCDialogue(player, NPCs.THORMAC_389, "I need ${String.format("%,d", 40000)} coins for materials. Come back when you have the money!", FacialExpression.NEUTRAL) + return@on true + } + + if (removeItem(player, Item(staff.basicID, 1)) && removeItem(player, Item(Items.COINS_995, 40000))) { + sendNPCDialogue(player, NPCs.THORMAC_389, "Just a moment... hang on... hocus pocus abra-cadabra... there you go! Enjoy your enchanted staff!", FacialExpression.NEUTRAL) + addItem(player, staff.enchantedID, 1) + } + return@on true + } + } + + enum class EnchantedStaff(val enchantedID: Int, val basicID: Int, val buttonID: Int) { + AIR(Items.MYSTIC_AIR_STAFF_1405, Items.AIR_BATTLESTAFF_1397, 21), + WATER(Items.MYSTIC_WATER_STAFF_1403, Items.WATER_BATTLESTAFF_1395, 22), + EARTH(Items.MYSTIC_EARTH_STAFF_1407, Items.EARTH_BATTLESTAFF_1399, 23), + FIRE(Items.MYSTIC_FIRE_STAFF_1401, Items.FIRE_BATTLESTAFF_1393, 24), + LAVA(Items.MYSTIC_LAVA_STAFF_3054, Items.LAVA_BATTLESTAFF_3053, 25), + MUD(Items.MYSTIC_MUD_STAFF_6563, Items.MUD_BATTLESTAFF_6562, 26), + STEAM(Items.MYSTIC_STEAM_STAFF_11738, Items.STEAM_BATTLESTAFF_11736, 27), + } + + companion object { + private const val INTERFACE_332 = 332 + + val buttonMap = HashMap() + + init { + for (staff in EnchantedStaff.values()) { + buttonMap[staff.buttonID] = staff + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java b/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java deleted file mode 100644 index af798008d..000000000 --- a/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java +++ /dev/null @@ -1,95 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import org.rs09.consts.Items; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Plugin; -import core.plugin.Initializable; -import core.tools.StringUtils; - -import java.util.HashMap; - -/** - * Represents the plugin used to handle the agility ticket interface. - * @author afaroutdude - */ -@Initializable -public final class MysticStaffEnchantingPlugin extends ComponentPlugin { - - private final Component COMPONENT = new Component(332); - private final int cost = 40000; - - protected enum EnchantedStaff { - AIR(Items.MYSTIC_AIR_STAFF_1405, Items.AIR_BATTLESTAFF_1397, 21), - WATER(Items.MYSTIC_WATER_STAFF_1403, Items.WATER_BATTLESTAFF_1395, 22), - EARTH(Items.MYSTIC_EARTH_STAFF_1407, Items.EARTH_BATTLESTAFF_1399, 23), - FIRE(Items.MYSTIC_FIRE_STAFF_1401, Items.FIRE_BATTLESTAFF_1393, 24), - LAVA(Items.MYSTIC_LAVA_STAFF_3054, Items.LAVA_BATTLESTAFF_3053, 25), - MUD(Items.MYSTIC_MUD_STAFF_6563, Items.MUD_BATTLESTAFF_6562, 26); - - public final int enchanted; - public final int basic; - public final int child; - - private static final HashMap basicToEnchanted = new HashMap<>(); - private static final HashMap childToBasic = new HashMap<>(); - - static { - for (EnchantedStaff staff : EnchantedStaff.values()) { - basicToEnchanted.put(staff.basic, staff.enchanted); - childToBasic.put(staff.child, staff.basic); - } - } - - EnchantedStaff(int enchantedId, int basicId, int childId) { - this.enchanted = enchantedId; - this.basic = basicId; - this.child = childId; - } - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(332).setPlugin(this); - return this; - } - - @Override - public void open(Player player, Component component) { - super.open(player, component); - // zoom doesn't work, but based on https://youtu.be/qxxhhCdxBsQ?t=75 seems correct anyway - for (EnchantedStaff staff : EnchantedStaff.values()) { - player.getPacketDispatch().sendItemZoomOnInterface(staff.basic, 240, COMPONENT.getId(), staff.child); - } - } - - @Override - public boolean handle(Player player, Component component, int opcode, int buttonId, int slot, int itemId) { - player.getPacketDispatch().sendMessage("op: " + opcode + " button: " + buttonId + " slot: "+ slot + " item: " + itemId); - - if (EnchantedStaff.childToBasic.containsKey(buttonId)) { - Item basicStaff = new Item(EnchantedStaff.childToBasic.get(buttonId)); - Item enchantedStaff = new Item(EnchantedStaff.basicToEnchanted.get(basicStaff.getId())); - - if (!player.getInventory().containsItem(basicStaff)) { - player.getPacketDispatch().sendMessage("You don't have a" + (StringUtils.isPlusN(basicStaff.getName()) ? "n " : " ") + basicStaff.getName() + " to enchant."); - return true; - } - if (!player.getInventory().contains(995, cost)) { - player.getInterfaceManager().close(); - player.getDialogueInterpreter().sendDialogues(389, null, "I need 40,000 coins for materials. Come", "back when you have the money!"); - return true; - } - if (player.getInventory().remove(basicStaff, new Item(995, cost))) { - player.getInterfaceManager().close(); - player.getDialogueInterpreter().sendDialogues(389, null, "Just a moment... hang on... hocus pocus abra-", "cadabra... there you go! Enjoy your enchanted staff!"); - player.getInventory().add(enchantedStaff); - } - } - - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/PrayerTabInterface.java b/Server/src/main/content/global/handlers/iface/PrayerTabInterface.java deleted file mode 100644 index 2d0932e89..000000000 --- a/Server/src/main/content/global/handlers/iface/PrayerTabInterface.java +++ /dev/null @@ -1,38 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.plugin.Initializable; -import core.plugin.Plugin; - -import static core.api.ContentAPIKt.hasRequirement; - -/** - * Represents the prayer interface. - * @author 'Vexia - */ -@Initializable -public final class PrayerTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(271, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - final PrayerType type = PrayerType.get(button); - if (type == PrayerType.CHIVALRY || type == PrayerType.PIETY) - if (!hasRequirement(player, "King's Ransom")) - return true; - if (type == null) { - return true; - } - player.getPrayer().toggle(type); - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/QuestTabInterface.java b/Server/src/main/content/global/handlers/iface/QuestTabInterface.java deleted file mode 100644 index 90d580af2..000000000 --- a/Server/src/main/content/global/handlers/iface/QuestTabInterface.java +++ /dev/null @@ -1,71 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.AchievementDiary; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.player.link.quest.Quest; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the quest tab reward buttons. - * @author Emperor - * @author Vexia - */ -@Initializable -public class QuestTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(274, this); // Quests - ComponentDefinition.put(259, this); // Achievement diary - return this; - } - - @Override - public boolean handle(Player p, Component component, int opcode, int button, int slot, int itemId) { - p.getPulseManager().clear(); - switch (component.getId()) { - case 274: -// if (!GameWorld.isEconomyWorld()) { -// p.getSavedData().getSpawnData().handleButton(p, button); -// } - switch (button) { - case 3: - p.getAchievementDiaryManager().openTab(); - return true; - case 10: - break; - default: -// if (GameWorld.isEconomyWorld()) { - - Quest quest = p.getQuestRepository().forButtonId(button); - if (quest != null) { - p.getInterfaceManager().open(new Component(275)); - quest.drawJournal(p, quest.getStage(p)); - return true; - } else QuestTabUtils.showRequirementsInterface(p, button); -// } - return false; - } - break; - case 259: - switch (button) { - case 8: - p.getInterfaceManager().openTab(2, new Component(274)); - return true; - default: - AchievementDiary diary = p.getAchievementDiaryManager().getDiary(DiaryType.forChild(button)); - if (diary != null) { - diary.open(p); - } - return true; - } - } - return true; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/QuestTabUtils.kt b/Server/src/main/content/global/handlers/iface/QuestTabUtils.kt deleted file mode 100644 index d7482c089..000000000 --- a/Server/src/main/content/global/handlers/iface/QuestTabUtils.kt +++ /dev/null @@ -1,244 +0,0 @@ -package content.global.handlers.iface - -import core.game.requirement.* -import core.api.* -import core.tools.* -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills - -import kotlin.math.* -import java.util.* - -import org.rs09.consts.* - -object QuestTabUtils { - @JvmStatic - fun showRequirementsInterface (player: Player, button: Int) { - val questName = getNameForButton (button) - val questReq = QuestRequirements.values().filter { it.questName.equals(questName, true) }.firstOrNull() ?: return - var (isMet, unmetReqs) = QuestReq(questReq).evaluate(player) - - var messageList = ArrayList() - - val statMap = HashMap() - val questList = HashSet() - var maxQpReq = 0 - var qpPenalty = 0 - closeInterface(player) - for (req in unmetReqs) { - if (req is QuestReq) - questList.add(req.questReq.questName) - else if (req is SkillReq) { - if (statMap[req.skillId] == null || (statMap[req.skillId] != null && statMap[req.skillId]!! < req.level)) - statMap[req.skillId] = req.level - } - else if (req is QPReq && req.amount > maxQpReq) - maxQpReq = req.amount - else if (req is QPCumulative) - qpPenalty += req.amount - } - - messageList.add(colorize("%B[Quests Needed]")) - messageList.addAll(questList.map { "Completion of $it" }) - - messageList.add(" ") - messageList.add(colorize("%B[Skills Needed]")) - - for ((skillId, level) in statMap) { - val name = Skills.SKILL_NAME[skillId] - messageList.add("$level $name") - } - - messageList.add(" ") - messageList.add(colorize("%B[Other Reqs]")) - - val totalQpRequirement = QPReq(min(max(maxQpReq, qpPenalty), player.questRepository.getAvailablePoints())) - val (meetsQp, _) = totalQpRequirement.evaluate(player) - isMet = isMet && meetsQp - - if (isMet) - messageList.add(colorize("%GCongratulations! You've earned this one.")) - - if (!meetsQp) messageList.add("A total of ${totalQpRequirement.amount} Quest Points.") - - messageList.add("") - messageList.add(colorize("%BDISCLAIMER: If you're seeing this screen, this quest is not")) - messageList.add(colorize("%Bimplemented yet. These are the requirements that you need in order")) - messageList.add(colorize("%Bto access implemented content that would normally require this quest")) - messageList.add("") - messageList.add("If you want to see more quests enter the game, consider") - messageList.add("contributing dialogue transcripts!") - - - setInterfaceText(player, questName, Components.QUESTJOURNAL_SCROLL_275, 2) - var lineId = 11 - for(i in 0..299) { - val entry = messageList.elementAtOrNull(i) - if (entry != null) - setInterfaceText(player, entry, Components.QUESTJOURNAL_SCROLL_275, lineId++) - else - setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) - } - openInterface(player, Components.QUESTJOURNAL_SCROLL_275) - } - - fun getNameForButton (button: Int) : String { - val name = when (button) { - 10 -> "Myths of the White Lands" - 11 -> "Myths of the White Lands" - 12 -> "Free Quests" - 13 -> "Black Knights' Fortress" - 14 -> "Cook's Assistant" - 15 -> "Demon Slayer" - 16 -> "Doric's Quest" - 17 -> "Dragon Slayer" - 18 -> "Ernest the Chicken" - 19 -> "Goblin Diplomacy" - 20 -> "Imp Catcher" - 21 -> "The Knight's Sword" - 22 -> "Pirate's Treasure" - 23 -> "Prince Ali Rescue" - 24 -> "The Restless Ghost" - 25 -> "Romeo & Juliet" - 26 -> "Rune Mysteries" - 27 -> "Sheep Shearer" - 28 -> "Shield of Arrav" - 29 -> "Vampire Slayer" - 30 -> "Witch's Potion" - 31 -> "Members' Quests" - 32 -> "Animal Magnetism" - 33 -> "Between a Rock..." - 34 -> "Big Chompy Bird Hunting" - 35 -> "Biohazard" - 36 -> "Cabin Fever" - 37 -> "Clock Tower" - 38 -> "Contact!" - 39 -> "Zogre Flesh Eaters" - 40 -> "Creature of Fenkenstrain" - 41 -> "Darkness of Hallowvale" - 42 -> "Death to the Dorgeshuun" - 43 -> "Death Plateau" - 44 -> "Desert Treasure" - 45 -> "Devious Minds" - 46 -> "The Dig Site" - 47 -> "Druidic Ritual" - 48 -> "Dwarf Cannon" - 49 -> "Eadgar's Ruse" - 50 -> "Eagles' Peak" - 51 -> "Elemental Workshop I" - 52 -> "Elemental Workshop II" - 53 -> "Enakhra's Lament" - 54 -> "Enlightened Journey" - 55 -> "The Eyes of Glouphrie" - 56 -> "Fairytale I - Growing Pains" - 57 -> "Fairytale II - Cure a Queen" - 58 -> "Family Crest" - 59 -> "The Feud" - 60 -> "Fight Arena" - 61 -> "Fishing Contest" - 62 -> "Forgettable Tale..." - 63 -> "The Fremennik Trials" - 64 -> "Waterfall Quest" - 65 -> "Garden of Tranquillity" - 66 -> "Gertrude's Cat" - 67 -> "Ghosts Ahoy" - 68 -> "The Giant Dwarf" - 69 -> "The Golem" - 70 -> "The Grand Tree" - 71 -> "The Hand in the Sand" - 72 -> "Haunted Mine" - 73 -> "Hazeel Cult" - 74 -> "Heroes' Quest" - 75 -> "Holy Grail" - 76 -> "Horror from the Deep" - 77 -> "Icthlarin's Little Helper" - 78 -> "In Aid of the Myreque" - 79 -> "In Search of the Myreque" - 80 -> "Jungle Potion" - 81 -> "Legend's Quest" - 82 -> "Lost City" - 83 -> "The Lost Tribe" - 84 -> "Lunar Diplomacy" - 85 -> "Making History" - 86 -> "Merlin's Crystal" - 87 -> "Monkey Madness" - 88 -> "Monk's Friend" - 89 -> "Mountain Daughter" - 90 -> "Mourning's End Part I" - 91 -> "Mourning's End Part II" - 92 -> "Murder Mystery" - 93 -> "My Arm's Big Adventure" - 94 -> "Nature Spirit" - 95 -> "Observatory Quest" - 96 -> "One Small Favour" - 97 -> "Plague City" - 98 -> "Priest in Peril" - 99 -> "Rag and Bone Man" - 100 -> "Ratcatchers" - 101 -> "Recipe for Disaster" - 102 -> "Recruitment Drive" - 103 -> "Regicide" - 104 -> "Roving Elves" - 105 -> "Royal Trouble" - 106 -> "Rum Deal" - 107 -> "Scorpion Catcher" - 108 -> "Sea Slug" - 109 -> "The Slug Menace" - 110 -> "Shades of Mort'ton" - 111 -> "Shadow of the Storm" - 112 -> "Sheep Herder" - 113 -> "Shilo Village" - 114 -> "A Soul's Bane" - 115 -> "Spirits of the Elid" - 116 -> "Swan Song" - 117 -> "Tai Bwo Wannai Trio" - 118 -> "A Tail of Two Cats" - 119 -> "Tears of Guthix" - 120 -> "Temple of Ikov" - 121 -> "Throne of Miscellania" - 122 -> "The Tourist Trap" - 123 -> "Witch's House" - 124 -> "Tree Gnome Village" - 125 -> "Tribal Totem" - 126 -> "Troll Romance" - 127 -> "Troll Stronghold" - 128 -> "Underground Pass" - 129 -> "Wanted!" - 130 -> "Watchtower" - 131 -> "Cold War" - 132 -> "The Fremennik Isles" - 133 -> "Tower of Life" - 134 -> "The Great Brain Robbery" - 135 -> "What Lies Below" - 136 -> "Olaf's Quest" - 137 -> "Another Slice of H.A.M" - 138 -> "Dream Mentor" - 139 -> "Grim Tales" - 140 -> "King's Ransom" - 141 -> "The Path of Glouphrie" - 142 -> "Back to my Roots" - 143 -> "Land of the Goblins" - 144 -> "Dealing with Scabaras" - 145 -> "Wolf Whistle" - 146 -> "As a First Resort..." - 147 -> "Catapult Construction" - 148 -> "Kennith's Concerns" - 149 -> "Legacy of Seergaze" - 150 -> "Perils of Ice Mountain" - 151 -> "TokTz-Ket-Dill" - 152 -> "Smoking Kills" - 153 -> "Rocking Out" - 154 -> "Spirit of Summer" - 155 -> "Meeting History" - 156 -> "All Fired Up" - 157 -> "Summer's End" - 158 -> "Defender of Varrock" - 159 -> "Swept Away" - 160 -> "While Guthix Sleeps" - 161 -> "In Pyre Need" - 162 -> "Myths of the White Lands" - else -> "" - } - return name - } -} diff --git a/Server/src/main/content/global/handlers/iface/SettingTabInterface.java b/Server/src/main/content/global/handlers/iface/SettingTabInterface.java deleted file mode 100644 index c8bac8cb7..000000000 --- a/Server/src/main/content/global/handlers/iface/SettingTabInterface.java +++ /dev/null @@ -1,89 +0,0 @@ -package content.global.handlers.iface; - -import static core.api.ContentAPIKt.*; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; - -/** - * @author 'Vexia - */ -@Initializable -public class SettingTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(261, this); - return this; - } - - @Override - public boolean handle(Player p, Component component, int opcode, int button, int slot, int itemId) { - switch (button) { - case 10:// brightness - int brightness = button - 7; - p.getSettings().setBrightness(brightness); - break; - case 11: - case 12: - case 13: - case 14: - case 15:// music - int volume = 15 - button; - p.getSettings().setMusicVolume(volume); - break; - case 16://530 settings - /* if (TutorialSession.getExtension(p).getStage() != TutorialSession.MAX_STAGE) { - p.sendMessage("You must finish the tutorial before opening the graphic settings."); - break; - }*/ - p.getInterfaceManager().open(new Component(742)); - break; - case 18://530 settings - p.getInterfaceManager().open(new Component(743)); - break; - case 17: - case 19: - case 20:// sonund - int volume1 = 20 - button; - p.getSettings().setSoundEffectVolume(volume1); - break; - case 29: - case 30: - case 31: - case 32: - case 33:// all sound - int volume11 = 33 - button; - p.getSettings().setAreaSoundVolume(volume11); - break; - case 6:// mouse - p.getSettings().toggleMouseButton(); - break; - case 4:// effects - p.getSettings().toggleChatEffects(); - break; - case 5:// private chat - p.getSettings().toggleSplitPrivateChat(); - break; - case 7:// aid - if (p.getIronmanManager().checkRestriction()) { - return true; - } - p.getSettings().toggleAcceptAid(); - break; - case 3:// run - p.getSettings().toggleRun(); - break; - case 8:// house - p.getInterfaceManager().close(); - setVarp(p, 261, getVarp(p, 261) & 0x1); - p.getInterfaceManager().openSingleTab(new Component(398)); - break; - } - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/SkillInterface.java b/Server/src/main/content/global/handlers/iface/SkillInterface.java deleted file mode 100644 index a0ac47d21..000000000 --- a/Server/src/main/content/global/handlers/iface/SkillInterface.java +++ /dev/null @@ -1,32 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -import static core.api.ContentAPIKt.*; - -/** - * Represents the plugin used for the skilling interface. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SkillInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(499, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - setVarbit(player, 3288, player.getAttribute("skillMenu", -1)); - setVarbit(player, 3289, button - 10); - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/SkillTabInterface.java b/Server/src/main/content/global/handlers/iface/SkillTabInterface.java deleted file mode 100644 index 9dba5877b..000000000 --- a/Server/src/main/content/global/handlers/iface/SkillTabInterface.java +++ /dev/null @@ -1,162 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.skill.LevelUp; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.world.GameWorld; -import core.plugin.Initializable; -import core.plugin.Plugin; - -import static core.api.ContentAPIKt.*; - -/** - * Represents the plugin used to handle the skilling tab. - * @author Vexia - * @author Splinter - * @version 1.1 - */ -@Initializable -public final class SkillTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(320, this); - return this; - } - - @Override - public boolean handle(Player p, Component component, int opcode, int button, int slot, int itemId) { - final SkillConfig config = SkillConfig.forId(button); - if (config == null) { - return true; - } - if (!GameWorld.getSettings().isPvp()) { - if (p.getAttribute("levelup:" + config.getSkillId(), false)) { - p.removeAttribute("levelup:" + config.getSkillId()); - LevelUp.sendFlashingIcons(p, -1); - setVarp(p, 1230, ADVANCE_CONFIGS[config.getSkillId()]); - p.getInterfaceManager().open(new Component(741)); - } else { - p.getPulseManager().clear(); - p.getInterfaceManager().open(new Component(499)); - setVarp(p, 965, config.getConfig()); - p.getAttributes().put("skillMenu", config.getConfig()); - } - } else { - if (config.getSkillId() > 6) { - p.getPacketDispatch().sendMessage("You cannot set a target level for this skill."); - return false; - } - if (p.canSpawn()) { - p.sendMessage("You must be inside Edgeville bank to set levels."); - return false; - } - } - return true; - } - - /** - * Holds all the config values of the skill advances. - */ - public static final int[] ADVANCE_CONFIGS = { - 9, 40, 17, 49, - 25, 57, 33, 641, - 659, 664, 121, 649, - 89, 114, 107, 72, - 64, 80, 673, 680, - 99, 698, 689, 705, - }; - - public enum SkillConfig { - ATTACK(125, 1, Skills.ATTACK), - STRENGTH(126, 2, Skills.STRENGTH), - DEFENCE(127, 5, Skills.DEFENCE), - RANGE(128, 3, Skills.RANGE), - PRAYER(129, 7, Skills.PRAYER), - MAGIC(130, 4, Skills.MAGIC), - RUNECRAFTING(131, 12, Skills.RUNECRAFTING), - HITPOINTS(133, 6, Skills.HITPOINTS), - AGILITY(134, 8, Skills.AGILITY), - HERBLORE(135, 9, Skills.HERBLORE), - THIEVING(136, 10, Skills.THIEVING), - CRAFTING(137, 11, Skills.CRAFTING), - FLETCHING(138, 19, Skills.FLETCHING), - SLAYER(139, 20, Skills.SLAYER), - MINING(141, 13, Skills.MINING), - SMITHING(142, 14, Skills.SMITHING), - FISHING(143, 15, Skills.FISHING), - COOKING(144, 16, Skills.COOKING), - FIREMAKING(145, 17, Skills.FIREMAKING), - WOODCUTTING(146, 18, Skills.WOODCUTTING), - FARMING(147, 21, Skills.FARMING), - CONSTRUCTION(132, 22, Skills.CONSTRUCTION), - HUNTER(140, 23, Skills.HUNTER), - SUMMONING(148, 24, Skills.SUMMONING); - - /** - * Constructs a new {@code SkillConfig} {@code Object}. - * @param button the button. - * @param config the config. - */ - SkillConfig(int button, int config, int skillId) { - this.button = button; - this.config = config; - this.skillId = skillId; - } - - /** - * Represents the button. - */ - private int button; - - /** - * Represents the config. - */ - private int config; - - /** - * The skill id. - */ - private int skillId; - - /** - * Gets the skill config. - * @param id the id. - * @return the skill config. - */ - public static SkillConfig forId(int id) { - for (SkillConfig config : SkillConfig.values()) { - if (config.button == id) - return config; - } - return null; - } - - /** - * Gets the button. - * @return the button. - */ - public int getButton() { - return button; - } - - /** - * Gets the config. - * @return the config. - */ - public int getConfig() { - return config; - } - - /** - * Gets the skill id. - * @return The skill id. - */ - public int getSkillId() { - return skillId; - } - } -} diff --git a/Server/src/main/content/global/handlers/iface/bank/BankInterface.kt b/Server/src/main/content/global/handlers/iface/bank/BankInterface.kt index 49d613f43..d6d0bd1cc 100644 --- a/Server/src/main/content/global/handlers/iface/bank/BankInterface.kt +++ b/Server/src/main/content/global/handlers/iface/bank/BankInterface.kt @@ -1,15 +1,15 @@ package content.global.handlers.iface.bank +import content.global.dialogue.BankDepositDialogue +import content.global.dialogue.BankHelpDialogue +import core.ServerConstants import core.api.* import core.game.component.Component import core.game.container.Container +import core.game.interaction.InterfaceListener import core.game.node.entity.player.Player import org.rs09.consts.Components import org.rs09.consts.Items -import core.ServerConstants -import content.global.dialogue.BankDepositDialogue -import content.global.dialogue.BankHelpDialogue -import core.game.interaction.InterfaceListener /** * Allows the user to interact with the Bank Interface. @@ -58,8 +58,6 @@ class BankInterface : InterfaceListener { } private fun onBankInterfaceOpen(player: Player, component: Component): Boolean { - player.bank.sendBankSpace() - val settings = IfaceSettingsBuilder() .enableAllOptions() .enableSlotSwitch() @@ -74,14 +72,12 @@ class BankInterface : InterfaceListener { ServerConstants.BANK_SIZE ) + resetSearch(player) return false } private fun handleTabInteraction(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { - if (getAttribute(player, "search", false)) { - player.bank.reopen() - } - + resetSearch(player) val clickedTabIndex = -((buttonID - 41) / 2) when (opcode) { @@ -102,36 +98,16 @@ class BankInterface : InterfaceListener { private fun handleBankMenu(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { val item = player.bank.get(slot) ?: return true + resetSearch(player) when (opcode) { - OP_AMOUNT_ONE -> runWorldTask { player.bank.takeItem(slot, 1) } - OP_AMOUNT_FIVE -> runWorldTask { player.bank.takeItem(slot, 5) } - OP_AMOUNT_TEN -> runWorldTask { player.bank.takeItem(slot, 10) } - OP_AMOUNT_LAST_X -> runWorldTask { - player.bank.takeItem( - slot, - player.bank.lastAmountX - ) - } - OP_AMOUNT_X -> runWorldTask { - BankUtils.transferX( - player, - slot, - true - ) - } - OP_AMOUNT_ALL -> runWorldTask { - player.bank.takeItem( - slot, - player.bank.getAmount(item) - ) - } - OP_AMOUNT_ALL_BUT_ONE -> runWorldTask { - player.bank.takeItem( - slot, - player.bank.getAmount(item) - 1 - ) - } + OP_AMOUNT_ONE -> player.bank.takeItem(slot, 1) + OP_AMOUNT_FIVE -> player.bank.takeItem(slot, 5) + OP_AMOUNT_TEN -> player.bank.takeItem(slot, 10) + OP_AMOUNT_LAST_X -> player.bank.takeItem(slot, player.bank.lastAmountX) + OP_AMOUNT_X -> BankUtils.transferX(player, slot, true) + OP_AMOUNT_ALL -> player.bank.takeItem(slot, player.bank.getAmount(item)) + OP_AMOUNT_ALL_BUT_ONE -> player.bank.takeItem(slot, player.bank.getAmount(item) - 1) OP_EXAMINE -> { var examineText = item.definition.examine val id = item.definition.id @@ -150,6 +126,7 @@ class BankInterface : InterfaceListener { private fun handleInventoryMenu(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { val item = player.inventory.get(slot) ?: return true + resetSearch(player) when (opcode) { OP_AMOUNT_ONE -> player.bank.addItem(slot, 1) @@ -211,4 +188,11 @@ class BankInterface : InterfaceListener { return false } + + private fun resetSearch (player: Player) + { + val lastTab = getAttribute(player, "bank:lasttab", 0) + player.bank.tabIndex = lastTab + setVarc(player, 190, 1) //re-enable "Search" right-click option on search button. + } } diff --git a/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java b/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java index 853808349..9b615b305 100644 --- a/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java +++ b/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java @@ -112,7 +112,7 @@ public class GrandExchangeInterface extends ComponentPlugin { GrandExchangeOffer offer; GrandExchangeRecords records = GrandExchangeRecords.getInstance(player); if (index > -1 && (offer = records.getOffer(records.getOfferRecords()[index])) != null) { - StockMarket.withdraw(player, offer, slot >> 1); + StockMarket.withdraw(player, offer, slot >> 1, opcode); } return true; } diff --git a/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt b/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt index dd1d345d3..eba0a2435 100644 --- a/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt +++ b/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt @@ -98,7 +98,7 @@ class StockMarket : InterfaceListener { 209,211 -> if (openedOffer == null){ SystemLogger.logGE("[WARN] Player tried to withdraw item with null openedOffer!") return@on false - } else withdraw(player, openedOffer, (button - 209) shr 1) + } else withdraw(player, openedOffer, (button - 209) shr 1, op) 190 -> confirmOffer(player, tempOffer, openedIndex).also { return@on true } 194 -> player.interfaceManager.openChatbox(Components.OBJDIALOG_389) 203 -> abortOffer(player, openedOffer) @@ -342,7 +342,7 @@ class StockMarket : InterfaceListener { } @JvmStatic - fun withdraw(player: Player, offer: GrandExchangeOffer, index: Int) + fun withdraw(player: Player, offer: GrandExchangeOffer, index: Int, op: Int) { val item = offer.withdraw[index] if(item == null) @@ -351,20 +351,32 @@ class StockMarket : InterfaceListener { return } - if(hasSpaceFor(player, item)) - { - addItem(player, item.id, item.amount) - } - else - { - val note = item.noteChange - if(note == -1 || !hasSpaceFor(player, Item(note, item.amount))) - { - playAudio(player, Sounds.GE_TRADE_ERROR_4039) - sendMessage(player, "You do not have enough room in your inventory.") - return + when (op) { + // withdraw notes + 155 -> { + val note = item.noteChange + if (note == -1) { + sendMessage(player, "This item cannot be noted") + return + } + if (hasSpaceFor(player, Item(note, item.amount))) { + addItem(player, note, item.amount) + } else { + playAudio(player, Sounds.GE_TRADE_ERROR_4039) + sendMessage(player, "You do not have enough room in your inventory.") + return + } + } + // withdraw items + 196 -> { + if (hasSpaceFor(player, item)) { + addItem(player, item.id, item.amount) + } else { + playAudio(player, Sounds.GE_TRADE_ERROR_4039) + sendMessage(player, "You do not have enough room in your inventory.") + return + } } - else addItem(player, note, item.amount) } offer.withdraw[index] = null diff --git a/Server/src/main/content/global/handlers/iface/tabs/ClanTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/ClanTabInterface.kt new file mode 100644 index 000000000..c46293f59 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/ClanTabInterface.kt @@ -0,0 +1,109 @@ +package content.global.handlers.iface.tabs + +import core.api.sendInputDialogue +import core.api.sendMessage +import core.game.interaction.InterfaceListener +import core.game.system.communication.ClanRank +import core.game.system.communication.ClanRepository +import core.net.amsc.MSPacketRepository +import core.net.amsc.WorldCommunicator +import core.tools.StringUtils +import org.rs09.consts.Components + +class ClanTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.CLANJOIN_589) { player, _, _, buttonID, _, _ -> + if (buttonID == 9) { + if (player.interfaceManager.opened != null) { + sendMessage(player, "Please close the interface you have open before using 'Clan Setup'") + } else { + ClanRepository.openSettings(player) + } + } + + if (buttonID == 14) { + player.communication.toggleLootshare(player) + } + return@on true + } + + on(Components.CLANSETUP_590) { player, _, opcode, buttonID, _, _ -> + val clan = ClanRepository.get(player.name, true) + + when (buttonID) { + 22 -> { + if (opcode == 155) { + sendInputDialogue(player, false, "Enter clan prefix:", ) { value -> + val clanName = StringUtils.formatDisplayName(value.toString()) + + if (WorldCommunicator.isEnabled()) { MSPacketRepository.sendClanRename(player, clanName) } + + if (clan.name == "Chat disabled") { + sendMessage(player, "Your clan channel has now been enabled!") + sendMessage(player, "Join your channel by clicking 'Join Chat' and typing: ${player.username}") + } + + clan.name = clanName + player.communication.clanName = clanName + clan.updateSettings(player) + clan.update() + } + } + + if (opcode == 196) { + clan.name = "Chat disabled" + player.communication.clanName = "" + if (WorldCommunicator.isEnabled()) { MSPacketRepository.sendClanRename(player, player.communication.clanName) } + clan.updateSettings(player) + clan.delete() + } + } + + 23 -> { + clan.joinRequirement = getRank(opcode) + player.communication.joinRequirement = clan.joinRequirement + MSPacketRepository.setClanSetting(player, 0, clan.joinRequirement) + } + + 24 -> { + clan.messageRequirement = getRank(opcode) + player.communication.messageRequirement = clan.messageRequirement + MSPacketRepository.setClanSetting(player, 1, clan.messageRequirement) + } + + 25 -> { + clan.kickRequirement = getRank(opcode) + player.communication.kickRequirement = clan.kickRequirement + MSPacketRepository.setClanSetting(player, 2, clan.kickRequirement) + } + + 26 -> { + clan.lootRequirement = if (opcode == 155) ClanRank.ADMINISTRATOR else getRank(opcode) + player.communication.lootRequirement = clan.lootRequirement + MSPacketRepository.setClanSetting(player, 3, clan.lootRequirement) + } + + 33 -> sendMessage(player, "CoinShare is not available.") + } + + clan.updateSettings(player) + clan.update() + return@on true + } + } + + fun getRank(opcode: Int): ClanRank { + return when (opcode) { + 155 -> ClanRank.NONE + 196 -> ClanRank.FRIEND + 124 -> ClanRank.RECRUIT + 199 -> ClanRank.CORPORAL + 234 -> ClanRank.SERGEANT + 168 -> ClanRank.LIEUTENANT + 166 -> ClanRank.CAPTAIN + 64 -> ClanRank.GENERAL + 53 -> ClanRank.OWNER + else -> ClanRank.NONE + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/CombatTabInterface.java b/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java similarity index 98% rename from Server/src/main/content/global/handlers/iface/CombatTabInterface.java rename to Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java index bd3938d5c..0ca513102 100644 --- a/Server/src/main/content/global/handlers/iface/CombatTabInterface.java +++ b/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java @@ -1,4 +1,4 @@ -package content.global.handlers.iface; +package content.global.handlers.iface.tabs; import core.game.component.Component; import core.game.component.ComponentDefinition; diff --git a/Server/src/main/content/global/handlers/iface/tabs/EmoteTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/EmoteTabInterface.kt new file mode 100644 index 000000000..db5efdd84 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/EmoteTabInterface.kt @@ -0,0 +1,14 @@ +package content.global.handlers.iface.tabs + +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.emote.Emotes +import org.rs09.consts.Components + +class EmoteTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.EMOTES_464) { player, _, _, buttonID, _, _ -> + Emotes.handle(player, buttonID) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/EquipmentTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/EquipmentTabInterface.kt new file mode 100644 index 000000000..e3dd69b0e --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/EquipmentTabInterface.kt @@ -0,0 +1,136 @@ +package content.global.handlers.iface.tabs + +import content.global.skill.summoning.familiar.BurdenBeast +import core.api.* + +import core.game.container.access.InterfaceContainer +import core.game.container.impl.EquipmentContainer +import core.game.global.action.EquipHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners +import core.game.interaction.InterfaceListener +import core.game.node.entity.combat.DeathTask +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.prayer.PrayerType +import core.tools.Log +import org.rs09.consts.Components +import org.rs09.consts.Items + +class EquipmentTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + onOpen(ITEMS_KEPT_ON_DEATH_102) { player, component -> + /** + * (Highlight white items are auto destroyed on death Enum#616 (Items kept on death interface) TODO: Parse server sided + * CS2 Script 118 - Items kept on death interface + * Credit Woahscam for figuring this all out. + * @arg_0 (Int): Zone type - 0 Default/1 Safe/2 POH/3 Castle Wars/4 Trouble Brewing/5 Barbarian Assault + * @arg_1 (Int): Amount of items kept on death - 0/1/3/4 + * @arg_2 (Object): Item kept on death - slot 0 item id + * @arg_3 (Object): Item kept on death - slot 1 item id + * @arg_4 (Object): Item kept on death - slot 2 item id + * @arg_5 (Object): Item kept on death - slot 3 item id + * @arg_6 (Int): Player is skulled - 0 Not Skulled/1 Skulled + * @arg_7 (Int): Player has BoB summoned with items - 0 BoB not summoned or has no items /1 BoB summoned with items + * @arg_8 (String): String to append based on amount of items kept on death. + */ + val zoneType = player.zoneMonitor.type + val itemsKeptOnDeath = DeathTask.getContainers(player)[0] + val amountKeptOnDeath = if (!player.skullManager.isSkulled && itemsKeptOnDeath.itemCount() < 3) { + if (player.prayer[PrayerType.PROTECT_ITEMS]) 4 else 3 + } else { + itemsKeptOnDeath.itemCount() + } + val slot0 = itemsKeptOnDeath.getId(0) + val slot1 = itemsKeptOnDeath.getId(1) + val slot2 = itemsKeptOnDeath.getId(2) + val slot3 = itemsKeptOnDeath.getId(3) + val hasSkull = if (player.skullManager.isSkulled) 1 else 0 + val beast: BurdenBeast? = if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar.isBurdenBeast) player.familiarManager.familiar as BurdenBeast else null + val hasBob = if (beast != null && !beast.container.isEmpty) 1 else 0 + val message = "You are skulled." + val cs2Args = arrayOf(hasBob, hasSkull, slot3, slot2, slot1, slot0, amountKeptOnDeath, zoneType, message) + + if (amountKeptOnDeath > 4 && zoneType == 0) { + log(this::class.java, Log.ERR, "Items kept on death interface should not contain more than 4 items when not in a safe zone!") + } + + player.packetDispatch.sendRunScript(118, "siiooooii", *cs2Args) + + val settings = IfaceSettingsBuilder().enableAllOptions().build() + player.packetDispatch.sendIfaceSettings(settings, 18, component.id, 0, itemsKeptOnDeath.itemCount()) + player.packetDispatch.sendIfaceSettings(settings, 21, component.id, 0, DeathTask.getContainers(player)[1].itemCount()) + return@onOpen true + } + + on(Components.WORNITEMS_387) { player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + 28 -> { + if (opcode == 81) EquipHandler.unequip(player, slot, itemID) + if (opcode == 206) operateItem(player, slot) + } + 52 -> openInterface(player, ITEMS_KEPT_ON_DEATH_102) + 55 -> openInterface(player, Components.EQUIP_SCREEN2_667) + } + return@on true + } + + onOpen(Components.EQUIP_SCREEN2_667) { player, component -> + val settings = IfaceSettingsBuilder().enableAllOptions().build() + player.packetDispatch.sendIfaceSettings(settings, 14, component.id, 0, 13) + EquipmentContainer.update(player) + openSingleTab(player, Components.INVENTORY_WEAR2_670) + return@onOpen true + } + + on(Components.EQUIP_SCREEN2_667) { player, _, opcode, buttonID, slot, itemID -> + if (buttonID == 14) { + when (opcode) { + 9 -> sendMessage(player, player.equipment.get(slot).definition.examine) + 155 -> EquipHandler.unequip(player, slot, itemID) + 196 -> operateItem(player, slot) + } + } + return@on true + } + + onClose(Components.EQUIP_SCREEN2_667) { player, _ -> + closeTabInterface(player) + return@onClose true + } + + onOpen(Components.INVENTORY_WEAR2_670) { player, component -> + InterfaceContainer.generateItems(player, player.inventory.toArray(), arrayOf("Equip"), component.id, 0, 7, 4, 93) + return@onOpen true + } + + on(Components.INVENTORY_WEAR2_670) { player, _, opcode, _, slot, _ -> + if (opcode == 9) sendMessage(player, player.inventory.get(slot).definition.examine) + if (opcode == 155) equipItem(player, slot) + return@on true + } + } + + private fun equipItem(player: Player, slot: Int) { + val item = player.inventory.get(slot) ?: return + + if (item.definition.options.any { it in arrayOf("Equip", "Wield", "Wear") } || item.id == Items.BEER_1917) { + InteractionListeners.run(item.id, IntType.ITEM, "equip", player, item) + } else { + sendMessage(player, "You can't wear that.") + } + } + + private fun operateItem(player: Player, slot: Int) { + val item = player.equipment.get(slot) ?: return + + when { + InteractionListeners.run(item.id, IntType.ITEM, "operate", player, item) -> return + item.operateHandler?.handle(player, item, "operate") == true -> return + else -> sendMessage(player, "You can't operate that.") + } + } + + companion object { + private const val ITEMS_KEPT_ON_DEATH_102 = 102 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/LogoutTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/LogoutTabInterface.kt new file mode 100644 index 000000000..f40ccd4a6 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/LogoutTabInterface.kt @@ -0,0 +1,34 @@ +package content.global.handlers.iface.tabs + +import core.api.sendMessage +import core.game.interaction.InterfaceListener +import core.game.world.repository.Repository +import org.rs09.consts.Components + +class LogoutTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.LOGOUT_182) { player, _, _, buttonID, _, _ -> + if (buttonID == 6) { + return@on when { + !player.zoneMonitor.canLogout() -> true + + player.inCombat() -> { + sendMessage(player, "You can't log out until 10 seconds after the end of combat.") + true + } + + player.isTeleporting -> { + sendMessage(player, "Please finish your teleport before logging out.") + true + } + + else -> { + Repository.disconnectionQueue.add(player) + true + } + } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/MagicTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/MagicTabInterface.kt new file mode 100644 index 000000000..5cdd88867 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/MagicTabInterface.kt @@ -0,0 +1,23 @@ +package content.global.handlers.iface.tabs + +import content.global.skill.magic.SpellListener +import content.global.skill.magic.SpellListeners +import core.api.getAttribute +import core.game.interaction.InterfaceListener +import core.game.node.entity.combat.spell.MagicSpell +import core.game.node.entity.player.link.SpellBookManager.SpellBook +import core.game.world.GameWorld + +class MagicTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + SpellBook.values().forEach { + on(it.interfaceId) { player, _, _, buttonID, _, _ -> + if (GameWorld.ticks < getAttribute(player, "magic:delay", -1)) return@on true + + SpellListeners.run(buttonID, SpellListener.NONE, it.name.lowercase(), player) + + return@on MagicSpell.castSpell(player, it, buttonID, player) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/MusicTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/MusicTabInterface.kt new file mode 100644 index 000000000..76643299c --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/MusicTabInterface.kt @@ -0,0 +1,38 @@ +package content.global.handlers.iface.tabs + +import core.api.sendMessage +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.music.MusicEntry +import org.rs09.consts.Components + +class MusicTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.MUSIC_V3_187) { player, _, opcode, buttonID, slot, _ -> + if (opcode == 155) { + if (buttonID == 11) { + player.musicPlayer.toggleLooping() + return@on true + } + + if (buttonID == 1) { + if (player.musicPlayer.unlocked[slot] != null) { + player.musicPlayer.play(player.musicPlayer.unlocked[slot]) + return@on true + } + + if (player.isAdmin) { + for (entry in MusicEntry.getSongs().values) { + if (entry.index == slot) { + player.musicPlayer.unlock(entry.id) + } + } + } else { + sendMessage(player, "You have not unlocked this piece of music yet!") + } + return@on true + } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/PrayerTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/PrayerTabInterface.kt new file mode 100644 index 000000000..684001eaa --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/PrayerTabInterface.kt @@ -0,0 +1,14 @@ +package content.global.handlers.iface.tabs + +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.prayer.PrayerType +import org.rs09.consts.Components + +class PrayerTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.PRAYER_271) { player, _, _, buttonID, _, _ -> + val prayer = PrayerType.get(buttonID) ?: return@on true + return@on player.prayer.toggle(prayer) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/QuestTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/QuestTabInterface.kt new file mode 100644 index 000000000..2a9703946 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/QuestTabInterface.kt @@ -0,0 +1,36 @@ +package content.global.handlers.iface.tabs + +import content.global.handlers.iface.tabs.QuestTabUtils.showRequirementsInterface +import core.api.openInterface +import core.game.component.Component +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.diary.DiaryType +import org.rs09.consts.Components + +class QuestTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.QUESTJOURNAL_V2_274) { player, _, _, buttonID, _, _ -> + if (buttonID == 3) { + player.achievementDiaryManager.openTab() + } else { + val quest = player.questRepository.forButtonId(buttonID) + if (quest != null) { + openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + quest.drawJournal(player, quest.getStage(player)) + } else { + showRequirementsInterface(player, buttonID) + } + } + return@on true + } + + on(Components.AREA_TASK_259) { player, _, _, buttonID, _, _ -> + if (buttonID == 8) { + player.interfaceManager.openTab(2, Component(Components.QUESTJOURNAL_V2_274)) + } else { + player.achievementDiaryManager.getDiary(DiaryType.forChild(buttonID))?.open(player) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/QuestTabUtils.kt b/Server/src/main/content/global/handlers/iface/tabs/QuestTabUtils.kt new file mode 100644 index 000000000..40e410348 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/QuestTabUtils.kt @@ -0,0 +1,245 @@ +package content.global.handlers.iface.tabs + +import core.game.requirement.* +import core.api.* +import core.tools.* +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills + +import kotlin.math.* +import java.util.* + +import org.rs09.consts.* +import content.data.Quests + +object QuestTabUtils { + @JvmStatic + fun showRequirementsInterface(player: Player, button: Int) { + val questName = getNameForButton(button) + val questReq = QuestRequirements.values().filter { it.quest.questName == questName }.firstOrNull() ?: return + var (isMet, unmetReqs) = QuestReq(questReq).evaluate(player) + + var messageList = ArrayList() + + val statMap = HashMap() + val questList = HashSet() + var maxQpReq = 0 + var qpPenalty = 0 + closeInterface(player) + for (req in unmetReqs) { + if (req is QuestReq) + questList.add(req.questReq.quest.questName) + else if (req is SkillReq) { + if (statMap[req.skillId] == null || (statMap[req.skillId] != null && statMap[req.skillId]!! < req.level)) + statMap[req.skillId] = req.level + } + else if (req is QPReq && req.amount > maxQpReq) + maxQpReq = req.amount + else if (req is QPCumulative) + qpPenalty += req.amount + } + + messageList.add(colorize("%B[Quests Needed]")) + messageList.addAll(questList.map { "Completion of $it" }) + + messageList.add(" ") + messageList.add(colorize("%B[Skills Needed]")) + + for ((skillId, level) in statMap) { + val name = Skills.SKILL_NAME[skillId] + messageList.add("$level $name") + } + + messageList.add(" ") + messageList.add(colorize("%B[Other Reqs]")) + + val totalQpRequirement = QPReq(min(max(maxQpReq, qpPenalty), player.questRepository.getAvailablePoints())) + val (meetsQp, _) = totalQpRequirement.evaluate(player) + isMet = isMet && meetsQp + + if (isMet) + messageList.add(colorize("%GCongratulations! You've earned this one.")) + + if (!meetsQp) messageList.add("A total of ${totalQpRequirement.amount} Quest Points.") + + messageList.add("") + messageList.add(colorize("%BDISCLAIMER: If you're seeing this screen, this quest is not")) + messageList.add(colorize("%Bimplemented yet. These are the requirements that you need in order")) + messageList.add(colorize("%Bto access implemented content that would normally require this quest")) + messageList.add("") + messageList.add("If you want to see more quests enter the game, consider") + messageList.add("contributing dialogue transcripts!") + + + setInterfaceText(player, questName, Components.QUESTJOURNAL_SCROLL_275, 2) + var lineId = 11 + for(i in 0..299) { + val entry = messageList.elementAtOrNull(i) + if (entry != null) + setInterfaceText(player, entry, Components.QUESTJOURNAL_SCROLL_275, lineId++) + else + setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + } + openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + } + + private fun getNameForButton(button: Int) : String { + val quest = when (button) { + 10 -> Quests.MYTHS_OF_THE_WHITE_LANDS.questName + 11 -> Quests.MYTHS_OF_THE_WHITE_LANDS.questName + 12 -> "Free Quests" + 13 -> Quests.BLACK_KNIGHTS_FORTRESS.questName + 14 -> Quests.COOKS_ASSISTANT.questName + 15 -> Quests.DEMON_SLAYER.questName + 16 -> Quests.DORICS_QUEST.questName + 17 -> Quests.DRAGON_SLAYER.questName + 18 -> Quests.ERNEST_THE_CHICKEN.questName + 19 -> Quests.GOBLIN_DIPLOMACY.questName + 20 -> Quests.IMP_CATCHER.questName + 21 -> Quests.THE_KNIGHTS_SWORD.questName + 22 -> Quests.PIRATES_TREASURE.questName + 23 -> Quests.PRINCE_ALI_RESCUE.questName + 24 -> Quests.THE_RESTLESS_GHOST.questName + 25 -> Quests.ROMEO_JULIET.questName + 26 -> Quests.RUNE_MYSTERIES.questName + 27 -> Quests.SHEEP_SHEARER.questName + 28 -> Quests.SHIELD_OF_ARRAV.questName + 29 -> Quests.VAMPIRE_SLAYER.questName + 30 -> Quests.WITCHS_POTION.questName + 31 -> "Members' Quests" + 32 -> Quests.ANIMAL_MAGNETISM.questName + 33 -> Quests.BETWEEN_A_ROCK.questName + 34 -> Quests.BIG_CHOMPY_BIRD_HUNTING.questName + 35 -> Quests.BIOHAZARD.questName + 36 -> Quests.CABIN_FEVER.questName + 37 -> Quests.CLOCK_TOWER.questName + 38 -> Quests.CONTACT.questName + 39 -> Quests.ZOGRE_FLESH_EATERS.questName + 40 -> Quests.CREATURE_OF_FENKENSTRAIN.questName + 41 -> Quests.DARKNESS_OF_HALLOWVALE.questName + 42 -> Quests.DEATH_TO_THE_DORGESHUUN.questName + 43 -> Quests.DEATH_PLATEAU.questName + 44 -> Quests.DESERT_TREASURE.questName + 45 -> Quests.DEVIOUS_MINDS.questName + 46 -> Quests.THE_DIG_SITE.questName + 47 -> Quests.DRUIDIC_RITUAL.questName + 48 -> Quests.DWARF_CANNON.questName + 49 -> Quests.EADGARS_RUSE.questName + 50 -> Quests.EAGLES_PEAK.questName + 51 -> Quests.ELEMENTAL_WORKSHOP_I.questName + 52 -> Quests.ELEMENTAL_WORKSHOP_II.questName + 53 -> Quests.ENAKHRAS_LAMENT.questName + 54 -> Quests.ENLIGHTENED_JOURNEY.questName + 55 -> Quests.THE_EYES_OF_GLOUPHRIE.questName + 56 -> Quests.FAIRYTALE_I_GROWING_PAINS.questName + 57 -> Quests.FAIRYTALE_II_CURE_A_QUEEN.questName + 58 -> Quests.FAMILY_CREST.questName + 59 -> Quests.THE_FEUD.questName + 60 -> Quests.FIGHT_ARENA.questName + 61 -> Quests.FISHING_CONTEST.questName + 62 -> Quests.FORGETTABLE_TALE.questName + 63 -> Quests.THE_FREMENNIK_TRIALS.questName + 64 -> Quests.WATERFALL_QUEST.questName + 65 -> Quests.GARDEN_OF_TRANQUILITY.questName + 66 -> Quests.GERTRUDES_CAT.questName + 67 -> Quests.GHOSTS_AHOY.questName + 68 -> Quests.THE_GIANT_DWARF.questName + 69 -> Quests.THE_GOLEM.questName + 70 -> Quests.THE_GRAND_TREE.questName + 71 -> Quests.THE_HAND_IN_THE_SAND.questName + 72 -> Quests.HAUNTED_MINE.questName + 73 -> Quests.HAZEEL_CULT.questName + 74 -> Quests.HEROES_QUEST.questName + 75 -> Quests.HOLY_GRAIL.questName + 76 -> Quests.HORROR_FROM_THE_DEEP.questName + 77 -> Quests.ICTHLARINS_LITTLE_HELPER.questName + 78 -> Quests.IN_AID_OF_THE_MYREQUE.questName + 79 -> Quests.IN_SEARCH_OF_THE_MYREQUE.questName + 80 -> Quests.JUNGLE_POTION.questName + 81 -> Quests.LEGENDS_QUEST.questName + 82 -> Quests.LOST_CITY.questName + 83 -> Quests.THE_LOST_TRIBE.questName + 84 -> Quests.LUNAR_DIPLOMACY.questName + 85 -> Quests.MAKING_HISTORY.questName + 86 -> Quests.MERLINS_CRYSTAL.questName + 87 -> Quests.MONKEY_MADNESS.questName + 88 -> Quests.MONKS_FRIEND.questName + 89 -> Quests.MOUNTAIN_DAUGHTER.questName + 90 -> Quests.MOURNINGS_END_PART_I.questName + 91 -> Quests.MOURNINGS_END_PART_II.questName + 92 -> Quests.MURDER_MYSTERY.questName + 93 -> Quests.MY_ARMS_BIG_ADVENTURE.questName + 94 -> Quests.NATURE_SPIRIT.questName + 95 -> Quests.OBSERVATORY_QUEST.questName + 96 -> Quests.ONE_SMALL_FAVOUR.questName + 97 -> Quests.PLAGUE_CITY.questName + 98 -> Quests.PRIEST_IN_PERIL.questName + 99 -> Quests.RAG_AND_BONE_MAN.questName + 100 -> Quests.RATCATCHERS.questName + 101 -> Quests.RECIPE_FOR_DISASTER.questName + 102 -> Quests.RECRUITMENT_DRIVE.questName + 103 -> Quests.REGICIDE.questName + 104 -> Quests.ROVING_ELVES.questName + 105 -> Quests.ROYAL_TROUBLE.questName + 106 -> Quests.RUM_DEAL.questName + 107 -> Quests.SCORPION_CATCHER.questName + 108 -> Quests.SEA_SLUG.questName + 109 -> Quests.THE_SLUG_MENACE.questName + 110 -> Quests.SHADES_OF_MORTTON.questName + 111 -> Quests.SHADOW_OF_THE_STORM.questName + 112 -> Quests.SHEEP_HERDER.questName + 113 -> Quests.SHILO_VILLAGE.questName + 114 -> Quests.A_SOULS_BANE.questName + 115 -> Quests.SPIRITS_OF_THE_ELID.questName + 116 -> Quests.SWAN_SONG.questName + 117 -> Quests.TAI_BWO_WANNAI_TRIO.questName + 118 -> Quests.A_TAIL_OF_TWO_CATS.questName + 119 -> Quests.TEARS_OF_GUTHIX.questName + 120 -> Quests.TEMPLE_OF_IKOV.questName + 121 -> Quests.THRONE_OF_MISCELLANIA.questName + 122 -> Quests.THE_TOURIST_TRAP.questName + 123 -> Quests.WITCHS_HOUSE.questName + 124 -> Quests.TREE_GNOME_VILLAGE.questName + 125 -> Quests.TRIBAL_TOTEM.questName + 126 -> Quests.TROLL_ROMANCE.questName + 127 -> Quests.TROLL_STRONGHOLD.questName + 128 -> Quests.UNDERGROUND_PASS.questName + 129 -> Quests.WANTED.questName + 130 -> Quests.WATCHTOWER.questName + 131 -> Quests.COLD_WAR.questName + 132 -> Quests.THE_FREMENNIK_ISLES.questName + 133 -> Quests.TOWER_OF_LIFE.questName + 134 -> Quests.THE_GREAT_BRAIN_ROBBERY.questName + 135 -> Quests.WHAT_LIES_BELOW.questName + 136 -> Quests.OLAFS_QUEST.questName + 137 -> Quests.ANOTHER_SLICE_OF_HAM.questName + 138 -> Quests.DREAM_MENTOR.questName + 139 -> Quests.GRIM_TALES.questName + 140 -> Quests.KINGS_RANSOM.questName + 141 -> Quests.THE_PATH_OF_GLOUPHRIE.questName + 142 -> Quests.BACK_TO_MY_ROOTS.questName + 143 -> Quests.LAND_OF_THE_GOBLINS.questName + 144 -> Quests.DEALING_WITH_SCABARAS.questName + 145 -> Quests.WOLF_WHISTLE.questName + 146 -> Quests.AS_A_FIRST_RESORT.questName + 147 -> Quests.CATAPULT_CONSTRUCTION.questName + 148 -> Quests.KENNITHS_CONCERNS.questName + 149 -> Quests.LEGACY_OF_SEERGAZE.questName + 150 -> Quests.PERILS_OF_ICE_MOUNTAIN.questName + 151 -> Quests.TOKTZ_KET_DILL.questName + 152 -> Quests.SMOKING_KILLS.questName + 153 -> Quests.ROCKING_OUT.questName + 154 -> Quests.SPIRIT_OF_SUMMER.questName + 155 -> Quests.MEETING_HISTORY.questName + 156 -> Quests.ALL_FIRED_UP.questName + 157 -> Quests.SUMMERS_END.questName + 158 -> Quests.DEFENDER_OF_VARROCK.questName + 159 -> Quests.SWEPT_AWAY.questName + 160 -> Quests.WHILE_GUTHIX_SLEEPS.questName + 161 -> Quests.IN_PYRE_NEED.questName + 162 -> Quests.MYTHS_OF_THE_WHITE_LANDS.questName + else -> "" + } + return quest as String + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/SettingsTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/SettingsTabInterface.kt new file mode 100644 index 000000000..29a1669e9 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/SettingsTabInterface.kt @@ -0,0 +1,35 @@ +package content.global.handlers.iface.tabs + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.IronmanMode +import org.rs09.consts.Components + +class SettingsTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.OPTIONS_261) { player, _, _, buttonID, _, _ -> + when (buttonID) { + RUN -> player.settings.toggleRun() + CHAT_EFFECTS -> player.settings.toggleChatEffects() + SPLIT_PM -> player.settings.toggleSplitPrivateChat() + MOUSE -> player.settings.toggleMouseButton() + AID -> restrictForIronman(player, IronmanMode.STANDARD) { player.settings.toggleAcceptAid() } + HOUSE -> openSingleTab(player, Components.POH_HOUSE_OPTIONS_398) + GRAPHICS -> openInterface(player, Components.GRAPHICS_OPTIONS_742) + AUDIO -> openInterface(player, Components.SOUND_OPTIONS_743) + } + return@on true + } + } + + companion object { + const val RUN = 3 + const val CHAT_EFFECTS = 4 + const val SPLIT_PM = 5 + const val MOUSE = 6 + const val AID = 7 + const val HOUSE = 8 + const val GRAPHICS = 16 + const val AUDIO = 18 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/StatsTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/StatsTabInterface.kt new file mode 100644 index 000000000..fbe1effc4 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/StatsTabInterface.kt @@ -0,0 +1,87 @@ +package content.global.handlers.iface.tabs + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.node.entity.skill.LevelUp +import core.game.node.entity.skill.Skills +import org.rs09.consts.Components + + +class StatsTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.STATS_320) { player, _, _, buttonID, _, _ -> + val config = skillMap[buttonID] ?: return@on true + + if (getAttribute(player, "levelup:${config.skillID}", false)) { + removeAttributes(player, "levelup:${config.skillID}") + LevelUp.sendFlashingIcons(player, -1) + setVarp(player, 1230, ADVANCE_CONFIGS[config.skillID]) + openInterface(player, 741) + } else { + openInterface(player, Components.SKILL_GUIDE_V2_499) + setVarp(player, 965, config.configID) + setAttribute(player, "skillMenu", config.configID) + } + return@on true + } + + on(Components.SKILL_GUIDE_V2_499) { player, _, _, buttonID, _, _ -> + setVarbit(player, 3288, getAttribute(player, "skillMenu", -1)) + setVarbit(player, 3289, buttonID - 10) + return@on true + } + + on(LEVEL_UP_INTERFACE_740) { player, component, opcode, buttonID, slot, itemID -> + if (buttonID == 3) { + closeInterface(player) + } + return@on true + } + } + + enum class SkillConfig(val buttonID: Int, val configID: Int, val skillID: Int) { + ATTACK(125, 1, Skills.ATTACK), + STRENGTH(126, 2, Skills.STRENGTH), + DEFENCE(127, 5, Skills.DEFENCE), + RANGE(128, 3, Skills.RANGE), + PRAYER(129, 7, Skills.PRAYER), + MAGIC(130, 4, Skills.MAGIC), + RUNECRAFT(131, 12, Skills.RUNECRAFTING), + HITPOINTS(133, 6, Skills.HITPOINTS), + AGILITY(134, 8, Skills.AGILITY), + HERBLORE(135, 9, Skills.HERBLORE), + THIEVING(136, 10, Skills.THIEVING), + CRAFTING(137, 11, Skills.CRAFTING), + FLETCHING(138, 19, Skills.FLETCHING), + SLAYER(139, 20, Skills.SLAYER), + MINING(141, 13, Skills.MINING), + SMITHING(142, 14, Skills.SMITHING), + FISHING(143, 15, Skills.FISHING), + COOKING(144, 16, Skills.COOKING), + FIREMAKING(145, 17, Skills.FIREMAKING), + WOODCUTTING(146, 18, Skills.WOODCUTTING), + FARMING(147, 21, Skills.FARMING), + CONSTRUCTION(132, 22, Skills.CONSTRUCTION), + HUNTER(140, 23, Skills.HUNTER), + SUMMONING(148, 24, Skills.SUMMONING), + } + + companion object { + private const val LEVEL_UP_INTERFACE_740 = 740 + val skillMap= HashMap() + val ADVANCE_CONFIGS = intArrayOf( + 9, 40, 17, 49, + 25, 57, 33, 641, + 659, 664, 121, 649, + 89, 114, 107, 72, + 64, 80, 673, 680, + 99, 698, 689, 705 + ) + + init { + for (skill in SkillConfig.values()) { + skillMap[skill.buttonID] = skill + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/EctophialListener.kt b/Server/src/main/content/global/handlers/item/EctophialListener.kt index 661d0d942..a3ab4d0ca 100644 --- a/Server/src/main/content/global/handlers/item/EctophialListener.kt +++ b/Server/src/main/content/global/handlers/item/EctophialListener.kt @@ -13,6 +13,7 @@ import core.game.world.update.flag.context.Graphics import org.rs09.consts.Items import org.rs09.consts.Scenery import org.rs09.consts.Sounds +import content.data.Quests @Suppress("unused") class EctophialListener : InteractionListener { @@ -24,8 +25,7 @@ class EctophialListener : InteractionListener { delayEntity(player, fillAnimation.duration) animate(player, fillAnimation) playAudio(player, Sounds.FILL_ECTOPLASM_1132) - if (removeItem(player, Items.ECTOPHIAL_4252)) { - addItem(player, Items.ECTOPHIAL_4251) + if (removeItem(player, Items.ECTOPHIAL_4252) && addItem(player, Items.ECTOPHIAL_4251)) { sendMessage(player, "You refill the ectophial from the Ectofuntus.") } } @@ -37,7 +37,7 @@ class EctophialListener : InteractionListener { } on(Items.ECTOPHIAL_4251, IntType.ITEM, "empty") { player, node -> - if (!hasRequirement(player, "Ghosts Ahoy")) + if (!hasRequirement(player, Quests.GHOSTS_AHOY)) return@on true if (player.isTeleBlocked) { @@ -45,28 +45,29 @@ class EctophialListener : InteractionListener { return@on true } delayEntity(player, 10) - queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - sendMessage(player, "You empty the ectoplasm onto the ground around your feet...") - playAudio(player, 4580) - visualize(player, emptyAnimation, emptyGraphics) - replaceSlot(player, node.asItem().slot, Item(Items.ECTOPHIAL_4252), Item(Items.ECTOPHIAL_4251)) - return@queueScript delayScript(player, emptyAnimation.duration) - } - 1 -> { - teleport(player, Location.create(3658, 3517, 0), TeleportType.ECTOPHIAL) - sendMessage(player, "...and the world changes around you.") - return@queueScript delayScript(player, 9) - } - 2 -> { - face(player, Location(3659, 3519, 0)) - refillEctophial(player) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) + closeAllInterfaces(player) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "You empty the ectoplasm onto the ground around your feet...") + playAudio(player, 4580) + visualize(player, emptyAnimation, emptyGraphics) + replaceSlot(player, node.asItem().slot, Item(Items.ECTOPHIAL_4252), Item(Items.ECTOPHIAL_4251)) + return@queueScript delayScript(player, emptyAnimation.duration) } + 1 -> { + teleport(player, Location.create(3658, 3517, 0), TeleportType.ECTOPHIAL) + sendMessage(player, "...and the world changes around you.") + return@queueScript delayScript(player, 9) + } + 2 -> { + face(player, Location(3659, 3519, 0)) + refillEctophial(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) } + } return@on true } } diff --git a/Server/src/main/content/global/handlers/item/EmptyOptionListener.kt b/Server/src/main/content/global/handlers/item/EmptyOptionListener.kt index 12771ec0b..89dd5caae 100644 --- a/Server/src/main/content/global/handlers/item/EmptyOptionListener.kt +++ b/Server/src/main/content/global/handlers/item/EmptyOptionListener.kt @@ -17,7 +17,7 @@ class EmptyOptionListener : InteractionListener { override fun defineListeners() { on(EmptyItem.emptyItemList.toIntArray(), IntType.ITEM, "empty", "empty bowl", "empty dish") { player, node -> - if (node.name.contains("brew") || node.name.contains("potion") || node.name.lowercase(Locale.getDefault()).contains("poison") || node.name.lowercase(Locale.getDefault()).contains("serum") || node.name.contains("cure") || node.name.contains("mix") || node.name.contains("balm")) { + if (node.name.contains("brew") || node.name.contains("potion") || node.name.lowercase(Locale.getDefault()).contains("poison") || node.name.lowercase(Locale.getDefault()).contains("serum") || node.name.contains("cure") || node.name.contains("mix") || node.name.contains("balm") || node.name.contains("Super ")) { replaceSlot(player, node.asItem().slot, Item(EmptyItem.getEmpty(Items.POTION_195)!!), node.asItem()) playAudio(player, EmptyItem.getEmptyAudio(Items.POTION_195)!!) return@on true @@ -48,6 +48,7 @@ class EmptyOptionListener : InteractionListener { 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), BURNT_CURRY(Items.BURNT_CURRY_2013, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401), @@ -70,7 +71,7 @@ class EmptyOptionListener : InteractionListener { emptyItemList.add(item.fullId) } for (item in ItemDefinition.getDefinitions().values) - if (item.name.contains("potion") || item.name.contains("brew") || item.name.contains("poison") || item.name.lowercase(Locale.getDefault()).contains("serum") || item.name.contains("cure") || item.name.contains("mix") || item.name.contains("balm")) { + if (item.name.contains("potion") || item.name.contains("brew") || item.name.contains("poison") || item.name.lowercase(Locale.getDefault()).contains("serum") || item.name.contains("cure") || item.name.contains("mix") || item.name.contains("balm") || item.name.contains("Super ")) { emptyItemList.add(item.id) } } diff --git a/Server/src/main/content/global/handlers/item/EnchantJewelleryTabListener.kt b/Server/src/main/content/global/handlers/item/EnchantJewelleryTabListener.kt index 8cea16326..c642372ff 100644 --- a/Server/src/main/content/global/handlers/item/EnchantJewelleryTabListener.kt +++ b/Server/src/main/content/global/handlers/item/EnchantJewelleryTabListener.kt @@ -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 -> - 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 - } - } + } + } } diff --git a/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt b/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt index 01d999e00..252dbe2ff 100644 --- a/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt +++ b/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt @@ -1,5 +1,6 @@ package content.global.handlers.item +import content.global.skill.slayer.SlayerUtils import core.api.* import content.global.skill.slayer.Tasks import org.rs09.consts.Items @@ -41,7 +42,8 @@ class EnchantedGemDialogue() : DialogueFile() { if(getSlayerTask(player!!) == Tasks.JAD) { npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You're currently assigned to kill TzTok-Jad!") } else { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You're currently assigned to kill ${getSlayerTaskName(player!!)}s; only ${getSlayerTaskKillsRemaining(player!!)} more to go.") + npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You're currently assigned to kill ${SlayerUtils.pluralise( + getSlayerTaskName(player!!))}; only ${getSlayerTaskKillsRemaining(player!!)} more to go.") } setVarp(player!!, 2502, getSlayerTaskFlags(player!!) shr 4) stage = 1 diff --git a/Server/src/main/content/global/handlers/item/EnchantedJewelleryListener.kt b/Server/src/main/content/global/handlers/item/EnchantedJewelleryListener.kt index 47947ec57..2878628ac 100644 --- a/Server/src/main/content/global/handlers/item/EnchantedJewelleryListener.kt +++ b/Server/src/main/content/global/handlers/item/EnchantedJewelleryListener.kt @@ -11,10 +11,11 @@ import content.data.EnchantedJewellery import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.tools.START_DIALOGUE +import org.rs09.consts.Items /** * Listener for enchanted jewellery options - * @author Ceikry & downthecrop + * @author Ceikry, downthecrop, Player Name */ class EnchantedJewelleryListener : InteractionListener { @@ -33,6 +34,10 @@ class EnchantedJewelleryListener : InteractionListener { private fun handle(player: Player, node: Node, isEquipped: Boolean) { player.pulseManager.clear(PulseType.STANDARD) val item = node.asItem() + if (item.id == Items.RING_OF_LIFE_2570) { + sendMessage(player, "You can't operate that.") + return + } val jewellery = EnchantedJewellery.idMap[item.id] if (jewellery != null) { if (jewellery.isLastItemIndex(jewellery.getItemIndex(item)) && !jewellery.isCrumble) { @@ -60,4 +65,4 @@ class EnchantedJewelleryListener : InteractionListener { } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt b/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt index aea9c4ba9..586967573 100644 --- a/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt +++ b/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt @@ -59,7 +59,7 @@ class ExplorersRingPlugin : InteractionListener { val item = player.inventory[slot] if (item == null) return@sendItemSelect - if (!ModernListeners().alchemize(player, item, false)) + if (!ModernListeners().alchemize(player, item, false, explorersRing = true)) return@sendItemSelect getStoreFile()[player.username.lowercase() + ":alchs"] = remaining - 1 } diff --git a/Server/src/main/content/global/handlers/item/GodBookListeners.kt b/Server/src/main/content/global/handlers/item/GodBookListeners.kt index 7e86ef581..764190e72 100644 --- a/Server/src/main/content/global/handlers/item/GodBookListeners.kt +++ b/Server/src/main/content/global/handlers/item/GodBookListeners.kt @@ -8,6 +8,9 @@ import org.rs09.consts.Items import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.node.Node +import core.game.node.entity.skill.Skills +import core.game.node.item.Item class GodBookListeners : InteractionListener { @@ -20,16 +23,42 @@ class GodBookListeners : InteractionListener { openDialogue(player, HOLY_DIALOGUE(BOOK.SARA)) return@on true } - on(GB_ZAMORAK, IntType.ITEM, "preach"){ player, _ -> openDialogue(player, HOLY_DIALOGUE(BOOK.ZAM)) return@on true } - on(GB_GUTHIX, IntType.ITEM, "preach"){ player, _ -> openDialogue(player, HOLY_DIALOGUE(BOOK.GUTHIX)) return@on true } + + fun bless(player: Player, node: Node, product: Int) { + if (player.skills.getStaticLevel(Skills.PRAYER) < 50) { + sendMessage(player, "You need a Prayer level of at least 50 in order to do this.") + } else if (player.skills.prayerPoints < 4) { + sendMessage(player, "You need at least 4 prayer points in order to do this.") + } else { + sendMessage(player, "You bless the ${node.asItem().name.lowercase()}.") + player.skills.decrementPrayerPoints(4.0) + replaceSlot(player, node.asItem().slot, Item(product), node.asItem()) + } + } + onUseWith(IntType.ITEM, GB_SARADOMIN, Items.UNBLESSED_SYMBOL_1716) { player, _, symbol -> + bless(player, symbol, Items.HOLY_SYMBOL_1718) + return@onUseWith true + } + onUseWith(IntType.ITEM, GB_ZAMORAK, Items.UNPOWERED_SYMBOL_1722) { player, _, symbol -> + bless(player, symbol, Items.UNHOLY_SYMBOL_1724) + return@onUseWith true + } + onUseWith(IntType.ITEM, GB_GUTHIX, Items.UNBLESSED_SYMBOL_1716) { player, _, symbol -> + bless(player, symbol, Items.HOLY_SYMBOL_1718) + return@onUseWith true + } + onUseWith(IntType.ITEM, GB_GUTHIX, Items.UNPOWERED_SYMBOL_1722) { player, _, symbol -> + bless(player, symbol, Items.UNHOLY_SYMBOL_1724) + return@onUseWith true + } } internal enum class BOOK { @@ -129,6 +158,5 @@ class GodBookListeners : InteractionListener { } }) } - } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/item/GodBookPlugin.java b/Server/src/main/content/global/handlers/item/GodBookPlugin.java index 6bdae37f1..9f955de41 100644 --- a/Server/src/main/content/global/handlers/item/GodBookPlugin.java +++ b/Server/src/main/content/global/handlers/item/GodBookPlugin.java @@ -27,7 +27,7 @@ public class GodBookPlugin extends OptionHandler { for (GodBook book : GodBook.values()) { book.getDamagedBook().getDefinition().getHandlers().put("option:check", this); } - ClassScanner.definePlugins(new PageHandler(), new GodBookItem(), new SymbolBlessHandler()); + ClassScanner.definePlugins(new PageHandler(), new GodBookItem()); return this; } @@ -51,77 +51,6 @@ public class GodBookPlugin extends OptionHandler { return true; } - /** - * Handles the blessing of a symbol with a god book. - * @author Vexia - */ - public class SymbolBlessHandler extends UseWithHandler { - - /** - * Constructs a new {@code SymbolBlessHandler} {@code Object} - */ - public SymbolBlessHandler() { - super(1716); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (GodBook book : GodBook.values()) { - addHandler(book.getBook().getId(), ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - Player player = event.getPlayer(); - GodBook book = GodBook.forItem(event.getUsedItem(), false); - if (book == null) { - return false; - } - final Item symbol = event.getUsedWith().asItem(); - if (player.getSkills().getLevel(Skills.PRAYER) < 50) { - player.sendMessage("You need a Prayer level of at least 50 in order to do this."); - return true; - } - if (player.getSkills().getPrayerPoints() < 4) { - player.sendMessage("You need at least 4 prayer points in order to do this."); - return true; - } - if (book == GodBook.BOOK_OF_BALANCE) { - player.getDialogueInterpreter().sendOptions("Select an Option", "Unholy symbol", "Holy symbol"); - player.getDialogueInterpreter().addAction(new DialogueAction() { - - @Override - public void handle(Player player, int buttonId) { - bless(player, symbol, buttonId == 1 ? GodBook.UNHOLY_BOOK : GodBook.HOLY_BOOK); - } - - }); - return true; - } - bless(player, symbol, book); - return true; - } - - /** - * Blesses a symbol. - * @param player the player. - * @param book the book. - */ - private void bless(Player player, Item symbol, GodBook book) { - if (!player.getInventory().containsItem(symbol)) { - return; - } - if (player.getInventory().get(symbol.getSlot()) == null) { - return; - } - player.getInventory().replace(book.getBlessItem()[0], symbol.getSlot()); - player.getSkills().decrementPrayerPoints(4); - } - - } - /** * A god book item. * @author Vexia diff --git a/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt b/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt index 087ce60e6..992d1009f 100644 --- a/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt +++ b/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt @@ -62,6 +62,7 @@ class GrandSeedPodHandler : InteractionListener { } if (opt == "squash") { + closeAllInterfaces(player) visualize(player, SQUASH_ANIM_BEGIN, SQUASH_GRAPHICS_BEGIN) delayEntity(player, 12) queueScript(player, 3, QueueStrength.SOFT) {stage: Int -> @@ -93,4 +94,4 @@ class GrandSeedPodHandler : InteractionListener { return@on true } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt b/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt index 4dd960bad..8acaa698a 100644 --- a/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt +++ b/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt @@ -4,6 +4,7 @@ import core.api.* import core.game.node.entity.player.link.quest.QuestRepository import org.rs09.consts.Items import core.game.interaction.InteractionListener +import content.data.Quests class ItemQuestRequirementListener : InteractionListener { @@ -106,7 +107,7 @@ class ItemQuestRequirementListener : InteractionListener { /* onEquip(fremennikIslesEquipment) { player, _ -> - if (!isQuestComplete(player, "Fremennik Isles")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_ISLES)) { sendMessage(player, "You must have completed The Fremennik Isles to equip this.") return@onEquip false } @@ -114,7 +115,7 @@ class ItemQuestRequirementListener : InteractionListener { } onEquip(fremennikIslesDuringQuestEquipment){ player, _ -> - if (questStage(player, "Fremennik Isles") > 0) { + if (questStage(player, Quests.THE_FREMENNIK_ISLES) > 0) { sendMessage(player, "You must have started The Fremennik Isles to equip this.") return@onEquip false } @@ -123,23 +124,23 @@ class ItemQuestRequirementListener : InteractionListener { */ onEquip(fremennikTrialsEquipment) { player, _ -> - return@onEquip hasRequirement(player, "Fremennik Trials") + return@onEquip hasRequirement(player, Quests.THE_FREMENNIK_TRIALS) } onEquip(fremennikIslesEquipment) {player, _ -> - return@onEquip hasRequirement(player, "The Fremennik Isles") + return@onEquip hasRequirement(player, Quests.THE_FREMENNIK_ISLES) } onEquip(avasBackpacks){ player, _ -> - return@onEquip hasRequirement(player, "Animal Magnetism") + return@onEquip hasRequirement(player, Quests.ANIMAL_MAGNETISM) } onEquip(lostCityWeapons){ player, _ -> - return@onEquip hasRequirement(player, "Lost City") + return@onEquip hasRequirement(player, Quests.LOST_CITY) } onEquip(Items.CAPE_OF_LEGENDS_1052) { player, _ -> - return@onEquip hasRequirement(player, "Legend's Quest") + return@onEquip hasRequirement(player, Quests.LEGENDS_QUEST) } onEquip(questCapes) { player, _ -> @@ -152,84 +153,84 @@ class ItemQuestRequirementListener : InteractionListener { } onEquip(Items.WOLFBANE_2952){ player, _ -> - return@onEquip hasRequirement(player, "Priest in Peril") + return@onEquip hasRequirement(player, Quests.PRIEST_IN_PERIL) } onEquip(Items.ANCIENT_MACE_11061){ player, _ -> - return@onEquip hasRequirement(player, "Another Slice of H.A.M") + return@onEquip hasRequirement(player, Quests.ANOTHER_SLICE_OF_HAM) } onEquip(Items.ANCIENT_STAFF_4675){ player, _ -> - return@onEquip hasRequirement(player, "Desert Treasure") + return@onEquip hasRequirement(player, Quests.DESERT_TREASURE) } onEquip(Items.ELEMENTAL_SHIELD_2890) { player, _ -> - return@onEquip hasRequirement(player, "Elemental Workshop I") + return@onEquip hasRequirement(player, Quests.ELEMENTAL_WORKSHOP_I) } onEquip(crystalEquipment){ player, _ -> - return@onEquip hasRequirement(player, "Roving Elves") + return@onEquip hasRequirement(player, Quests.ROVING_ELVES) } onEquip(dragonSlayerEquipment) {player, _ -> - return@onEquip hasRequirement(player, "Dragon Slayer") + return@onEquip hasRequirement(player, Quests.DRAGON_SLAYER) } onEquip(Items.DRAGON_SCIMITAR_4587) {player, _ -> - return@onEquip hasRequirement(player, "Monkey Madness") + return@onEquip hasRequirement(player, Quests.MONKEY_MADNESS) } onEquip(Items.GLOVES_7462) {player, _ -> - return@onEquip hasRequirement(player, "Recipe for Disaster") + return@onEquip hasRequirement(player, Quests.RECIPE_FOR_DISASTER) } onEquip(Items.SLAYER_HELMET_13263) {player, _ -> - return@onEquip hasRequirement(player, "Smoking Kills") + return@onEquip hasRequirement(player, Quests.SMOKING_KILLS) } onEquip (Items.DRAGON_HALBERD_3204) {player, _ -> - return@onEquip hasRequirement(player, "Regicide") + return@onEquip hasRequirement(player, Quests.REGICIDE) } onEquip (Items.CLIMBING_BOOTS_3105) {player, _ -> - return@onEquip hasRequirement(player, "Death Plateau") + return@onEquip hasRequirement(player, Quests.DEATH_PLATEAU) } onEquip (godBooks) {player, _ -> - return@onEquip hasRequirement(player, "Horror from the Deep") + return@onEquip hasRequirement(player, Quests.HORROR_FROM_THE_DEEP) } onEquip (pharaohScepters) {player, _ -> - return@onEquip hasRequirement(player, "Icthlarin's Little Helper") + return@onEquip hasRequirement(player, Quests.ICTHLARINS_LITTLE_HELPER) } onEquip (Items.DRAGON_SQ_SHIELD_1187) {player, _ -> //because I know people won't believe it: https://runescape.wiki/w/Dragon_sq_shield?oldid=899636 - return@onEquip hasRequirement(player, "Legend's Quest") + return@onEquip hasRequirement(player, Quests.LEGENDS_QUEST) } onEquip (initiateArmour) {player, _ -> - return@onEquip hasRequirement(player, "Recruitment Drive") + return@onEquip hasRequirement(player, Quests.RECRUITMENT_DRIVE) } onEquip (proselyteArmour) {player, _ -> - return@onEquip hasRequirement(player, "The Slug Menace") + return@onEquip hasRequirement(player, Quests.THE_SLUG_MENACE) } onEquip (spiritShields) {player, _ -> - return@onEquip hasRequirement(player, "Summer's End") + return@onEquip hasRequirement(player, Quests.SUMMERS_END) } onEquip (Items.DRAGON_MACE_1434) {player, _ -> - return@onEquip hasRequirement(player, "Heroes' Quest") + return@onEquip hasRequirement(player, Quests.HEROES_QUEST) } onEquip (Items.DRAGON_BATTLEAXE_1377) {player, _ -> - return@onEquip hasRequirement(player, "Heroes' Quest") + return@onEquip hasRequirement(player, Quests.HEROES_QUEST) } onEquip (Items.DARKLIGHT_6746) {player, _ -> - return@onEquip hasRequirement(player, "Shadow of the Storm") + return@onEquip hasRequirement(player, Quests.SHADOW_OF_THE_STORM) } } } diff --git a/Server/src/main/content/global/handlers/item/MorphItemPlugin.java b/Server/src/main/content/global/handlers/item/MorphItemPlugin.java index 29c84d805..672de389f 100644 --- a/Server/src/main/content/global/handlers/item/MorphItemPlugin.java +++ b/Server/src/main/content/global/handlers/item/MorphItemPlugin.java @@ -12,6 +12,9 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.ClassScanner; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles a morph item. @@ -65,6 +68,7 @@ public class MorphItemPlugin implements Plugin { */ private void morph(Player player, Item item) { int morphId = item.getId() == 6583 ? 2626 : EASTER_EGG_IDS[RandomFunction.random(EASTER_EGG_IDS.length)]; + playAudio(player, 1520); player.getInterfaceManager().close(); player.getAppearance().transformNPC(morphId); player.getInterfaceManager().removeTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); diff --git a/Server/src/main/content/global/handlers/item/SilverSicklePlugin.java b/Server/src/main/content/global/handlers/item/SilverSicklePlugin.java index eb403f33b..e7529d646 100644 --- a/Server/src/main/content/global/handlers/item/SilverSicklePlugin.java +++ b/Server/src/main/content/global/handlers/item/SilverSicklePlugin.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; import content.region.morytania.quest.naturespirit.NSUtils; +import content.data.Quests; /** * Handles the Silver Sickle (b) to collect Mort Myre Fungus. @@ -28,7 +29,7 @@ public final class SilverSicklePlugin extends OptionHandler { switch (option) { case "operate": case "cast bloom": - if(player.getQuestRepository().getQuest("Nature Spirit").getStage(player) >= 75) { + if(player.getQuestRepository().getQuest(Quests.NATURE_SPIRIT).getStage(player) >= 75) { player.getPacketDispatch().sendAnimation(9021); NSUtils.castBloom(player); } else { diff --git a/Server/src/main/content/global/handlers/item/TeleTabsListener.kt b/Server/src/main/content/global/handlers/item/TeleTabsListener.kt index f90bcf502..9cc72f6c2 100644 --- a/Server/src/main/content/global/handlers/item/TeleTabsListener.kt +++ b/Server/src/main/content/global/handlers/item/TeleTabsListener.kt @@ -1,18 +1,20 @@ package content.global.handlers.item -import core.api.inInventory -import core.api.removeItem -import core.api.teleport +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners.Companion.ARDOUGNE_TELE_ATTRIBUTE +import core.api.* import core.game.interaction.IntType import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager import core.game.node.item.Item import core.game.world.map.Location class TeleTabsListener : InteractionListener { - enum class TeleTabs(val item: Int, val location: Location, val exp: Double) { - ADDOUGNE_TELEPORT(8011, Location.create(2662, 3307, 0), 61.0), + enum class TeleTabs(val item: Int, val location: Location, val exp: Double, val requirementCheck: (Player) -> Boolean = { true }) { + ADDOUGNE_TELEPORT(8011, Location.create(2662, 3307, 0), 61.0, { + player -> getAttribute(player, ARDOUGNE_TELE_ATTRIBUTE, false) + }), AIR_ALTAR_TELEPORT(13599, Location.create(2978, 3296, 0), 0.0), ASTRAL_ALTAR_TELEPORT(13611, Location.create(2156, 3862, 0), 0.0), BLOOD_ALTAR_TELEPORT(13610, Location.create(3559, 9778, 0), 0.0), @@ -48,8 +50,16 @@ class TeleTabsListener : InteractionListener { if (tabEnum != null && inInventory(player,tab)) { val tabloc = tabEnum.location if (inInventory(player, tab)) { - if (teleport(player, tabloc, TeleportManager.TeleportType.TELETABS)) { - removeItem(player, Item(node.id, 1)) + if (tabEnum.requirementCheck(player)){ + if (teleport(player, tabloc, TeleportManager.TeleportType.TELETABS)) { + removeItem(player, Item(node.id, 1)) + } + } + else { + when (tabEnum){ + TeleTabs.ADDOUGNE_TELEPORT -> sendMessage(player, "You need to complete Plague City to use this tablet.") + else -> sendMessage(player, "You do not have the requirements to use this tablet.") + } } } } diff --git a/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java b/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java index 94c3b4eb7..5bbda0ffc 100644 --- a/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java +++ b/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java @@ -14,6 +14,7 @@ import core.game.world.map.zone.impl.WildernessZone; import core.plugin.Plugin; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Represents the rotten potato plugin. @@ -34,7 +35,7 @@ public final class TeleportCrystalPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - if (!hasRequirement(player, "Mourning's End Part I")) + if (!hasRequirement(player, Quests.MOURNINGS_END_PART_I)) return true; if (!WildernessZone.checkTeleport(player, 20)) { player.getPacketDispatch().sendMessage("The crystal is unresponsive."); diff --git a/Server/src/main/content/global/handlers/item/ToyListeners.kt b/Server/src/main/content/global/handlers/item/ToyListeners.kt index 973166815..edf14fa89 100644 --- a/Server/src/main/content/global/handlers/item/ToyListeners.kt +++ b/Server/src/main/content/global/handlers/item/ToyListeners.kt @@ -6,6 +6,13 @@ import core.game.world.update.flag.context.Graphics import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType +import kotlinx.coroutines.delay +import org.rs09.consts.Sounds +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + + class ToyListeners : InteractionListener { companion object { @@ -14,10 +21,12 @@ class ToyListeners : InteractionListener { private val MARIONETTE_WALK = Animation(3004) private val MARIONETTE_BOW = Animation(3005) private val MARIONETTE_DANCE = Animation(3006) - private val MARIONETTE_GFX = arrayOf(intArrayOf(507, 508, 509, 510), intArrayOf(511, 512, 513, 514), intArrayOf(515, 516, 517, 518)) + private val MARIONETTE_GFX = + arrayOf(intArrayOf(507, 508, 509, 510), intArrayOf(511, 512, 513, 514), intArrayOf(515, 516, 517, 518)) private val SNOWGLOBE_SHAKE = Animation(7535) //Initial Shake private val SNOWGLOBE_HOLDFACE = Animation(7536) //Immediately after shake, player holds the snow globe to face - private val SNOWGLOBE_INTERFACE = 659 //After HOLDFACE this interface is displayed, player either clicks 'continue' for inv of snowballs, or 'close' for no snowballs + private val SNOWGLOBE_INTERFACE = + 659 //After HOLDFACE this interface is displayed, player either clicks 'continue' for inv of snowballs, or 'close' for no snowballs private val SNOWGLOBE_DOWNFAST = Animation(7537) //Used when player hit 'close' on the interface private val SNOWGLOBE_DOWNSLOW = Animation(7538) //Used when the player hit 'continue' on the interface private val SNOWGLOBE_STOMP = Animation(7528) //When player hits continue this animation plays @@ -34,7 +43,7 @@ class ToyListeners : InteractionListener { } override fun defineListeners() { - on(Items.CHOCATRICE_CAPE_12634, IntType.ITEM, "operate"){ player, _ -> + on(Items.CHOCATRICE_CAPE_12645, IntType.ITEM, "operate") { player, _ -> lockInteractions(player, 2) visualize(player, 8903, 1566) return@on true @@ -44,18 +53,18 @@ class ToyListeners : InteractionListener { val index = MARIONETTES.indexOf(marionette.id) lockInteractions(player, 2) - when(getUsedOption(player)) { + when (getUsedOption(player)) { "jump" -> visualize(player, MARIONETTE_JUMP, MARIONETTE_GFX[index][0]) "walk" -> visualize(player, MARIONETTE_WALK, MARIONETTE_GFX[index][1]) - "bow" -> visualize(player, MARIONETTE_BOW, MARIONETTE_GFX[index][2]) + "bow" -> visualize(player, MARIONETTE_BOW, MARIONETTE_GFX[index][2]) "dance" -> visualize(player, MARIONETTE_DANCE, MARIONETTE_GFX[index][3]) } return@on true } - on(Items.REINDEER_HAT_10507, IntType.ITEM, "operate"){ player, _ -> + on(Items.REINDEER_HAT_10507, IntType.ITEM, "operate") { player, _ -> lockInteractions(player, 2) - animate(player, 5059) + visualize(player, 5059, 859) return@on true } @@ -64,7 +73,7 @@ class ToyListeners : InteractionListener { animate(player, SNOWGLOBE_SHAKE) runTask(player, 3) { animate(player, SNOWGLOBE_HOLDFACE) - runTask(player){ + runTask(player) { openInterface(player, SNOWGLOBE_INTERFACE) } } @@ -80,17 +89,17 @@ class ToyListeners : InteractionListener { return@on true } - on(Items.TOY_KITE_12844, IntType.ITEM, "fly"){ player, _ -> + on(Items.TOY_KITE_12844, IntType.ITEM, "fly","operate") { player, _ -> lockInteractions(player, 2) animate(player, TOY_KITE_FLY) return@on true } - on(Items.YO_YO_4079, IntType.ITEM, "play", "loop", "walk", "crazy"){ player, _ -> + on(Items.YO_YO_4079, IntType.ITEM, "play", "loop", "walk", "crazy") { player, _ -> val option = getUsedOption(player) lockInteractions(player, 2) - when(option) { + when (option) { "play" -> animate(player, YOYO_PLAY) "loop" -> animate(player, YOYO_LOOP) "walk" -> animate(player, YOYO_WALK) @@ -99,11 +108,11 @@ class ToyListeners : InteractionListener { return@on true } - on(Items.ZOMBIE_HEAD_6722, IntType.ITEM, "talk-at", "display", "question"){ player, _ -> + on(Items.ZOMBIE_HEAD_6722, IntType.ITEM, "talk-at", "display", "question") { player, _ -> val option = getUsedOption(player) lockInteractions(player, 2) - when(option) { + when (option) { "talk-at" -> { animate(player, ZOMBIE_HEAD_TALK_AT) sendChat(player, "Alas!") @@ -116,5 +125,12 @@ class ToyListeners : InteractionListener { } return@on true } + on(Items.RUBBER_CHICKEN_4566, IntType.ITEM, "operate", "Dance") { player, _ -> + lockInteractions(player, 2) + visualize(player, 1835, -1) + playJingle(player, 99); + playAudio(player, 355, 100); + return@on true + } } } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipment.kt b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipment.kt new file mode 100644 index 000000000..5cdbb874a --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipment.kt @@ -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 + ) { + 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 by lazy { + barrowsItemDefinitions.flatMap { def -> + def.degradationStates.map { id -> id to def } + }.toMap() + } + + /** Gets all degradation state arrays for degradation registration */ + fun getAllEquipmentSets(): Collection = barrowsItemDefinitions.map { it.degradationStates.toIntArray() } + + /** Gets all repairable Barrows item IDs (anything not fully repaired) */ + fun getAllRepairableBarrowsIds(): List = 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) + } +} diff --git a/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt index d7e57ab9e..1a0960711 100644 --- a/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt +++ b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt @@ -4,73 +4,16 @@ import core.plugin.Initializable import core.plugin.Plugin @Initializable -class BarrowsEquipmentRegister : Plugin{ - 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 { override fun newInstance(arg: Any?): Plugin { - 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 } - } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/AncientMaceSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/AncientMaceSpecialHandler.java index 330523540..9c04d990f 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/AncientMaceSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/AncientMaceSpecialHandler.java @@ -57,7 +57,7 @@ public final class AncientMaceSpecialHandler extends MeleeSwingHandler implement state.setStyle(CombatStyle.MELEE); int hit = 0; if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.1, 0.98)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1)); + hit = RandomFunction.random(calculateHit(entity, victim, 1) + 1); if (entity.getSkills().getPrayerPoints() < entity.getSkills().getStaticLevel(5)) { entity.getSkills().setPrayerPoints(entity.getSkills().getPrayerPoints() + hit); } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/BackstabSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/BackstabSpecialHandler.java index bb243ad37..2baaab97e 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/BackstabSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/BackstabSpecialHandler.java @@ -59,13 +59,10 @@ public final class BackstabSpecialHandler extends MeleeSwingHandler implements P } state.setStyle(CombatStyle.MELEE); int hit = 0; - double accuracy = 1.0; - if (!victim.getProperties().getCombatPulse().isAttacking()) { - accuracy = 1.75; - } - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, accuracy, 0.98)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); - victim.getSkills().updateLevel(Skills.DEFENCE, -hit / 10, 0); + if (!victim.getProperties().getCombatPulse().isAttacking() || isAccurateImpact(entity, victim, CombatStyle.MELEE)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); + if (victim.getSkills().getStaticLevel(Skills.DEFENCE) >= victim.getSkills().getDynamicLevels()[Skills.DEFENCE]) + victim.getSkills().updateLevel(Skills.DEFENCE, -hit, 0); } state.setEstimatedHit(hit); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java index a4682149f..b3e276dcd 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java @@ -1,9 +1,7 @@ package content.global.handlers.item.equipment.special; 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.DeathTask; +import core.game.node.entity.combat.*; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.impl.Projectile; @@ -17,7 +15,6 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; -import core.game.node.entity.combat.RangeSwingHandler; import core.game.world.GameWorld; import core.game.world.repository.Repository; import org.rs09.consts.Sounds; @@ -34,6 +31,13 @@ import static core.api.ContentAPIKt.playGlobalAudio; @Initializable public final class ChainhitSpecialHandler extends RangeSwingHandler implements Plugin { + /** + * Constructs a new {@code ChainhitSpecialHandler} {@code Object}. + */ + public ChainhitSpecialHandler() { + super(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE); + } + /** * The sp::ecial energy required. */ @@ -159,7 +163,7 @@ public final class ChainhitSpecialHandler extends RangeSwingHandler implements P public boolean pulse() { BattleState bs = new BattleState(entity, e); bs.setMaximumHit(calculateHit(player, e, 1.0)); - bs.setEstimatedHit(RandomFunction.RANDOM.nextInt(bs.getMaximumHit())); + bs.setEstimatedHit(RandomFunction.random(bs.getMaximumHit() + 1)); handleHit(victim, e, player, bs); ChainhitSpecialHandler.super.visualizeImpact(player, e, bs); return true; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java index fea739a85..34b52b9c0 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java @@ -31,13 +31,6 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler { */ private static final Graphics END_GRAPHIC = new Graphics(157, 96); - /** - * Constructs a new {@code ChinchompaSwingHandler} {@code Object}. - */ - public ChinchompaSwingHandler() { - super(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE); - } - @Override public int swing(Entity entity, Entity victim, BattleState state) { boolean multi = entity.getProperties().isMultiZone() && victim.getProperties().isMultiZone(); @@ -72,7 +65,7 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler { s.setStyle(CombatStyle.RANGE); int hit = 0; if (isAccurateImpact(entity, e, CombatStyle.RANGE)) { - hit = RandomFunction.random(calculateHit(entity, e, 1.0)); + hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); } s.setEstimatedHit(hit); } @@ -100,7 +93,7 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler { } } entity.getSkills().addExperience(Skills.HITPOINTS, hit * 1.33, true); - if (entity.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_DEFENSIVE) { + if (entity.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_LONG_RANGE) { entity.getSkills().addExperience(Skills.RANGE, hit * 2, true); entity.getSkills().addExperience(Skills.DEFENCE, hit * 2, true); } else { diff --git a/Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java index dd669c168..f728dbaf2 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java @@ -57,8 +57,8 @@ public final class CleaveSpecialHandler extends MeleeSwingHandler implements Plu } state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.18, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.2203)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.25) + 1); } state.setEstimatedHit(hit); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/DescentOfDarknessSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/DescentOfDarknessSpecialHandler.java index 519f9462a..75ae1f8a8 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/DescentOfDarknessSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/DescentOfDarknessSpecialHandler.java @@ -30,7 +30,7 @@ public final class DescentOfDarknessSpecialHandler extends RangeSwingHandler imp /** * The special energy required. */ - private static final int SPECIAL_ENERGY = 65; + private static final int SPECIAL_ENERGY = 55; /** * The descent of dragons projectile. @@ -102,13 +102,13 @@ public final class DescentOfDarknessSpecialHandler extends RangeSwingHandler imp state.setMaximumHit(max); int hit = minDamage; if (isAccurateImpact(entity, victim, CombatStyle.RANGE, 1.15, 1.0)) { - hit += RandomFunction.random(max - minDamage); + hit += RandomFunction.random(max - minDamage + 1); } state.setEstimatedHit(hit); if (w.getType() == WeaponType.DOUBLE_SHOT) { hit = minDamage; if (isAccurateImpact(entity, victim, CombatStyle.RANGE, 1.15, 1.0)) { - hit += RandomFunction.random(max - minDamage); + hit += RandomFunction.random(max - minDamage + 1); } state.setSecondaryHit(hit); } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/EnergyDrainSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/EnergyDrainSpecialHandler.java index c7b6dec41..4686eb2b8 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/EnergyDrainSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/EnergyDrainSpecialHandler.java @@ -57,8 +57,8 @@ public final class EnergyDrainSpecialHandler extends MeleeSwingHandler implement } state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.2, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.25, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1) + 1); } if (victim instanceof Player) { ((Player) victim).getSettings().updateRunEnergy(10); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/FeintSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/FeintSpecialHandler.java index a22ba8c10..b85962790 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/FeintSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/FeintSpecialHandler.java @@ -52,8 +52,9 @@ public final class FeintSpecialHandler extends MeleeSwingHandler implements Plug } state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.0, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, RandomFunction.random(1.0, 1.2))); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.0, 0.25)) { + int minDamage = calculateHit(entity, victim, 0.2); + hit = minDamage + RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); } state.setEstimatedHit(hit); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/HamstringSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/HamstringSpecialHandler.java index fefffaabd..6a89af4fc 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/HamstringSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/HamstringSpecialHandler.java @@ -46,7 +46,8 @@ public final class HamstringSpecialHandler extends RangeSwingHandler implements state.setMaximumHit(max); int hit = 0; if (isAccurateImpact(entity, victim)) { - hit = RandomFunction.random(max); + int minDamage = calculateHit(entity, victim, 0.2); + hit = minDamage + RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); } state.setEstimatedHit(hit); Companion.useAmmo(entity, state, victim.getLocation()); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/HealingBladeSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/HealingBladeSpecialHandler.java index 76780908b..5589c48c0 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/HealingBladeSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/HealingBladeSpecialHandler.java @@ -57,20 +57,20 @@ public final class HealingBladeSpecialHandler extends MeleeSwingHandler implemen } state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.12, 0.98)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.005)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 2.0, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.1) + 1); + int healthRestore = hit / 2; + double prayerRestore = hit * 0.25; + if (healthRestore < 10) { + healthRestore = 10; + } + if (prayerRestore < 5) { + prayerRestore = 5; + } + entity.getSkills().heal(healthRestore); + entity.getSkills().incrementPrayerPoints(prayerRestore); } state.setEstimatedHit(hit); - int healthRestore = hit / 2; - double prayerRestore = hit * 0.25; - if (healthRestore < 10) { - healthRestore = 10; - } - if (prayerRestore < 5) { - prayerRestore = 5; - } - entity.getSkills().heal(healthRestore); - entity.getSkills().incrementPrayerPoints(prayerRestore); return 1; } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/IceCleaveSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/IceCleaveSpecialHandler.java index 42053679b..83c1e4a50 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/IceCleaveSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/IceCleaveSpecialHandler.java @@ -57,8 +57,8 @@ public final class IceCleaveSpecialHandler extends MeleeSwingHandler implements } state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.075, 0.98)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.005)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 2.0, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.1) + 1); } state.setEstimatedHit(hit); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ImpaleSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ImpaleSpecialHandler.java index 11ddbd9f9..2e0ff5aa4 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ImpaleSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ImpaleSpecialHandler.java @@ -59,7 +59,7 @@ public final class ImpaleSpecialHandler extends MeleeSwingHandler implements Plu state.setStyle(CombatStyle.MELEE); int hit = 0; if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.1, 0.98)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.1)); + hit = RandomFunction.random(calculateHit(entity, victim, 1.1) + 1); } state.setEstimatedHit(hit); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/JudgementSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/JudgementSpecialHandler.java index 0fb647924..abdb00cc5 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/JudgementSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/JudgementSpecialHandler.java @@ -57,8 +57,8 @@ public final class JudgementSpecialHandler extends MeleeSwingHandler implements } state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.25, 0.98)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.25)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 2.0, 1.0)) { + hit = RandomFunction.random((int) (calculateHit(entity, victim, 1.1) * 1.25) + 1); } state.setEstimatedHit(hit); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/PhantomStrikeSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/PhantomStrikeSpecialHandler.java index 18af20d24..82f5b3eb3 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/PhantomStrikeSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/PhantomStrikeSpecialHandler.java @@ -49,7 +49,7 @@ public final class PhantomStrikeSpecialHandler extends RangeSwingHandler impleme state.setMaximumHit(max); int hit = 0; if (isAccurateImpact(entity, victim)) { - hit = RandomFunction.random(max); + hit = RandomFunction.random(max + 1); } state.setEstimatedHit(hit); Companion.useAmmo(entity, state, victim.getLocation()); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/PowershotSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/PowershotSpecialHandler.java index aca1a0070..4fe5e5f90 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/PowershotSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/PowershotSpecialHandler.java @@ -56,10 +56,7 @@ public final class PowershotSpecialHandler extends RangeSwingHandler implements return -1; } state.setStyle(CombatStyle.RANGE); - int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.RANGE, 1.98, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); - } + int hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); state.setEstimatedHit(hit); Companion.useAmmo(entity, state, victim.getLocation()); return 1 + (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java index 4c7ea7a83..172ca036c 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java @@ -85,7 +85,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements BattleState s = targets[count++] = new BattleState(entity, e); int hit = 0; if (isAccurateImpact(entity, e)) { - hit = RandomFunction.random(calculateHit(entity, e, 1.0)); + hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); } s.setStyle(CombatStyle.MELEE); s.setEstimatedHit(hit); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/PunctureSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/PunctureSpecialHandler.java index e489173fc..b5fc3357f 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/PunctureSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/PunctureSpecialHandler.java @@ -61,18 +61,13 @@ public final class PunctureSpecialHandler extends MeleeSwingHandler implements P return -1; } state.setStyle(CombatStyle.MELEE); - // First hit - //double accuracyMod, double defenceMod int hit = 0; - // accuracyMod defenceMod - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.05, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.1306)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.15, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.15) + 1); } state.setEstimatedHit(hit); - // Second hit - // accuracyMod defenceMod - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.05, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.1306)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.15, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.15) + 1); } else { hit = 0; } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/QuickSmashSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/QuickSmashSpecialHandler.java index cc5e5a557..e1d043c9d 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/QuickSmashSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/QuickSmashSpecialHandler.java @@ -3,9 +3,11 @@ package content.global.handlers.item.equipment.special; 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.DeathTask; import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.prayer.PrayerType; import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; @@ -65,16 +67,22 @@ public final class QuickSmashSpecialHandler extends MeleeSwingHandler implements return -1; } } + if (DeathTask.isDead(victim)) { + return -1; + } if (!p.getSettings().drainSpecial(SPECIAL_ENERGY)) { return -1; } - // TODO: apply protection prayers/experience manually (since this is bypassing normal BattleState machinery) visualize(entity, victim, null); int hit = 0; if (isAccurateImpact(entity, victim)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.)); + hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); } - victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MELEE); + if (victim.hasProtectionPrayer(CombatStyle.MELEE)) + hit *= (victim instanceof Player) ? 0.6 : 0; + BattleState b = new BattleState(); + b.setEstimatedHit(victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MELEE).getAmount()); + addExperience(entity, victim, b); return 1; } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/RampageSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/RampageSpecialHandler.java index 0aef9cd2b..2e70ab9ec 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/RampageSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/RampageSpecialHandler.java @@ -65,15 +65,15 @@ public final class RampageSpecialHandler extends MeleeSwingHandler implements Pl p.visualize(ANIMATION, GRAPHIC); @SuppressWarnings("unused") int boost = 0; - for (int i = 0; i < 6; i++) { - if (i == 2 || i == 3 || i == 5) { - continue; + for (int i = 0; i < 7; i++) { + if (i == Skills.ATTACK || i == Skills.DEFENCE || i == Skills.RANGE || i == Skills.MAGIC) { + int drain = (int) (p.getSkills().getLevel(i) * 0.1); + boost += drain; + p.getSkills().updateLevel(i, -drain, 0); } - double drain = p.getSkills().getLevel(i) * 0.1; - boost += drain; - p.getSkills().updateLevel(i, (int) -drain, (int) (p.getSkills().getStaticLevel(i) - drain)); } - p.getSkills().updateLevel(Skills.STRENGTH, (int) (p.getSkills().getStaticLevel(Skills.STRENGTH) * 0.20)); + boost = 10 + (boost / 4); + p.getSkills().updateLevel(Skills.STRENGTH, boost, Math.max(p.getSkills().getStaticLevel(Skills.STRENGTH) + boost, p.getSkills().getLevel(Skills.STRENGTH))); return -1; } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SalamanderSwingHandler.kt b/Server/src/main/content/global/handlers/item/equipment/special/SalamanderSwingHandler.kt index 9af01e7f9..a0c5f6941 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SalamanderSwingHandler.kt +++ b/Server/src/main/content/global/handlers/item/equipment/special/SalamanderSwingHandler.kt @@ -90,6 +90,8 @@ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandle } override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { + entity ?: return 0 + //Checking style is necessary for ::calcmaxhit to function after changing attack style but before attacking checkStyle(entity) if (style == CombatStyle.MAGIC) { @@ -144,7 +146,7 @@ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandle /** * The instance for the swing handler. */ - val INSTANCE = SalamanderSwingHandler(CombatStyle.MELEE) + val INSTANCE = SalamanderSwingHandler(CombatStyle.RANGE) /** * Gets the id of the tar used by a salamander. diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SaradominsLightningHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SaradominsLightningHandler.java index 3875ba7be..9e672f4db 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SaradominsLightningHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SaradominsLightningHandler.java @@ -52,9 +52,9 @@ public final class SaradominsLightningHandler extends MeleeSwingHandler implemen state.setStyle(CombatStyle.MAGIC); int hit = 0; int secondary = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.10, 0.98)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.1)); - secondary = 5 + RandomFunction.RANDOM.nextInt(14); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.10, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.1) + 1); + secondary = 1 + RandomFunction.random(16); } state.setEstimatedHit(hit); state.setSecondaryHit(secondary); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SeercullSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SeercullSpecialHandler.java index 5d9bc30ef..b4a374d3e 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SeercullSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SeercullSpecialHandler.java @@ -1,5 +1,6 @@ package content.global.handlers.item.equipment.special; +import core.game.node.entity.combat.SwingHandlerFlag; import core.game.node.entity.skill.Skills; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -24,6 +25,13 @@ import static core.api.ContentAPIKt.playGlobalAudio; @Initializable public final class SeercullSpecialHandler extends RangeSwingHandler implements Plugin { + /** + * Constructs a new {@code SeercullSpecialHandler} {@code Object}. + */ + public SeercullSpecialHandler() { + super(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE); + } + /** * The special energy required. */ @@ -57,11 +65,9 @@ public final class SeercullSpecialHandler extends RangeSwingHandler implements P if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { return -1; } - int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.RANGE, 1.05, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); + int hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); + if (victim.getSkills().getLevel(Skills.MAGIC) >= victim.getSkills().getStaticLevel(Skills.MAGIC)) victim.getSkills().updateLevel(Skills.MAGIC, -hit, 0); - } Companion.useAmmo(entity, state, victim.getLocation()); state.setEstimatedHit(hit); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SeverSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SeverSpecialHandler.java index 116e993d4..eb8419c10 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SeverSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SeverSpecialHandler.java @@ -56,8 +56,8 @@ public final class SeverSpecialHandler extends MeleeSwingHandler implements Plug return -1; state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.124, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.25, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); if (victim instanceof Player) { Player p = (Player) victim; if (p.getPrayer().get(PrayerType.PROTECT_FROM_MAGIC)) { diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ShatterSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ShatterSpecialHandler.java index aebbd909f..1b226c390 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ShatterSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ShatterSpecialHandler.java @@ -56,8 +56,8 @@ public final class ShatterSpecialHandler extends MeleeSwingHandler implements Pl } state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 0.87, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.3546)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.25, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.5) + 1); } state.setEstimatedHit(hit); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SliceAndDiceSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SliceAndDiceSpecialHandler.java index 808cae7a4..9f755280c 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SliceAndDiceSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SliceAndDiceSpecialHandler.java @@ -56,24 +56,25 @@ public final class SliceAndDiceSpecialHandler extends MeleeSwingHandler implemen return -1; } int maximum = calculateHit(entity, victim, 1.0); - int[] hits = new int[] {0, 1}; - int hit = getHit(entity, victim, maximum); + int[] hits; + int hit = getHit(entity, victim, maximum - 1, maximum / 2); if (hit > 0) { - hits = new int[] {hit, hit / 2, (hit / 2) / 2, (hit / 2) - ((hit / 2) / 2)}; + hits = new int[] {hit, hit / 2, (hit / 2) / 2, (hit / 2) / 2 + 1}; } else { - hit = getHit(entity, victim, maximum); + hit = getHit(entity, victim, maximum * 7 / 8, maximum * 3 / 8); if (hit > 0) { - hits = new int[] {0, hit, hit / 2, hit - (hit / 2)}; + hits = new int[] {0, hit, hit / 2, hit / 2 + 1}; } else { - hit = getHit(entity, victim, maximum); + hit = getHit(entity, victim, maximum * 3 / 4, maximum / 4); if (hit > 0) { - hits = new int[] {0, 0, hit / 2, (hit / 2) + 10}; + hits = new int[] {0, 0, hit, hit + 1}; } else { - hit = getHit(entity, victim, (int) (maximum * 1.5)); + hit = getHit(entity, victim, maximum * 5 / 4, maximum / 4); if (hit > 0) { hits = new int[] {0, 0, 0, hit}; } else { - hits = new int[] {0, RandomFunction.random(2)}; + hit = RandomFunction.random(2); + hits = new int[] {0, 0, hit, hit}; } } } @@ -95,9 +96,9 @@ public final class SliceAndDiceSpecialHandler extends MeleeSwingHandler implemen * @param maximum The maximum hit. * @return The hit. */ - private int getHit(Entity entity, Entity victim, int maximum) { - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.25, 0.98)) { - return RandomFunction.random(maximum); + private int getHit(Entity entity, Entity victim, int maximum, int minimum) { + if (isAccurateImpact(entity, victim, CombatStyle.MELEE)) { + return RandomFunction.random(minimum, maximum + 1); } return 0; } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SmashSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SmashSpecialHandler.java index e4137d26c..ba4989eda 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SmashSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SmashSpecialHandler.java @@ -59,7 +59,8 @@ public final class SmashSpecialHandler extends MeleeSwingHandler implements Plug state.setStyle(CombatStyle.MELEE); int hit = 0; if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.0, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, RandomFunction.random(1.0, 1.5))); + int max = calculateHit(entity, victim, 1.0); + hit = max / 4 + RandomFunction.random(max + 1); int lower = (int) (victim.getSkills().getLevel(Skills.DEFENCE) * 0.30); victim.getSkills().updateLevel(Skills.DEFENCE, -lower, 0); } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SnapshotSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SnapshotSpecialHandler.java index ddd761a21..618a0f4b2 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SnapshotSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SnapshotSpecialHandler.java @@ -3,6 +3,7 @@ package content.global.handlers.item.equipment.special; 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.SwingHandlerFlag; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; @@ -25,6 +26,13 @@ import static core.api.ContentAPIKt.playGlobalAudio; @Initializable public final class SnapshotSpecialHandler extends RangeSwingHandler implements Plugin { + /** + * Constructs a new {@code SnapshotSpecialHandler} {@code Object}. + */ + public SnapshotSpecialHandler() { + super(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE); + } + /** * The special energy required. */ @@ -67,13 +75,13 @@ public final class SnapshotSpecialHandler extends RangeSwingHandler implements P int max = calculateHit(entity, victim, 1.0); state.setMaximumHit(max); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 0.9, 1.0)) { - hit = RandomFunction.random(max); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.43, 1.0)) { + hit = RandomFunction.random(max + 1); } state.setEstimatedHit(hit); hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 0.9, 1.0)) { - hit = RandomFunction.random(max); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.43, 1.0)) { + hit = RandomFunction.random(max + 1); } state.setSecondaryHit(hit); Companion.useAmmo(entity, state, victim.getLocation()); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SnipeSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SnipeSpecialHandler.java index b3941ef7c..4d5b66609 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SnipeSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SnipeSpecialHandler.java @@ -60,9 +60,10 @@ public final class SnipeSpecialHandler extends RangeSwingHandler implements Plug } state.setStyle(CombatStyle.RANGE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.RANGE, 1.05, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); - victim.getSkills().updateLevel(Skills.DEFENCE, -hit, 0); + if (!victim.getProperties().getCombatPulse().isAttacking() || isAccurateImpact(entity, victim, CombatStyle.RANGE)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); + if (victim.getSkills().getStaticLevel(Skills.DEFENCE) >= victim.getSkills().getDynamicLevels()[Skills.DEFENCE]) + victim.getSkills().updateLevel(Skills.DEFENCE, -hit, 0); } Companion.useAmmo(entity, state, victim.getLocation()); state.setEstimatedHit(hit); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java index bdbef4ee4..c83d2fd5d 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java @@ -85,7 +85,7 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) { BattleState s = targets[count++] = new BattleState(entity, e); int hit = 0; - hit = RandomFunction.random(calculateHit(entity, e, 1.0)); + hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); s.setStyle(CombatStyle.MELEE); s.setEstimatedHit(hit); } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java index 4b7947736..3ef8ce0f7 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java @@ -66,14 +66,14 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug for (BattleState s : targets) { s.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, s.getVictim(), CombatStyle.MELEE, 1, 0.94)) { - hit = RandomFunction.random(calculateHit(entity, s.getVictim(), 1.1)); + if (isAccurateImpact(entity, s.getVictim(), CombatStyle.MELEE)) { + hit = RandomFunction.random(calculateHit(entity, s.getVictim(), 1.1) + 1); } s.setEstimatedHit(hit); if (s.getVictim().size() > 1) { hit = 0; - if (isAccurateImpact(entity, s.getVictim(), CombatStyle.MELEE, 1, 0.94)) { - hit = RandomFunction.random(calculateHit(entity, s.getVictim(), 1.1)); + if (isAccurateImpact(entity, s.getVictim(), CombatStyle.MELEE, 0.75, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, s.getVictim(), 1.1) + 1); } s.setSecondaryHit(hit); } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/WarstrikeSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/WarstrikeSpecialHandler.java index 8982b198b..177769d5d 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/WarstrikeSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/WarstrikeSpecialHandler.java @@ -57,8 +57,8 @@ public final class WarstrikeSpecialHandler extends MeleeSwingHandler implements } state.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.049, 0.98)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.1)); + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 2.0, 1.0)) { + hit = RandomFunction.random((int) (calculateHit(entity, victim, 1.1) * 1.1) + 1); } state.setEstimatedHit(hit); if (victim instanceof Player) { @@ -68,10 +68,10 @@ public final class WarstrikeSpecialHandler extends MeleeSwingHandler implements if (left > 0) { left = -victim.getSkills().updateLevel(Skills.STRENGTH, -left, 0); if (left > 0) { - left = -victim.getSkills().updateLevel(Skills.ATTACK, -left, 0); + left = (int) -(victim.getSkills().getPrayerPoints() + left); + victim.getSkills().decrementPrayerPoints(left); if (left > 0) { - left = (int) -(victim.getSkills().getPrayerPoints() + left); - victim.getSkills().decrementPrayerPoints(left); + left = -victim.getSkills().updateLevel(Skills.ATTACK, -left, 0); if (left > 0) { left = -victim.getSkills().updateLevel(Skills.MAGIC, -left, 0); if (left > 0) diff --git a/Server/src/main/content/global/handlers/item/equipment/special/WeakenSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/WeakenSpecialHandler.java index 0fda16c8c..b8f45d90f 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/WeakenSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/WeakenSpecialHandler.java @@ -57,18 +57,19 @@ public final class WeakenSpecialHandler extends MeleeSwingHandler implements Plu state.setStyle(CombatStyle.MELEE); int hit = 0; if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.0, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); + hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); + if (victim instanceof Player) { + ((Player) victim).getPacketDispatch().sendMessage("You have been drained."); + } + // TODO 10% drain to demons + int lower = (int) (victim.getSkills().getStaticLevel(Skills.DEFENCE) * 0.05) + 1; + victim.getSkills().updateLevel(Skills.DEFENCE, -lower, 0); + lower = (int) (victim.getSkills().getStaticLevel(Skills.ATTACK) * 0.05) + 1; + victim.getSkills().updateLevel(Skills.ATTACK, -lower, 0); + lower = (int) (victim.getSkills().getStaticLevel(Skills.STRENGTH) * 0.05) + 1; + victim.getSkills().updateLevel(Skills.STRENGTH, -lower, 0); } state.setEstimatedHit(hit); - if (victim instanceof Player) { - ((Player) victim).getPacketDispatch().sendMessage("You have been drained."); - } - int lower = (int) (victim.getSkills().getLevel(Skills.DEFENCE) * 0.05); - victim.getSkills().updateLevel(Skills.DEFENCE, -lower, 0); - int lower2 = (int) (victim.getSkills().getLevel(Skills.ATTACK) * 0.05); - victim.getSkills().updateLevel(Skills.ATTACK, -lower2, 0); - int lower3 = (int) (victim.getSkills().getLevel(Skills.STRENGTH) * 0.05); - victim.getSkills().updateLevel(Skills.STRENGTH, -lower3, 0); return hit; } diff --git a/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java b/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java index 82afc70ab..4fda74d6b 100644 --- a/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java +++ b/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java @@ -27,7 +27,7 @@ import static core.api.ContentAPIKt.inInventory; public class DiangoReclaimInterface extends ComponentPlugin { private static final int COMPONENT_ID = 468; public static final List ITEMS = new ArrayList<>(20); - public static final Item[] HOLIDAY_ITEMS = {new Item(Items.YO_YO_4079), new Item(Items.REINDEER_HAT_10507), new Item(Items.RUBBER_CHICKEN_4566),new Item(Items.ZOMBIE_HEAD_6722), new Item(6857), new Item(6856), new Item(6858), new Item(6859), new Item(6860), new Item(6861), new Item(6862), new Item(6863), new Item(9920), new Item(9921),new Item(9922), new Item(9923), new Item(9924), new Item(9925), new Item(11019), new Item(11020), new Item(11021), new Item(11022), new Item(11789), new Item(11949), new Item(12634), new Item(14076), new Item(14077), new Item(14081),new Item(14595), new Item(14602), new Item(14603), new Item(14605), new Item(14654), new Item(Items.RED_MARIONETTE_6867), new Item(Items.GREEN_MARIONETTE_6866), new Item(Items.BLUE_MARIONETTE_6865)}; + public static final Item[] HOLIDAY_ITEMS = {new Item(Items.YO_YO_4079), new Item(Items.REINDEER_HAT_10507), new Item(Items.WINTUMBER_TREE_10508), new Item(Items.RUBBER_CHICKEN_4566),new Item(Items.ZOMBIE_HEAD_6722), new Item(6857), new Item(6856), new Item(6858), new Item(6859), new Item(6860), new Item(6861), new Item(6862), new Item(6863), new Item(9920), new Item(9921),new Item(9922), new Item(9923), new Item(9924), new Item(9925), new Item(11019), new Item(11020), new Item(11021), new Item(11022), new Item(11789), new Item(11949), new Item(12645), new Item(14076), new Item(14077), new Item(14081),new Item(14595), new Item(14602), new Item(14603), new Item(14605), new Item(14654), new Item(Items.ICE_AMULET_14596), new Item(Items.RED_MARIONETTE_6867), new Item(Items.GREEN_MARIONETTE_6866), new Item(Items.BLUE_MARIONETTE_6865)}; //initialize the plugin, add lists of items to the ITEMS list... @Override diff --git a/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt b/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt index 207102b5f..15d12ca62 100644 --- a/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt +++ b/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt @@ -24,9 +24,11 @@ class CrystalKeyCreateListener : InteractionListener { return@onUseWith false } - addItem(player, Items.CRYSTAL_KEY_989) - sendMessage(player, "You join the loop half of a key and the tooth half of a key to make a crystal key.") + if (!addItem(player, Items.CRYSTAL_KEY_989)) { + return@onUseWith false + } + sendMessage(player, "You join the loop half of a key and the tooth half of a key to make a crystal key.") return@onUseWith true } } diff --git a/Server/src/main/content/global/handlers/item/withitem/GrindToothListener.kt b/Server/src/main/content/global/handlers/item/withitem/GrindToothListener.kt new file mode 100644 index 000000000..93a6989da --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/GrindToothListener.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt b/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt index 1dcd15cd4..318f8a0a8 100644 --- a/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt +++ b/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt @@ -15,10 +15,11 @@ class OilFishingRodListener : InteractionListener { override fun pulse(): Boolean { when (counter++) { 1 -> { - removeItem(player, used.asItem()) && removeItem(player, with.asItem()) - addItem(player, Items.VIAL_229) - addItem(player, Items.OILY_FISHING_ROD_1585) - sendMessage(player, "You rub the oil into the fishing rod.") + if (removeItem(player, used.asItem()) && removeItem(player, with.asItem()) && + addItem(player, Items.VIAL_229) && + addItem(player, Items.OILY_FISHING_ROD_1585)) { + sendMessage(player, "You rub the oil into the fishing rod.") + } } } return false @@ -35,9 +36,10 @@ class OilFishingRodListener : InteractionListener { when (counter++) { 0 -> player.animator.animate(Animation(364)) 3 -> { - removeItem(player, Items.THIN_SNAIL_3363) - removeItem(player, Items.SAMPLE_BOTTLE_3377) - addItem(player, Items.BLAMISH_SNAIL_SLIME_1581) + if (removeItem(player, Items.THIN_SNAIL_3363) && + removeItem(player, Items.SAMPLE_BOTTLE_3377)) { + addItem(player, Items.BLAMISH_SNAIL_SLIME_1581) + } } } return false diff --git a/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt b/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt index da829ed82..f332b0590 100644 --- a/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt +++ b/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt @@ -14,11 +14,10 @@ class WatermelonOnSack : InteractionListener { override fun defineListeners() { onUseWith(IntType.ITEM, SACK, WATERMELON){ player, used, _ -> if(getStatLevel(player, Skills.FARMING) >= 23){ - removeItem(player,SACK, Container.INVENTORY) - removeItem(player,WATERMELON,Container.INVENTORY) - addItem(player, Items.SCARECROW_6059) - rewardXP(player, Skills.FARMING, 25.0) - sendMessage(player, "You stab the watermelon on top of the spear, finishing your scarecrow") + if (removeItem(player,SACK, Container.INVENTORY) && removeItem(player,WATERMELON,Container.INVENTORY) && addItem(player, Items.SCARECROW_6059)) { + rewardXP(player, Skills.FARMING, 25.0) + sendMessage(player, "You stab the watermelon on top of the spear, finishing your scarecrow") + } }else{ sendMessage(player, "Your Farming level is not high enough to do this") } diff --git a/Server/src/main/content/global/handlers/item/withnpc/CatOnArdougneCivilian.kt b/Server/src/main/content/global/handlers/item/withnpc/CatOnArdougneCivilian.kt index 7046b50a2..e69de29bb 100644 --- a/Server/src/main/content/global/handlers/item/withnpc/CatOnArdougneCivilian.kt +++ b/Server/src/main/content/global/handlers/item/withnpc/CatOnArdougneCivilian.kt @@ -1,56 +0,0 @@ -package content.global.handlers.item.withnpc - -import core.api.Container -import core.api.* -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import core.game.interaction.InteractionListener -import core.game.interaction.IntType - -class CatOnArdougneCivilian: InteractionListener { - - private val civilians = intArrayOf( - NPCs.CIVILIAN_785, - NPCs.CIVILIAN_786, - NPCs.CIVILIAN_787 - ) - - private val cats = intArrayOf( - Items.PET_CAT_1561, - Items.PET_CAT_1562, - Items.PET_CAT_1563, - Items.PET_CAT_1564, - Items.PET_CAT_1565, - Items.PET_CAT_1566, - Items.OVERGROWN_CAT_1567, - Items.OVERGROWN_CAT_1568, - Items.OVERGROWN_CAT_1569, - Items.OVERGROWN_CAT_1570, - Items.OVERGROWN_CAT_1571, - Items.OVERGROWN_CAT_1572, - Items.LAZY_CAT_6551, - Items.LAZY_CAT_6552, - Items.LAZY_CAT_6553, - Items.LAZY_CAT_6554, - Items.WILY_CAT_6555, - Items.WILY_CAT_6556, - Items.WILY_CAT_6557, - Items.WILY_CAT_6558, - Items.WILY_CAT_6559, - Items.WILY_CAT_6560, - Items.HELL_CAT_7582, - Items.OVERGROWN_HELLCAT_7581, - Items.LAZY_HELL_CAT_7584, - Items.WILY_HELLCAT_7585, - ) - - override fun defineListeners() { - onUseWith(IntType.NPC,cats,*civilians){ player, used, _ -> - sendItemDialogue(player,Items.DEATH_RUNE_560,"You hand over the cat.
You are given 100 Death Runes.") - player.familiarManager.removeDetails(used.idHash) - removeItem(player,used,Container.INVENTORY) - addItem(player,Items.DEATH_RUNE_560,100) - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withnpc/GCItemOnCat.kt b/Server/src/main/content/global/handlers/item/withnpc/GCItemOnCat.kt index 3209b4f45..0b54ccf55 100644 --- a/Server/src/main/content/global/handlers/item/withnpc/GCItemOnCat.kt +++ b/Server/src/main/content/global/handlers/item/withnpc/GCItemOnCat.kt @@ -10,27 +10,27 @@ import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.world.GameWorld.Pulser +import content.data.Quests class GCItemOnCat : InteractionListener { override fun defineListeners() { - val GERTCAT = "Gertrude's Cat" val BEND_DOWN = 827 onUseWith(IntType.NPC, Items.BUCKET_OF_MILK_1927, NPCs.GERTRUDES_CAT_2997) { player, used, with -> - if(getQuestStage(player, GERTCAT) == 20 && removeItem(player, used.asItem())){ + if(getQuestStage(player, Quests.GERTRUDES_CAT) == 20 && removeItem(player, used.asItem())){ addItem(player, Items.BUCKET_1925) animate(player, BEND_DOWN) //bend down sendChat(with.asNpc(), "Mew!") - setQuestStage(player, GERTCAT, 30) + setQuestStage(player, Quests.GERTRUDES_CAT, 30) } return@onUseWith true } onUseWith(IntType.NPC, Items.DOOGLE_SARDINE_1552, NPCs.GERTRUDES_CAT_2997){ player, used, with -> - if(getQuestStage(player, GERTCAT) == 30 && removeItem(player, used.asItem())){ + if(getQuestStage(player, Quests.GERTRUDES_CAT) == 30 && removeItem(player, used.asItem())){ animate(player, BEND_DOWN) sendChat(with.asNpc(), "Mew!") - setQuestStage(player, GERTCAT, 40) + setQuestStage(player, Quests.GERTRUDES_CAT, 40) } return@onUseWith true } @@ -42,7 +42,7 @@ class GCItemOnCat : InteractionListener { onUseWith(IntType.NPC, Items.THREE_LITTLE_KITTENS_13236, NPCs.GERTRUDES_CAT_2997){ player, used, with -> if(removeItem(player, used.asItem())){ - setQuestStage(player, GERTCAT, 60) + setQuestStage(player, Quests.GERTRUDES_CAT, 60) //below copied verbatim from original, I don't like it. Pulser.submit(object : Pulse(1) { var count = 0 diff --git a/Server/src/main/content/global/handlers/item/withnpc/GertrudeCatUsePlugin.java b/Server/src/main/content/global/handlers/item/withnpc/GertrudeCatUsePlugin.java index b939f3735..efddb3bed 100644 --- a/Server/src/main/content/global/handlers/item/withnpc/GertrudeCatUsePlugin.java +++ b/Server/src/main/content/global/handlers/item/withnpc/GertrudeCatUsePlugin.java @@ -14,6 +14,7 @@ import core.game.world.map.path.Pathfinder; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the plugin used to handle the use with interactions. @@ -50,7 +51,7 @@ public final class GertrudeCatUsePlugin extends UseWithHandler { public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); final NPC npc = ((NPC) event.getUsedWith()); - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); if (event.getUsedItem().getId() == 1927 && quest.getStage(player) == 20) { if (player.getInventory().remove(event.getUsedItem())) { player.getInventory().add(EMPTY_BUCKET); diff --git a/Server/src/main/content/global/handlers/item/withnpc/RopeOnLadyKeli.kt b/Server/src/main/content/global/handlers/item/withnpc/RopeOnLadyKeli.kt index dab9e0c12..3dbd34a6d 100644 --- a/Server/src/main/content/global/handlers/item/withnpc/RopeOnLadyKeli.kt +++ b/Server/src/main/content/global/handlers/item/withnpc/RopeOnLadyKeli.kt @@ -5,20 +5,19 @@ import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType +import content.data.Quests class RopeOnLadyKeli : InteractionListener { override fun defineListeners() { - val PAR = "Prince Ali Rescue" - onUseWith(IntType.NPC, Items.ROPE_954, NPCs.LADY_KELI_919) { player, used, with -> - if(getQuestStage(player, PAR) in 40..50 && getAttribute(player, "guard-drunk", false)){ + if(getQuestStage(player, Quests.PRINCE_ALI_RESCUE) in 40..50 && getAttribute(player, "guard-drunk", false)){ if(removeItem(player, used.asItem())){ sendDialogue(player, "You overpower Keli, tie her up, and put her in a cupboard.") - setQuestStage(player, PAR, 50) + setQuestStage(player, Quests.PRINCE_ALI_RESCUE, 50) setAttribute(player, "keli-gone", getWorldTicks() + 350) } } else { - if (getQuestStage(player, PAR) == 40){ + if (getQuestStage(player, Quests.PRINCE_ALI_RESCUE) == 40){ sendMessage(player, "You need to do something about the guard first.") } } diff --git a/Server/src/main/content/global/handlers/item/withobject/AmmoMouldOnFurnace.kt b/Server/src/main/content/global/handlers/item/withobject/AmmoMouldOnFurnace.kt index 5b78dd96b..0d5929d0a 100644 --- a/Server/src/main/content/global/handlers/item/withobject/AmmoMouldOnFurnace.kt +++ b/Server/src/main/content/global/handlers/item/withobject/AmmoMouldOnFurnace.kt @@ -1,7 +1,7 @@ package content.global.handlers.item.withobject +import content.data.Quests import core.api.* -import content.region.kandarin.quest.dwarfcannon.DwarfCannon import core.game.node.Node import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills @@ -19,8 +19,8 @@ class AmmoMouldOnFurnace : InteractionListener { private fun cannonBallOnUseWithHandler(player: Player, used: Node, with: Node): Boolean { face(player, with.centerLocation) - if(!isQuestComplete(player, DwarfCannon.NAME)) { - sendDialogue(player, "You need to complete the ${DwarfCannon.NAME} quest in order to do this.") + if(!isQuestComplete(player, Quests.DWARF_CANNON)) { + sendDialogue(player, "You need to complete the ${Quests.DWARF_CANNON} quest in order to do this.") return true } if (getDynLevel(player, Skills.SMITHING) < levelRequirement) { diff --git a/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt b/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt index 799084610..bbde6bc21 100644 --- a/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt @@ -35,8 +35,9 @@ class SandSourceListener : InteractionListener { animate(player, ANIMATION) } - sendMessage(player, "You fill the bucket with sand.") - addItem(player, Items.BUCKET_OF_SAND_1783) + if (addItem(player, Items.BUCKET_OF_SAND_1783)) { + sendMessage(player, "You fill the bucket with sand.") + } } animationTrigger++ diff --git a/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt b/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt index 3607fda98..13ee4000a 100644 --- a/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt @@ -61,8 +61,9 @@ class SapCollectListener : InteractionListener { override fun pulse(): Boolean { if (removeItem(player, Items.BUCKET_1925)) { animate(player, ANIMATION) - sendMessage(player, "You cut the tree and allow its sap to drip down into your bucket.") - addItem(player, Items.BUCKET_OF_SAP_4687) + if (addItem(player, Items.BUCKET_OF_SAP_4687)) { + sendMessage(player, "You cut the tree and allow its sap to drip down into your bucket.") + } return true } return false diff --git a/Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java b/Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java index 95d10cb2c..e9ef946b3 100644 --- a/Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java +++ b/Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java @@ -15,6 +15,7 @@ import core.game.node.scenery.Scenery; import core.plugin.Plugin; import core.plugin.Initializable; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the option handler used for smithing. @@ -68,7 +69,7 @@ public final class SmithingPlugin extends UseWithHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - if (((Scenery) event.getUsedWith()).getId() == 2782 && !player.getQuestRepository().isComplete("Doric's Quest")) { + if (((Scenery) event.getUsedWith()).getId() == 2782 && !player.getQuestRepository().isComplete(Quests.DORICS_QUEST)) { player.getDialogueInterpreter().sendDialogue("Property of Doric the Dwarf."); return true; } diff --git a/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt b/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt index 45b0dad76..084e7f117 100644 --- a/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt @@ -82,9 +82,11 @@ class SpiritShieldBlessListener : InteractionListener { return@onUseWith false } - addItem(player, Items.BLESSED_SPIRIT_SHIELD_13736) - sendMessage(player, "You successfully bless the shield using the holy elixir.") + if (!addItem(player, Items.BLESSED_SPIRIT_SHIELD_13736)) { + return@onUseWith false + } + sendMessage(player, "You successfully bless the shield using the holy elixir.") return@onUseWith true } diff --git a/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt b/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt index a53198e44..59a8e2049 100644 --- a/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt @@ -7,6 +7,7 @@ import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.world.map.Location import org.rs09.consts.Sounds /** @@ -14,8 +15,10 @@ import org.rs09.consts.Sounds * @author Ceikry */ class WaterSourceListener : InteractionListener { - //this is ugly! - private val waterSources = intArrayOf(21355, 16302, 6827, 11661, 24160, 34577, 15936, 15937, 15938, 23920, 35469, 24265, 153, 879, 880, 2864, 6232, 10436, 10437, 10827, 11007, 11759, 21764, 22973, 24161, 24214, 24265, 28662, 30223, 30820, 34579, 36781, 873, 874, 4063, 6151, 8699, 9143, 9684, 10175, 12279, 12974, 13563, 13564, 14868, 14917, 14918, 15678, 16704, 16705, 20358, 22715, 24112, 24314, 25729, 25929, 26966, 29105, 33458, 34082, 34411, 34496, 34547, 34566, 35762, 36971, 37154, 37155, 878, 884, 3264, 3305, 3359, 4004, 4005, 6097, 6249, 6549, 8747, 8927, 11793, 12201, 12897, 24166, 26945, 31359, 32023, 32024, 34576, 35671, 40063, 13561, 13563, 13559, 12089) + // NOTE '9695' should be removed from the array below once the sink object can be used directly, this is a temporary + // solution. + // this is ugly! + private val waterSources = intArrayOf(21355, 16302, 6827, 11661, 24160, 34577, 15936, 15937, 15938, 23920, 35469, 24265, 153, 879, 880, 2864, 6232, 10436, 10437, 10827, 11007, 11759, 21764, 22973, 24161, 24214, 24265, 28662, 30223, 30820, 34579, 36781, 873, 874, 4063, 6151, 8699, 9143, 9684, 9695, 10175, 12279, 12974, 13563, 13564, 14868, 14917, 14918, 15678, 16704, 16705, 20358, 22715, 24112, 24314, 25729, 25929, 26966, 29105, 33458, 34082, 34411, 34496, 34547, 34566, 35762, 36971, 37154, 37155, 878, 884, 3264, 3305, 3359, 4004, 4005, 6097, 6249, 6549, 8747, 8927, 11793, 12201, 12897, 24166, 26945, 31359, 32023, 32024, 34576, 35671, 40063, 13561, 13563, 13559, 12089) private val nonWellableMsg = "If I drop my @ down there, I don't think I'm likely to get it back." private val animation = Animation(832) @@ -31,6 +34,11 @@ class WaterSourceListener : InteractionListener { return@onUseWith true } + if(with.id == 9695 && player.location.regionId != 11571) + { + return@onUseWith false + } + //ugly achievement code, achievement system sux if(vessel == WaterVessel.BUCKET && with.id == 11661) { @@ -59,6 +67,15 @@ class WaterSourceListener : InteractionListener { return@onUseWith true } + + // This is needed to make the crafting guild sink work, but technically it's wrong as it is the noticeboard and + // not the sink being used. This is a temporary solution until the sink object can be used directly. + setDest(IntType.SCENERY, intArrayOf(9695), "use") { player, node -> + if (player.location.regionId == 11571){ + return@setDest Location.create(2934, 3280, 0) + } + return@setDest node.location + } } private fun formatMsgText(name: String, template: String): String diff --git a/Server/src/main/content/global/handlers/npc/BankerNPC.kt b/Server/src/main/content/global/handlers/npc/BankerNPC.kt index e59ac6b69..7dff2f0d7 100644 --- a/Server/src/main/content/global/handlers/npc/BankerNPC.kt +++ b/Server/src/main/content/global/handlers/npc/BankerNPC.kt @@ -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 } diff --git a/Server/src/main/content/global/handlers/npc/GuardNPC.java b/Server/src/main/content/global/handlers/npc/GuardNPC.java index ccdf7e10d..9cde38cc7 100644 --- a/Server/src/main/content/global/handlers/npc/GuardNPC.java +++ b/Server/src/main/content/global/handlers/npc/GuardNPC.java @@ -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}. diff --git a/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt index 5235fc82a..4f5764b0d 100644 --- a/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt +++ b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt @@ -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) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/npc/RatNPC.java b/Server/src/main/content/global/handlers/npc/RatNPC.java index 11f4fce94..f8a380db4 100644 --- a/Server/src/main/content/global/handlers/npc/RatNPC.java +++ b/Server/src/main/content/global/handlers/npc/RatNPC.java @@ -7,6 +7,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.Initializable; import core.game.world.map.Location; +import content.data.Quests; /** * Represents a rat npc. @@ -51,7 +52,7 @@ public class RatNPC extends AbstractNPC { super.finalizeDeath(killer); if (killer instanceof Player) { final Player p = ((Player) killer); - if (p.getQuestRepository().getQuest("Witch's Potion").isStarted(p)) { + if (p.getQuestRepository().getQuest(Quests.WITCHS_POTION).isStarted(p)) { GroundItemManager.create(RAT_TAIL, getLocation(), p); } } diff --git a/Server/src/main/content/global/handlers/npc/SheepBehavior.kt b/Server/src/main/content/global/handlers/npc/SheepBehavior.kt index d9767f8a4..fbd283e77 100644 --- a/Server/src/main/content/global/handlers/npc/SheepBehavior.kt +++ b/Server/src/main/content/global/handlers/npc/SheepBehavior.kt @@ -17,6 +17,7 @@ import org.rs09.consts.NPCs import org.rs09.consts.Sounds import core.game.world.map.Location import core.game.world.map.Direction +import content.data.Quests private val sheepIds = intArrayOf( NPCs.SHEEP_42, @@ -74,7 +75,7 @@ class SheepBehavior : NPCBehavior(*sheepIds), InteractionListener { val sheep = node as NPC sheep.faceTemporary(player, 1) if (sheep.id == NPCs.SHEEP_3579) { - if (player.questRepository.getQuest("Sheep Shearer").isStarted(player)) { + if (player.questRepository.getQuest(Quests.SHEEP_SHEARER).isStarted(player)) { setAttribute(player, ATTR_IS_PENGUIN_SHEEP_SHEARED, true) } animate(player, Animation(893)) @@ -104,8 +105,10 @@ class SheepBehavior : NPCBehavior(*sheepIds), InteractionListener { sheep.locks.lockMovement(2) sheep.transform(NPCs.SHEEP_5153) playAudio(player, Sounds.SHEAR_SHEEP_761) + if (!addItem(player, Items.WOOL_1737)) { // 5160 + return@on false + } sendMessage(player, "You get some wool.") - addItem(player, Items.WOOL_1737) // 5160 GameWorld.Pulser.submit(object : Pulse(80, sheep) { override fun pulse(): Boolean { sheep.reTransform() diff --git a/Server/src/main/content/global/handlers/scenery/DoogleLeafPlugin.java b/Server/src/main/content/global/handlers/scenery/DoogleLeafPlugin.java deleted file mode 100644 index 99da19d9c..000000000 --- a/Server/src/main/content/global/handlers/scenery/DoogleLeafPlugin.java +++ /dev/null @@ -1,39 +0,0 @@ -package content.global.handlers.scenery; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the doogle leaf plugin for this object. - * @author 'Vexia - * @version 1.0 - */ -public class DoogleLeafPlugin extends OptionHandler { - - /** - * Represents the leaf item. - */ - private static final Item LEAF = new Item(1573, 1); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(31155).getHandlers().put("option:pick-leaf", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (!player.getInventory().add(LEAF)) { - player.getPacketDispatch().sendMessage("You don't have have enough space in your inventory."); - } else { - player.getPacketDispatch().sendMessage("You pick some doogle leaves."); - } - return true; - } - -} diff --git a/Server/src/main/content/global/handlers/scenery/DoorManagingPlugin.java b/Server/src/main/content/global/handlers/scenery/DoorManagingPlugin.java index 295badfd8..b6d18c0fa 100644 --- a/Server/src/main/content/global/handlers/scenery/DoorManagingPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/DoorManagingPlugin.java @@ -11,6 +11,9 @@ import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Plugin used for handling the opening/closing of (double) @@ -39,6 +42,14 @@ public final class DoorManagingPlugin extends OptionHandler { if (name.contains("drawers") || name.contains("wardrobe") || name.contains("cupboard")) { switch(option) { case "open": + if (name.contains("drawers")) { + playAudio(player, Sounds.DRAWER_OPEN_64); + } else if (name.contains("wardrobe")) { + animate(player, 545, false); + playAudio(player, Sounds.WARDROBE_OPEN_96); + } else if (name.contains("cupboard")) { + playAudio(player, Sounds.CUPBOARD_OPEN_58); + } case "go-through": if (object.isActive()) { SceneryBuilder.replace(object, object.transform(object.getId() + 1), 80); @@ -46,6 +57,14 @@ public final class DoorManagingPlugin extends OptionHandler { return true; case "close": case "shut": + if (name.contains("drawers")) { + playAudio(player, Sounds.DRAWER_CLOSE_63); + } else if (name.contains("wardrobe")) { + animate(player, 544, false); + playAudio(player, Sounds.WARDROBE_CLOSE_95); + } else if (name.contains("cupboard")) { + playAudio(player, Sounds.CUPBOARD_CLOSE_57); + } SceneryBuilder.replace(object, object.transform(object.getId() - 1)); return true; } diff --git a/Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java b/Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java index 307c09b20..1d0fa5c07 100644 --- a/Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java @@ -72,7 +72,7 @@ public final class FieldPickingPlugin extends OptionHandler { player.dispatch(new ResourceProducedEvent(reward.getId(), reward.getAmount(), node, -1)); if (plant.name().startsWith("NETTLES") && (player.getEquipment().get(EquipmentContainer.SLOT_HANDS) == null || player.getEquipment().get(EquipmentContainer.SLOT_HANDS) != null && !player.getEquipment().get(EquipmentContainer.SLOT_HANDS).getName().contains("glove"))) { player.getPacketDispatch().sendMessage("You have been stung by the nettles!"); - player.getImpactHandler().manualHit(player, 2, HitsplatType.POISON); + player.getImpactHandler().manualHit(player, 6, HitsplatType.POISON); return true; } if (plant.respawn != -1 && plant != PickingPlant.FLAX) { diff --git a/Server/src/main/content/global/handlers/scenery/LadderManagingPlugin.java b/Server/src/main/content/global/handlers/scenery/LadderManagingPlugin.java index 0cb9a6500..9712fe790 100644 --- a/Server/src/main/content/global/handlers/scenery/LadderManagingPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/LadderManagingPlugin.java @@ -23,6 +23,8 @@ public final class LadderManagingPlugin extends OptionHandler { SceneryDefinition.setOptionHandler("climb-up", this); SceneryDefinition.setOptionHandler("climb-down", this); SceneryDefinition.setOptionHandler("climb", this); + SceneryDefinition.setOptionHandler("walk-up", this); + SceneryDefinition.setOptionHandler("walk-down", this); return this; } diff --git a/Server/src/main/content/global/handlers/scenery/MillingListener.kt b/Server/src/main/content/global/handlers/scenery/MillingListener.kt index c61a8c82a..f88132cc1 100644 --- a/Server/src/main/content/global/handlers/scenery/MillingListener.kt +++ b/Server/src/main/content/global/handlers/scenery/MillingListener.kt @@ -85,13 +85,15 @@ class MillingListener : InteractionListener { if (removeItem(player, EMPTY_POT)) { if (getAttribute(player, "milling:sweetcorn", 0) > 0) { setAttribute(player, "/save:milling:sweetcorn", (getAttribute(player, "milling:sweetcorn", 0) - 1)) - addItem(player, POT_OF_CORNFLOUR) - sendMessage(player, if (player.getAttribute("milling:sweetcorn", 0) > 0) "You fill a pot with cornflour from the bin." else "You fill a pot with the last of the cornflour in the bin.") + if (addItem(player, POT_OF_CORNFLOUR)) { + sendMessage(player, if (player.getAttribute("milling:sweetcorn", 0) > 0) "You fill a pot with cornflour from the bin." else "You fill a pot with the last of the cornflour in the bin.") + } } else if (getAttribute(player, "milling:grain", 0) > 0) { setAttribute(player, "/save:milling:grain", (getAttribute(player, "milling:grain", 0) - 1)) - addItem(player, POT_OF_FLOUR) - sendMessage(player, if (player.getAttribute("milling:grain", 0) > 0) "You fill a pot with flour from the bin." else "You fill a pot with the last of the flour in the bin.") + if (addItem(player, POT_OF_FLOUR)) { + sendMessage(player, if (player.getAttribute("milling:grain", 0) > 0) "You fill a pot with flour from the bin." else "You fill a pot with the last of the flour in the bin.") + } } if (getAttribute(player, "milling:sweetcorn", 0) + getAttribute(player, "milling:grain", 0) <= 0) { setVarp(player, VARP, 0, true) diff --git a/Server/src/main/content/global/handlers/scenery/SignpostListener.kt b/Server/src/main/content/global/handlers/scenery/SignpostListener.kt index 837462159..583aa259d 100644 --- a/Server/src/main/content/global/handlers/scenery/SignpostListener.kt +++ b/Server/src/main/content/global/handlers/scenery/SignpostListener.kt @@ -11,18 +11,21 @@ class SignpostListener : InteractionListener { override fun defineListeners() { on(Scenery.SIGNPOST_18493, IntType.SCENERY, "read") { player, node -> if (node.asScenery().location.equals(Location(3235, 3228))) { + // Authentic setInterfaceText(player, "Head north towards Fred's farm, and the windmill.", 135, 3) // North setInterfaceText(player, "South to the swamps of Lumbridge.", 135, 9) // South setInterfaceText(player, "Cross the bridge and head east to Al Kharid or north to Varrock.", 135, 8) // East setInterfaceText(player, "West to the Lumbridge Castle and Draynor Village. Beware the goblins!", 135, 12) // West openInterface(player, Components.AIDE_COMPASS_135) } else if (node.asScenery().location.equals(Location(3261, 3230))) { + // Authentic setInterfaceText(player, "North to farms and Varrock.", 135, 3) // North setInterfaceText(player, "The River Lum lies to the south.", 135, 9) // South setInterfaceText(player, "East to Al Kharid - toll gate; bring some money.", 135, 8) // East setInterfaceText(player, "West to Lumbridge.", 135, 12) // West openInterface(player, Components.AIDE_COMPASS_135) } else if (node.asScenery().location.equals(Location(2983, 3278))) { + // Authentic setInterfaceText(player, "North to the glorious White Knights' city of Falador.", 135, 3) // North setInterfaceText(player, "South to Rimmington.", 135, 9) // South setInterfaceText(player, "East to Port Sarim and Draynor Village.", 135, 8) // East @@ -45,12 +48,14 @@ class SignpostListener : InteractionListener { } on(Scenery.SIGNPOST_24263, IntType.SCENERY, "read") { player, node -> if (node.asScenery().location.equals(Location(3268, 3332))) { + // Authentic setInterfaceText(player, "Sheep lay this way.", 135, 3) // North setInterfaceText(player, "South through farms to Al Kharid and Lumbridge.", 135, 9) // South setInterfaceText(player, "East to Al Kharid mine and follow the path north to Varrock east gate.", 135, 8) // East setInterfaceText(player, "West to Champion's Guild and Varrock south gate.", 135, 12) // West openInterface(player, Components.AIDE_COMPASS_135) } else if (node.asScenery().location.equals(Location(3283, 3333))) { + // Authentic setInterfaceText(player, "North to Varrock mine and Varrock east gate.", 135, 3) // North setInterfaceText(player, "South to large Mining area and Al Kharid.", 135, 9) // South setInterfaceText(player, "Follow the path east to the Dig Site.", 135, 8) // East @@ -66,7 +71,13 @@ class SignpostListener : InteractionListener { return@on true } on(Scenery.SIGNPOST_4132, IntType.SCENERY, "read") { player, node -> - if (node.asScenery().location.equals(Location(3166, 3286))) { + if (node.asScenery().location.equals(Location(3223, 3427))) { + setInterfaceText(player, "North to Varrock Palace.", 135, 3) // North + setInterfaceText(player, "South to the Champion's Guild.", 135, 9) // South + setInterfaceText(player, "East to the Dig Site.", 135, 8) // East + setInterfaceText(player, "West to Barbarian Village and Falador.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else if (node.asScenery().location.equals(Location(3166, 3286))) { setInterfaceText(player, "North to the windmill.", 135, 3) // North setInterfaceText(player, "South to a fishing pond next to Fred's farm.", 135, 9) // South setInterfaceText(player, "East to Lumbridge.", 135, 8) // East @@ -133,6 +144,56 @@ class SignpostListener : InteractionListener { } return@on true } + on(Scenery.SIGNPOST_4135, IntType.SCENERY, "read") { player, node -> + if (node.asScenery().location.equals(Location(3448, 3486))) { + setInterfaceText(player, "North to the Slayer Tower.", 135, 3) // North + setInterfaceText(player, "South to Mort Myre Swamp.", 135, 9) // South + setInterfaceText(player, "East to Canifis.", 135, 8) // East + setInterfaceText(player, "West to Varrock.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else { + setInterfaceText(player, "North to unknown.", 135, 3) // North + setInterfaceText(player, "South to unknown.", 135, 9) // South + setInterfaceText(player, "East to unknown.", 135, 8) // East + setInterfaceText(player, "West to unknown.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } + return@on true + } + on(Scenery.SIGNPOST_31296, IntType.SCENERY, "read") { player, node -> + if (node.asScenery().location.equals(Location(3304, 3109))) { + // Authentic + setInterfaceText(player, "North to Al Kharid.", 135, 3) // North + setInterfaceText(player, "South to the Desert Mining Camp and Pollnivneach.", 135, 9) // South + setInterfaceText(player, "East and across the river to the Ruins of Uzer.", 135, 8) // East + setInterfaceText(player, "West to the Kalphite Lair.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else { + setInterfaceText(player, "North to unknown.", 135, 3) // North + setInterfaceText(player, "South to unknown.", 135, 9) // South + setInterfaceText(player, "East to unknown.", 135, 8) // East + setInterfaceText(player, "West to unknown.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } + return@on true + } + on(Scenery.STONE_SIGNPOST_11630, IntType.SCENERY, "read") { player, node -> + // Authentic // https://youtu.be/vvHXKTOh_g4 + if (node.asScenery().location.equals(Location(2967, 3412))) { + setInterfaceText(player, "North to Goblin Village.", 135, 3) // North + setInterfaceText(player, "South to Falador.", 135, 9) // South + setInterfaceText(player, "East to Varrock.", 135, 8) // East + setInterfaceText(player, "West to Taverley.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else { + setInterfaceText(player, "North to unknown.", 135, 3) // North + setInterfaceText(player, "South to unknown.", 135, 9) // South + setInterfaceText(player, "East to unknown.", 135, 8) // East + setInterfaceText(player, "West to unknown.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } + return@on true + } } /** * Old handlers of Signpost. Not all should be opening the AIDE_COMPASS. diff --git a/Server/src/main/content/global/skill/agility/WildernessCourse.kt b/Server/src/main/content/global/skill/agility/WildernessCourse.kt index ab6644421..00f08cf31 100644 --- a/Server/src/main/content/global/skill/agility/WildernessCourse.kt +++ b/Server/src/main/content/global/skill/agility/WildernessCourse.kt @@ -116,25 +116,19 @@ class WildernessCourse when (counter++) { 0 -> { AgilityHandler.forceWalk(player, -1, Location.create(x, 3937, 0), Location.create(x, 3940, 0), Animation.create(10580), 15, 0.0, null, 1) //10 - println("1") player.teleporter.send(Location.create(3004, 3947, 0), TeleportManager.TeleportType.INSTANT, TeleportManager.WILDY_TELEPORT) - println("tele") counter++ AgilityHandler.forceWalk(player, 0, Location.create(x, 3948, 0), Location.create(x, 3950, 0), Animation.create(10579), 20, 12.5, null, 5) //20 - println("3") return true } 2 -> { player.teleporter.send(Location.create(3004, 3947, 0), TeleportManager.TeleportType.INSTANT, TeleportManager.WILDY_TELEPORT) - println("tele") counter++ AgilityHandler.forceWalk(player, 0, Location.create(x, 3948, 0), Location.create(x, 3950, 0), Animation.create(10579), 20, 12.5, null, 5) - println("3") return true } 3 -> { AgilityHandler.forceWalk(player, 0, Location.create(x, 3948, 0), Location.create(x, 3950, 0), Animation.create(10579), 20, 12.5, null, 5) - println("3") return true } } diff --git a/Server/src/main/content/global/skill/agility/shortcuts/BarSqueezeShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/BarSqueezeShortcut.java index 2321706b1..efe0f727f 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/BarSqueezeShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/BarSqueezeShortcut.java @@ -9,6 +9,7 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import content.data.Quests; /** * Handles the bar squeezing shortcut. @@ -61,7 +62,7 @@ public class BarSqueezeShortcut extends AgilityShortcut { @Override public boolean checkRequirements(Player player) { - if (!player.getQuestRepository().isComplete("Priest in Peril") && !(player.getLocation().getY() >= 3159 && player.getLocation().getY() <= 3161)) { + if (!player.getQuestRepository().isComplete(Quests.PRIEST_IN_PERIL) && !(player.getLocation().getY() >= 3159 && player.getLocation().getY() <= 3161)) { player.getDialogueInterpreter().sendDialogue("You need to have completed Priest in Peril in order to do this."); return false; } diff --git a/Server/src/main/content/global/skill/agility/shortcuts/CatherbyGrappleShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/CatherbyGrappleShortcut.kt deleted file mode 100644 index 2cefab3da..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/CatherbyGrappleShortcut.kt +++ /dev/null @@ -1,114 +0,0 @@ -package content.global.skill.agility.shortcuts - -import core.api.* -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items -import org.rs09.consts.Scenery -import core.game.interaction.IntType -import core.game.interaction.InteractionListener - -/** - * Handles the Catherby to Taverley grapple shortcut - * @author Byte - */ -class CatherbyGrappleShortcut : InteractionListener { - - companion object { - private val START_LOCATION: Location = Location.create(2866, 3429, 0) - private val END_LOCATION: Location = Location.create(2869,3430,0) - - private val REQUIREMENTS = hashMapOf( - Skills.AGILITY to 32, - Skills.RANGE to 35, - Skills.STRENGTH to 35 - ) - - private val VALID_CROSSBOWS = intArrayOf( - Items.MITH_CROSSBOW_9181, - Items.ADAMANT_CROSSBOW_9183, - Items.RUNE_CROSSBOW_9185, - Items.DORGESHUUN_CBOW_8880 - ) - } - - private var rocks = getScenery(Location.create(2869,3429, 0)) - - override fun defineListeners() { - flagInstant() // execute listeners instantly without determining path - - on(Scenery.ROCKS_17042, IntType.SCENERY, "grapple") { player, _ -> - if (isPlayerInRangeToGrapple(player)) { - forceWalk(player, START_LOCATION, "smart") - } else { - sendMessage(player, "Nothing interesting happens.") - return@on true - } - - if (!doesPlayerHaveRequiredItemsEquipped(player)) { - sendDialogue(player, "You need a Mithril crossbow and a Mithril grapple in order to do this.") - return@on true - } - - if (!doesPlayerHaveRequiredLevels(player)) { - sendDialogueLines(player, - "You need at least " + - REQUIREMENTS[Skills.AGILITY] + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " + - REQUIREMENTS[Skills.RANGE] + " " + Skills.SKILL_NAME[Skills.RANGE] + ", ", - "and " + - REQUIREMENTS[Skills.STRENGTH] + " " + Skills.SKILL_NAME[Skills.STRENGTH] + " to use this shortcut." - ) - return@on true - } - - lock(player, 15) - submitWorldPulse(object : Pulse(2) { - var counter = 0 - override fun pulse() : Boolean { - when (counter++) { - 1 -> { - face(player, END_LOCATION) - // Audit: shows player climbing (probably a wall), need a cliff climb animation - animate(player, Animation(4455)) - } - 3 -> { - // Audit: shows grapple on rocks, but there is no rope - replaceScenery(rocks!!, rocks!!.id + 1, 10) - } - 8 -> { - teleport(player, END_LOCATION) - } - 9 -> { - sendMessage(player, "You successfully grapple the rock and climb the cliffside.") - unlock(player) - return true - } - } - return false - } - }) - - return@on true - } - } - - private fun doesPlayerHaveRequiredItemsEquipped(player: Player): Boolean { - return inEquipment(player, Items.MITH_GRAPPLE_9419) && anyInEquipment(player, *VALID_CROSSBOWS) - } - - private fun doesPlayerHaveRequiredLevels(player: Player): Boolean { - for ((skill, requiredLevel) in REQUIREMENTS) { - if (!hasLevelDyn(player, skill, requiredLevel)) { - return false - } - } - return true - } - - private fun isPlayerInRangeToGrapple(player: Player): Boolean { - return inBorders(player, START_LOCATION.x - 2, START_LOCATION.y - 2, START_LOCATION.x, START_LOCATION.y) - } -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/FaladorGrapplePlugin.java b/Server/src/main/content/global/skill/agility/shortcuts/FaladorGrapplePlugin.java deleted file mode 100644 index f9bf6f6a9..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/FaladorGrapplePlugin.java +++ /dev/null @@ -1,145 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import org.rs09.consts.Items; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.impl.ForceMovement; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.net.packet.PacketRepository; -import core.net.packet.context.MinimapStateContext; -import core.net.packet.out.MinimapState; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents the plugin used to handle the grappling of the falador wall. - * - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FaladorGrapplePlugin extends OptionHandler { - private static final HashMap REQUIREMENTS = new HashMap<>(); - private static String requirementsString; - - static { - REQUIREMENTS.putIfAbsent(Skills.AGILITY, 11); - REQUIREMENTS.putIfAbsent(Skills.RANGE, 19); - REQUIREMENTS.putIfAbsent(Skills.STRENGTH, 37); - - requirementsString = "You need at least " - + REQUIREMENTS.get(Skills.AGILITY) + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " - + REQUIREMENTS.get(Skills.RANGE) + " " + Skills.SKILL_NAME[Skills.RANGE] + ", and " - + REQUIREMENTS.get(Skills.STRENGTH) + " " + Skills.SKILL_NAME[Skills.STRENGTH] - + " to use this shortcut."; - } - - private static final int[] CBOWS = new int[]{ - Items.MITH_CROSSBOW_9181, - Items.ADAMANT_CROSSBOW_9183, - Items.RUNE_CROSSBOW_9185, - Items.DORGESHUUN_CBOW_8880 - }; - private static final Item MITH_GRAPPLE = new Item(9419); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(17049).getHandlers().put("option:grapple", this); - SceneryDefinition.forId(17050).getHandlers().put("option:grapple", this); - SceneryDefinition.forId(17051).getHandlers().put("option:jump", this); - SceneryDefinition.forId(17052).getHandlers().put("option:jump", this); - return this; - } - - @Override - public boolean handle(final Player player, final Node node, String option) { - Location destination; - Location current = player.getLocation(); - - switch (option) { - case "jump": - ForceMovement.run(player, - current, - node.asScenery().getId() == 17051 - ? Location.create(3033, 3390, 0) - : Location.create(3032, 3388, 0), - new Animation(7268), - 10); - break; - case "grapple": - destination = node.asScenery().getId() == 17049 - ? Location.create(3033, 3389, 1) - : Location.create(3032, 3391, 1); - - for (Map.Entry e : REQUIREMENTS.entrySet()) { - if (player.getSkills().getLevel(e.getKey()) < e.getValue()) { - player.getDialogueInterpreter().sendDialogue(requirementsString); - return true; - } - } - - if (!player.getEquipment().containsAtLeastOneItem(CBOWS) || !player.getEquipment().containsItem(MITH_GRAPPLE)) { - player.getDialogueInterpreter().sendDialogue("You need a Mithril crossbow and a Mithril grapple in order to do this."); - return true; - } - - player.lock(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 1; - Component tab; - - @Override - public boolean pulse() { - switch (counter++) { - case 1: - player.faceLocation(destination); - player.visualize(new Animation(4455), new Graphics(760, 100)); - break; - case 8: - tab = player.getInterfaceManager().getSingleTab(); - player.getInterfaceManager().openOverlay(new Component(115)); - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); - player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); - break; - case 13: - player.getProperties().setTeleportLocation(destination); - break; - case 14: - player.getInterfaceManager().restoreTabs(); - if (tab != null) { - player.getInterfaceManager().openTab(tab); - } - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); - player.getInterfaceManager().closeOverlay(); - player.getInterfaceManager().close(); - player.unlock(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 1, 2); - return true; - } - return false; - } - }); - break; - } - return true; - } - - @Override - public Location getDestination(final Node moving, final Node destination) { - return destination.asScenery().getId() == 17050 ? Location.create(3032, 3388, 0) : null; - } - -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/KaramjaGrapple.java b/Server/src/main/content/global/skill/agility/shortcuts/KaramjaGrapple.java deleted file mode 100644 index b623cf8f9..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/KaramjaGrapple.java +++ /dev/null @@ -1,145 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import org.rs09.consts.Items; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; -import java.util.Map; - -@Initializable -public class KaramjaGrapple extends OptionHandler { - private static final HashMap REQUIREMENTS = new HashMap<>(); - private static final String requirementsString; - - static { - REQUIREMENTS.putIfAbsent(Skills.AGILITY, 53); - REQUIREMENTS.putIfAbsent(Skills.RANGE, 42); - REQUIREMENTS.putIfAbsent(Skills.STRENGTH, 21); - - requirementsString = "You need at least " - + REQUIREMENTS.get(Skills.AGILITY) + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " - + REQUIREMENTS.get(Skills.RANGE) + " " + Skills.SKILL_NAME[Skills.RANGE] + ", and " - + REQUIREMENTS.get(Skills.STRENGTH) + " " + Skills.SKILL_NAME[Skills.STRENGTH] - + " to use this shortcut."; - } - - private static final int[] CBOWS = new int[]{ - Items.MITH_CROSSBOW_9181, - Items.ADAMANT_CROSSBOW_9183, - Items.RUNE_CROSSBOW_9185, - Items.DORGESHUUN_CBOW_8880 - }; - private static final Item MITH_GRAPPLE = new Item(9419); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(17074).getHandlers().put("option:grapple", this); - // island tree 17074 +1 rope loop, +2 grappled one way, +3 grappled other way - // north tree 17056 +1 rope loop, +2 grappled - // south tree 17059 +1 rope loop, +2 grappled - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Location destination; - Location current = player.getLocation(); - Scenery startTree, endTree; - Direction direction; - if (current.getY() > 3134) { // starting at north side - startTree = RegionManager.getObject(Location.create(2874, 3144, 0)); - endTree = RegionManager.getObject(Location.create(2873, 3125, 0)); - destination = Location.create(2874, 3127, 0); - direction = Direction.SOUTH; - } else { - startTree = RegionManager.getObject(Location.create(2873, 3125, 0)); - endTree = RegionManager.getObject(Location.create(2874, 3144, 0)); - destination = Location.create(2874, 3142, 0); - direction = Direction.NORTH; - } - Scenery islandTree = RegionManager.getObject(Location.create(2873, 3134, 0)); - - - switch (option) { - case "grapple": - - for (Map.Entry e : REQUIREMENTS.entrySet()) { - if (player.getSkills().getLevel(e.getKey()) < e.getValue()) { - player.getDialogueInterpreter().sendDialogue(requirementsString); - return true; - } - } - - if (!player.getEquipment().containsAtLeastOneItem(CBOWS) || !player.getEquipment().containsItem(MITH_GRAPPLE)) { - player.getDialogueInterpreter().sendDialogue("You need a Mithril crossbow and a Mithril grapple in order to do this."); - return true; - } - - player.lock(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 1; - Component tab; - - @Override - public boolean pulse() { - switch (counter++) { - // TODO animations not implemented. - // See ~11min in https://www.youtube.com/watch?v=qpB53rzYqrA - // don't know how to get ropes to show up. The tree objects have grapples and stuff but don't look like the video and aren't the right directions - // splash gfx are 68 and 69, not sure why there are two - // not sure what swimming animation is, could be 4464 thru 4468 - case 1: - player.faceLocation(player.getLocation().transform(direction)); - player.animate(new Animation(4230)); - break; - case 3: - //player.getPacketDispatch().sendPositionedGraphic(67, 10, 0, player.getLocation().transform(direction, 5)); // - break; - case 4: - //ObjectBuilder.replace(startTree, startTree.transform(startTree.getId() + 1), 10); - //ObjectBuilder.replace(islandTree, islandTree.transform(islandTree.getId() + 1), 10); - break; - case 5: - break; - case 13: - player.getProperties().setTeleportLocation(destination); - break; - case 14: - player.unlock(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 6); - return true; - } - return false; - } - }); - break; - } - return true; - } - - @Override - public Location getDestination(final Node moving, final Node destination) { - // Run between tree and water before firing grapple - if (moving.getLocation().getY() > 3134) { // starting at north side - return Location.create(2874, 3142, 0); - } else { - return Location.create(2874, 3127, 0); - } - } -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt index bb3120ecc..035ed53d7 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt @@ -40,6 +40,7 @@ class SteppingStoneShortcut : OptionHandler() { } val offset = getOffset(player,finalDest) player.debug("Offset: ${offset.first},${offset.second}") + closeAllInterfaces(player) lock(player, 3) player.locks.lockTeleport(3) queueScript(player, 2, QueueStrength.SOFT) { @@ -87,4 +88,4 @@ class SteppingStoneShortcut : OptionHandler() { */ private val ANIMATION = Animation(741) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.kt index 3116cecd2..e492b6b9d 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.kt @@ -19,6 +19,7 @@ class StileShortcut : InteractionListener { val startLoc = p.location.transform(direction, 1) val endLoc = p.location.transform(direction, 2) + closeAllInterfaces(p) p.walkingQueue.reset() p.walkingQueue.addPath(startLoc.x, startLoc.y) forceMove(p, startLoc, endLoc, 0, animationCycles(839), direction, 839) @@ -75,4 +76,4 @@ class StileShortcut : InteractionListener { Horizontal, Vertical } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java index 7388fde01..42cf77e34 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java @@ -15,6 +15,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles a tunnel shortcut. @@ -73,7 +74,7 @@ public class TunnelShortcut extends AgilityShortcut { @Override public void run(final Player player, Scenery object, String option, boolean failed) { if (object.getId() == 14922) { - if (!hasRequirement(player, "Swan Song")) + if (!hasRequirement(player, Quests.SWAN_SONG)) return; } player.lock(6); diff --git a/Server/src/main/content/global/skill/agility/shortcuts/WaterOrbGrapple.java b/Server/src/main/content/global/skill/agility/shortcuts/WaterOrbGrapple.java deleted file mode 100644 index ab59e66d7..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/WaterOrbGrapple.java +++ /dev/null @@ -1,126 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import org.rs09.consts.Items; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; -import java.util.Map; - -@Initializable -public class WaterOrbGrapple extends OptionHandler { - private static final HashMap REQUIREMENTS = new HashMap<>(); - private static final String requirementsString; - - static { - REQUIREMENTS.putIfAbsent(Skills.AGILITY, 36); - REQUIREMENTS.putIfAbsent(Skills.RANGE, 39); - REQUIREMENTS.putIfAbsent(Skills.STRENGTH, 22); - - requirementsString = "You need at least " - + REQUIREMENTS.get(Skills.AGILITY) + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " - + REQUIREMENTS.get(Skills.RANGE) + " " + Skills.SKILL_NAME[Skills.RANGE] + ", and " - + REQUIREMENTS.get(Skills.STRENGTH) + " " + Skills.SKILL_NAME[Skills.STRENGTH] - + " to use this shortcut."; - } - - private static final int[] CBOWS = new int[]{ - Items.MITH_CROSSBOW_9181, - Items.ADAMANT_CROSSBOW_9183, - Items.RUNE_CROSSBOW_9185, - Items.DORGESHUUN_CBOW_8880 - }; - private static final Item MITH_GRAPPLE = new Item(9419); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(17062).getHandlers().put("option:grapple", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Location destination; - Location current = player.getLocation(); - Scenery rock = RegionManager.getObject(Location.create(2841, 3426, 0)); - Scenery tree = RegionManager.getObject(Location.create(2841, 3434, 0)); - - switch (option) { - case "grapple": - destination = Location.create(2841, 3433, 0); - - for (Map.Entry e : REQUIREMENTS.entrySet()) { - if (player.getSkills().getLevel(e.getKey()) < e.getValue()) { - player.getDialogueInterpreter().sendDialogue(requirementsString); - return true; - } - } - - if (!player.getEquipment().containsAtLeastOneItem(CBOWS) || !player.getEquipment().containsItem(MITH_GRAPPLE)) { - player.getDialogueInterpreter().sendDialogue("You need a Mithril crossbow and a Mithril grapple in order to do this."); - return true; - } - - player.lock(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 1; - Component tab; - - @Override - public boolean pulse() { - switch (counter++) { - // TODO this animation sequence is wrong. sendPositionedGraphic doesn't work correctly, and rest of water crossing not well implemented - // See 4:24 in https://www.youtube.com/watch?v=O90y-N_vwTc - // rope gfx is 67 - // splash gfx are 68 and 69, not sure why there are two - // not sure what swimming animation is, could be 4464 thru 4468 - case 1: - player.faceLocation(destination); - player.animate(new Animation(4230)); - break; - case 3: - player.getPacketDispatch().sendPositionedGraphic(67, 10, 0, Location.create(2840,3427,0)); // - break; - case 4: - SceneryBuilder.replace(rock, rock.transform(rock.getId() + 1), 10); - SceneryBuilder.replace(tree, tree.transform(tree.getId() + 1), 10); - break; - case 5: - break; - case 13: - player.getProperties().setTeleportLocation(destination); - break; - case 14: - player.unlock(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 10); - return true; - } - return false; - } - }); - break; - } - return true; - } - - @Override - public Location getDestination(final Node moving, final Node destination) { - // Run between rock and stream before firing grapple - return Location.create(2841, 3427, 0); - } -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/YanilleGrapple.java b/Server/src/main/content/global/skill/agility/shortcuts/YanilleGrapple.java deleted file mode 100644 index d37e1964a..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/YanilleGrapple.java +++ /dev/null @@ -1,121 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.impl.ForceMovement; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.net.packet.PacketRepository; -import core.net.packet.context.MinimapStateContext; -import core.net.packet.out.MinimapState; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; -import java.util.Map; - -@Initializable -public class YanilleGrapple extends OptionHandler { - private static final HashMap REQUIREMENTS = new HashMap<>(); - private static String requirementsString; - - static { - REQUIREMENTS.putIfAbsent(Skills.AGILITY, 39); - REQUIREMENTS.putIfAbsent(Skills.RANGE, 21); - REQUIREMENTS.putIfAbsent(Skills.STRENGTH, 38); - - requirementsString = "You need at least " - + REQUIREMENTS.get(Skills.AGILITY) + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " - + REQUIREMENTS.get(Skills.RANGE) + " " + Skills.SKILL_NAME[Skills.RANGE] + ", and " - + REQUIREMENTS.get(Skills.STRENGTH) + " " + Skills.SKILL_NAME[Skills.STRENGTH] - + " to use this shortcut."; - } - - private static final Item MITH_CBOW = new Item(9181); - private static final Item MITH_GRAPPLE = new Item(9419); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(17047).getHandlers().put("option:grapple", this); - SceneryDefinition.forId(17048).getHandlers().put("option:jump", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Location destination; - Location current = player.getLocation(); - - switch (option) { - case "jump": - ForceMovement.run(player, - current, - current.getY() < 3074 ? Location.create(2556,3072,0) : Location.create(2556,3075,0), - new Animation(7268), - 10); - break; - case "grapple": - destination = current.getY() < 3073 - ? Location.create(2556, 3073, 1) - : Location.create(2556, 3074, 1); - - for (Map.Entry e : REQUIREMENTS.entrySet()) { - if (player.getSkills().getLevel(e.getKey()) < e.getValue()) { - player.getDialogueInterpreter().sendDialogue(requirementsString); - return true; - } - } - - if (!player.getEquipment().containsItem(MITH_CBOW) || !player.getEquipment().containsItem(MITH_GRAPPLE)) { - player.getDialogueInterpreter().sendDialogue("You need a Mithril crossbow and a Mithril grapple in order to do this."); - return true; - } - - player.lock(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 1; - Component tab; - - @Override - public boolean pulse() { - switch (counter++) { - case 1: - player.faceLocation(destination); - player.visualize(new Animation(4455), new Graphics(760, 100)); - break; - case 8: - tab = player.getInterfaceManager().getSingleTab(); - player.getInterfaceManager().openOverlay(new Component(115)); - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); - player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); - break; - case 13: - player.getProperties().setTeleportLocation(destination); - break; - case 14: - player.getInterfaceManager().restoreTabs(); - if (tab != null) { - player.getInterfaceManager().openTab(tab); - } - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); - player.getInterfaceManager().closeOverlay(); - player.getInterfaceManager().close(); - player.unlock(); - return true; - } - return false; - } - }); - break; - } - return true; - } -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractGrappleShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractGrappleShortcut.kt new file mode 100644 index 000000000..d6672efe1 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractGrappleShortcut.kt @@ -0,0 +1,136 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.scenery.Scenery +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items + +abstract class AbstractGrappleShortcut : InteractionListener { + /** + * Make sure that you have flagInstant in your listeners. + * If you do not the player will run and try to touch the grapple point + */ + private val VALID_CROSSBOWS = intArrayOf( + Items.MITH_CROSSBOW_9181, + Items.ADAMANT_CROSSBOW_9183, + Items.RUNE_CROSSBOW_9185, + Items.DORGESHUUN_CBOW_8880 + ) + + protected abstract val REQUIREMENTS: HashMap + + protected abstract val grappleStartLocation: Location + protected abstract val grappleEndLocation: Location + + // use lazy so that the skill requirements can be populated after the child builds them + private val requirementString1: String by lazy { + "You need at least " + + REQUIREMENTS[Skills.AGILITY] + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " + + REQUIREMENTS[Skills.RANGE] + " " + Skills.SKILL_NAME[Skills.RANGE] + "," + } + private val requirementString2: String by lazy { + "and " + + REQUIREMENTS[Skills.STRENGTH] + " " + Skills.SKILL_NAME[Skills.STRENGTH] + " to use this shortcut." + } + + + // What needs to have its model changed during a grapple + protected abstract val grappleScenery: List + + // What animation to use when getting the player across + protected abstract val animation: Animation + // How long should the animation last (in ticks) + protected abstract val animationDuration: Int + + protected abstract fun animation(animationStage: Int, player: Player): Boolean + + + // The message that can appear if there should be one after grappling + protected val message: String? = null + + private fun getRequirementString(): Array { + val requirementString = arrayOf(requirementString1, requirementString2) + return requirementString + } + + private fun doesPlayerHaveRequiredItemsEquipped(player: Player): Boolean { + return inEquipment(player, Items.MITH_GRAPPLE_9419) && anyInEquipment(player, *VALID_CROSSBOWS) + } + + private fun doesPlayerHaveRequiredLevels(player: Player): Boolean { + for ((skill, requiredLevel) in REQUIREMENTS) { + if (!hasLevelDyn(player, skill, requiredLevel)) { + return false + } + } + return true + } + + protected open fun isPlayerInRangeToGrapple(player: Player, startLoc: Location, range: Int): Boolean { + return inBorders(player, startLoc.x - range, startLoc.y - range, + startLoc.x + range, startLoc.y + range) + } + + /** + * See if the [player] is close enough to the [startLoc] (based on [range] to + * try and grapple. This will return false if the [player] is too far away, + * if the player does not have the right levels or if the player does not have + * the correct gear equipped. + */ + protected fun canGrapple(player: Player, startLoc: Location, range: Int): Boolean { + if (isPlayerInRangeToGrapple(player, startLoc, range)) { + forceWalk(player, startLoc, "smart") + } else { + // todo should this be "you are too far away" or something like that? + sendMessage(player, "Nothing interesting happens.") + return false + } + + if (!doesPlayerHaveRequiredItemsEquipped(player)) { + sendDialogue(player, "You need a Mithril crossbow and a Mithril grapple in order to do this.") + return false + } + + if (!doesPlayerHaveRequiredLevels(player)) { + sendDialogueLines(player, + *getRequirementString() + ) + return false + } + return true + } + + protected fun grapple(player: Player, message: String?): Boolean { + closeAllInterfaces(player) + lock(player, animationDuration) + // TODO is this right? should we force the player to cross? + queueScript(player, strength = QueueStrength.SOFT) { stage: Int -> + if (animation(stage, player)){ + // We're done with the animation + return@queueScript stopExecuting(player) + } + else{ + return@queueScript delayScript(player, 1) + } + } + + message?.let{ + player.sendMessage(message) + } + return true + } + + + /** + * If an achievement diary can be updated it should be done here + */ + protected open fun updateDiary(player: Player): Boolean{ + return false + } + +} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractOneWayGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractOneWayGrapple.kt new file mode 100644 index 000000000..7645b0315 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractOneWayGrapple.kt @@ -0,0 +1,39 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.node.entity.player.Player + +abstract class AbstractOneWayGrapple : AbstractGrappleShortcut() { + + + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + // Point towards the grapple landing zone + face(player, grappleEndLocation) + // Start the grapple animation + animate(player, animation) + } + + 5 -> { + for (tgt in grappleScenery) { + // Add grapple effects to all scenery (for 10 ticks) + replaceScenery(tgt!!, tgt.id + 1, 10) + } + } + + 5 + animationDuration -> { + // After the animation is done teleport to the landing zone + teleport(player, grappleEndLocation) + } + + 5 + animationDuration + 1 -> { + // free the player + unlock(player) + updateDiary(player) + return true + } + } + return false + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractTwoWayGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractTwoWayGrapple.kt new file mode 100644 index 000000000..4cbb3aa1c --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractTwoWayGrapple.kt @@ -0,0 +1,16 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location + +abstract class AbstractTwoWayGrapple : AbstractGrappleShortcut(){ + + abstract var direction: Direction? + + abstract var startLoc: Location? + abstract var endLoc: Location? + + protected abstract fun setStartEndSide(player: Player, margin: Int = 5) + protected abstract fun getGrappleScenery(direction: Direction): List +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/AlKharidGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AlKharidGrapple.kt new file mode 100644 index 000000000..5f98b03e4 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AlKharidGrapple.kt @@ -0,0 +1,108 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Scenery + +class AlKharidGrapple : AbstractTwoWayGrapple(){ + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 8, Skills.RANGE to 37, Skills.STRENGTH to 19) + + // Lumbridge + override val grappleStartLocation: Location = Location(3246, 3179, 0) + + // Al Kharid + override val grappleEndLocation: Location = Location(3259, 3179, 0) + + override var direction: Direction? = null + override var startLoc: Location? = null + override var endLoc: Location? = null + + override var grappleScenery: List = listOf() + + override val animation: Animation = Animation(4230) + override val animationDuration: Int = 9 + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + face(player, endLoc!!) + animate(player, animation) + } + + 5 -> { + for (tgt in grappleScenery) { + if ((tgt!!.id == 17068)) { + // This is the raft + continue + } + replaceScenery(tgt, tgt.id + 1, 10) + } + } + + 5 + animationDuration -> { + teleport(player, endLoc!!) + } + 5 + animationDuration + 1 -> { + unlock(player) + updateDiary(player) + return true + } + } + return false + } + + override fun defineListeners() { + flagInstant() // execute listeners instantly without determining path + + on(Scenery.BROKEN_RAFT_17068, IntType.SCENERY, "grapple") { player, target -> + // Check if we are on the east or the west of the broken raft + // East = Lum + setStartEndSide(player) + if (!canGrapple(player, startLoc!!, 2)){ + return@on true + } + grapple(player,message) + return@on true + } + } + + + override fun setStartEndSide(player: Player, margin: Int) { + if (player.location.x < grappleStartLocation.x + margin){ + // We're on the west side + direction = Direction.EAST // got to jump east + startLoc = grappleStartLocation + endLoc = grappleEndLocation + } + else { + // we're on the east side + direction = Direction.WEST // got to jump west + startLoc = grappleEndLocation + endLoc = grappleStartLocation + } + + grappleScenery = getGrappleScenery(direction!!) + } + + override fun getGrappleScenery(direction: Direction): List { + val lumbridgeTree = getScenery(Location(3244, 3179, 0)) + val alKharidTree = getScenery(Location(3260, 3178, 0)) + val startTree : core.game.node.scenery.Scenery? + val endTree : core.game.node.scenery.Scenery? + val raft = getScenery(Location(3252, 3179, 0)) + if (direction == Direction.EAST){ + startTree = lumbridgeTree + endTree = alKharidTree + } + else{ + startTree = alKharidTree + endTree = lumbridgeTree + } + return listOf(startTree,endTree, raft) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/CatherbyGrappleShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/CatherbyGrappleShortcut.kt new file mode 100644 index 000000000..e5bcd2a8f --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/CatherbyGrappleShortcut.kt @@ -0,0 +1,43 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.plugin.Initializable +import kotlin.collections.HashMap + + +@Initializable +class CatherbyGrappleShortcut : AbstractOneWayGrapple(){ + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 32, Skills.RANGE to 35, Skills.STRENGTH to 35) + + override val grappleStartLocation: Location = Location.create(2866, 3429, 0) + + override val grappleEndLocation: Location = Location.create(2869,3430,0) + + // todo this is the wrong animation + override val animation: Animation = Animation(4455) + + override val animationDuration: Int = 9 + + override val grappleScenery: List = listOf( + getScenery(Location.create(2869,3429, 0)) // rocks + ) + + override fun defineListeners() { + flagInstant() + + on(Scenery.ROCKS_17042, IntType.SCENERY, "grapple"){ player, _ -> + if (!canGrapple(player, grappleStartLocation, 1)) { + return@on true + } + grapple(player, message) + return@on true + } + } + +} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/FaladorGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/FaladorGrapple.kt new file mode 100644 index 000000000..58e8e2fe2 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/FaladorGrapple.kt @@ -0,0 +1,123 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import org.rs09.consts.Scenery + +abstract class AbstractFaladorGrapple(private var wallGrappleInterface: WallGrappleInterface = WallGrappleInterfaceImpl()): AbstractOneWayGrapple() { + + override val animation: Animation = Animation(4455) + + override val animationDuration: Int = 14 + + // There are no scenery items to hook so don't let children override this + final override val grappleScenery: List = listOf() + + protected fun jump(player: Player, destination: Location): Boolean { + return wallGrappleInterface.jump(player, destination) + } + + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + player.faceLocation(grappleEndLocation) + visualize(player, animation, Graphics(760, 100)) + } + + 8 -> { + wallGrappleInterface.fadeToBlack(player) + } + + 13 -> teleport(player, grappleEndLocation) + 14 -> { + wallGrappleInterface.showGame(player) + unlock(player) + updateDiary(player) + return true + } + } + return false + } + +} +@Initializable +class FaladorGrappleNorth : AbstractFaladorGrapple() { + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 11, Skills.RANGE to 19, Skills.STRENGTH to 37) + + override val grappleStartLocation: Location = Location.create(3033, 3390, 0) + + + override val grappleEndLocation: Location = Location.create(3033, 3389, 1) + + override fun defineListeners() { + flagInstant() + on(Scenery.WALL_17049, IntType.SCENERY, "grapple") { player, _ -> + if(!canGrapple(player, grappleStartLocation, 4)) { + return@on true + } + grapple(player, message) + return@on true + } + + on(Scenery.WALL_17051, IntType.SCENERY, "jump"){ player, _ -> + jump(player, grappleStartLocation) + return@on true + } + } + + override fun isPlayerInRangeToGrapple(player: Player, startLoc: Location, range: Int): Boolean { + // Do not let the player grapple from the other side of the wall + return inBorders(player, startLoc.x - range, startLoc.y, + startLoc.x + range, startLoc.y + 2) + } + + override fun updateDiary(player: Player): Boolean { + player.achievementDiaryManager.finishTask(player, DiaryType.FALADOR, 1, 2) + return true + } +} + +@Initializable +class FaladorGrappleSouth : AbstractFaladorGrapple() { + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 11, Skills.RANGE to 19, Skills.STRENGTH to 37) + + override val grappleStartLocation: Location = Location.create(3032, 3388, 0) + + override val grappleEndLocation: Location = Location.create(3032, 3389, 1) + + override fun defineListeners() { + flagInstant() + on(Scenery.WALL_17050, IntType.SCENERY, "grapple") { player, _ -> + if(!canGrapple(player, grappleStartLocation, 4)) { + return@on true + } + grapple(player, message) + return@on true + } + + on(Scenery.WALL_17052, IntType.SCENERY, "jump"){ player, _ -> + jump(player, grappleStartLocation) + return@on true + } + } + + override fun isPlayerInRangeToGrapple(player: Player, startLoc: Location, range: Int): Boolean { + // Do not let the player grapple from the other side of the wall + return inBorders(player, startLoc.x - range, startLoc.y, + startLoc.x + range, startLoc.y - 2) + } + + override fun updateDiary(player: Player): Boolean { + player.achievementDiaryManager.finishTask(player, DiaryType.FALADOR, 1, 2) + return true + } +} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/KaramjaGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/KaramjaGrapple.kt new file mode 100644 index 000000000..96337db8e --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/KaramjaGrapple.kt @@ -0,0 +1,108 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import org.rs09.consts.Scenery + +@Initializable +class KaramjaGrapple : AbstractTwoWayGrapple(){ + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 53, Skills.RANGE to 42, Skills.STRENGTH to 21) + + // South + override val grappleStartLocation: Location = Location.create(2874, 3127, 0) + + + // North + override val grappleEndLocation: Location = Location.create(2874,3142,0) + + override var direction: Direction? = null + override var startLoc: Location? = null + override var endLoc: Location? = null + + override var grappleScenery: List = listOf() + + override val animation: Animation = Animation(4230) + override val animationDuration: Int = 9 + + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + face(player, endLoc!!) + animate(player, animation) + } + + 5 -> { + for (tgt in grappleScenery) { + replaceScenery(tgt!!, tgt.id + 1, 10) + } + } + + 5 + animationDuration -> { + teleport(player, endLoc!!) + } + 5 + animationDuration + 1 -> { + unlock(player) + updateDiary(player) + return true + } + } + return false + } + + override fun defineListeners() { + flagInstant() + + on(Scenery.STRONG_TREE_17074, IntType.SCENERY, "grapple"){ player, _ -> + setStartEndSide(player) + if(!canGrapple(player, startLoc!!, 1)){ + return@on true + } + grapple(player, message) + return@on true + } + + } + + override fun setStartEndSide(player: Player, margin: Int) { + if (player.location.y > grappleEndLocation.y - margin){ // we're on the north side + direction = Direction.SOUTH // got to jump south + startLoc = grappleEndLocation + endLoc = grappleStartLocation + } + else { + direction = Direction.NORTH // got to jump north + startLoc = grappleStartLocation + endLoc = grappleEndLocation + } + + grappleScenery = getGrappleScenery(direction!!) + } + + override fun getGrappleScenery(direction: Direction): List { + val startTree : core.game.node.scenery.Scenery? + val endTree : core.game.node.scenery.Scenery? + val islandTree = getScenery(Location(2873, 3134, 0)) + if (direction == Direction.NORTH){ + startTree = getScenery(Location(2874, 3144, 0)) + endTree = getScenery(Location(2873, 3125, 0)) + } + else{ + startTree = getScenery(Location(2874, 3144, 0)) + endTree = getScenery(Location(2873, 3125, 0)) + } + return listOf(startTree,endTree, islandTree) + } + + override fun updateDiary(player: Player): Boolean { + player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 2, 6) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/WallGrappleInterface.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/WallGrappleInterface.kt new file mode 100644 index 000000000..8eeadcf71 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/WallGrappleInterface.kt @@ -0,0 +1,69 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.component.Component +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.net.packet.PacketRepository +import core.net.packet.context.MinimapStateContext +import core.net.packet.out.MinimapState + +interface WallGrappleInterface { + var tab: Component? + fun jump(player: Player, destination: Location): Boolean + fun fadeToBlack(player: Player): Component + fun showGame(player: Player): Boolean +} + + +class WallGrappleInterfaceImpl: WallGrappleInterface{ + override var tab: Component? = null + + override fun jump(player: Player, destination: Location): Boolean { + // todo this doesn't look great compared to what it used to look like + closeAllInterfaces(player) + forceWalk(player, destination,"smart" ) + face(player, destination) + // We're teleporting if we are animating so make the strength SOFT + queueScript(player, strength = QueueStrength.SOFT){ stage: Int -> + when (stage){ + 1 -> animate(player, Animation(7268)) + 2 ->{ + teleport(player, destination) + return@queueScript stopExecuting(player) + } + } + return@queueScript delayScript(player, 1) + + } + return true + } + + + override fun fadeToBlack(player: Player): Component { + // todo make this work. Right now the tab is always null + tab = player.interfaceManager.singleTab + player.interfaceManager.openOverlay(Component(115)) + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12) + if (tab == null){ + println("Panic") + return Component(1) + } + return tab!! + } + + override fun showGame(player: Player): Boolean { + player.interfaceManager.restoreTabs() + if (tab != null) { + player.interfaceManager.openTab(tab) + } + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + closeOverlay(player) + closeInterface(player) + return true + } + +} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/WaterOrbGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/WaterOrbGrapple.kt new file mode 100644 index 000000000..8fc2a2167 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/WaterOrbGrapple.kt @@ -0,0 +1,48 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.getScenery +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import org.rs09.consts.Scenery +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable + +@Initializable +class WaterOrbGrapple : AbstractOneWayGrapple(){ + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 36, Skills.RANGE to 39, Skills.STRENGTH to 22) + + override val grappleStartLocation: Location = Location.create(2841, 3427, 0) + + override val grappleEndLocation: Location = Location.create(2841, 3433, 0) + + override val animation: Animation = Animation(4230) + + override val animationDuration: Int = 9 + + override val grappleScenery: List = listOf( + getScenery(Location.create(2841, 3426, 0)), // rock + getScenery(Location.create(2841, 3434, 0)) // tree + ) + + override fun defineListeners() { + flagInstant() + + on(Scenery.CROSSBOW_TREE_17062, IntType.SCENERY, "grapple"){ player, _ -> + if (!canGrapple(player, grappleStartLocation, 1)) { + return@on true + } + grapple(player, message) + return@on true + } + } + + override fun updateDiary(player: Player): Boolean { + player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 10) + return true + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/YanilleGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/YanilleGrapple.kt new file mode 100644 index 000000000..dc5351275 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/YanilleGrapple.kt @@ -0,0 +1,99 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.inBorders +import core.api.teleport +import core.api.unlock +import core.api.visualize +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import org.rs09.consts.Scenery + +@Initializable +class YanilleGrapple(private var wallGrappleInterface: WallGrappleInterface = WallGrappleInterfaceImpl()): AbstractTwoWayGrapple() { + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 11, Skills.RANGE to 19, Skills.STRENGTH to 37) + + override val grappleStartLocation: Location = Location.create(2556, 3072, 0) + + override val grappleEndLocation: Location = Location.create(2556, 3073, 1) + + override var direction: Direction? = null + override var startLoc: Location? = null + override var endLoc: Location? = null + override fun setStartEndSide(player: Player, margin: Int) { + // Start location is where you end after jumping in the opposite way + if (player.location.y > 3073 ){ + // We are north of the middle of the wall + startLoc = Location.create(2556, 3075, 0) // This is where you grapple from/land after jumping + endLoc = Location.create(2556, 3074, 1) // + } + else { + // We are south of the middle of the wall + startLoc = Location.create(2556, 3072, 0) + endLoc = Location.create(2556, 3073, 1) + } + } + + override fun getGrappleScenery(direction: Direction): List { + return emptyList() + } + + override var grappleScenery: List = listOf() + + override val animation: Animation = Animation(4455) + override val animationDuration: Int = 9 + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + player.faceLocation(endLoc) + visualize(player, animation, Graphics(760, 100)) + } + + 8 -> { + wallGrappleInterface.fadeToBlack(player) + } + + 13 -> teleport(player, endLoc!!) + 14 -> { + wallGrappleInterface.showGame(player) + unlock(player) + updateDiary(player) + return true + } + } + return false + } + + + override fun defineListeners() { + // Do not use flagListeners here + // The player needs to be able to touch the target + on(Scenery.WALL_17047, IntType.SCENERY, "grapple") { player, _ -> + setStartEndSide(player, 0) + if(!canGrapple(player, startLoc!!, 4)){ + return@on true + } + grapple(player, message) + return@on true + } + + on(Scenery.WALL_17048, IntType.SCENERY, "jump") { player, _ -> + setStartEndSide(player, 0) + wallGrappleInterface.jump(player, startLoc!!) + return@on true + } + } + + override fun isPlayerInRangeToGrapple(player: Player, startLoc: Location, range: Int): Boolean { + // Do not let the player grapple from the other side of the wall + return inBorders( + player, startLoc.x - range, startLoc.y, + startLoc.x + range, startLoc.y - 2) + } +} diff --git a/Server/src/main/content/global/skill/construction/BuildHotspot.java b/Server/src/main/content/global/skill/construction/BuildHotspot.java index de3fd7123..c5ca6c8e7 100644 --- a/Server/src/main/content/global/skill/construction/BuildHotspot.java +++ b/Server/src/main/content/global/skill/construction/BuildHotspot.java @@ -33,7 +33,7 @@ public enum BuildHotspot { FIREPLACE(15418, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CLAY_FIREPLACE, Decoration.STONE_FIREPLACE,Decoration.MARBLE_FIREPLACE), FIREPLACE2(15267, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CLAY_FIREPLACE, Decoration.STONE_FIREPLACE,Decoration.MARBLE_FIREPLACE), CURTAINS(15419, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.TORN_CURTAINS, Decoration.CURTAINS, Decoration.OPULENT_CURTAINS), - BOOKCASE(15416, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_BOOKCASE, Decoration.OAK_BOOKCASE, Decoration.MAHOGANY_BOOKCASE), + BOOKCASE(15416, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_BOOKCASE, Decoration.OAK_BOOKCASE, Decoration.MAHOGANY_BOOKCASE), RUG(15415, BuildHotspotType.LINKED, BuildingUtils.BUILD_LOW_ANIM, Decoration.BROWN_RUG_CORNER, Decoration.RED_RUG_CORNER, Decoration.OPULENT_RUG_CORNER), RUG2(15414, BuildHotspotType.LINKED, BuildingUtils.BUILD_LOW_ANIM, Decoration.BROWN_RUG_END, Decoration.RED_RUG_END, Decoration.OPULENT_RUG_END), RUG3(15413, BuildHotspotType.LINKED, BuildingUtils.BUILD_LOW_ANIM, Decoration.BROWN_RUG_CENTER, Decoration.RED_RUG_CENTER, Decoration.OPULENT_RUG_CENTER), @@ -59,7 +59,7 @@ public enum BuildHotspot { DINING_BENCH_1(15300, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.BENCH_WOODEN, Decoration.BENCH_OAK, Decoration.BENCH_CARVED_OAK, Decoration.BENCH_TEAK, Decoration.BENCH_CARVED_TEAK, Decoration.BENCH_MAHOGANY, Decoration.BENCH_GILDED), DINING_BENCH_2(15299, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.BENCH_WOODEN, Decoration.BENCH_OAK, Decoration.BENCH_CARVED_OAK, Decoration.BENCH_TEAK, Decoration.BENCH_CARVED_TEAK, Decoration.BENCH_MAHOGANY,Decoration.BENCH_GILDED), ROPE_BELL_PULL(15304, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.ROPE_PULL, Decoration.BELL_PULL, Decoration.FANCY_BELL_PULL), - WALL_DECORATION(15303, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_DECORATION, Decoration.TEAK_DECORATION, Decoration.GILDED_DECORATION), + WALL_DECORATION(15303, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_DECORATION, Decoration.TEAK_DECORATION, Decoration.GILDED_DECORATION), /** * Low-level Work shop hotspots. @@ -67,11 +67,11 @@ public enum BuildHotspot { REPAIR(15448, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.REPAIR_BENCH, Decoration.WHETSTONE, Decoration.ARMOUR_STAND), WORKBENCH(15439, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.WORKBENCH_WOODEN, Decoration.WORKBENCH_OAK,Decoration.WORKBENCH_STEEL_FRAME, Decoration.WORKBENCH_WITH_VICE,Decoration.WORKBENCH_WITH_LATHE), CRAFTING(15441, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRAFTING_TABLE_1, Decoration.CRAFTING_TABLE_2,Decoration.CRAFTING_TABLE_3, Decoration.CRAFTING_TABLE_4), - TOOL1(15443, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2, Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4, Decoration.TOOL_STORE_5), - TOOL2(15444, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), - TOOL3(15445, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), - TOOL4(15446, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), - TOOL5(15447, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), + TOOL1(15443, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2, Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4, Decoration.TOOL_STORE_5), + TOOL2(15444, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), + TOOL3(15445, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), + TOOL4(15446, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), + TOOL5(15447, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), HERALDRY(15450, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.PLUMING_STAND, Decoration.SHIELD_EASEL,Decoration.BANNER_EASEL), /** @@ -186,22 +186,12 @@ public enum BuildHotspot { LANDSCAPE(15393, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.LUMBRIDGE, Decoration.THE_DESERT, Decoration.MORYTANIA, Decoration.KARAMJA, Decoration.ISAFDAR), SWORD(15395, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.SILVERLIGHT, Decoration.EXCALIBUR, Decoration.DARKLIGHT), MAP(15396, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.SMALL_MAP, Decoration.MEDIUM_MAP, Decoration.LARGE_MAP), - BOOKCASE2(15397, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_BOOKCASE, Decoration.OAK_BOOKCASE, Decoration.MAHOGANY_BOOKCASE), + BOOKCASE2(15397, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_BOOKCASE, Decoration.OAK_BOOKCASE, Decoration.MAHOGANY_BOOKCASE), - /** - * Manegerie Hotspots - */ - OBELISK(44911, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.MINI_OBELISK), - PET_FEEDER(44910, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_PET_FEEDER, Decoration.TEAK_PET_FEEDER, Decoration.MAHOGANY_PET_FEEDER), - PET_HOUSE(44909, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_PET_HOUSE, Decoration.TEAK_PET_HOUSE, Decoration.MAHOGANY_PET_HOUSE, Decoration.CONSECRATED_PET_HOUSE, Decoration.DESECRATED_PET_HOUSE, Decoration.NATURAL_PET_HOUSE), - HABITAT_1(44907, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.GARDEN_HABITAT, Decoration.JUNGLE_HABITAT, Decoration.DESERT_HABITAT, Decoration.POLAR_HABITAT, Decoration.VOLCANIC_HABITAT), - HABITAT_2(44908, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.GARDEN_HABITAT, Decoration.JUNGLE_HABITAT, Decoration.DESERT_HABITAT, Decoration.POLAR_HABITAT, Decoration.VOLCANIC_HABITAT), - - /** * Combat room hotspots. */ - WALL_DECORATION2(15297, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_DECORATION, Decoration.TEAK_DECORATION, Decoration.GILDED_DECORATION), + WALL_DECORATION2(15297, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_DECORATION, Decoration.TEAK_DECORATION, Decoration.GILDED_DECORATION), STORAGE_SPACE(15296, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.GLOVE_RACK, Decoration.WEAPONS_RACK, Decoration.EXTRA_WEAPONS_RACK), CR_RING(15277, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.BOXING_RING, Decoration.FENCING_RING, Decoration.COMBAT_RING, Decoration.NOTHING, Decoration.NOTHING2), CR_CORNER(15278, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.BOXING_RING, Decoration.FENCING_RING, Decoration.COMBAT_RING, Decoration.NOTHING, Decoration.NOTHING2), @@ -237,8 +227,8 @@ public enum BuildHotspot { GLOBE(15421, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.GLOBE, Decoration.ORNAMENTAL_GLOBE, Decoration.LUNAR_GLOBE, Decoration.CELESTIAL_GLOBE, Decoration.ARMILLARY_SPHERE, Decoration.SMALL_ORREY, Decoration.LARGE_ORREY), LECTERN(15420, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_LECTERN, Decoration.EAGLE_LECTERN, Decoration.DEMON_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), CRYSTAL_BALL(15422, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRYSTAL_BALL, Decoration.ELEMENTAL_SPHERE, Decoration.CRYSTAL_OF_POWER), - BOOKCASE3(15425, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_BOOKCASE, Decoration.OAK_BOOKCASE, Decoration.MAHOGANY_BOOKCASE), - WALL_CHART(15423, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.ALCHEMICAL_CHART, Decoration.ASTRONOMICAL_CHART, Decoration.INFERNAL_CHART), + BOOKCASE3(15425, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_BOOKCASE, Decoration.OAK_BOOKCASE, Decoration.MAHOGANY_BOOKCASE), + WALL_CHART(15423, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.ALCHEMICAL_CHART, Decoration.ASTRONOMICAL_CHART, Decoration.INFERNAL_CHART), TELESCOPE(15424, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.TELESCOPE1, Decoration.TELESCOPE2, Decoration.TELESCOPE3), /** @@ -394,7 +384,6 @@ public enum BuildHotspot { linkedHotspots.add(new BuildHotspot[] { PRISON, PRISON_DOOR }); linkedHotspots.add(new BuildHotspot[] { DUNGEON_DOOR_LEFT, DUNGEON_DOOR_RIGHT }); linkedHotspots.add(new BuildHotspot[] { DUNGEON_DOOR_LEFT2, DUNGEON_DOOR_RIGHT2 }); - linkedHotspots.add(new BuildHotspot[] { HABITAT_1, HABITAT_2 }); linkedHotspots.add(new BuildHotspot[] { SMALL_PLANT_1, SMALL_PLANT1 }); linkedHotspots.add(new BuildHotspot[] { SHELVES, SHELVES_2 }); } @@ -524,4 +513,4 @@ public enum BuildHotspot { return buildingAnimation; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/BuildingUtils.java b/Server/src/main/content/global/skill/construction/BuildingUtils.java index 424e5aa94..02bfbd9c2 100644 --- a/Server/src/main/content/global/skill/construction/BuildingUtils.java +++ b/Server/src/main/content/global/skill/construction/BuildingUtils.java @@ -349,7 +349,7 @@ public final class BuildingUtils { */ public static void removeDecoration(Player player, Scenery object) { if (object.getId() == Decoration.PORTAL.getObjectId() && player.getHouseManager().getPortalAmount() <= 1) { - player.getPacketDispatch().sendMessage("You need atleast one portal, how else would you leave your house?"); + sendMessage(player, "You need at least one portal, how else would you leave your house?"); return; } Location l = object.getLocation(); @@ -369,6 +369,10 @@ public final class BuildingUtils { if (objectId == object.getId() && hotspot.getCurrentX() == l.getChunkOffsetX() && hotspot.getCurrentY() == l.getChunkOffsetY()) { player.animate(REMOVE_ANIMATION); removeDecoration(player, region, room, hotspot, object, style); + Decoration decoration = Decoration.forObjectId(object.getId()); + for (Item item : decoration.getRefundItems()) { + addItemOrDrop(player, item.getId(), item.getAmount()); + } break; } } diff --git a/Server/src/main/content/global/skill/construction/CrestType.java b/Server/src/main/content/global/skill/construction/CrestType.java index f54466772..277c77231 100644 --- a/Server/src/main/content/global/skill/construction/CrestType.java +++ b/Server/src/main/content/global/skill/construction/CrestType.java @@ -3,6 +3,7 @@ package content.global.skill.construction; import core.game.node.entity.player.Player; import org.rs09.consts.Items; import core.game.node.entity.skill.Skills; +import content.data.Quests; /** * Family crest types. @@ -16,7 +17,7 @@ public enum CrestType implements CrestRequirement { @Override public boolean eligible(Player player) { - return player.getQuestRepository().isComplete("Shield of Arrav"); + return player.getQuestRepository().isComplete(Quests.SHIELD_OF_ARRAV); } }, ASGARNIA("the symbol of Asgarnia"), // no requirements @@ -24,21 +25,21 @@ public enum CrestType implements CrestRequirement { @Override public boolean eligible(Player player) { - return player.getQuestRepository().isComplete("The Lost Tribe"); + return player.getQuestRepository().isComplete(Quests.THE_LOST_TRIBE); } }, DRAGON("a dragon") { // requires Dragon Slayer @Override public boolean eligible(Player player) { - return player.getQuestRepository().isComplete("Dragon Slayer"); + return player.getQuestRepository().isComplete(Quests.DRAGON_SLAYER); } }, FAIRY("a fairy") { // requries Lost City @Override public boolean eligible(Player player) { - return player.getQuestRepository().isComplete("Lost City"); + return player.getQuestRepository().isComplete(Quests.LOST_CITY); } }, GUTHIX("the symbol of Guthix") { // Requires 70+ Prayer diff --git a/Server/src/main/content/global/skill/construction/Decoration.java b/Server/src/main/content/global/skill/construction/Decoration.java index 058de5a8b..6229dff82 100644 --- a/Server/src/main/content/global/skill/construction/Decoration.java +++ b/Server/src/main/content/global/skill/construction/Decoration.java @@ -1,6 +1,4 @@ package content.global.skill.construction; - - import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.node.scenery.Scenery; @@ -9,1071 +7,690 @@ import org.rs09.consts.Items; /** * Represents the decorations. - * @author Emperor + * @author Emperor, Player Name * */ public enum Decoration { - /** * Garden centrepiece decorations. */ - PORTAL(13405, 8168, 1, 100.0, new Item(Items.IRON_BAR_2351, 10)), - ROCK(13406, 8169, 5, 100.0, new Item(Items.LIMESTONE_BRICK_3420, 5)), - POND(13407, 8170, 10, 100.0, new Item(Items.SOFT_CLAY_1761, 10)), - IMP_STATUE(13408, 8171, 15, 150.0, new Item(Items.LIMESTONE_BRICK_3420, 5), new Item(Items.SOFT_CLAY_1761, 5)), - SMALL_OBELISK(42004, 14657, 41, 676, new Item(Items.MARBLE_BLOCK_8786, 1), new Item(Items.SPIRIT_SHARDS_12183, 1000), new Item(Items.CRIMSON_CHARM_12160, 10), new Item(Items.BLUE_CHARM_12163, 10)), - DUNGEON_ENTRANCE(13409, 8172, 70, 500.0, new Item(Items.MARBLE_BLOCK_8786)), + PORTAL (13405, 8168, 1, 100, new Item[] { new Item(Items.IRON_BAR_2351, 10) }), + ROCK (13406, 8169, 5, 100, new Item[] { new Item(Items.LIMESTONE_BRICK_3420, 5) }), + POND (13407, 8170, 10, 100, new Item[] { new Item(Items.SOFT_CLAY_1761, 10) }), + IMP_STATUE (13408, 8171, 15, 150, new Item[] { new Item(Items.LIMESTONE_BRICK_3420, 5), new Item(Items.SOFT_CLAY_1761, 5) }), + SMALL_OBELISK (42004, 14657, 41, 676, new Item[] { new Item(Items.MARBLE_BLOCK_8786), new Item(Items.SPIRIT_SHARDS_12183, 1000), new Item(Items.CRIMSON_CHARM_12160, 10), new Item(Items.BLUE_CHARM_12163, 10) }), + DUNGEON_ENTRANCE(13409, 8172, 70, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), - /** * Garden big tree decorations. */ - BIG_DEAD_TREE(13411, 8173, 5, 31.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_DEAD_TREE_8417)), - BIG_TREE(13412, 8174, 10, 44.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_NICE_TREE_8419)), - BIG_OAK_TREE(13413, 8175, 15, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_OAK_TREE_8421)), - BIG_WILLOW_TREE(13414, 8176, 30, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_WILLOW_TREE_8423)), - BIG_MAPLE_TREE(13415, 8177, 45, 122.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_MAPLE_TREE_8425)), - BIG_YEW_TREE(13416, 8178, 60, 141.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_YEW_TREE_8427)), - BIG_MAGIC_TREE(13417, 8179, 75, 223.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_MAGIC_TREE_8429)), - + BIG_DEAD_TREE (13411, 8173, 5, 31, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_DEAD_TREE_8417) }), + BIG_TREE (13412, 8174, 10, 44, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_NICE_TREE_8419) }), + BIG_OAK_TREE (13413, 8175, 15, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_OAK_TREE_8421) }), + BIG_WILLOW_TREE(13414, 8176, 30, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_WILLOW_TREE_8423) }), + BIG_MAPLE_TREE (13415, 8177, 45, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_MAPLE_TREE_8425) }), + BIG_YEW_TREE (13416, 8178, 60, 141, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_YEW_TREE_8427) }), + BIG_MAGIC_TREE (13417, 8179, 75, 223, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_MAGIC_TREE_8429) }), + /** * Garden tree decorations. */ - DEAD_TREE(13418, 8173, 5, 31.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_DEAD_TREE_8417)), - TREE(13419, 8174, 10, 44.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_NICE_TREE_8419)), - OAK_TREE(13420, 8175, 15, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_OAK_TREE_8421)), - WILLOW_TREE(13421, 8176, 30, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_WILLOW_TREE_8423)), - MAPLE_TREE(13423, 8177, 45, 122.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_MAPLE_TREE_8425)), - YEW_TREE(13422, 8178, 60, 141.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_YEW_TREE_8427)), - MAGIC_TREE(13424, 8179, 75, 223.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_MAGIC_TREE_8429)), - + DEAD_TREE (13418, 8173, 5, 31, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_DEAD_TREE_8417) }), + TREE (13419, 8174, 10, 44, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_NICE_TREE_8419) }), + OAK_TREE (13420, 8175, 15, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_OAK_TREE_8421) }), + WILLOW_TREE(13421, 8176, 30, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_WILLOW_TREE_8423) }), + MAPLE_TREE (13423, 8177, 45, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_MAPLE_TREE_8425) }), + YEW_TREE (13422, 8178, 60, 141, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_YEW_TREE_8427) }), + MAGIC_TREE (13424, 8179, 75, 223, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_MAGIC_TREE_8429) }), + /** * Garden big plant 1 decorations. */ - FERN(13425, 8186, 1, 31.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_1_8431)), - BUSH(13426, 8187, 6, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_2_8433)), - TALL_PLANT(13427, 8188, 12, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_3_8435)), - + FERN (13425, 8186, 1, 31, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_1_8431) }), + BUSH (13426, 8187, 6, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_2_8433) }), + TALL_PLANT(13427, 8188, 12, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_3_8435) }), + /** * Garden big plant 2 decorations. */ - SHORT_PLANT(13428, 8189, 1, 31.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_1_8431)), - LARGE_LEAF_PLANT(13429, 8190, 6, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_2_8433)), - HUGE_PLANT(13430, 8191, 12, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_3_8435)), + SHORT_PLANT (13428, 8189, 1, 31, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_1_8431) }), + LARGE_LEAF_PLANT(13429, 8190, 6, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_2_8433) }), + HUGE_PLANT (13430, 8191, 12, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_3_8435) }), /** * Garden small plant 1 decorations. */ - PLANT(13431, 8180, 1, 31.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_1_8431)), - SMALL_FERN(13432, 8181, 6, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_2_8433)), - FERN_SP(13433, 8182, 12, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_3_8435)), + PLANT (13431, 8180, 1, 31, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_1_8431) }), + SMALL_FERN(13432, 8181, 6, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_2_8433) }), + FERN_SP (13433, 8182, 12, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_3_8435) }), /** * Garden small plant 2 decorations. */ - DOCK_LEAF(13434, 8183, 1, 31.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_1_8431)), - THISTLE(13435, 8184, 6, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_2_8433)), - REEDS(13436, 8185, 12, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_PLANT_3_8435)), - + DOCK_LEAF(13434, 8183, 1, 31, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_1_8431) }), + THISTLE (13435, 8184, 6, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_2_8433) }), + REEDS (13436, 8185, 12, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_PLANT_3_8435) }), + /** * Parlour chair spot */ - CRUDE_CHAIR(13581, 8309, 1, 66.0, new Item(Items.PLANK_960, 2)), - WOODEN_CHAIR(13582, 8310, 8, 96.0, new Item(Items.PLANK_960, 3)), - ROCKING_CHAIR(13583, 8311, 14, 96.0, new Item(Items.PLANK_960, 3)), - OAK_CHAIR(13584, 8312, 19, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - OAK_ARMCHAIR(13585, 8313, 26, 180.0, new Item(Items.OAK_PLANK_8778, 3)), - TEAK_ARMCHAIR(13586, 8314, 35, 180.0, new Item(Items.TEAK_PLANK_8780, 2)), - MAHOGANY_ARMCHAIR(13587, 8315, 50, 280.0, new Item(Items.MAHOGANY_PLANK_8782, 2)), + CRUDE_CHAIR (13581, 8309, 1, 58, new Item[] { new Item(Items.PLANK_960, 2) }), + WOODEN_CHAIR (13582, 8310, 8, 87, new Item[] { new Item(Items.PLANK_960, 3) }), + ROCKING_CHAIR (13583, 8311, 14, 87, new Item[] { new Item(Items.PLANK_960, 3) }), + OAK_CHAIR (13584, 8312, 19, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + OAK_ARMCHAIR (13585, 8313, 26, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), + TEAK_ARMCHAIR (13586, 8314, 35, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + MAHOGANY_ARMCHAIR(13587, 8315, 50, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), /** * Rugs rugs rugs */ - BROWN_RUG_CORNER(13588, 8316, 2, 30.0, new Item(Items.BOLT_OF_CLOTH_8790, 2)), - RED_RUG_CORNER(13591, 8317, 13, 60.0, new Item(Items.BOLT_OF_CLOTH_8790, 4)), - OPULENT_RUG_CORNER(13594, 8318, 65, 360.0, new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784, 1)), - - BROWN_RUG_END(13589, 8316, 2, 30.0, new Item(Items.BOLT_OF_CLOTH_8790, 2)), - RED_RUG_END(13592, 8317, 13, 60.0, new Item(Items.BOLT_OF_CLOTH_8790, 4)), - OPULENT_RUG_END(13595, 8318, 65, 360.0, new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784, 1)), - - BROWN_RUG_CENTER(13590, 8316, 2, 30.0, new Item(Items.BOLT_OF_CLOTH_8790, 2)), - RED_RUG_CENTER(13593, 8317, 13, 60.0, new Item(Items.BOLT_OF_CLOTH_8790, 4)), - OPULENT_RUG_CENTER(13596, 8318, 65, 360.0, new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784, 1)), + BROWN_RUG_CORNER (13588, 8316, 2, 30, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + RED_RUG_CORNER (13591, 8317, 13, 60, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + OPULENT_RUG_CORNER(13594, 8318, 65, 360, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784) }), + BROWN_RUG_END (13589, 8316, 2, 30, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + RED_RUG_END (13592, 8317, 13, 60, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + OPULENT_RUG_END (13595, 8318, 65, 360, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784) }), + BROWN_RUG_CENTER (13590, 8316, 2, 30, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + RED_RUG_CENTER (13593, 8317, 13, 60, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + OPULENT_RUG_CENTER(13596, 8318, 65, 360, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784) }), /** * Parlour fireplaces */ - CLAY_FIREPLACE(13609, 8325, 3, 30.0, new Item(Items.SOFT_CLAY_1761, 3)), - STONE_FIREPLACE(13611, 8326, 33, 40.0, new Item(Items.LIMESTONE_BRICK_3420, 2)), - MARBLE_FIREPLACE(13613, 8327, 63, 500.0, new Item(Items.MARBLE_BLOCK_8786, 1)), + CLAY_FIREPLACE (13609, 8325, 3, 30, new Item[] { new Item(Items.SOFT_CLAY_1761, 3) }), + STONE_FIREPLACE (13611, 8326, 33, 40, new Item[] { new Item(Items.LIMESTONE_BRICK_3420, 2) }), + MARBLE_FIREPLACE(13613, 8327, 63, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), /** * Parlour curtain spot */ - TORN_CURTAINS(13603, 8322, 2, 132.0, new Item(Items.PLANK_960, 3), new Item(Items.BOLT_OF_CLOTH_8790, 3)), - CURTAINS(13604, 8323, 18, 225.0, new Item(Items.OAK_PLANK_8778, 3), new Item(Items.BOLT_OF_CLOTH_8790, 3)), - OPULENT_CURTAINS(13605, 8324, 40, 315.0, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.BOLT_OF_CLOTH_8790, 3)), + TORN_CURTAINS (13603, 8322, 2, 132, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.BOLT_OF_CLOTH_8790, 3) }), + CURTAINS (13604, 8323, 18, 225, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.BOLT_OF_CLOTH_8790, 3) }), + OPULENT_CURTAINS(13605, 8324, 40, 315, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.BOLT_OF_CLOTH_8790, 3) }), /** - * Parlour bookcases + * Bookcases */ - WOODEN_BOOKCASE(13597, 8319, 4, 132.0, new Item(Items.PLANK_960, 4)), - OAK_BOOKCASE(13598, 8320, 29, 225.0, new Item(Items.OAK_PLANK_8778, 3)), - MAHOGANY_BOOKCASE(13599, 8321, 40, 315.0, new Item(Items.MAHOGANY_PLANK_8782, 3)), + WOODEN_BOOKCASE (13597, 8319, 4, 115, new Item[] { new Item(Items.PLANK_960, 4) }), + OAK_BOOKCASE (13598, 8320, 29, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), + MAHOGANY_BOOKCASE(13599, 8321, 40, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3) }), - /** * Kitchen Beer Barrels * TODO: These also require cooking levels! * Basic: 1, Cider: 14, Asgarnian: 24, Greenman's: 29, D.Bitter: 39, Chef's: 54 * */ - BASIC_BEER_BARREL(13568, 8239, 7, 87.0, new Item(Items.PLANK_960, 3)), - CIDER_BARREL(13569, 8240, 12, 91.0, new Item(Items.PLANK_960, 3), new Item(Items.CIDER_5763, 8)), - ASGARNIAN_ALE_BARREL(13570, 8241, 18, 184.0, new Item(Items.OAK_PLANK_8778, 3), new Item(Items.ASGARNIAN_ALE_1905, 8)), - GREENMANS_ALE_BARREL(13571, 8242, 26, 184.0, new Item(Items.OAK_PLANK_8778, 3), new Item(Items.GREENMANS_ALE_1909, 8)), - DRAGON_BITTER_BARREL(13572, 8243, 36, 224.0, new Item(Items.OAK_PLANK_8778, 3), new Item(Items.DRAGON_BITTER_1911, 8), new Item(Items.STEEL_BAR_2353, 2)), - CHEFS_DELIGHT_BARREL(13573, 8244, 48, 224.0, new Item(Items.OAK_PLANK_8778, 3), new Item(Items.CHEFS_DELIGHT_5755, 8), new Item(Items.STEEL_BAR_2353, 2)), - - + BASIC_BEER_BARREL (13568, 8239, 7, 87, new Item[] { new Item(Items.PLANK_960, 3) }), + CIDER_BARREL (13569, 8240, 12, 91, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.CIDER_5763, 8) }), + ASGARNIAN_ALE_BARREL(13570, 8241, 18, 184, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.ASGARNIAN_ALE_1905, 8) }), + GREENMANS_ALE_BARREL(13571, 8242, 26, 184, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.GREENMANS_ALE_1909, 8) }), + DRAGON_BITTER_BARREL(13572, 8243, 36, 224, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.DRAGON_BITTER_1911, 8), new Item(Items.STEEL_BAR_2353, 2) }), + CHEFS_DELIGHT_BARREL(13573, 8244, 48, 224, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.CHEFS_DELIGHT_5755, 8), new Item(Items.STEEL_BAR_2353, 2) }), + /** * Kitchen Tables! */ - KITCHEN_WOODEN_TABLE(13577, 8246, 12, 87.0, new Item(Items.PLANK_960, 3)), - KITCHEN_OAK_TABLE(13578, 8247, 32, 180.0, new Item(Items.OAK_PLANK_8778, 3)), - KITCHEN_TEAK_TABLE(13579, 8248, 52, 270.0, new Item(Items.TEAK_PLANK_8780, 3)), - - + KITCHEN_WOODEN_TABLE(13577, 8246, 12, 87, new Item[] { new Item(Items.PLANK_960, 3) }), + KITCHEN_OAK_TABLE (13578, 8247, 32, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), + KITCHEN_TEAK_TABLE (13579, 8248, 52, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }), + /** * Kitchen Stoves */ - BASIC_FIREPIT(13528, 8216, 5, 40.0, new Item(Items.SOFT_CLAY_1761, 2), new Item(Items.STEEL_BAR_2353, 1)), - FIREPIT_WITH_HOOK(13529, 8217, 11, 60.0, new Item(Items.SOFT_CLAY_1761, 2), new Item(Items.STEEL_BAR_2353, 2)), - FIREPIT_WITH_POT(13531, 8218, 17, 80.0, new Item(Items.SOFT_CLAY_1761, 2), new Item(Items.STEEL_BAR_2353, 3)), - SMALL_OVEN(13533, 8219, 24, 80.0, new Item(Items.STEEL_BAR_2353, 4)), - LARGE_OVEN(13536, 8220, 29, 100.0, new Item(Items.STEEL_BAR_2353, 5)), - BASIC_RANGE(13539, 8221, 34, 120.0, new Item(Items.STEEL_BAR_2353, 6)), - FANCY_RANGE(13542, 8222, 42, 160.0, new Item(Items.STEEL_BAR_2353, 8)), - + BASIC_FIREPIT (13528, 8216, 5, 40, new Item[] { new Item(Items.SOFT_CLAY_1761, 2), new Item(Items.STEEL_BAR_2353) }), + FIREPIT_WITH_HOOK(13529, 8217, 11, 60, new Item[] { new Item(Items.SOFT_CLAY_1761, 2), new Item(Items.STEEL_BAR_2353, 2) }), + FIREPIT_WITH_POT (13531, 8218, 17, 80, new Item[] { new Item(Items.SOFT_CLAY_1761, 2), new Item(Items.STEEL_BAR_2353, 3) }), + SMALL_OVEN (13533, 8219, 24, 80, new Item[] { new Item(Items.STEEL_BAR_2353, 4) }), + LARGE_OVEN (13536, 8220, 29, 100, new Item[] { new Item(Items.STEEL_BAR_2353, 5) }), + BASIC_RANGE (13539, 8221, 34, 120, new Item[] { new Item(Items.STEEL_BAR_2353, 6) }), + FANCY_RANGE (13542, 8222, 42, 160, new Item[] { new Item(Items.STEEL_BAR_2353, 8) }), + /** * Kitchen larders */ - WOODEN_LARDER(13565, 8233, 9, 228.0, new Item(Items.PLANK_960, 8)), - OAK_LARDER(13566, 8234, 33, 480.0, new Item(Items.OAK_PLANK_8778, 8)), - TEAK_LARDER(13567, 8235, 43, 750.0, new Item(Items.TEAK_PLANK_8780, 8), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - - + WOODEN_LARDER(13565, 8233, 9, 228, new Item[] { new Item(Items.PLANK_960, 8) }), + OAK_LARDER (13566, 8234, 33, 480, new Item[] { new Item(Items.OAK_PLANK_8778, 8) }), + TEAK_LARDER (13567, 8235, 43, 750, new Item[] { new Item(Items.TEAK_PLANK_8780, 8), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + /** * Kitchen shelves */ - WOODEN_SHELVES_1(13545, 8223, 6, 87.0, new Item(Items.PLANK_960, 3)), - WOODEN_SHELVES_2(13546, 8224, 12, 147.0, new Item(Items.PLANK_960, 3), new Item(Items.SOFT_CLAY_1761, 6)), - WOODEN_SHELVES_3(13547, 8225, 23, 147.0, new Item(Items.PLANK_960, 3), new Item(Items.SOFT_CLAY_1761, 6)), - OAK_SHELVES_1(13548, 8226, 34, 240.0, new Item(Items.OAK_PLANK_8778, 3), new Item(Items.SOFT_CLAY_1761, 6)), - OAK_SHELVES_2(13549, 8227, 45, 240.0, new Item(Items.OAK_PLANK_8778, 3), new Item(Items.SOFT_CLAY_1761, 6)), - TEAK_SHELVES_1(13550, 8228, 56, 330.0, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.SOFT_CLAY_1761, 6)), - TEAK_SHELVES_2(13551, 8229, 67, 930.0, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.SOFT_CLAY_1761, 6), new Item(Items.GOLD_LEAF_8784, 2)), - + WOODEN_SHELVES_1(13545, 8223, 6, 87, new Item[] { new Item(Items.PLANK_960, 3) }), + WOODEN_SHELVES_2(13546, 8224, 12, 147, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.SOFT_CLAY_1761, 6) }), + WOODEN_SHELVES_3(13547, 8225, 23, 147, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.SOFT_CLAY_1761, 6) }), + OAK_SHELVES_1 (13548, 8226, 34, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.SOFT_CLAY_1761, 6) }), + OAK_SHELVES_2 (13549, 8227, 45, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.SOFT_CLAY_1761, 6) }), + TEAK_SHELVES_1 (13550, 8228, 56, 330, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.SOFT_CLAY_1761, 6) }), + TEAK_SHELVES_2 (13551, 8229, 67, 930, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.SOFT_CLAY_1761, 6), new Item(Items.GOLD_LEAF_8784, 2) }), + /** * Kitchen sinks */ - PUMP_AND_DRAIN(13559, 8230, 7, 100.0, new Item(Items.STEEL_BAR_2353, 5)), - PUMP_AND_TUB(13561, 8231, 27, 200.0, new Item(Items.STEEL_BAR_2353, 10)), - SINK(13563, 8232, 47, 300.0, new Item(Items.STEEL_BAR_2353, 15)), - - + PUMP_AND_DRAIN(13559, 8230, 7, 100, new Item[] { new Item(Items.STEEL_BAR_2353, 5) }), + PUMP_AND_TUB (13561, 8231, 27, 200, new Item[] { new Item(Items.STEEL_BAR_2353, 10) }), + SINK (13563, 8232, 47, 300, new Item[] { new Item(Items.STEEL_BAR_2353, 15) }), + /** * Kitchen cat baskets/blankets */ - CAT_BLANKET(13574, 8236, 5, 15.0, new Item(Items.BOLT_OF_CLOTH_8790, 1)), - CAT_BASKET(13575, 8237, 19, 58.0, new Item(Items.PLANK_960, 2)), - CAST_BASKET_CUSHIONED(13576, 8238, 33, 58.0, new Item(Items.PLANK_960, 2), new Item(Items.WOOL_1737, 2)), - - + CAT_BLANKET (13574, 8236, 5, 15, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790) }), + CAT_BASKET (13575, 8237, 19, 58, new Item[] { new Item(Items.PLANK_960, 2) }), + CAST_BASKET_CUSHIONED(13576, 8238, 33, 58, new Item[] { new Item(Items.PLANK_960, 2), new Item(Items.WOOL_1737, 2) }), + /** * Dining room tables */ - DINING_TABLE_WOOD(13293, 8246, 10, 115.0, new Item(Items.PLANK_960, 4)), - DINING_TABLE_OAK(13294, 8247, 22, 240.0, new Item(Items.OAK_PLANK_8778, 4)), - DINING_TABLE_CARVED_OAK(13295, 8247, 31, 360.0, new Item(Items.OAK_PLANK_8778, 6)), - DINING_TABLE_TEAK(13296, 8248, 38, 360.0, new Item(Items.TEAK_PLANK_8780, 4)), - DINING_TABLE_CARVED_TEAK(13297, 8248, 45, 600.0, new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4)), - DINING_TABLE_MAHOGANY(13298, 8120, 52, 840.0, new Item(Items.MAHOGANY_PLANK_8782, 6)), - DINING_TABLE_OPULENT(13299, 8121, 72, 3100.0, new Item(Items.MAHOGANY_PLANK_8782, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4), - new Item(Items.GOLD_LEAF_8784, 4), new Item(Items.MARBLE_BLOCK_8786, 2)), - + DINING_TABLE_WOOD (13293, 8246, 10, 115, new Item[] { new Item(Items.PLANK_960, 4) }), + DINING_TABLE_OAK (13294, 8247, 22, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + DINING_TABLE_CARVED_OAK (13295, 8247, 31, 360, new Item[] { new Item(Items.OAK_PLANK_8778, 6) }), + DINING_TABLE_TEAK (13296, 8248, 38, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + DINING_TABLE_CARVED_TEAK(13297, 8248, 45, 600, new Item[] { new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + DINING_TABLE_MAHOGANY (13298, 8120, 52, 840, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6) }), + DINING_TABLE_OPULENT (13299, 8121, 72, 3100, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784, 4), new Item(Items.MARBLE_BLOCK_8786, 2) }), /** * Dining room benches */ - BENCH_WOODEN(13300, 8108, 10, 115.0, new Item(Items.PLANK_960, 4)), - BENCH_OAK(13301, 8109, 22, 240.0, new Item(Items.OAK_PLANK_8778, 4)), - BENCH_CARVED_OAK(13302, 8110, 31, 240.0, new Item(Items.OAK_PLANK_8778, 4)), - BENCH_TEAK(13303, 8111, 38, 360.0, new Item(Items.TEAK_PLANK_8780, 4)), - BENCH_CARVED_TEAK(13304, 8112, 44, 360.0, new Item(Items.TEAK_PLANK_8780, 4)), - BENCH_MAHOGANY(13305, 8113, 52, 560.0, new Item(Items.MAHOGANY_PLANK_8782, 6)), - BENCH_GILDED(13306, 8114, 61, 1760.0, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 4)), - + BENCH_WOODEN (13300, 8108, 10, 115, new Item[] { new Item(Items.PLANK_960, 4) }), + BENCH_OAK (13301, 8109, 22, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + BENCH_CARVED_OAK (13302, 8110, 31, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + BENCH_TEAK (13303, 8111, 38, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + BENCH_CARVED_TEAK(13304, 8112, 44, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + BENCH_MAHOGANY (13305, 8113, 52, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6) }), + BENCH_GILDED (13306, 8114, 61, 1760, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 4) }), + /** * Dining room bell-pulls */ - ROPE_PULL(13307, 8099, 5, 15.0, new Item(Items.ROPE_954, 1), new Item(Items.OAK_PLANK_8778, 1)), - BELL_PULL(13308, 8100, 19, 58.0, new Item(Items.TEAK_PLANK_8780, 1), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - FANCY_BELL_PULL(13309, 8101, 33, 58.0, new Item(Items.TEAK_PLANK_8780, 1), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784, 1)), - + ROPE_PULL (13307, 8099, 5, 15, new Item[] { new Item(Items.ROPE_954), new Item(Items.OAK_PLANK_8778) }), + BELL_PULL (13308, 8100, 19, 58, new Item[] { new Item(Items.TEAK_PLANK_8780), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + FANCY_BELL_PULL(13309, 8101, 33, 58, new Item[] { new Item(Items.TEAK_PLANK_8780), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784) }), + /** * Workshop workbench */ - WORKBENCH_WOODEN(13704, 8375, 17, 145.0, new Item(Items.PLANK_960, 1)), - WORKBENCH_OAK(13705, 8376, 32, 300.0, new Item(Items.OAK_PLANK_8778, 5)), - WORKBENCH_STEEL_FRAME(13706, 8377, 46, 440.0, new Item(Items.OAK_PLANK_8778, 6), new Item(Items.STEEL_BAR_2353, 4)), - WORKBENCH_WITH_VICE(13707, 8378, 62, 750.0, new Item(Items.STEEL_FRAMED_BENCH_8377, 1), new Item(Items.OAK_PLANK_8778, 2), new Item(Items.STEEL_BAR_2353, 1)), - WORKBENCH_WITH_LATHE(13708, 8379, 77, 1000.0, new Item(Items.OAK_WORKBENCH_8376, 1), new Item(Items.OAK_PLANK_8778, 2), new Item(Items.STEEL_BAR_2353, 1)), - + WORKBENCH_WOODEN (13704, 8375, 17, 143, new Item[] { new Item(Items.PLANK_960, 5) }), + WORKBENCH_OAK (13705, 8376, 32, 300, new Item[] { new Item(Items.OAK_PLANK_8778, 5) }), + WORKBENCH_STEEL_FRAME(13706, 8377, 46, 440, new Item[] { new Item(Items.OAK_PLANK_8778, 6), new Item(Items.STEEL_BAR_2353, 4) }), + WORKBENCH_WITH_VICE (13707, 8378, 62, 750, new Item[] { new Item(Items.STEEL_FRAMED_BENCH_8377), new Item(Items.OAK_PLANK_8778, 2), new Item(Items.STEEL_BAR_2353) }), + WORKBENCH_WITH_LATHE (13708, 8379, 77, 1000, new Item[] { new Item(Items.OAK_WORKBENCH_8376), new Item(Items.OAK_PLANK_8778, 2), new Item(Items.STEEL_BAR_2353) }), + /** * Workshop repair benches/stands */ - REPAIR_BENCH(13713, 8389, 15, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - WHETSTONE(13714, 8390, 35, 260.0, new Item(Items.OAK_PLANK_8778, 4), new Item(Items.LIMESTONE_BRICK_3420, 1)), - ARMOUR_STAND(13715, 8391, 55, 500.0, new Item(Items.OAK_PLANK_8778, 8), new Item(Items.LIMESTONE_BRICK_3420, 1)), - + REPAIR_BENCH(13713, 8389, 15, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + WHETSTONE (13714, 8390, 35, 260, new Item[] { new Item(Items.OAK_PLANK_8778, 4), new Item(Items.LIMESTONE_BRICK_3420) }), + ARMOUR_STAND(13715, 8391, 55, 500, new Item[] { new Item(Items.OAK_PLANK_8778, 8), new Item(Items.LIMESTONE_BRICK_3420) }), + /** * Workshop easels */ - PLUMING_STAND(13716, 8392, 16, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - SHIELD_EASEL(13717, 8393, 41, 240.0, new Item(Items.OAK_PLANK_8778, 4)), - BANNER_EASEL(13718, 8394, 66, 510.0, new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - + PLUMING_STAND(13716, 8392, 16, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + SHIELD_EASEL (13717, 8393, 41, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + BANNER_EASEL (13718, 8394, 66, 510, new Item[] { new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + /** * Workshop crafting tables * TODO: These are upgradable hotspots, therefore crafting table 3 would require * crafting table 2 to be already built in that spot. */ - CRAFTING_TABLE_1(13709, 8380, 16, 50.0, new Item(Items.OAK_PLANK_8778, 4)), - CRAFTING_TABLE_2(13710, 8381, 25, 100.0, new Item(Items.MOLTEN_GLASS_1775, 1)), - CRAFTING_TABLE_3(13711, 8382, 34, 175.0, new Item(Items.MOLTEN_GLASS_1775, 2)), - CRAFTING_TABLE_4(13712, 8383, 42, 240.0, new Item(Items.OAK_PLANK_8778, 2)), - + CRAFTING_TABLE_1(13709, 8380, 16, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + CRAFTING_TABLE_2(13710, 8381, 25, 1, new Item[] { new Item(Items.MOLTEN_GLASS_1775) }), + CRAFTING_TABLE_3(13711, 8382, 34, 2, new Item[] { new Item(Items.MOLTEN_GLASS_1775, 2) }), + CRAFTING_TABLE_4(13712, 8383, 42, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + /** * Workshop tool stores * These are also upgradable just like the tables above. */ - TOOL_STORE_1(13699, 8384, 15, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - TOOL_STORE_2(13700, 8385, 25, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - TOOL_STORE_3(13701, 8386, 35, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - TOOL_STORE_4(13702, 8387, 44, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - TOOL_STORE_5(13703, 8388, 55, 120.0, new Item(Items.OAK_PLANK_8778, 2)), + TOOL_STORE_1(13699, 8384, 15, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TOOL_STORE_2(13700, 8385, 25, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TOOL_STORE_3(13701, 8386, 35, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TOOL_STORE_4(13702, 8387, 44, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TOOL_STORE_5(13703, 8388, 55, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), - /** * Wall-mounted decorations */ - OAK_DECORATION(13606, 8102, 16, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - TEAK_DECORATION(13606, 8103, 36, 180.0, new Item(Items.TEAK_PLANK_8780, 2)), - GILDED_DECORATION(13607, 8104, 56, 1020.0, new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.GOLD_LEAF_8784, 2)), - + OAK_DECORATION (13606, 8102, 16, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TEAK_DECORATION (13606, 8103, 36, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + GILDED_DECORATION(13607, 8104, 56, 1020, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.GOLD_LEAF_8784, 2) }), + /** * Staircases. */ - OAK_STAIRCASE(13497, 8249, 27, 680.0, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 4)), - TEAK_STAIRCASE(13499, 8252, 48, 980.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 4)), - SPIRAL_STAIRCASE(13503, 8258, 67, 1040.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.LIMESTONE_BRICK_3420, 7)), - MARBLE_STAIRCASE(13501, 8257, 82, 3200.0, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MARBLE_BLOCK_8786, 5)), - MARBLE_SPIRAL(13505, 8259, 97, 4400.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.MARBLE_BLOCK_8786, 7)), - + OAK_STAIRCASE (13497, 8249, 27, 680, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 4) }), + TEAK_STAIRCASE (13499, 8252, 48, 980, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 4) }), + SPIRAL_STAIRCASE(13503, 8258, 67, 1040, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.LIMESTONE_BRICK_3420, 7) }), + MARBLE_STAIRCASE(13501, 8257, 82, 3200, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MARBLE_BLOCK_8786, 5) }), + MARBLE_SPIRAL (13505, 8259, 97, 4400, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.MARBLE_BLOCK_8786, 7) }), + /** * Staircases going down. */ - OAK_STAIRS_DOWN(13498, 8249, 27, 680.0, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 4)), - TEAK_STAIRS_DOWN(13500, 8252, 48, 980.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 4)), - SPIRAL_STAIRS_DOWN(13504, 8258, 67, 1040.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.LIMESTONE_BRICK_3420, 7)), - MARBLE_STAIRS_DOWN(13502, 8257, 82, 3200.0, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MARBLE_BLOCK_8786, 5)), - MARBLE_SPIRAL_DOWN(13506, 8259, 97, 4400.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.MARBLE_BLOCK_8786, 7)), - + OAK_STAIRS_DOWN (13498, 8249, 27, 680, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 4) }), + TEAK_STAIRS_DOWN (13500, 8252, 48, 980, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 4) }), + SPIRAL_STAIRS_DOWN(13504, 8258, 67, 1040, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.LIMESTONE_BRICK_3420, 7) }), + MARBLE_STAIRS_DOWN(13502, 8257, 82, 3200, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MARBLE_BLOCK_8786, 5) }), + MARBLE_SPIRAL_DOWN(13506, 8259, 97, 4400, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.MARBLE_BLOCK_8786, 7) }), + /** * Portal room decorations. */ - TEAK_PORTAL(13636, 8328, 50, 270.0, new Item(Items.TEAK_PLANK_8780, 3)), - MAHOGANY_PORTAL(13637, 8329, 65, 420.0, new Item(Items.MAHOGANY_PLANK_8782, 3)), - MARBLE_PORTAL(13638, 8330, 80, 1500.0, new Item(Items.MARBLE_BLOCK_8786, 3)), - TELEPORT_FOCUS(13640, 8331, 50, 40, new Item(Items.LIMESTONE_BRICK_3420, 2)), - GREATER_TELEPORT_FOCUS(13641, 8332, 65, 500.0, new Item(Items.MARBLE_BLOCK_8786, 1)), - SCRYING_POOL(13639, 8333, 80, 2000.0, new Item(Items.MARBLE_BLOCK_8786, 4)), - TEAK_VARROCK_PORTAL(13615, true), - MAHOGANY_VARROCK_PORTAL(13622, true), - MARBLE_VARROCK_PORTAL(13629, true), - TEAK_LUMBRIDGE_PORTAL(13616, true), + TEAK_PORTAL (13636, 8328, 50, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }), + MAHOGANY_PORTAL (13637, 8329, 65, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3) }), + MARBLE_PORTAL (13638, 8330, 80, 1500, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 3) }), + TELEPORT_FOCUS (13640, 8331, 50, 40, new Item[] { new Item(Items.LIMESTONE_BRICK_3420, 2) }), + GREATER_TELEPORT_FOCUS (13641, 8332, 65, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), + SCRYING_POOL (13639, 8333, 80, 2000, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 4) }), + TEAK_VARROCK_PORTAL (13615, true), + MAHOGANY_VARROCK_PORTAL (13622, true), + MARBLE_VARROCK_PORTAL (13629, true), + TEAK_LUMBRIDGE_PORTAL (13616, true), MAHOGANY_LUMBRIDGE_PORTAL(13623, true), - MARBLE_LUMBRIDGE_PORTAL(13630, true), - TEAK_FALADOR_PORTAL(13617, true), - MAHOGANY_FALADOR_PORTAL(13624, true), - MARBLE_FALADOR_PORTAL(13631, true), - TEAK_CAMELOT_PORTAL(13618, true), - MAHOGANY_CAMELOT_PORTAL(13625, true), - MARBLE_CAMELOT_PORTAL(13632, true), - TEAK_ARDOUGNE_PORTAL(13619, true), - MAHOGANY_ARDOUGNE_PORTAL(13626, true), - MARBLE_ARDOUGNE_PORTAL(13633, true), - TEAK_YANILLE_PORTAL(13620, true), - MAHOGANY_YANILLE_PORTAL(13627, true), - MARBLE_YANILLE_PORTAL(13634, true), - TEAK_KHARYRLL_PORTAL(13621, true), - MAHOGANY_KHARYRLL_PORTAL(13628, true), - MARBLE_KHARYRLL_PORTAL(13635, true), - + MARBLE_LUMBRIDGE_PORTAL (13630, true), + TEAK_FALADOR_PORTAL (13617, true), + MAHOGANY_FALADOR_PORTAL (13624, true), + MARBLE_FALADOR_PORTAL (13631, true), + TEAK_CAMELOT_PORTAL (13618, true), + MAHOGANY_CAMELOT_PORTAL (13625, true), + MARBLE_CAMELOT_PORTAL (13632, true), + TEAK_ARDOUGNE_PORTAL (13619, true), + MAHOGANY_ARDOUGNE_PORTAL (13626, true), + MARBLE_ARDOUGNE_PORTAL (13633, true), + TEAK_YANILLE_PORTAL (13620, true), + MAHOGANY_YANILLE_PORTAL (13627, true), + MARBLE_YANILLE_PORTAL (13634, true), + TEAK_KHARYRLL_PORTAL (13621, true), + MAHOGANY_KHARYRLL_PORTAL (13628, true), + MARBLE_KHARYRLL_PORTAL (13635, true), + /** * Skill hall decorations. */ - MITHRIL_ARMOUR(13491, 8270, 28, 135.0, new Item(Items.OAK_PLANK_8778, 2), new Item(Items.MITHRIL_FULL_HELM_1159, 1), new Item(Items.MITHRIL_PLATEBODY_1121, 1), new Item(Items.MITHRIL_PLATESKIRT_1085, 1)), - ADAMANT_ARMOUR(13492, 8271, 28, 150.0, new Item(Items.OAK_PLANK_8778, 2), new Item(Items.ADAMANT_FULL_HELM_1161, 1), new Item(Items.ADAMANT_PLATEBODY_1123, 1), new Item(Items.ADAMANT_PLATESKIRT_1091, 1)), - RUNE_ARMOUR(13493, 8272, 28, 165.0, new Item(Items.OAK_PLANK_8778, 2),new Item(Items.RUNE_FULL_HELM_1163, 1), new Item(Items.RUNE_PLATEBODY_1127, 1), new Item(Items.RUNE_PLATESKIRT_1093, 1)), - CRAWLING_HAND(13481, 8260, 38, 211.0, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.CRAWLING_HAND_7982, 1)), - COCKATRICE_HEAD(13482, 8261, 38, 224.0, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.COCKATRICE_HEAD_7983, 1)), - BASILISK_HEAD(13483, 8262, 38, 243.0, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.BASILISK_HEAD_7984, 1)), - KURASK_HEAD(13484, 8263, 58, 357.0, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.KURASK_HEAD_7985, 1)), - ABYSSAL_DEMON_HEAD(13485, 8264, 58, 389.0, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.ABYSSAL_HEAD_7986, 1)), - KBD_HEAD(13486, 8265, 78, 1103.0, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.KBD_HEADS_7987, 1)), - KQ_HEAD(13487, 8266, 78, 1103.0, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.KQ_HEAD_7988, 1)), - MOUNTED_BASS(13488, 8267, 36, 151.0, new Item(Items.OAK_PLANK_8778, 2), new Item(Items.BIG_BASS_7990, 1)), - MOUNTED_SWORDFISH(13489, 8268, 56, 230.0, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.BIG_SWORDFISH_7992, 1)), - MOUNTED_SHARK(13490, 8269, 76, 350.0, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.BIG_SHARK_7994, 1)), - RUNE_CASE1(13507, 8095, 41, 190.0, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 2), new Item(Items.FIRE_RUNE_554, 1), new Item(Items.AIR_RUNE_556, 1), new Item(Items.EARTH_RUNE_557, 1), new Item(Items.WATER_RUNE_555, 1)), - RUNE_CASE2(13508, 8095, 41, 212.0, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 2), new Item(Items.BODY_RUNE_559, 1), new Item(Items.COSMIC_RUNE_564, 1), new Item(Items.CHAOS_RUNE_562, 1), new Item(Items.NATURE_RUNE_561, 1)), - - + MITHRIL_ARMOUR (13491, 8270, 28, 135, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.MITHRIL_FULL_HELM_1159, 1), new Item(Items.MITHRIL_PLATEBODY_1121, 1), new Item(Items.MITHRIL_PLATESKIRT_1085, 1) }, new Item[] { new Item(Items.MITHRIL_FULL_HELM_1159, 1), new Item(Items.MITHRIL_PLATEBODY_1121, 1), new Item(Items.MITHRIL_PLATESKIRT_1085, 1) }), + ADAMANT_ARMOUR (13492, 8271, 28, 150, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.ADAMANT_FULL_HELM_1161, 1), new Item(Items.ADAMANT_PLATEBODY_1123, 1), new Item(Items.ADAMANT_PLATESKIRT_1091, 1) }, new Item[] { new Item(Items.ADAMANT_FULL_HELM_1161, 1), new Item(Items.ADAMANT_PLATEBODY_1123, 1), new Item(Items.ADAMANT_PLATESKIRT_1091, 1) }), + RUNE_ARMOUR (13493, 8272, 28, 165, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.RUNE_FULL_HELM_1163, 1), new Item(Items.RUNE_PLATEBODY_1127, 1), new Item(Items.RUNE_PLATESKIRT_1093, 1) }, new Item[] { new Item(Items.RUNE_FULL_HELM_1163, 1), new Item(Items.RUNE_PLATEBODY_1127, 1), new Item(Items.RUNE_PLATESKIRT_1093, 1) }), + CRAWLING_HAND (13481, 8260, 38, 211, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.CRAWLING_HAND_7982) }), + COCKATRICE_HEAD (13482, 8261, 38, 224, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.COCKATRICE_HEAD_7983) }), + BASILISK_HEAD (13483, 8262, 38, 243, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.BASILISK_HEAD_7984) }), + KURASK_HEAD (13484, 8263, 58, 357, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.KURASK_HEAD_7985) }), + ABYSSAL_DEMON_HEAD(13485, 8264, 58, 389, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.ABYSSAL_HEAD_7986) }), + KBD_HEAD (13486, 8265, 78, 1103, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.KBD_HEADS_7987) }), + KQ_HEAD (13487, 8266, 78, 1103, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.KQ_HEAD_7988) }), + MOUNTED_BASS (13488, 8267, 36, 151, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.BIG_BASS_7990) }), + MOUNTED_SWORDFISH (13489, 8268, 56, 230, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.BIG_SWORDFISH_7992) }), + MOUNTED_SHARK (13490, 8269, 76, 350, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.BIG_SHARK_7994) }), + RUNE_CASE1 (13507, 8095, 41, 190, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 2), new Item(Items.FIRE_RUNE_554, 1), new Item(Items.AIR_RUNE_556, 1), new Item(Items.EARTH_RUNE_557, 1), new Item(Items.WATER_RUNE_555, 1) }), + RUNE_CASE2 (13508, 8095, 41, 212, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 2), new Item(Items.BODY_RUNE_559, 1), new Item(Items.COSMIC_RUNE_564, 1), new Item(Items.CHAOS_RUNE_562, 1), new Item(Items.NATURE_RUNE_561, 1) }), + /** * Games room decorations. */ - CLAY_STONE(13392, 8153, 39, 100.0, new Item(Items.SOFT_CLAY_1761, 10)), - LIMESTONE_STONE(13393, 8154, 59, 200.0, new Item(Items.LIMESTONE_BRICK_3420, 10)), - MARBLE_STONE(13394, 8155, 79, 2000.0, new Item(Items.MARBLE_BLOCK_8786, 4)), - HOOP_AND_STICK(13398, 8162, 30, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - DARTBOARD(13400, 8163, 54, 290.0, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.STEEL_BAR_2353, 1)), - ARCHERY_TARGET(13402, 8164, 81, 600.0, new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.STEEL_BAR_2353, 3)), - BALANCE_1(13395, 8156, 37, 176.0, new Item(Items.FIRE_RUNE_554, 500), new Item(Items.AIR_RUNE_556, 500), new Item(Items.EARTH_RUNE_557, 500), new Item(Items.WATER_RUNE_555, 500)), - BALANCE_2(13396, 8157, 57, 252.0, new Item(Items.FIRE_RUNE_554, 1000), new Item(Items.AIR_RUNE_556, 1000), new Item(Items.EARTH_RUNE_557, 1000), new Item(Items.WATER_RUNE_555, 1000)), - BALANCE_3(13397, 8158, 77, 356.0, new Item(Items.FIRE_RUNE_554, 2000), new Item(Items.AIR_RUNE_556, 2000), new Item(Items.EARTH_RUNE_557, 2000), new Item(Items.WATER_RUNE_555, 2000)), - OAK_CHEST(13385, 8165, 34, 240.0, new Item(Items.OAK_PLANK_8778, 4)), - TEAK_CHEST(13387, 8166, 44, 660.0, new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.GOLD_LEAF_8784, 1)), - MAHOGANY_CHEST(13389, 8167, 54, 860.0, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 1)), - JESTER(13390, 8159, 39, 360.0, new Item(Items.TEAK_PLANK_8780, 4)), - TREASURE_HUNT(13379, 8160, 49, 800.0, new Item(Items.TEAK_PLANK_8780, 8), new Item(Items.STEEL_BAR_2353, 4)), - HANGMAN(13404, 8161, 59, 1200.0, new Item(Items.TEAK_PLANK_8780, 12), new Item(Items.STEEL_BAR_2353, 6)), - - + CLAY_STONE (13392, 8153, 39, 100, new Item[] { new Item(Items.SOFT_CLAY_1761, 10) }), + LIMESTONE_STONE(13393, 8154, 59, 200, new Item[] { new Item(Items.LIMESTONE_BRICK_3420, 10) }), + MARBLE_STONE (13394, 8155, 79, 2000, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 4) }), + HOOP_AND_STICK (13398, 8162, 30, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + DARTBOARD (13400, 8163, 54, 290, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.STEEL_BAR_2353) }), + ARCHERY_TARGET (13402, 8164, 81, 600, new Item[] { new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.STEEL_BAR_2353, 3) }), + BALANCE_1 (13395, 8156, 37, 176, new Item[] { new Item(Items.FIRE_RUNE_554, 500), new Item(Items.AIR_RUNE_556, 500), new Item(Items.EARTH_RUNE_557, 500), new Item(Items.WATER_RUNE_555, 500) }), + BALANCE_2 (13396, 8157, 57, 252, new Item[] { new Item(Items.FIRE_RUNE_554, 1000), new Item(Items.AIR_RUNE_556, 1000), new Item(Items.EARTH_RUNE_557, 1000), new Item(Items.WATER_RUNE_555, 1000) }), + BALANCE_3 (13397, 8158, 77, 356, new Item[] { new Item(Items.FIRE_RUNE_554, 2000), new Item(Items.AIR_RUNE_556, 2000), new Item(Items.EARTH_RUNE_557, 2000), new Item(Items.WATER_RUNE_555, 2000) }), + OAK_CHEST (13385, 8165, 34, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + TEAK_CHEST (13387, 8166, 44, 660, new Item[] { new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.GOLD_LEAF_8784) }), + MAHOGANY_CHEST (13389, 8167, 54, 860, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784) }), + JESTER (13390, 8159, 39, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + TREASURE_HUNT (13379, 8160, 49, 800, new Item[] { new Item(Items.TEAK_PLANK_8780, 8), new Item(Items.STEEL_BAR_2353, 4) }), + HANGMAN (13404, 8161, 59, 1200, new Item[] { new Item(Items.TEAK_PLANK_8780, 12), new Item(Items.STEEL_BAR_2353, 6) }), + /** * Combat room decorations. */ - BOXING_RING(13129, 8023, 32, 570.0, new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4)), - FENCING_RING(13133, 8024, 41, 570.0, new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 6)), - COMBAT_RING(13137, 8025, 51, 630.0, new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 6)), - BALANCE_BEAM_LEFT(13143, 8027, 81, 1000.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 5)), - BALANCE_BEAM_CENTER(13142, 8027, 81, 1000.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 5)), - BALANCE_BEAM_RIGHT(13144, 8027, 81, 1000.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 5)), - RANGING_PEDESTALS(13147, 8026, 71, 720.0, new Item(Items.TEAK_PLANK_8780, 8)), - MAGIC_BARRIER(13145, 8026, 71, 720.0, new Item(Items.TEAK_PLANK_8780, 8)), - NOTHING(13721, 8027, 81, 1000.0, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 5)), - NOTHING2(13721, 8026, 71, 720.0, new Item(Items.TEAK_PLANK_8780, 8)), - INVISIBLE_WALL(15283, 8023, 32, 570.0, new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4)), - INVISIBLE_WALL2(15284, 8023, 32, 570.0, new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4)), - INVISIBLE_WALL3(15285, 8023, 32, 570.0, new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4)), - GLOVE_RACK(13381, 8028, 34, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - WEAPONS_RACK(13382, 8029, 44, 180.0, new Item(Items.TEAK_PLANK_8780, 2)), - EXTRA_WEAPONS_RACK(13383, 8030, 54, 440.0, new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.STEEL_BAR_2353, 4)), - BOXING_MAT_CORNER(13126, 8023, 32, 570.0, new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4)), - FENCING_MAT_CORNER(13135, 8024, 41, 570.0, new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 6)), - COMBAT_MAT_CORNER(13138, 8025, 51, 630.0, new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 6)), - BOXING_MAT_SIDE(13128, 8023, 32, 570.0, new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4)), - FENCING_MAT_SIDE(13134, 8024, 41, 570.0, new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 6)), - COMBAT_MAT_SIDE(13139, 8025, 51, 630.0, new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 6)), - BOXING_MAT(13127, 8023, 32, 570.0, new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4)), - FENCING_MAT(13136, 8024, 41, 570.0, new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 6)), - COMBAT_MAT(13140, 8025, 51, 630.0, new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 6)), - - + BOXING_RING (13129, 8023, 32, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + FENCING_RING (13133, 8024, 41, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 6) }), + COMBAT_RING (13137, 8025, 51, 630, new Item[] { new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 6) }), + BALANCE_BEAM_LEFT (13143, 8027, 81, 1000, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 5) }), + BALANCE_BEAM_CENTER(13142, 8027, 81, 1000, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 5) }), + BALANCE_BEAM_RIGHT (13144, 8027, 81, 1000, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 5) }), + RANGING_PEDESTALS (13147, 8026, 71, 720, new Item[] { new Item(Items.TEAK_PLANK_8780, 8) }), + MAGIC_BARRIER (13145, 8026, 71, 720, new Item[] { new Item(Items.TEAK_PLANK_8780, 8) }), + NOTHING (13721, 8027, 81, 1000, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 5) }), + NOTHING2 (13721, 8026, 71, 720, new Item[] { new Item(Items.TEAK_PLANK_8780, 8) }), + INVISIBLE_WALL (15283, 8023, 32, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + INVISIBLE_WALL2 (15284, 8023, 32, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + INVISIBLE_WALL3 (15285, 8023, 32, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + GLOVE_RACK (13381, 8028, 34, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + WEAPONS_RACK (13382, 8029, 44, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + EXTRA_WEAPONS_RACK (13383, 8030, 54, 440, new Item[] { new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.STEEL_BAR_2353, 4) }), + BOXING_MAT_CORNER (13126, 8023, 32, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + FENCING_MAT_CORNER (13135, 8024, 41, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 6) }), + COMBAT_MAT_CORNER (13138, 8025, 51, 630, new Item[] { new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 6) }), + BOXING_MAT_SIDE (13128, 8023, 32, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + FENCING_MAT_SIDE (13134, 8024, 41, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 6) }), + COMBAT_MAT_SIDE (13139, 8025, 51, 630, new Item[] { new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 6) }), + BOXING_MAT (13127, 8023, 32, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + FENCING_MAT (13136, 8024, 41, 570, new Item[] { new Item(Items.OAK_PLANK_8778, 8), new Item(Items.BOLT_OF_CLOTH_8790, 6) }), + COMBAT_MAT (13140, 8025, 51, 630, new Item[] { new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 6) }), + /** * Formal garden decorations */ - GAZEBO(13477, 8192, 65, 1200, new Item(Items.MAHOGANY_PLANK_8782, 8), new Item(Items.STEEL_BAR_2353, 4)), - SMALL_FOUNTAIN(13478, 8193, 71, 500, new Item(Items.MARBLE_BLOCK_8786, 1)), - LARGE_FOUNTAIN(13479, 8194, 75, 1000, new Item(Items.MARBLE_BLOCK_8786, 2)), - POSH_FOUNTAIN(13480, 8195, 81, 1500, new Item(Items.MARBLE_BLOCK_8786, 3)), - SUNFLOWER(13446, 8213, 66, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_SUNFLOWER_8457, 1)), - MARIGOLDS(13447, 8214, 71, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_MARIGOLDS_8459, 1)), - ROSES(13448, 8215, 76, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_ROSES_8461, 1)), - SUNFLOWER_BIG(13443, 8213, 66, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_SUNFLOWER_8457, 1)), - MARIGOLDS_BIG(13444, 8214, 71, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_MARIGOLDS_8459, 1)), - ROSES_BIG(13445, 8215, 76, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_ROSES_8461, 1)), - ROSEMARY(13440, 8210, 66, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_ROSEMARY_8451, 1)), - DAFFODILS(13441, 8211, 71, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_DAFFODILS_8453, 1)), - BLUEBELLS(13442, 8212, 76, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_BLUEBELLS_8455, 1)), - ROSEMARY_BIG(13437, 8210, 66, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_ROSEMARY_8451, 1)), - DAFFODILS_BIG(13438, 8211, 71, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_DAFFODILS_8453, 1)), - BLUEBELLS_BIG(13439, 8212, 76, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.BAGGED_BLUEBELLS_8455, 1)), - THORNY_HEDGE1(13456, 8203, 56, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.THORNY_HEDGE_8437, 1)), - THORNY_HEDGE2(13457, 8203, 56, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.THORNY_HEDGE_8437, 1)), - THORNY_HEDGE3(13458, 8203, 56, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.THORNY_HEDGE_8437, 1)), - NICE_HEDGE1(13459, 8204, 60, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.NICE_HEDGE_8439, 1)), - NICE_HEDGE2(13461, 8204, 60, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.NICE_HEDGE_8439, 1)), - NICE_HEDGE3(13460, 8204, 60, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.NICE_HEDGE_8439, 1)), - SMALL_BOX_HEDGE1(13462, 8205, 64, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.SMALL_BOX_HEDGE_8441, 1)), - SMALL_BOX_HEDGE2(13464, 8205, 64, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.SMALL_BOX_HEDGE_8441, 1)), - SMALL_BOX_HEDGE3(13463, 8205, 64, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.SMALL_BOX_HEDGE_8441, 1)), - TOPIARY_HEDGE1(13465, 8206, 68, 141, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TOPIARY_HEDGE_8443, 1)), - TOPIARY_HEDGE2(13467, 8206, 68, 141, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TOPIARY_HEDGE_8443, 1)), - TOPIARY_HEDGE3(13466, 8206, 68, 141, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TOPIARY_HEDGE_8443, 1)), - FANCY_HEDGE1(13468, 8207, 72, 158, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.FANCY_HEDGE_8445, 1)), - FANCY_HEDGE2(13470, 8207, 72, 158, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.FANCY_HEDGE_8445, 1)), - FANCY_HEDGE3(13469, 8207, 72, 158, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.FANCY_HEDGE_8445, 1)), - TALL_FANCY_HEDGE1(13471, 8208, 76, 223, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TALL_FANCY_HEDGE_8447, 1)), - TALL_FANCY_HEDGE2(13473, 8208, 76, 223, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TALL_FANCY_HEDGE_8447, 1)), - TALL_FANCY_HEDGE3(13472, 8208, 76, 223, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TALL_FANCY_HEDGE_8447, 1)), - TALL_BOX_HEDGE1(13474, 8209, 80, 316, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TALL_BOX_HEDGE_8449, 1)), - TALL_BOX_HEDGE2(13476, 8209, 80, 316, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TALL_BOX_HEDGE_8449, 1)), - TALL_BOX_HEDGE3(13475, 8209, 80, 316, new int[] {BuildingUtils.WATERING_CAN}, new Item(Items.TALL_BOX_HEDGE_8449, 1)), - BOUNDARY_STONES(13449, 8196, 55, 100, new Item(Items.SOFT_CLAY_1761, 10)), - WOODEN_FENCE(13450, 8197, 59, 280, new Item(Items.PLANK_960, 10)), - STONE_WALL(13451, 8198, 63, 200, new Item(Items.LIMESTONE_BRICK_3420, 10)), - IRON_RAILINGS(13452, 8199, 67, 220, new Item(Items.IRON_BAR_2351, 10), new Item(Items.LIMESTONE_BRICK_3420, 6)), - PICKET_FENCE(13453, 8200, 71, 640, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 2)), - GARDEN_FENCE(13454, 8201, 75, 940, new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 2)), - MARBLE_WALL(13455, 8202, 79, 4000, new Item(Items.MARBLE_BLOCK_8786, 10)), - - + GAZEBO (13477, 8192, 65, 1200, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 8), new Item(Items.STEEL_BAR_2353, 4) }), + SMALL_FOUNTAIN (13478, 8193, 71, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), + LARGE_FOUNTAIN (13479, 8194, 75, 1000, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 2) }), + POSH_FOUNTAIN (13480, 8195, 81, 1500, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 3) }), + SUNFLOWER (13446, 8213, 66, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_SUNFLOWER_8457) }), + MARIGOLDS (13447, 8214, 71, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_MARIGOLDS_8459) }), + ROSES (13448, 8215, 76, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_ROSES_8461) }), + SUNFLOWER_BIG (13443, 8213, 66, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_SUNFLOWER_8457) }), + MARIGOLDS_BIG (13444, 8214, 71, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_MARIGOLDS_8459) }), + ROSES_BIG (13445, 8215, 76, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_ROSES_8461) }), + ROSEMARY (13440, 8210, 66, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_ROSEMARY_8451) }), + DAFFODILS (13441, 8211, 71, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_DAFFODILS_8453) }), + BLUEBELLS (13442, 8212, 76, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_BLUEBELLS_8455) }), + ROSEMARY_BIG (13437, 8210, 66, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_ROSEMARY_8451) }), + DAFFODILS_BIG (13438, 8211, 71, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_DAFFODILS_8453) }), + BLUEBELLS_BIG (13439, 8212, 76, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.BAGGED_BLUEBELLS_8455) }), + THORNY_HEDGE1 (13456, 8203, 56, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.THORNY_HEDGE_8437) }), + THORNY_HEDGE2 (13457, 8203, 56, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.THORNY_HEDGE_8437) }), + THORNY_HEDGE3 (13458, 8203, 56, 70, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.THORNY_HEDGE_8437) }), + NICE_HEDGE1 (13459, 8204, 60, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.NICE_HEDGE_8439) }), + NICE_HEDGE2 (13461, 8204, 60, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.NICE_HEDGE_8439) }), + NICE_HEDGE3 (13460, 8204, 60, 100, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.NICE_HEDGE_8439) }), + SMALL_BOX_HEDGE1 (13462, 8205, 64, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.SMALL_BOX_HEDGE_8441) }), + SMALL_BOX_HEDGE2 (13464, 8205, 64, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.SMALL_BOX_HEDGE_8441) }), + SMALL_BOX_HEDGE3 (13463, 8205, 64, 122, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.SMALL_BOX_HEDGE_8441) }), + TOPIARY_HEDGE1 (13465, 8206, 68, 141, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TOPIARY_HEDGE_8443) }), + TOPIARY_HEDGE2 (13467, 8206, 68, 141, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TOPIARY_HEDGE_8443) }), + TOPIARY_HEDGE3 (13466, 8206, 68, 141, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TOPIARY_HEDGE_8443) }), + FANCY_HEDGE1 (13468, 8207, 72, 158, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.FANCY_HEDGE_8445) }), + FANCY_HEDGE2 (13470, 8207, 72, 158, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.FANCY_HEDGE_8445) }), + FANCY_HEDGE3 (13469, 8207, 72, 158, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.FANCY_HEDGE_8445) }), + TALL_FANCY_HEDGE1(13471, 8208, 76, 223, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TALL_FANCY_HEDGE_8447) }), + TALL_FANCY_HEDGE2(13473, 8208, 76, 223, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TALL_FANCY_HEDGE_8447) }), + TALL_FANCY_HEDGE3(13472, 8208, 76, 223, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TALL_FANCY_HEDGE_8447) }), + TALL_BOX_HEDGE1 (13474, 8209, 80, 316, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TALL_BOX_HEDGE_8449) }), + TALL_BOX_HEDGE2 (13476, 8209, 80, 316, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TALL_BOX_HEDGE_8449) }), + TALL_BOX_HEDGE3 (13475, 8209, 80, 316, new int[] { BuildingUtils.WATERING_CAN }, new Item[] { new Item(Items.TALL_BOX_HEDGE_8449) }), + BOUNDARY_STONES (13449, 8196, 55, 100, new Item[] { new Item(Items.SOFT_CLAY_1761, 10) }), + WOODEN_FENCE (13450, 8197, 59, 280, new Item[] { new Item(Items.PLANK_960, 10) }), + STONE_WALL (13451, 8198, 63, 200, new Item[] { new Item(Items.LIMESTONE_BRICK_3420, 10) }), + IRON_RAILINGS (13452, 8199, 67, 220, new Item[] { new Item(Items.IRON_BAR_2351, 10), new Item(Items.LIMESTONE_BRICK_3420, 6) }), + PICKET_FENCE (13453, 8200, 71, 640, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 2) }), + GARDEN_FENCE (13454, 8201, 75, 940, new Item[] { new Item(Items.TEAK_PLANK_8780, 10), new Item(Items.STEEL_BAR_2353, 2) }), + MARBLE_WALL (13455, 8202, 79, 4000, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 10) }), + /** * Bedroom decorations. */ - WOODEN_BED(13148, 8031, 20, 117, new Item(Items.PLANK_960, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - OAK_BED(13149, 8032, 30, 210, new Item(Items.OAK_PLANK_8778, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - LARGE_OAK_BED(13150, 8033, 34, 330, new Item(Items.OAK_PLANK_8778, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - TEAK_BED(13151, 8034, 40, 300, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - LARGE_TEAK_BED(13152, 8035, 45, 480, new Item(Items.TEAK_PLANK_8780, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - FOUR_POSTER(13153, 8036, 53, 450, new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - GILDED_FOUR_POSTER(13154, 8037, 60, 1330, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784, 2)), - OAK_CLOCK(13169, 8052, 25, 142, new Item(Items.OAK_PLANK_8778, 2), new Item(Items.CLOCKWORK_8792, 1)), - TEAK_CLOCK(13170, 8053, 55, 202, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.CLOCKWORK_8792, 1)), - GILDED_CLOCK(13171, 8054, 85, 602, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.CLOCKWORK_8792, 1), new Item(Items.GOLD_LEAF_8784, 1)), - SHAVING_STAND(13162, 8045, 21, 30, new Item(Items.PLANK_960, 1), new Item(Items.MOLTEN_GLASS_1775, 1)), - OAK_SHAVING_STAND(13163, 8046, 29, 61, new Item(Items.OAK_PLANK_8778, 1), new Item(Items.MOLTEN_GLASS_1775, 1)), - OAK_DRESSER(13164, 8047, 37, 121, new Item(Items.OAK_PLANK_8778, 2), new Item(Items.MOLTEN_GLASS_1775, 1)), - TEAK_DRESSER(13165, 8048, 46, 181, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 1)), - FANCY_TEAK_DRESSER(13166, 8049, 56, 182, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 2)), - MAHOGANY_DRESSER(13167, 8050, 64, 281, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775, 1)), - GILDED_DRESSER(13168, 8051, 74, 582, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775, 2), new Item(Items.GOLD_LEAF_8784, 1)), - SHOE_BOX(13155, 8038, 20, 58, new Item(Items.PLANK_960, 2)), - OAK_DRAWERS(13156, 8039, 27, 120, new Item(Items.OAK_PLANK_8778, 2)), - OAK_WARDROBE(13157, 8040, 39, 180, new Item(Items.OAK_PLANK_8778, 3)), - TEAK_DRAWERS(13158, 8041, 51, 180, new Item(Items.TEAK_PLANK_8780, 2)), - TEAK_WARDROBE(13159, 8042, 63, 270, new Item(Items.TEAK_PLANK_8780, 3)), - MAHOGANY_WARDROBE(13160, 8043, 75, 420, new Item(Items.MAHOGANY_PLANK_8782, 2)), - GILDED_WARDROBE(13161, 8044, 87, 720, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784, 1)), - - + WOODEN_BED (13148, 8031, 20, 117, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + OAK_BED (13149, 8032, 30, 210, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + LARGE_OAK_BED (13150, 8033, 34, 330, new Item[] { new Item(Items.OAK_PLANK_8778, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + TEAK_BED (13151, 8034, 40, 300, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + LARGE_TEAK_BED (13152, 8035, 45, 480, new Item[] { new Item(Items.TEAK_PLANK_8780, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + FOUR_POSTER (13153, 8036, 53, 450, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + GILDED_FOUR_POSTER(13154, 8037, 60, 1330, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784, 2) }), + OAK_CLOCK (13169, 8052, 25, 142, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.CLOCKWORK_8792) }), + TEAK_CLOCK (13170, 8053, 55, 202, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.CLOCKWORK_8792) }), + GILDED_CLOCK (13171, 8054, 85, 602, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.CLOCKWORK_8792), new Item(Items.GOLD_LEAF_8784) }), + SHAVING_STAND (13162, 8045, 21, 30, new Item[] { new Item(Items.PLANK_960), new Item(Items.MOLTEN_GLASS_1775) }), + OAK_SHAVING_STAND (13163, 8046, 29, 61, new Item[] { new Item(Items.OAK_PLANK_8778), new Item(Items.MOLTEN_GLASS_1775) }), + OAK_DRESSER (13164, 8047, 37, 121, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.MOLTEN_GLASS_1775) }), + TEAK_DRESSER (13165, 8048, 46, 181, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775) }), + FANCY_TEAK_DRESSER(13166, 8049, 56, 182, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 2) }), + MAHOGANY_DRESSER (13167, 8050, 64, 281, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775) }), + GILDED_DRESSER (13168, 8051, 74, 582, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775, 2), new Item(Items.GOLD_LEAF_8784) }), + SHOE_BOX (13155, 8038, 20, 58, new Item[] { new Item(Items.PLANK_960, 2) }), + OAK_DRAWERS (13156, 8039, 27, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + OAK_WARDROBE (13157, 8040, 39, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), + TEAK_DRAWERS (13158, 8041, 51, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + TEAK_WARDROBE (13159, 8042, 63, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }), + MAHOGANY_WARDROBE (13160, 8043, 75, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), + GILDED_WARDROBE (13161, 8044, 87, 720, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784) }), + /** * Quest hall decorations. */ - ANTIDRAGON_SHIELD(13522, 8282, 47, 280, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ANTI_DRAGON_SHIELD_1540, 1)), - AMULET_OF_GLORY(13523, 8283, 47, 290, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.AMULET_OF_GLORY_1704, 1)), - CAPE_OF_LEGENDS(13524, 8284, 47, 300, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.CAPE_OF_LEGENDS_1052, 1)), - KING_ARTHUR(13510, 8285, 35, 211, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ARTHUR_PORTRAIT_7995, 1)), - ELENA(13511, 8286, 35, 211, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ELENA_PORTRAIT_7996, 1)), - GIANT_DWARF(13512, 8287, 35, 211, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.KELDAGRIM_PORTRAIT_7997, 1)), - MISCELLANIANS(13513, 8288, 35, 311, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.MISC_PORTRAIT_7998, 1)), - LUMBRIDGE(13517, 8289, 44, 314, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.LUMBRIDGE_PAINTING_8002, 1)), - THE_DESERT(13514, 8290, 44, 314, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.DESERT_PAINTING_7999, 1)), - MORYTANIA(13518, 8291, 44, 314, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.MORYTANIA_PAINTING_8003, 1)), - KARAMJA(13516, 8292, 65, 464, new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.KARAMJA_PAINTING_8001, 1)), - ISAFDAR(13515, 8293, 65, 464, new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.ISAFDAR_PAINTING_8000, 1)), - SILVERLIGHT(13519, 8279, 42, 187, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.SILVERLIGHT_2402, 1)), - EXCALIBUR(13521, 8280, 42, 194, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.EXCALIBUR_35, 1)), - DARKLIGHT(13520, 8281, 42, 202, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.DARKLIGHT_6746, 1)), - SMALL_MAP(13525, 8294, 38, 211, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.SMALL_MAP_8004, 1)), - MEDIUM_MAP(13526, 8295, 58, 451, new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.MEDIUM_MAP_8005, 1)), - LARGE_MAP(13527, 8296, 78, 591, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.LARGE_MAP_8006, 1)), - - /** - * Menagerie Decorations - */ - //OBELISK - MINI_OBELISK(44837, 15236, 41, 676, new Item(Items.MARBLE_BLOCK_8786, 1), new Item(Items.SPIRIT_SHARDS_12183, 1000), new Item(Items.GOLD_CHARM_12158, 10), new Item(Items.GREEN_CHARM_12159, 10), new Item(Items.CRIMSON_CHARM_12160, 10), new Item(Items.BLUE_CHARM_12163, 10)) - //PET_FEEDER - , - OAK_PET_FEEDER(44834, 15233, 37, 240, new Item(Items.OAK_PLANK_8778, 4)), - TEAK_PET_FEEDER(44835, 15234, 52, 380, new Item(Items.TEAK_PLANK_8780, 4)), - MAHOGANY_PET_FEEDER(44836, 15235, 67, 880, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 1)) - //PET_HOUSE - , - OAK_PET_HOUSE(44828, 15227, 37, 240, new Item(Items.OAK_PLANK_8778, 4)), - TEAK_PET_HOUSE(44829, 15228, 52, 380, new Item(Items.TEAK_PLANK_8780, 4)), - MAHOGANY_PET_HOUSE(44830, 15229, 67, 580, new Item(Items.MAHOGANY_PLANK_8782, 4)), - CONSECRATED_PET_HOUSE(44831, 15230, 92, 1580, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.MAGIC_STONE_8788, 1)), - DESECRATED_PET_HOUSE(44832, 15231, 92, 1580, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.MAGIC_STONE_8788, 1)), - NATURAL_PET_HOUSE(44833, 15232, 92, 1580, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.MAGIC_STONE_8788, 1)) - //HABITAT_SPACE - , - GARDEN_HABITAT(new int[]{ - 4497, - 4498, - 44500, - 44501, - 44502, - 44503, - 44504, - 44505, - 44506, - 44507, - 44508, - 44509, - 44510, - 44511, - 44512, - 44513, - 44514, - 44515, - 44516, - 44517, - 44518, - 44519, - 44520, - 44521, - 44522, - 44523, - 44524, - 44525, - 44526, - 44527, - 44528, - 44529, - 44530, - 44531, - 44532, - 44533, - 44534, - 44535, - 44536, - 44537, - 44538, - 44539, - 44540, - 44541, - 44542, - 44543, - 44544, - 44545, - 44546, - 44547, - 44548, - 44549, - 44550, - 44551, - 44552, - 44553, - 44554, - 44555, - 44556, - 44557, - 44558, - 44559, - 44560, - 44561, - 44562, - 44563 }, 15222, 37, 201, new Item(Items.BAGGED_PLANT_1_8431, 1), new Item(Items.BAGGED_PLANT_2_8433, 1), new Item(Items.BAGGED_PLANT_3_8435, 1)), - JUNGLE_HABITAT(new int[] - { - 44564, - 44565, - 44566, - 44567, - 44568, - 44569, - 44570, - 44571, - 44572, - 44573, - 44574, - 44575, - 44576, - 44577, - 44578, - 44579, - 44580, - 44581, - 44582, - 44583, - 44584, - 44585, - 44586, - 44587, - 44588, - 44589, - 44590, - 44591, - 44592, - 44593, - 44594, - 44595, - 44596, - 44597, - 44598, - 44599, - 44600, - 44601, - 44602, - 44603, - 44604, - 44605, - 44606, - 44607, - 44608, - 44609, - 44610, - 44611, - 44612, - 44613, - 44614, - 44615, - 44616, - 44617, - 44618, - 44619, - 44620, - 44621, - 44622, - 44623, - 44624, - 44625, - 44626, - 44627, - 44628, - 44629 }, 15223, 47, 278, new Item(Items.BAGGED_PLANT_3_8435, 3), new Item(Items.BAGGED_WILLOW_TREE_8423, 1), new Item(Items.BUCKET_OF_WATER_1929, 5)), - DESERT_HABITAT(new int[] - { - 44630, - 44631, - 44632, - 44633, - 44634, - 44635, - 44636, - 44637, - 44638, - 44639, - 44640, - 44641, - 44642, - 44643, - 44644, - 44645, - 44646, - 44647, - 44648, - 44649, - 44650, - 44651, - 44652, - 44653, - 44654, - 44655, - 44656, - 44657, - 44658, - 44659, - 44660, - 44661, - 44662, - 44663, - 44664, - 44665, - 44666, - 44667, - 44668, - 44669, - 44670, - 44671, - 44672, - 44673, - 44674, - 44675, - 44676, - 44677, - 44678, - 44679, - 44680, - 44681, - 44682, - 44683, - 44684, - 44685, - 44686, - 44687, - 44688, - 44689, - 44690, - 44691, - 44692, - 44693, - 44694, - 44695 }, 15224, 57, 238, new Item(Items.BUCKET_OF_SAND_1783, 10), new Item(Items.LIMESTONE_BRICK_3420, 5), new Item(15237, 1)), - POLAR_HABITAT(new int[] - { - 44696, - 44697, - 44698, - 44699, - 44700, - 44701, - 44702, - 44703, - 44704, - 44705, - 44706, - 44707, - 44708, - 44709, - 44710, - 44711, - 44712, - 44713, - 44714, - 44715, - 44716, - 44717, - 44718, - 44719, - 44720, - 44721, - 44722, - 44723, - 44724, - 44725, - 44726, - 44727, - 44728, - 44729, - 44730, - 44731, - 44732, - 44733, - 44734, - 44735, - 44736, - 44737, - 44738, - 44739, - 44740, - 44741, - 44742, - 44743, - 44744, - 44745, - 44746, - 44747, - 44748, - 44749, - 44750, - 44751, - 44752, - 44753, - 44754, - 44755, - 44756, - 44757, - 44758, - 44759, - 44760, - 44761 }, 15225, 67, 373, new Item(Items.AIR_RUNE_556, 1000), new Item(Items.WATER_RUNE_555, 1000), new Item(15239, 1)), - VOLCANIC_HABITAT(new int[] - { - 44762, - 44763, - 44764, - 44765, - 44766, - 44767, - 44768, - 44769, - 44770, - 44771, - 44772, - 44773, - 44774, - 44775, - 44776, - 44777, - 44778, - 44779, - 44780, - 44781, - 44782, - 44783, - 44784, - 44785, - 44786, - 44787, - 44788, - 44789, - 44790, - 44791, - 44792, - 44793, - 44794, - 44795, - 44796, - 44797, - 44798, - 44799, - 44800, - 44801, - 44802, - 44803, - 44804, - 44805, - 44806, - 44807, - 44808, - 44809, - 44810, - 44811, - 44812, - 44813, - 44814, - 44815, - 44816, - 44817, - 44818, - 44819, - 44820, - 44821, - 44822, - 44823, - 44824, - 44825, - 44826, - 44827 }, 15226, 77, 77, new Item(Items.FIRE_RUNE_554, 1000), new Item(Items.EARTH_RUNE_557, 1000), new Item(Items.BAGGED_DEAD_TREE_8417, 1), new Item(Items.STONE_SLAB_13245, 5)), - + ANTIDRAGON_SHIELD(13522, 8282, 47, 280, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ANTI_DRAGON_SHIELD_1540) }, new Item[] { new Item(Items.ANTI_DRAGON_SHIELD_1540) }), + AMULET_OF_GLORY (13523, 8283, 47, 290, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.AMULET_OF_GLORY_1704) }, new Item[] { new Item(Items.AMULET_OF_GLORY_1704) }), + CAPE_OF_LEGENDS (13524, 8284, 47, 300, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.CAPE_OF_LEGENDS_1052) }, new Item[] { new Item(Items.CAPE_OF_LEGENDS_1052) }), + KING_ARTHUR (13510, 8285, 35, 211, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ARTHUR_PORTRAIT_7995) }), + ELENA (13511, 8286, 35, 211, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ELENA_PORTRAIT_7996) }), + GIANT_DWARF (13512, 8287, 35, 211, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.KELDAGRIM_PORTRAIT_7997) }), + MISCELLANIANS (13513, 8288, 35, 311, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.MISC_PORTRAIT_7998) }), + LUMBRIDGE (13517, 8289, 44, 314, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.LUMBRIDGE_PAINTING_8002) }), + THE_DESERT (13514, 8290, 44, 314, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.DESERT_PAINTING_7999) }), + MORYTANIA (13518, 8291, 44, 314, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.MORYTANIA_PAINTING_8003) }), + KARAMJA (13516, 8292, 65, 464, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.KARAMJA_PAINTING_8001) }), + ISAFDAR (13515, 8293, 65, 464, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.ISAFDAR_PAINTING_8000) }), + SILVERLIGHT (13519, 8279, 42, 187, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.SILVERLIGHT_2402) }, new Item[] { new Item(Items.SILVERLIGHT_2402) }), + EXCALIBUR (13521, 8280, 42, 194, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.EXCALIBUR_35) }, new Item[] { new Item(Items.EXCALIBUR_35) }), + DARKLIGHT (13520, 8281, 42, 202, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.DARKLIGHT_6746) }, new Item[] { new Item(Items.DARKLIGHT_6746) }), + SMALL_MAP (13525, 8294, 38, 211, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.SMALL_MAP_8004) }), + MEDIUM_MAP (13526, 8295, 58, 451, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.MEDIUM_MAP_8005) }), + LARGE_MAP (13527, 8296, 78, 591, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.LARGE_MAP_8006) }), /** * Study decorations. */ - GLOBE(13649, 8341, 41, 180, new Item(Items.OAK_PLANK_8778, 3)), - ORNAMENTAL_GLOBE(13650, 8342, 50, 270, new Item(Items.TEAK_PLANK_8780, 3)), - LUNAR_GLOBE(13651, 8343, 59, 570, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.GOLD_LEAF_8784, 1)), - CELESTIAL_GLOBE(13652, 8344, 68, 570, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.GOLD_LEAF_8784, 1)), - ARMILLARY_SPHERE(13653, 8345, 77, 960, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784, 2), new Item(Items.STEEL_BAR_2353, 4)), - SMALL_ORREY(13654, 8346, 86, 1320, new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.GOLD_LEAF_8784, 3)), - LARGE_ORREY(13655, 8347, 95, 1420, new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.GOLD_LEAF_8784, 5)), - OAK_LECTERN(13642, 8334, 40, 60, new Item(Items.OAK_PLANK_8778, 1)), - EAGLE_LECTERN(13643, 8335, 47, 120, new Item(Items.OAK_PLANK_8778, 2)), - DEMON_LECTERN(13644, 8336, 47, 120, new Item(Items.OAK_PLANK_8778, 2)), - TEAK_EAGLE_LECTERN(13645, 8337, 57, 180, new Item(Items.TEAK_PLANK_8780, 2)), - TEAK_DEMON_LECTERN(13646, 8338, 57, 180, new Item(Items.TEAK_PLANK_8780, 2)), - MAHOGANY_EAGLE_LECTERN(13647, 8339, 67, 580, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784, 1)), - MAHOGANY_DEMON_LECTERN(13648, 8340, 67, 580, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784, 1)), - CRYSTAL_BALL(13659, 8351, 42, 280, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.UNPOWERED_ORB_567, 1)), - ELEMENTAL_SPHERE(13660, 8352, 54, 580, new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.UNPOWERED_ORB_567, 1), new Item(Items.GOLD_LEAF_8784, 1)), - CRYSTAL_OF_POWER(13661, 8353, 66, 890, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.UNPOWERED_ORB_567, 1), new Item(Items.GOLD_LEAF_8784, 2)), - ALCHEMICAL_CHART(13662, 8354, 43, 30, new Item(Items.BOLT_OF_CLOTH_8790, 2)), - ASTRONOMICAL_CHART(13663, 8355, 63, 45, new Item(Items.BOLT_OF_CLOTH_8790, 3)), - INFERNAL_CHART(13664, 8356, 83, 60, new Item(Items.BOLT_OF_CLOTH_8790, 4)), - TELESCOPE1(13656, 8348, 44, 121, new Item(Items.OAK_PLANK_8778, 2), new Item(Items.MOLTEN_GLASS_1775, 1)), - TELESCOPE2(13657, 8349, 64, 181, new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 1)), - TELESCOPE3(13658, 8350, 84, 580, new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775, 1)), - + GLOBE (13649, 8341, 41, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), + ORNAMENTAL_GLOBE (13650, 8342, 50, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }), + LUNAR_GLOBE (13651, 8343, 59, 570, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.GOLD_LEAF_8784) }), + CELESTIAL_GLOBE (13652, 8344, 68, 570, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.GOLD_LEAF_8784) }), + ARMILLARY_SPHERE (13653, 8345, 77, 960, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784, 2), new Item(Items.STEEL_BAR_2353, 4) }), + SMALL_ORREY (13654, 8346, 86, 1320, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.GOLD_LEAF_8784, 3) }), + LARGE_ORREY (13655, 8347, 95, 1420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.GOLD_LEAF_8784, 5) }), + OAK_LECTERN (13642, 8334, 40, 60, new Item[] { new Item(Items.OAK_PLANK_8778) }), + EAGLE_LECTERN (13643, 8335, 47, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + DEMON_LECTERN (13644, 8336, 47, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TEAK_EAGLE_LECTERN (13645, 8337, 57, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + TEAK_DEMON_LECTERN (13646, 8338, 57, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + MAHOGANY_EAGLE_LECTERN(13647, 8339, 67, 580, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784) }), + MAHOGANY_DEMON_LECTERN(13648, 8340, 67, 580, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784) }), + CRYSTAL_BALL (13659, 8351, 42, 280, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.UNPOWERED_ORB_567) }), + ELEMENTAL_SPHERE (13660, 8352, 54, 580, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.UNPOWERED_ORB_567), new Item(Items.GOLD_LEAF_8784) }), + CRYSTAL_OF_POWER (13661, 8353, 66, 890, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.UNPOWERED_ORB_567), new Item(Items.GOLD_LEAF_8784, 2) }), + ALCHEMICAL_CHART (13662, 8354, 43, 30, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + ASTRONOMICAL_CHART (13663, 8355, 63, 45, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 3) }), + INFERNAL_CHART (13664, 8356, 83, 60, new Item[] { new Item(Items.BOLT_OF_CLOTH_8790, 4) }), + TELESCOPE1 (13656, 8348, 44, 121, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.MOLTEN_GLASS_1775) }), + TELESCOPE2 (13657, 8349, 64, 181, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775) }), + TELESCOPE3 (13658, 8350, 84, 580, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775) }), + /** * Costume room decorations. */ - OAK_TREASURE_CHEST(18804, 9839, 48, 120, new Item(Items.OAK_PLANK_8778, 2)), - TEAK_TREASURE_CHEST(18806, 9840, 66, 180, new Item(Items.TEAK_PLANK_8780, 2)), - MAHOGANY_TREASURE_CHEST(18808, 9841, 84, 280, new Item(Items.MAHOGANY_PLANK_8782, 2)), - OAK_ARMOUR_CASE(18778, 9826, 46, 180, new Item(Items.OAK_PLANK_8778, 3)), - TEAK_ARMOUR_CASE(18780, 9827, 64, 270, new Item(Items.TEAK_PLANK_8780, 3)), - MGANY_ARMOUR_CASE(18782, 9828, 82, 420, new Item(Items.MAHOGANY_PLANK_8782, 3)), - OAK_MAGIC_WARDROBE(18784, 9829, 42, 240, new Item(Items.OAK_PLANK_8778, 4)), - C_OAK_MAGIC_WARDROBE(18786, 9830, 51, 360, new Item(Items.OAK_PLANK_8778, 6)), - TEAK_MAGIC_WARDROBE(18788, 9831, 60, 360, new Item(Items.TEAK_PLANK_8780, 4)), - C_TEAK_MAGIC_WARDROBE(18790, 9832, 69, 540, new Item(Items.TEAK_PLANK_8780, 6)), - MGANY_MAGIC_WARDROBE(18792, 9833, 78, 560, new Item(Items.MAHOGANY_PLANK_8782, 4)), - GILDED_MAGIC_WARDROBE(18794, 9834, 87, 860, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 1)), - MARBLE_MAGIC_WARDROBE(18796, 9835, 96, 500, new Item(Items.MARBLE_BLOCK_8786, 1)), - OAK_CAPE_RACK(18766, 9817, 54, 240, new Item(Items.OAK_PLANK_8778, 4)), - TEAK_CAPE_RACK(18767, 9818, 63, 360, new Item(Items.TEAK_PLANK_8780, 4)), - MGANY_CAPE_RACK(18768, 9819, 72, 560, new Item(Items.MAHOGANY_PLANK_8782, 4)), - GILDED_CAPE_RACK(18769, 9820, 81, 860, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 1)), - MARBLE_CAPE_RACK(18770, 9821, 90, 500, new Item(Items.MARBLE_BLOCK_8786, 1)), - MAGIC_CAPE_RACK(18771, 9822, 99, 1000, new Item(Items.MAGIC_STONE_8788, 1)), - OAK_TOY_BOX(18798, 9836, 50, 120, new Item(Items.OAK_PLANK_8778, 2)), - TEAK_TOY_BOX(18800, 9837, 68, 180, new Item(Items.TEAK_PLANK_8780, 2)), - MAHOGANY_TOY_BOX(18802, 9838, 86, 280, new Item(Items.MAHOGANY_PLANK_8782, 2)), - OAK_COSTUME_BOX(18772, 9823, 44, 120, new Item(Items.OAK_PLANK_8778, 2)), - TEAK_COSTUME_BOX(18774, 9824, 62, 180, new Item(Items.TEAK_PLANK_8780, 2)), - MAHOGANY_COSTUME_BOX(18776, 9825, 80, 280, new Item(Items.MAHOGANY_PLANK_8782, 2)), - + OAK_TREASURE_CHEST (18804, 9839, 48, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TEAK_TREASURE_CHEST (18806, 9840, 66, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + MAHOGANY_TREASURE_CHEST(18808, 9841, 84, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), + OAK_ARMOUR_CASE (18778, 9826, 46, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), + TEAK_ARMOUR_CASE (18780, 9827, 64, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }), + MGANY_ARMOUR_CASE (18782, 9828, 82, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3) }), + OAK_MAGIC_WARDROBE (18784, 9829, 42, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + C_OAK_MAGIC_WARDROBE (18786, 9830, 51, 360, new Item[] { new Item(Items.OAK_PLANK_8778, 6) }), + TEAK_MAGIC_WARDROBE (18788, 9831, 60, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + C_TEAK_MAGIC_WARDROBE (18790, 9832, 69, 540, new Item[] { new Item(Items.TEAK_PLANK_8780, 6) }), + MGANY_MAGIC_WARDROBE (18792, 9833, 78, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4) }), + GILDED_MAGIC_WARDROBE (18794, 9834, 87, 860, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784) }), + MARBLE_MAGIC_WARDROBE (18796, 9835, 96, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), + OAK_CAPE_RACK (18766, 9817, 54, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + TEAK_CAPE_RACK (18767, 9818, 63, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + MGANY_CAPE_RACK (18768, 9819, 72, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4) }), + GILDED_CAPE_RACK (18769, 9820, 81, 860, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784) }), + MARBLE_CAPE_RACK (18770, 9821, 90, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), + MAGIC_CAPE_RACK (18771, 9822, 99, 1000, new Item[] { new Item(Items.MAGIC_STONE_8788) }), + OAK_TOY_BOX (18798, 9836, 50, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TEAK_TOY_BOX (18800, 9837, 68, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + MAHOGANY_TOY_BOX (18802, 9838, 86, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), + OAK_COSTUME_BOX (18772, 9823, 44, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TEAK_COSTUME_BOX (18774, 9824, 62, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + MAHOGANY_COSTUME_BOX (18776, 9825, 80, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), + /** * Chapel decorations. */ - OAK_ALTAR(13179, 8062, 45, 240, new Item(Items.OAK_PLANK_8778, 4)), - TEAK_ALTAR(13182, 8063, 50, 360, new Item(Items.TEAK_PLANK_8780, 4)), - CLOTH_ALTAR(13185, 8064, 56, 390, new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - MAHOGANY_ALTAR(13188, 8065, 60, 590, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - LIMESTONE_ALTAR(13191, 8066, 64, 910, new Item(Items.MAHOGANY_PLANK_8782, 6), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.LIMESTONE_BRICK_3420, 2)), - MARBLE_ALTAR(13194, 8067, 70, 1030, new Item(Items.MARBLE_BLOCK_8786, 2), new Item(Items.BOLT_OF_CLOTH_8790, 2)), - GILDED_ALTAR(13197, 8068, 75, 2230, new Item(Items.MARBLE_BLOCK_8786, 2), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784, 4)), - SMALL_STATUE(13271, 8082, 49, 40, new Item(Items.LIMESTONE_BRICK_3420, 2)), - MEDIUM_STATUE(13272, 8083, 69, 500, new Item(Items.MARBLE_BLOCK_8786, 1)), - LARGE_STATUE(13282, 8084, 89, 1500, new Item(Items.MARBLE_BLOCK_8786, 3)), - WINDCHIMES(13214, 8079, 49, 323, new Item(Items.OAK_PLANK_8778, 4), new Item(Items.STEEL_BAR_2353, 4)), - BELLS(13215, 8080, 58, 480, new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.STEEL_BAR_2353, 6)), - ORGAN(13216, 8081, 69, 680, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.STEEL_BAR_2353, 6)), - SARADOMIN_SYMBOL(13172, 8055, 48, 120, new Item(Items.OAK_PLANK_8778, 2)), - ZAMORAK_SYMBOL(13173, 8056, 48, 120, new Item(Items.OAK_PLANK_8778, 2)), - GUTHIX_SYMBOL(13174, 8057, 48, 120, new Item(Items.OAK_PLANK_8778, 2)), - SARADOMIN_ICON(13175, 8058, 59, 960, new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.GOLD_LEAF_8784, 2)), - ZAMORAK_ICON(13176, 8059, 59, 960, new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.GOLD_LEAF_8784, 2)), - GUTHIX_ICON(13177, 8060, 59, 960, new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.GOLD_LEAF_8784, 2)), - ICON_OF_BOB(13178, 8061, 71, 1160, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 2)), - STEEL_TORCHES(13202, 8070, 45, 80, new Item(Items.STEEL_BAR_2353, 2)), - WOODEN_TORCHES(13200, 8069, 49, 58, new Item(Items.PLANK_960, 2)), - STEEL_CANDLESTICKS(13204, 8071, 53, 124, new Item(Items.STEEL_BAR_2353, 6), new Item(Items.CANDLE_36, 6)), - GOLD_CANDLESTICKS(13206, 8072, 57, 46, new Item(Items.GOLD_BAR_2357, 6), new Item(Items.CANDLE_36, 6)), - INCENSE_BURNERS(13208, 8073, 61, 280, new Item(Items.OAK_PLANK_8778, 4), new Item(Items.STEEL_BAR_2353, 2)), - MAHOGANY_BURNERS(13210, 8074, 65, 600, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.STEEL_BAR_2353, 2)), - MARBLE_BURNERS(13212, 8075, 69, 1600, new Item(Items.MARBLE_BLOCK_8786, 2), new Item(Items.STEEL_BAR_2353, 2)), - SHUTTERED_WINDOW(new int[] { 13253, 13226, 13235, 13244, 13217, 13262 }, 8076, 49, 228, new Item(Items.PLANK_960, 8)), - DECORATIVE_WINDOW(new int[] { 13254, 13227, 13236, 13245, 13218, 13263 }, 8077, 69, 200, new Item(Items.MOLTEN_GLASS_1775, 8)), - STAINED_GLASS(new int[] { 13255, 13228, 13237, 13246, 13219, 13264 }, 8078, 89, 400, new Item(Items.MOLTEN_GLASS_1775, 16)), + OAK_ALTAR (13179, 8062, 45, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), + TEAK_ALTAR (13182, 8063, 50, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + CLOTH_ALTAR (13185, 8064, 56, 390, new Item[] { new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + MAHOGANY_ALTAR (13188, 8065, 60, 590, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + LIMESTONE_ALTAR (13191, 8066, 64, 910, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.LIMESTONE_BRICK_3420, 2) }), + MARBLE_ALTAR (13194, 8067, 70, 1030, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 2), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), + GILDED_ALTAR (13197, 8068, 75, 2230, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 2), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784, 4) }), + SMALL_STATUE (13271, 8082, 49, 40, new Item[] { new Item(Items.LIMESTONE_BRICK_3420, 2) }), + MEDIUM_STATUE (13272, 8083, 69, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), + LARGE_STATUE (13282, 8084, 89, 1500, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 3) }), + WINDCHIMES (13214, 8079, 49, 323, new Item[] { new Item(Items.OAK_PLANK_8778, 4), new Item(Items.STEEL_BAR_2353, 4) }), + BELLS (13215, 8080, 58, 480, new Item[] { new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.STEEL_BAR_2353, 6) }), + ORGAN (13216, 8081, 69, 680, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.STEEL_BAR_2353, 6) }), + SARADOMIN_SYMBOL (13172, 8055, 48, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + ZAMORAK_SYMBOL (13173, 8056, 48, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + GUTHIX_SYMBOL (13174, 8057, 48, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + SARADOMIN_ICON (13175, 8058, 59, 960, new Item[] { new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.GOLD_LEAF_8784, 2) }), + ZAMORAK_ICON (13176, 8059, 59, 960, new Item[] { new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.GOLD_LEAF_8784, 2) }), + GUTHIX_ICON (13177, 8060, 59, 960, new Item[] { new Item(Items.TEAK_PLANK_8780, 4), new Item(Items.GOLD_LEAF_8784, 2) }), + ICON_OF_BOB (13178, 8061, 71, 1160, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 2) }), + STEEL_TORCHES (13202, 8070, 45, 80, new Item[] { new Item(Items.STEEL_BAR_2353, 2) }), + WOODEN_TORCHES (13200, 8069, 49, 58, new Item[] { new Item(Items.PLANK_960, 2) }), + STEEL_CANDLESTICKS(13204, 8071, 53, 124, new Item[] { new Item(Items.STEEL_BAR_2353, 6), new Item(Items.CANDLE_36, 6) }), + GOLD_CANDLESTICKS (13206, 8072, 57, 46, new Item[] { new Item(Items.GOLD_BAR_2357, 6), new Item(Items.CANDLE_36, 6) }), + INCENSE_BURNERS (13208, 8073, 61, 280, new Item[] { new Item(Items.OAK_PLANK_8778, 4), new Item(Items.STEEL_BAR_2353, 2) }), + MAHOGANY_BURNERS (13210, 8074, 65, 600, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.STEEL_BAR_2353, 2) }), + MARBLE_BURNERS (13212, 8075, 69, 1600, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 2), new Item(Items.STEEL_BAR_2353, 2) }), + SHUTTERED_WINDOW (new int[] { 13253, 13226, 13235, 13244, 13217, 13262 }, 8076, 49, 228, new Item[] { new Item(Items.PLANK_960, 8) }), + DECORATIVE_WINDOW (new int[] { 13254, 13227, 13236, 13245, 13218, 13263 }, 8077, 69, 200, new Item[] { new Item(Items.MOLTEN_GLASS_1775, 8) }), + STAINED_GLASS (new int[] { 13255, 13228, 13237, 13246, 13219, 13264 }, 8078, 89, 400, new Item[] { new Item(Items.MOLTEN_GLASS_1775, 16) }), /** * Throne room */ - OAK_THRONE(13665, 8357, 60, 800, new Item(Items.OAK_PLANK_8778, 5), new Item(Items.MARBLE_BLOCK_8786, 1)), - TEAK_THRONE(13666, 8358, 67, 1450, new Item(Items.TEAK_PLANK_8780, 5), new Item(Items.MARBLE_BLOCK_8786, 2)), - MAHOGANY_THRONE(13667, 8359, 74, 2200, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MARBLE_BLOCK_8786, 3)), - GILDED_THRONE(13668, 8360, 81, 1700, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MARBLE_BLOCK_8786, 2), new Item(Items.GOLD_LEAF_8784, 3)), - SKELETON_THRONE(13669, 8361, 88, 7003, new Item(Items.MAGIC_STONE_8788, 5), new Item(Items.MARBLE_BLOCK_8786, 4), new Item(Items.BONES_526, 5), new Item(Items.SKULL_964, 2)), - CRYSTAL_THRONE(13670, 8362, 95, 15000, new Item(Items.MAGIC_STONE_8788, 15)), - DEMONIC_THRONE(13671, 8363, 99, 25000, new Item(Items.MAGIC_STONE_8788, 25)), - OAK_LEVER(13672, 8364, 68, 300, new Item(Items.OAK_PLANK_8778, 5)), - TEAK_LEVER(13673, 8365, 78, 450, new Item(Items.TEAK_PLANK_8780, 5)), - MAHOGANY_LEVER(13674, 8366, 88, 700, new Item(Items.MAHOGANY_PLANK_8782, 5)), - FLOOR_DECORATION(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8370, 61, 700, new Item(Items.MAHOGANY_PLANK_8782, 5)), - STEEL_CAGE(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8371, 68, 1100, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.STEEL_BAR_2353, 20)), - FLOOR_TRAP(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8372, 74, 770, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.CLOCKWORK_8792, 10)), - MAGIC_CIRCLE(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8373, 82, 2700, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MAGIC_STONE_8788, 2)), - MAGIC_CAGE(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8374, 89, 4700, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MAGIC_STONE_8788, 4)), - OAK_TRAPDOOR(13675, 8367, 68, 300, new Item(Items.OAK_PLANK_8778, 5)), - TEAK_TRAPDOOR(13676, 8368, 78, 450, new Item(Items.TEAK_PLANK_8780, 5)), - MAHOGANY_TRAPDOOR(13677, 8369, 88, 700, new Item(Items.MAHOGANY_PLANK_8782, 5)), - CARVED_TEAK_BENCH(13694, 8112, 44, 360, new Item(Items.TEAK_PLANK_8780, 4)), - MAHOGANY_BENCH(13695, 8113, 52, 560, new Item(Items.MAHOGANY_PLANK_8782, 4)), - GILDED_BENCH(13696, 8114, 61, 1760, new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 4)), - OAK_DECO(13798, 8102, 16, 120.0, new Item(Items.OAK_PLANK_8778, 2)), - TEAK_DECO(13814, 8103, 36, 180.0, new Item(Items.TEAK_PLANK_8780, 2)), - GILDED_DECO(13782, 8104, 56, 1020.0, new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.GOLD_LEAF_8784, 2)), - ROUND_SHIELD(13734, 8105, 66, 120, new Item(Items.OAK_PLANK_8778, 2)), - SQUARE_SHIELD(13766, 8106, 76, 360, new Item(Items.TEAK_PLANK_8780, 4)), - KITE_SHIELD(13750, 8107, 86, 420, new Item(Items.MAHOGANY_PLANK_8782, 3)), - + OAK_THRONE (13665, 8357, 60, 800, new Item[] { new Item(Items.OAK_PLANK_8778, 5), new Item(Items.MARBLE_BLOCK_8786) }), + TEAK_THRONE (13666, 8358, 67, 1450, new Item[] { new Item(Items.TEAK_PLANK_8780, 5), new Item(Items.MARBLE_BLOCK_8786, 2) }), + MAHOGANY_THRONE (13667, 8359, 74, 2200, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MARBLE_BLOCK_8786, 3) }), + GILDED_THRONE (13668, 8360, 81, 1700, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MARBLE_BLOCK_8786, 2), new Item(Items.GOLD_LEAF_8784, 3) }), + SKELETON_THRONE (13669, 8361, 88, 7003, new Item[] { new Item(Items.MAGIC_STONE_8788, 5), new Item(Items.MARBLE_BLOCK_8786, 4), new Item(Items.BONES_526, 5), new Item(Items.SKULL_964, 2) }), + CRYSTAL_THRONE (13670, 8362, 95, 15000, new Item[] { new Item(Items.MAGIC_STONE_8788, 15) }), + DEMONIC_THRONE (13671, 8363, 99, 25000, new Item[] { new Item(Items.MAGIC_STONE_8788, 25) }), + OAK_LEVER (13672, 8364, 68, 300, new Item[] { new Item(Items.OAK_PLANK_8778, 5) }), + TEAK_LEVER (13673, 8365, 78, 450, new Item[] { new Item(Items.TEAK_PLANK_8780, 5) }), + MAHOGANY_LEVER (13674, 8366, 88, 700, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5) }), + FLOOR_DECORATION (new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8370, 61, 700, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5) }), + STEEL_CAGE (new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8371, 68, 1100, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.STEEL_BAR_2353, 20) }), + FLOOR_TRAP (new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8372, 74, 770, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.CLOCKWORK_8792, 10) }), + MAGIC_CIRCLE (new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8373, 82, 2700, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MAGIC_STONE_8788, 2) }), + MAGIC_CAGE (new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8374, 89, 4700, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.MAGIC_STONE_8788, 4) }), + OAK_TRAPDOOR (13675, 8367, 68, 300, new Item[] { new Item(Items.OAK_PLANK_8778, 5) }), + TEAK_TRAPDOOR (13676, 8368, 78, 450, new Item[] { new Item(Items.TEAK_PLANK_8780, 5) }), + MAHOGANY_TRAPDOOR(13677, 8369, 88, 700, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5) }), + CARVED_TEAK_BENCH(13694, 8112, 44, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + MAHOGANY_BENCH (13695, 8113, 52, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4) }), + GILDED_BENCH (13696, 8114, 61, 1760, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 4) }), + OAK_DECO (13798, 8102, 16, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + TEAK_DECO (13814, 8103, 36, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), + GILDED_DECO (13782, 8104, 56, 1020, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.GOLD_LEAF_8784, 2) }), + ROUND_SHIELD (13734, 8105, 66, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), + SQUARE_SHIELD (13766, 8106, 76, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), + KITE_SHIELD (13750, 8107, 86, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3) }), + /** * Oubliette */ - SPIKES_MID(13334, 8302, 65, 623, new Item(Items.STEEL_BAR_2353, 20), new Item(Items.COINS_995, 50000)), - SPIKES_SIDE(13335, 8302, 65, 623, new Item(Items.STEEL_BAR_2353, 20), new Item(Items.COINS_995, 50000)), - SPIKES_CORNER(13336, 8302, 65, 623, new Item(Items.STEEL_BAR_2353, 20), new Item(Items.COINS_995, 50000)), - SPIKES_FL(13338, 8302, 65, 623, new Item(Items.STEEL_BAR_2353, 20), new Item(Items.COINS_995, 50000)), - TENTACLE_MID(13331, 8303, 71, 326, new Item(Items.BUCKET_OF_WATER_1929, 20), new Item(Items.COINS_995, 100000)), - TENTACLE_SIDE(13332, 8303, 71, 326, new Item(Items.BUCKET_OF_WATER_1929, 20), new Item(Items.COINS_995, 100000)), - TENTACLE_CORNER(13333, 8303, 71, 326, new Item(Items.BUCKET_OF_WATER_1929, 20), new Item(Items.COINS_995, 100000)), - TENTACLE_FL(13338, 8303, 71, 326, new Item(Items.BUCKET_OF_WATER_1929, 20), new Item(Items.COINS_995, 100000)), - FP_FLOOR_MID(13371, 8304, 77, 357, new Item(Items.TINDERBOX_590, 20), new Item(Items.COINS_995, 125000)), - FP_FLOOR_SIDE(13371, 8304, 77, 357, new Item(Items.TINDERBOX_590, 20), new Item(Items.COINS_995, 125000)), - FP_FLOOR_CORNER(13371, 8304, 77, 357, new Item(Items.TINDERBOX_590, 20), new Item(Items.COINS_995, 125000)), - FLAME_PIT(13337, 8304, 77, 357, new Item(Items.TINDERBOX_590, 20), new Item(Items.COINS_995, 125000)), - ROCNAR_FLOOR_MID(13371, 8305, 83, 387, new Item(Items.COINS_995, 150000)), - ROCNAR_FLOOR_SIDE(13371, 8305, 83, 387, new Item(Items.COINS_995, 150000)), - ROCNAR_FLOOR_CORNER(13371, 8305, 83, 387, new Item(Items.COINS_995, 150000)), - ROCNAR(13373, 8305, 83, 387, new Item(Items.COINS_995, 150000)), - ROCNAR_FL(13338, 8305, 83, 387, new Item(Items.COINS_995, 150000)), - OAK_CAGE(13313, 8297, 65, 640, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 2)), - OAK_CAGE_DOOR(13314, 8297, 65, 640, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 2)), - OAK_STEEL_CAGE(13316, 8298, 70, 800, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 10)), - OAK_STEEL_CAGE_DOOR(13317, 8298, 70, 800, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 10)), - STEEL_CAGE_OU(13319, 8299, 75, 400, new Item(Items.STEEL_BAR_2353, 20)), - STEEL_CAGE_DOOR(13320, 8299, 75, 400, new Item(Items.STEEL_BAR_2353, 20)), - SPIKED_CAGE(13322, 8300, 80, 500, new Item(Items.STEEL_BAR_2353, 25)), - SPIKED_CAGE_DOOR(13323, 8300, 80, 500, new Item(Items.STEEL_BAR_2353, 25)), - BONE_CAGE(13325, 8301, 85, 603, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.BONES_526, 10)), - BONE_CAGE_DOOR(13326, 8301, 85, 603, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.BONES_526, 10)), - SKELETON_GUARD(13366, 8131, 70, 223, new Item(Items.COINS_995, 50000)), - GUARD_DOG(13367, 8132, 74, 273, new Item(Items.COINS_995, 75000)), - HOBGOBLIN(13368, 8133, 78, 316, new Item(Items.COINS_995, 100000)), - BABY_RED_DRAGON(13372, 8134, 82, 387, new Item(Items.COINS_995, 150000)), - HUGE_SPIDER(13370, 8135, 86, 447, new Item(Items.COINS_995, 200000)), - TROLL(13369, 8136, 90, 1000, new Item(Items.COINS_995, 1000000)), - HELLHOUND(2715, 8137, 94, 2236, new Item(Items.COINS_995, 5000000)), - OAK_LADDER(13328, 8306, 68, 300, new Item(Items.OAK_PLANK_8778, 5)), - TEAK_LADDER(13329, 8307, 78, 450, new Item(Items.TEAK_PLANK_8780, 5)), - MAHOGANY_LADDER(13330, 8308, 88, 700, new Item(Items.MAHOGANY_PLANK_8782, 5)), - DECORATIVE_BLOOD(13312, 8125, 72, 4, new Item(Items.RED_DYE_1763, 4)), - DECORATIVE_PIPE(13311, 8126, 83, 120, new Item(Items.STEEL_BAR_2353, 6)), - HANGING_SKELETON(13310, 8127, 94, 3, new Item(Items.SKULL_964, 2), new Item(Items.BONES_526, 6)), - CANDLE(13342, 8128, 72, 243, new Item(Items.OAK_PLANK_8778, 4), new Item(Items.LIT_CANDLE_33, 4)), - TORCH(13341, 8129, 84, 244, new Item(Items.OAK_PLANK_8778, 4), new Item(Items.LIT_TORCH_594, 4)), - SKULL_TORCH(13343, 8130, 94, 246, new Item(Items.OAK_PLANK_8778, 4), new Item(Items.LIT_TORCH_594, 4), new Item(Items.SKULL_964, 4)), - + SPIKES_MID (13334, 8302, 65, 623, new Item[] { new Item(Items.STEEL_BAR_2353, 20), new Item(Items.COINS_995, 50000) }), + SPIKES_SIDE (13335, 8302, 65, 623, new Item[] { new Item(Items.STEEL_BAR_2353, 20), new Item(Items.COINS_995, 50000) }), + SPIKES_CORNER (13336, 8302, 65, 623, new Item[] { new Item(Items.STEEL_BAR_2353, 20), new Item(Items.COINS_995, 50000) }), + SPIKES_FL (13338, 8302, 65, 623, new Item[] { new Item(Items.STEEL_BAR_2353, 20), new Item(Items.COINS_995, 50000) }), + TENTACLE_MID (13331, 8303, 71, 326, new Item[] { new Item(Items.BUCKET_OF_WATER_1929, 20), new Item(Items.COINS_995, 100000) }), + TENTACLE_SIDE (13332, 8303, 71, 326, new Item[] { new Item(Items.BUCKET_OF_WATER_1929, 20), new Item(Items.COINS_995, 100000) }), + TENTACLE_CORNER (13333, 8303, 71, 326, new Item[] { new Item(Items.BUCKET_OF_WATER_1929, 20), new Item(Items.COINS_995, 100000) }), + TENTACLE_FL (13338, 8303, 71, 326, new Item[] { new Item(Items.BUCKET_OF_WATER_1929, 20), new Item(Items.COINS_995, 100000) }), + FP_FLOOR_MID (13371, 8304, 77, 357, new Item[] { new Item(Items.TINDERBOX_590, 20), new Item(Items.COINS_995, 125000) }), + FP_FLOOR_SIDE (13371, 8304, 77, 357, new Item[] { new Item(Items.TINDERBOX_590, 20), new Item(Items.COINS_995, 125000) }), + FP_FLOOR_CORNER (13371, 8304, 77, 357, new Item[] { new Item(Items.TINDERBOX_590, 20), new Item(Items.COINS_995, 125000) }), + FLAME_PIT (13337, 8304, 77, 357, new Item[] { new Item(Items.TINDERBOX_590, 20), new Item(Items.COINS_995, 125000) }), + ROCNAR_FLOOR_MID (13371, 8305, 83, 387, new Item[] { new Item(Items.COINS_995, 150000) }), + ROCNAR_FLOOR_SIDE (13371, 8305, 83, 387, new Item[] { new Item(Items.COINS_995, 150000) }), + ROCNAR_FLOOR_CORNER(13371, 8305, 83, 387, new Item[] { new Item(Items.COINS_995, 150000) }), + ROCNAR (13373, 8305, 83, 387, new Item[] { new Item(Items.COINS_995, 150000) }), + ROCNAR_FL (13338, 8305, 83, 387, new Item[] { new Item(Items.COINS_995, 150000) }), + OAK_CAGE (13313, 8297, 65, 640, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 2) }), + OAK_CAGE_DOOR (13314, 8297, 65, 640, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 2) }), + OAK_STEEL_CAGE (13316, 8298, 70, 800, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 10) }), + OAK_STEEL_CAGE_DOOR(13317, 8298, 70, 800, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 10) }), + STEEL_CAGE_OU (13319, 8299, 75, 400, new Item[] { new Item(Items.STEEL_BAR_2353, 20) }), + STEEL_CAGE_DOOR (13320, 8299, 75, 400, new Item[] { new Item(Items.STEEL_BAR_2353, 20) }), + SPIKED_CAGE (13322, 8300, 80, 500, new Item[] { new Item(Items.STEEL_BAR_2353, 25) }), + SPIKED_CAGE_DOOR (13323, 8300, 80, 500, new Item[] { new Item(Items.STEEL_BAR_2353, 25) }), + BONE_CAGE (13325, 8301, 85, 603, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.BONES_526, 10) }), + BONE_CAGE_DOOR (13326, 8301, 85, 603, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.BONES_526, 10) }), + SKELETON_GUARD (13366, 8131, 70, 223, new Item[] { new Item(Items.COINS_995, 50000) }), + GUARD_DOG (13367, 8132, 74, 273, new Item[] { new Item(Items.COINS_995, 75000) }), + HOBGOBLIN (13368, 8133, 78, 316, new Item[] { new Item(Items.COINS_995, 100000) }), + BABY_RED_DRAGON (13372, 8134, 82, 387, new Item[] { new Item(Items.COINS_995, 150000) }), + HUGE_SPIDER (13370, 8135, 86, 447, new Item[] { new Item(Items.COINS_995, 200000) }), + TROLL (13369, 8136, 90, 1000, new Item[] { new Item(Items.COINS_995, 1000000) }), + HELLHOUND (2715, 8137, 94, 2236, new Item[] { new Item(Items.COINS_995, 5000000) }), + OAK_LADDER (13328, 8306, 68, 300, new Item[] { new Item(Items.OAK_PLANK_8778, 5) }), + TEAK_LADDER (13329, 8307, 78, 450, new Item[] { new Item(Items.TEAK_PLANK_8780, 5) }), + MAHOGANY_LADDER (13330, 8308, 88, 700, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5) }), + DECORATIVE_BLOOD (13312, 8125, 72, 4, new Item[] { new Item(Items.RED_DYE_1763, 4) }), + DECORATIVE_PIPE (13311, 8126, 83, 120, new Item[] { new Item(Items.STEEL_BAR_2353, 6) }), + HANGING_SKELETON (13310, 8127, 94, 3, new Item[] { new Item(Items.SKULL_964, 2), new Item(Items.BONES_526, 6) }), + CANDLE (13342, 8128, 72, 243, new Item[] { new Item(Items.OAK_PLANK_8778, 4), new Item(Items.LIT_CANDLE_33, 4) }), + TORCH (13341, 8129, 84, 244, new Item[] { new Item(Items.OAK_PLANK_8778, 4), new Item(Items.LIT_TORCH_594, 4) }), + SKULL_TORCH (13343, 8130, 94, 246, new Item[] { new Item(Items.OAK_PLANK_8778, 4), new Item(Items.LIT_TORCH_594, 4), new Item(Items.SKULL_964, 4) }), + /** * Dungeon corridor, junction, stairs & pit */ - OAK_DOOR_LEFT(13344, 8122, 74, 600, new Item(Items.OAK_PLANK_8778, 10)), - OAK_DOOR_RIGHT(13345, 8122, 74, 600, new Item(Items.OAK_PLANK_8778, 10)), - STEEL_DOOR_LEFT(13346, 8123, 84, 800, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 10)), - STEEL_DOOR_RIGHT(13347, 8123, 84, 800, new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 10)), - MARBLE_DOOR_LEFT(13348, 8124, 94, 2000, new Item(Items.MARBLE_BLOCK_8786, 4)), - MARBLE_DOOR_RIGHT(13349, 8124, 94, 2000, new Item(Items.MARBLE_BLOCK_8786, 4)), - SPIKE_TRAP(13356, 8143, 72, 223, new Item(Items.COINS_995, 50000)), - MAN_TRAP(13357, 8144, 76, 273, new Item(Items.COINS_995, 75000)), - TANGLE_TRAP(13358, 8145, 80, 316, new Item(Items.COINS_995, 100000)), - MARBLE_TRAP(13359, 8146, 84, 387, new Item(Items.COINS_995, 150000)), - TELEPORT_TRAP(13360, 8147, 88, 447, new Item(Items.COINS_995, 200000)), - - /* objID, int, lvl, exp, materials */ - PIT_DOG(39260, 18791, 70, 200, new Item(Items.COINS_995, 40000)), - PIT_OGRE(39261, 18792, 73, 234, new Item(Items.COINS_995, 55000)), - PIT_ROCK_PROTECTOR(39262, 18793, 79, 300, new Item(Items.COINS_995, 90000)), - PIT_SCABARITE(39263, 18794, 84, 387, new Item(Items.COINS_995, 150000)), - PIT_BLACK_DEMON(39264, 18795, 89, 547, new Item(Items.COINS_995, 300000)), - PIT_IRON_DRAGON(39265, 18796, 97, 2738, new Item(Items.COINS_995, 7500000)), + OAK_DOOR_LEFT (13344, 8122, 74, 600, new Item[] { new Item(Items.OAK_PLANK_8778, 10) }), + OAK_DOOR_RIGHT (13345, 8122, 74, 600, new Item[] { new Item(Items.OAK_PLANK_8778, 10) }), + STEEL_DOOR_LEFT (13346, 8123, 84, 800, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 10) }), + STEEL_DOOR_RIGHT (13347, 8123, 84, 800, new Item[] { new Item(Items.OAK_PLANK_8778, 10), new Item(Items.STEEL_BAR_2353, 10) }), + MARBLE_DOOR_LEFT (13348, 8124, 94, 2000, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 4) }), + MARBLE_DOOR_RIGHT (13349, 8124, 94, 2000, new Item[] { new Item(Items.MARBLE_BLOCK_8786, 4) }), + SPIKE_TRAP (13356, 8143, 72, 223, new Item[] { new Item(Items.COINS_995, 50000) }), + MAN_TRAP (13357, 8144, 76, 273, new Item[] { new Item(Items.COINS_995, 75000) }), + TANGLE_TRAP (13358, 8145, 80, 316, new Item[] { new Item(Items.COINS_995, 100000) }), + MARBLE_TRAP (13359, 8146, 84, 387, new Item[] { new Item(Items.COINS_995, 150000) }), + TELEPORT_TRAP (13360, 8147, 88, 447, new Item[] { new Item(Items.COINS_995, 200000) }), + PIT_DOG (39260, 18791, 70, 200, new Item[] { new Item(Items.COINS_995, 40000) }), + PIT_OGRE (39261, 18792, 73, 234, new Item[] { new Item(Items.COINS_995, 55000) }), + PIT_ROCK_PROTECTOR(39262, 18793, 79, 300, new Item[] { new Item(Items.COINS_995, 90000) }), + PIT_SCABARITE (39263, 18794, 84, 387, new Item[] { new Item(Items.COINS_995, 150000) }), + PIT_BLACK_DEMON (39264, 18795, 89, 547, new Item[] { new Item(Items.COINS_995, 300000) }), + PIT_IRON_DRAGON (39265, 18796, 97, 2738, new Item[] { new Item(Items.COINS_995, 7500000) }), /** * Treasure room */ - DEMON(13378, 8138, 75, 707, new Item(Items.COINS_995, 500000)), - KALPHITE_SOLDIER(13374, 8139, 80, 866, new Item(Items.COINS_995, 750000)), - TOK_XIL(13377, 8140, 85, 2236, new Item(Items.COINS_995, 5000000)), - DAGANNOTH(13376, 8141, 90, 2738, new Item(Items.COINS_995, 7500000)), - STEEL_DRAGON(13375, 8142, 95, 3162, new Item(Items.COINS_995, 1000000)), - WOODEN_CRATE(13283, 8148, 75, 143, new Item(Items.PLANK_960, 5)), - OAK_T_CHEST(13285, 8149, 79, 340, new Item(Items.OAK_PLANK_8778, 5), new Item(Items.STEEL_BAR_2353, 2)), - TEAK_T_CHEST(13287, 8150, 83, 530, new Item(Items.TEAK_PLANK_8780, 5), new Item(Items.STEEL_BAR_2353, 4)), - MGANY_T_CHEST(13289, 8151, 87, 1000, new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.GOLD_LEAF_8784, 1)), - MAGIC_CHEST(13291, 8152, 91, 1000, new Item(Items.MAGIC_STONE_8788, 1)), - + DEMON (13378, 8138, 75, 707, new Item[] { new Item(Items.COINS_995, 500000) }), + KALPHITE_SOLDIER (13374, 8139, 80, 866, new Item[] { new Item(Items.COINS_995, 750000) }), + TOK_XIL (13377, 8140, 85, 2236, new Item[] { new Item(Items.COINS_995, 5000000) }), + DAGANNOTH (13376, 8141, 90, 2738, new Item[] { new Item(Items.COINS_995, 7500000) }), + STEEL_DRAGON (13375, 8142, 95, 3162, new Item[] { new Item(Items.COINS_995, 1000000) }), + WOODEN_CRATE (13283, 8148, 75, 143, new Item[] { new Item(Items.PLANK_960, 5) }), + OAK_T_CHEST (13285, 8149, 79, 340, new Item[] { new Item(Items.OAK_PLANK_8778, 5), new Item(Items.STEEL_BAR_2353, 2) }), + TEAK_T_CHEST (13287, 8150, 83, 530, new Item[] { new Item(Items.TEAK_PLANK_8780, 5), new Item(Items.STEEL_BAR_2353, 4) }), + MGANY_T_CHEST (13289, 8151, 87, 1000, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.GOLD_LEAF_8784) }), + MAGIC_CHEST (13291, 8152, 91, 1000, new Item[] { new Item(Items.MAGIC_STONE_8788) }), + /** * Style related decoration. */ - BASIC_WOOD_WINDOW(13099, -1, 1, 0.0), - BASIC_STONE_WINDOW(13091, -1, 1, 0.0), - WHITEWASHED_STONE_WINDOW(13005, -1, 1, 0.0), - FREMENNIK_WINDOW(13112, -1, 1, 0.0), - TROPICAL_WOOD_WINDOW(10816, -1, 1, 0.0), - FANCY_STONE_WINDOW(13117, -1, 1, 0.0), - + BASIC_WOOD_WINDOW (13099, -1, 1, 0), + BASIC_STONE_WINDOW (13091, -1, 1, 0), + WHITEWASHED_STONE_WINDOW (13005, -1, 1, 0), + FREMENNIK_WINDOW (13112, -1, 1, 0), + TROPICAL_WOOD_WINDOW (10816, -1, 1, 0), + FANCY_STONE_WINDOW (13117, -1, 1, 0), ; + /** * The object id. */ private final int objectId; - + /** * The item id for the interface. */ @@ -1083,17 +700,22 @@ public enum Decoration { * The level requirement. */ private final int level; - + /** * The experience gained for building this decoration. */ - private final double experience; - + private final int experience; + /** * The item required. */ private final Item[] items; - + + /** + * The items that will be refunded. + */ + private final Item[] refundItems; + /** * The tools required. */ @@ -1103,33 +725,72 @@ public enum Decoration { * The object ids depending on styling. */ private final int[] objectIds; - + /** * If this node should be invisible to user build options */ private boolean invisibleNode; - + /** - * Constructs a new {@code Portal} {@code Object}. + * Constructs a new object, no items, no tools, no refund items. * @param objectId The object id. * @param interfaceItem The item id for the building interface. * @param level The level required. * @param experience The experience gained. - * @param items The items required. */ - private Decoration(int objectId, int interfaceItem, int level, double experience, Item... items) { - this(objectId, interfaceItem, level, experience, new int[] { 2347, 8794 }, items); + Decoration(int objectId, int interfaceItem, int level, int experience) { + this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, new Item[] {}, new Item[] {}); } - + /** - * Constructs a new {@code Portal} {@code Object}. + * Constructs a new object, no tools, no refund items. * @param objectId The object id. * @param interfaceItem The item id for the building interface. * @param level The level required. * @param experience The experience gained. * @param items The items required. */ - private Decoration(int objectId, int interfaceItem, int level, double experience, int[] tools, Item... items) { + Decoration(int objectId, int interfaceItem, int level, int experience, Item[] items) { + this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, new Item[] {}); + } + + /** + * Constructs a new object, no refund items. + * @param objectId The object id. + * @param interfaceItem The item id for the building interface. + * @param level The level required. + * @param experience The experience gained. + * @param tools The tools needed. + * @param items The items required. + */ + Decoration(int objectId, int interfaceItem, int level, int experience, int[] tools, Item[] items) { + this(objectId, interfaceItem, level, experience, tools, items, new Item[] {}); + } + + /** + * Constructs a new object, no tools. + * @param objectId The object id. + * @param interfaceItem The item id for the building interface. + * @param level The level required. + * @param experience The experience gained. + * @param items The items required. + * @param refundItems The items to be refunded when the item is removed. + */ + Decoration(int objectId, int interfaceItem, int level, int experience, Item[] items, Item[] refundItems) { + this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, refundItems); + } + + /** + * Constructs a new object. + * @param objectId The object id. + * @param interfaceItem The item id for the building interface. + * @param level The level required. + * @param experience The experience gained. + * @param tools The tools needed. + * @param items The items required. + * @param refundItems The items to be refunded when the item is removed. + */ + Decoration(int objectId, int interfaceItem, int level, int experience, int[] tools, Item[] items, Item[] refundItems) { this.objectId = objectId; this.objectIds = null; this.interfaceItem = interfaceItem; @@ -1137,39 +798,54 @@ public enum Decoration { this.experience = experience; this.tools = tools; this.items = items; + this.refundItems = refundItems; } - + /** * Decoration * @param objectId * @param invisibleNode */ - private Decoration(int objectId, boolean invisibleNode) { + Decoration(int objectId, boolean invisibleNode) { this(objectId, -1, -1, -1); this.invisibleNode = true; } - + /** - * Constructs a new {@code Portal} {@code Object}. + * Constructs a new object, no tools, no refund items. * @param objectIds The object id. * @param interfaceItem The item id for the building interface. * @param level The level required. * @param experience The experience gained. * @param items The items required. */ - private Decoration(int[] objectIds, int interfaceItem, int level, double experience, Item... items) { - this(objectIds, interfaceItem, level, experience, new int[] { 2347, 8794 }, items); + Decoration(int[] objectIds, int interfaceItem, int level, int experience, Item[] items) { + this(objectIds, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, new Item[] {}); } - /** - * Constructs a new {@code Portal} {@code Object}. + * Constructs a new object no refund items. * @param objectIds The object id. * @param interfaceItem The item id for the building interface. * @param level The level required. * @param experience The experience gained. + * @param tools The tools needed. * @param items The items required. */ - private Decoration(int[] objectIds, int interfaceItem, int level, double experience, int[] tools, Item... items) { + Decoration(int[] objectIds, int interfaceItem, int level, int experience, int[] tools, Item[] items) { + this(objectIds, interfaceItem, level, experience, tools, items, new Item[] {}); + } + + /** + * Constructs a new object. + * @param objectIds The object id. + * @param interfaceItem The item id for the building interface. + * @param level The level required. + * @param experience The experience gained. + * @param tools The tools needed. + * @param items The items required. + * @param refundItems The items to be refunded when the item is removed. + */ + Decoration(int[] objectIds, int interfaceItem, int level, int experience, int[] tools, Item[] items, Item[] refundItems) { this.objectId = objectIds[0]; this.objectIds = objectIds; this.interfaceItem = interfaceItem; @@ -1177,6 +853,7 @@ public enum Decoration { this.experience = experience; this.tools = tools; this.items = items; + this.refundItems = refundItems; } /** @@ -1203,7 +880,7 @@ public enum Decoration { } return null; } - + /** * Gets a decoration for the given object id * @param objectId - the object id of the built object @@ -1217,7 +894,7 @@ public enum Decoration { } return null; } - + public static Decoration forName(String name) { for (Decoration d : Decoration.values()) { if (d.name().equals(name)) { @@ -1239,7 +916,7 @@ public enum Decoration { } return 0; } - + /** * Gets the objectId. * @param style The current housing style. @@ -1251,7 +928,7 @@ public enum Decoration { } return objectId; } - + /** * Gets the objectId. * @return The objectId. @@ -1272,7 +949,7 @@ public enum Decoration { * Gets the experience. * @return The experience. */ - public double getExperience() { + public int getExperience() { return experience; } @@ -1284,6 +961,14 @@ public enum Decoration { return items; } + /** + * Gets the refund items. + * @return The refund items. + */ + public Item[] getRefundItems() { + return refundItems; + } + /** * Gets the tools. * @return The tools. @@ -1308,10 +993,11 @@ public enum Decoration { return objectIds; } + /** + * If this node should be invisible to user build options + * @return true if so. + */ public boolean isInvisibleNode() { return invisibleNode; } - - - } diff --git a/Server/src/main/content/global/skill/construction/HouseManager.java b/Server/src/main/content/global/skill/construction/HouseManager.java index 9e5e4f1ea..01a37f377 100644 --- a/Server/src/main/content/global/skill/construction/HouseManager.java +++ b/Server/src/main/content/global/skill/construction/HouseManager.java @@ -1,9 +1,4 @@ package content.global.skill.construction; - - -//import org.arios.game.content.global.DeadmanTimedAction; -//import org.arios.game.node.entity.player.info.login.SavingModule; - import core.api.regionspec.RegionSpecification; import core.api.regionspec.contracts.FillChunkContract; import core.game.dialogue.FacialExpression; @@ -17,17 +12,14 @@ import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBuilder; import core.game.world.update.flag.context.Animation; import core.tools.Log; -import kotlin.Unit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import core.tools.SystemLogger; import core.game.world.GameWorld; import org.rs09.consts.Sounds; import java.awt.*; -import java.nio.ByteBuffer; import static core.api.ContentAPIKt.*; import static core.api.regionspec.RegionSpecificationKt.fillWith; @@ -106,36 +98,6 @@ public final class HouseManager { } - public void save(ByteBuffer buffer) { - buffer.put((byte) location.ordinal()); - buffer.put((byte) style.ordinal()); - if (hasServant()) { - servant.save(buffer); - } else { - buffer.put((byte) -1); - } - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - Room room = rooms[z][x][y]; - if (room != null) { - buffer.put((byte) z).put((byte) x).put((byte) y); - buffer.put((byte) room.getProperties().ordinal()); - buffer.put((byte) room.getRotation().toInteger()); - for (int i = 0; i < room.getHotspots().length; i++) { - if (room.getHotspots()[i].getDecorationIndex() > -1) { - buffer.put((byte) i); - buffer.put((byte) room.getHotspots()[i].getDecorationIndex()); - } - } - buffer.put((byte) -1); - } - } - } - } - buffer.put((byte) -1);//Eof - } - public void parse(JSONObject data){ location = HouseLocation.values()[Integer.parseInt( data.get("location").toString())]; style = HousingStyle.values()[Integer.parseInt( data.get("style").toString())]; @@ -162,28 +124,6 @@ public final class HouseManager { } } - - public void parse(ByteBuffer buffer) { - location = HouseLocation.values()[buffer.get() & 0xFF]; - style = HousingStyle.values()[buffer.get() & 0xFF]; - servant = Servant.parse(buffer); - int z = 0; - while ((z = buffer.get()) != -1) { - if (z == 3) { - hasDungeon = true; - } - int x = buffer.get(); - int y = buffer.get(); - Room room = rooms[z][x][y] = new Room(RoomProperties.values()[buffer.get() & 0xFF]); - room.configure(style); - room.setRotation(Direction.get(buffer.get() & 0xFF)); - int spot = 0; - while ((spot = buffer.get()) != -1) { - room.getHotspots()[spot].setDecorationIndex(buffer.get() & 0xFF); - } - } - } - /** * Prepares for entering the player's house. * @param player @@ -195,8 +135,9 @@ public final class HouseManager { construct(); } player.setAttribute("poh_entry", HouseManager.this); + player.setAttribute("/save:original-loc", location.getExitLocation()); player.lock(1); - player.sendMessage("House location: " + houseRegion.getBaseLocation() + ", entry: " + getEnterLocation()); + player.debug("House location: " + houseRegion.getBaseLocation() + ", entry: " + getEnterLocation()); } /** @@ -263,7 +204,7 @@ public final class HouseManager { } /** - * Leaves this house. + * Leaves this house through the portal. * @param player The player leaving. */ public static void leave(Player player) { @@ -567,6 +508,7 @@ public final class HouseManager { break; } case SOUTH: { + chunkX = 7 - chunkX; chunkY = 7 - chunkY; break; } diff --git a/Server/src/main/content/global/skill/construction/HouseZone.java b/Server/src/main/content/global/skill/construction/HouseZone.java index dd0075f23..83bbd1da2 100644 --- a/Server/src/main/content/global/skill/construction/HouseZone.java +++ b/Server/src/main/content/global/skill/construction/HouseZone.java @@ -4,10 +4,10 @@ package content.global.skill.construction; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.world.map.zone.MapZone; -import core.game.world.map.zone.ZoneRestriction; import core.game.world.map.RegionManager; import core.game.world.map.Region; import core.game.system.task.Pulse; +import core.game.world.map.zone.ZoneType; import static core.api.ContentAPIKt.*; @@ -37,12 +37,13 @@ public final class HouseZone extends MapZone { * Constructs the house zone object. */ public HouseZone(HouseManager house) { - super("poh-zone" + house, true, ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.FOLLOWERS); + super("poh-zone" + house, true); this.house = house; } @Override public void configure() { + setZoneType(ZoneType.P_O_H.getId()); unregisterOldRegions(); registerRegion(house.getHouseRegion().getId()); if (house.getDungeonRegion() != null) { @@ -81,14 +82,16 @@ public final class HouseZone extends MapZone { if (e instanceof Player) { Player p = (Player) e; HouseManager.leave(p); + return true; } - return true; + return super.death(e, killer); } @Override public boolean leave(Entity e, boolean logout) { if (e instanceof Player) { Player p = (Player) e; + // The below tears down the house if the owner was the one who left if (house == p.getHouseManager()) { house.expelGuests(p); int toRemove = previousRegion; @@ -109,7 +112,11 @@ public final class HouseZone extends MapZone { } }); } + // Clear logout listener and original-loc (if appropriate) clearLogoutListener(p, "houselogout"); + if (!getAttribute(p, "kidnapped-by-random", false)) { + removeAttribute(p, "/save:original-loc"); + } return true; } return true; diff --git a/Server/src/main/content/global/skill/construction/Room.java b/Server/src/main/content/global/skill/construction/Room.java index c8a02bfda..883a4776c 100644 --- a/Server/src/main/content/global/skill/construction/Room.java +++ b/Server/src/main/content/global/skill/construction/Room.java @@ -2,9 +2,13 @@ package content.global.skill.construction; import core.game.node.entity.player.Player; +import core.game.node.scenery.Constructed; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.world.map.*; +import core.tools.Log; + +import static core.api.ContentAPIKt.log; /** * Represents a room. @@ -93,7 +97,7 @@ public final class Room { Region.load(region, true); chunk = region.getPlanes()[style.getPlane()].getRegionChunk(properties.getChunkX(), properties.getChunkY()); } - + /** * Gets the hotspot object for the given hotspot type. * @param hotspot The hotspot type. @@ -154,6 +158,7 @@ public final class Room { chunk.rotate(rotation); } if (!house.isBuildingMode()) { + placeDoors(housePlane, house, chunk); removeHotspots(housePlane, house, chunk); } } @@ -165,16 +170,17 @@ public final class Room { * @param chunk The region chunk used. */ private void removeHotspots(int housePlane, HouseManager house, BuildRegionChunk chunk) { - for (int i = 0; i < BuildRegionChunk.ARRAY_SIZE; i++) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { + if (properties.isRoof()) return; + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + for (int i = 0; i < BuildRegionChunk.ARRAY_SIZE; i++) { Scenery object = chunk.get(x, y, i); - if (object != null && object.getDefinition().hasAction("Build")) { - if (properties.isChamber() && BuildingUtils.isDoorHotspot(object)) { - if (!placeDoors(house, chunk, object, housePlane, x, y, rotation)) { - chunk.remove(object); - } - } else { + if (object != null) { + boolean isBuilt = object instanceof Constructed; + boolean isWall = object.getId() == 13065 || object.getId() == house.getStyle().getWallId(); + boolean isDoor = object.getId() == house.getStyle().getDoorId() || object.getId() == house.getStyle().getSecondDoorId(); + if (!isBuilt && !isWall && !isDoor) { + SceneryBuilder.remove(object); chunk.remove(object); } } @@ -184,65 +190,92 @@ public final class Room { } /** - * Places the doors when needed. - * @param chunk The chunk. - * @param object The object. - * @param x The x-coordinate of the object. - * @param y The y-coordinate of the object. + * Replaces the door hotspots with doors, walls, or passageways as needed. + * TODO: it is believed that doors authentically remember their open/closed state for the usual duration (see e.g. https://www.youtube.com/watch?v=nRGux739h8s 1:00 vs 1:55), but this is not possible with the current HouseManager approach, which deallocates the instance as soon as the player leaves. + * @param housePlane The room's plane in house. + * @param house The house manager. + * @param chunk The region chunk used. */ - private boolean placeDoors(HouseManager house, BuildRegionChunk chunk, Scenery object, int z, int x, int y, Direction rotation) { - int doorX; - int doorY; - switch (rotation) { - case EAST: - doorX = y; - doorY = 7 - x; - break; - case SOUTH: - doorX = 7 - x; - doorY = 7 - y; - break; - case WEST: - doorX = 7 - y; - doorY = x; - break; - default: - doorX = x; - doorY = y; - break; - } - int chunkX = chunk.getCurrentBase().getChunkX(); - int chunkY = chunk.getCurrentBase().getChunkY(); - boolean houseExit = true; - Room r; - if (doorX == 0 && chunkX > 0 && (r = house.getRooms()[z][chunkX - 1][chunkY]) != null && r.getProperties().isChamber()) { - houseExit = false; - } - else if (doorX == 7 && chunkX < 7 && (r = house.getRooms()[z][chunkX + 1][chunkY]) != null && r.getProperties().isChamber()) { - houseExit = false; - } - else if (doorY == 0 && chunkY > 0 && (r = house.getRooms()[z][chunkX][chunkY - 1]) != null && r.getProperties().isChamber()) { - houseExit = false; - } - else if (doorY == 7 && chunkY < 7 && (r = house.getRooms()[z][chunkX][chunkY + 1]) != null && r.getProperties().isChamber()) { - houseExit = false; - } - int replaceId = object.getId() % 2 != 0 ? house.getStyle().getDoorId() : house.getStyle().getSecondDoorId(); - houseExit = false; - if (z != 0 && houseExit) { - r = house.getRooms()[z][chunkX][chunkY]; - if (r.getProperties().isDungeon()) { - replaceId = 13065; - } else { - replaceId = house.getStyle().getWallId(); + private void placeDoors(int housePlane, HouseManager house, BuildRegionChunk chunk) { + Room[][][] rooms = house.getRooms(); + int rx = chunk.getCurrentBase().getChunkX(); + int ry = chunk.getCurrentBase().getChunkY(); + for (int i = 0; i < BuildRegionChunk.ARRAY_SIZE; i++) { + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + Scenery object = chunk.get(x, y, i); + if (object != null && BuildingUtils.isDoorHotspot(object)) { + boolean edge = false; + Room otherRoom = null; + switch (object.getRotation()) { + case 0: //east + edge = rx == 0; + otherRoom = edge ? null : rooms[housePlane][rx - 1][ry]; + break; + case 1: //south + edge = ry == 7; + otherRoom = edge ? null : rooms[housePlane][rx][ry + 1]; + break; + case 2: //west + edge = rx == 7; + otherRoom = edge ? null : rooms[housePlane][rx + 1][ry]; + break; + case 3: //north + edge = ry == 0; + otherRoom = edge ? null : rooms[housePlane][rx][ry - 1]; + break; + default: + log(this.getClass(), Log.ERR, "Impossible rotation when placing doors??"); + } + int replaceId = getReplaceId(housePlane, house, this, edge, otherRoom, object); + if (replaceId == -1) { + continue; + } + SceneryBuilder.replace(object, object.transform(replaceId)); + } + } } } - else if (!houseExit) { - return false; - } - return SceneryBuilder.replace(object, object.transform(replaceId, object.getRotation(), chunk.getCurrentBase().transform(x, y, 0)), true, true); } - + + /** + * Checks if rooms transition between inside<>outside the house and returns the appropriate door replacement. + * @param housePlane The room's plane in house. + * @param house The house manager. + * @param room The room the door is in. + * @param edge Whether the door is adjacent to an edge. + * @param otherRoom The room the door is adjacent to. + * @param object The door object itself. + */ + private int getReplaceId(int housePlane, HouseManager house, Room room, boolean edge, Room otherRoom, Scenery object) { + boolean thisOutside = !room.getProperties().isChamber(); + if (edge && thisOutside) { + // No door or wall + return -1; + } + if (!edge) { + boolean otherOutside = otherRoom == null || !otherRoom.getProperties().isChamber(); + if (thisOutside == otherOutside) { + // Free passage, unless the other room has a blind wall here + if (otherRoom == null) { + return -1; + } + boolean exit = otherRoom.getExits()[object.getRotation()]; + if (exit) { + return -1; + } + } + if (thisOutside != otherOutside && housePlane == 0) { + // Door if we are the inside room only + if (thisOutside) { + return -1; + } + return object.getId() % 2 != 0 ? house.getStyle().getDoorId() : house.getStyle().getSecondDoorId(); + } + } + return room.getProperties().isDungeon() ? 13065 : house.getStyle().getWallId(); + } + /** * Sets the decoration index for a group of object ids * @param index The index. @@ -390,4 +423,4 @@ public final class Room { return rotation; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/RoomProperties.java b/Server/src/main/content/global/skill/construction/RoomProperties.java index fc147e534..80bc6e6ed 100644 --- a/Server/src/main/content/global/skill/construction/RoomProperties.java +++ b/Server/src/main/content/global/skill/construction/RoomProperties.java @@ -8,11 +8,10 @@ import core.game.world.map.RegionManager; /** * Represents the room properties. - * @author Emperor + * @author Emperor, Player Name * >ORDINAL BOUND< */ public enum RoomProperties { - /** * The parlour. */ @@ -50,25 +49,24 @@ public enum RoomProperties { new Hotspot(BuildHotspot.RUG2, 5, 4), new Hotspot(BuildHotspot.RUG, 5, 5), new Hotspot(BuildHotspot.CHAIRS_1, 2, 4), - new Hotspot(BuildHotspot.FIREPLACE, 3, 7,4,7), + new Hotspot(BuildHotspot.FIREPLACE, 3, 7, 4, 7), new Hotspot(BuildHotspot.CHAIRS_3, 4, 3), new Hotspot(BuildHotspot.CHAIRS_2, 5, 4), new Hotspot(BuildHotspot.BOOKCASE, 7, 1)), - /** * The garden. (centrepiece has to be first!) */ - GARDEN(1000, 1, 0, 0, 1, Room.LAND, new Hotspot(BuildHotspot.CENTREPIECE_1, 3, 3,4,4), - new Hotspot(BuildHotspot.BIG_PLANT_2, 0, 0,1,1), - new Hotspot(BuildHotspot.BIG_TREE_1, 1, 5,2,6), + GARDEN(1000, 1, 0, 0, 1, Room.LAND, new Hotspot(BuildHotspot.CENTREPIECE_1, 3, 3, 4, 4), + new Hotspot(BuildHotspot.BIG_PLANT_2, 0, 0, 1, 1), + new Hotspot(BuildHotspot.BIG_TREE_1, 1, 5, 2, 6), new Hotspot(BuildHotspot.SMALL_PLANT_1, 3, 1), new Hotspot(BuildHotspot.SMALL_PLANT_2, 4, 5), - new Hotspot(BuildHotspot.BIG_PLANT_1, 6, 1,7,0), - new Hotspot(BuildHotspot.TREE_1, 6, 6,7,7)), + new Hotspot(BuildHotspot.BIG_PLANT_1, 6, 0, 7, 1), + new Hotspot(BuildHotspot.TREE_1, 6, 6, 7, 7)), /** - * Woman's place. + * The kitchen. */ KITCHEN(5000, 5, 0, 2, 7, Room.CHAMBER, new Hotspot(BuildHotspot.CAT_BLANKET, 0, 0), new Hotspot(BuildHotspot.WINDOW, 0, 2), @@ -80,10 +78,10 @@ public enum RoomProperties { new Hotspot(BuildHotspot.WINDOW, 7, 2), new Hotspot(BuildHotspot.WINDOW, 7, 5), new Hotspot(BuildHotspot.BARRELS, 0, 6), - new Hotspot(BuildHotspot.KITCHEN_TABLE, 3, 3,4,4), - new Hotspot(BuildHotspot.STOVE, 3, 7), - new Hotspot(BuildHotspot.LARDER, 6, 0,7,1), - new Hotspot(BuildHotspot.SINK, 7, 3,7,4), + new Hotspot(BuildHotspot.KITCHEN_TABLE, 3, 3, 4, 4), + new Hotspot(BuildHotspot.STOVE, 3, 7, 4, 7), + new Hotspot(BuildHotspot.LARDER, 6, 0, 7, 1), + new Hotspot(BuildHotspot.SINK, 7, 3, 7, 4), new Hotspot(BuildHotspot.SHELVES, 1, 7), new Hotspot(BuildHotspot.SHELVES, 6, 7), new Hotspot(BuildHotspot.SHELVES_2, 7, 6)), @@ -91,7 +89,7 @@ public enum RoomProperties { /** * Dining room. */ - DINING_ROOM(5000, 10, 0, 4, 7, Room.CHAMBER, new Hotspot(BuildHotspot.FIREPLACE_DINING, 3, 7,4,7), + DINING_ROOM(5000, 10, 0, 4, 7, Room.CHAMBER, new Hotspot(BuildHotspot.FIREPLACE_DINING, 3, 7, 4, 7), new Hotspot(BuildHotspot.WINDOW, 0, 2), new Hotspot(BuildHotspot.WINDOW, 0, 5), new Hotspot(BuildHotspot.WINDOW, 2, 0), @@ -115,12 +113,12 @@ public enum RoomProperties { new Hotspot(BuildHotspot.DINING_BENCH_1, 4, 2), new Hotspot(BuildHotspot.DINING_BENCH_1, 5, 2), new Hotspot(BuildHotspot.ROPE_BELL_PULL, 0, 0), - new Hotspot(BuildHotspot.DINING_TABLE, 2, 3,5,4)), + new Hotspot(BuildHotspot.DINING_TABLE, 2, 3, 5, 4)), /** * Workshop. */ - WORKSHOP(10000, 15, 0, 0, 5, Room.CHAMBER, new Hotspot(BuildHotspot.WORKBENCH, 3, 4,4,4), + WORKSHOP(10000, 15, 0, 0, 5, Room.CHAMBER, new Hotspot(BuildHotspot.WORKBENCH, 3, 4, 4, 4), new Hotspot(BuildHotspot.WINDOW, 0, 2), new Hotspot(BuildHotspot.WINDOW, 0, 5), new Hotspot(BuildHotspot.WINDOW, 2, 0), @@ -129,9 +127,9 @@ public enum RoomProperties { new Hotspot(BuildHotspot.WINDOW, 7, 5), new Hotspot(BuildHotspot.WINDOW, 2, 7), new Hotspot(BuildHotspot.WINDOW, 5, 7), - new Hotspot(BuildHotspot.REPAIR, 7, 3,7,4), - new Hotspot(BuildHotspot.HERALDRY, 7, 6,7,7), - new Hotspot(BuildHotspot.CRAFTING, 0, 3,0,4), + new Hotspot(BuildHotspot.REPAIR, 7, 3, 7, 4), + new Hotspot(BuildHotspot.HERALDRY, 7, 6, 7, 7), + new Hotspot(BuildHotspot.CRAFTING, 0, 3, 0, 4), new Hotspot(BuildHotspot.WORKBENCH, 3, 4), new Hotspot(BuildHotspot.TOOL4, 7, 1), new Hotspot(BuildHotspot.TOOL2, 6, 0), @@ -142,10 +140,10 @@ public enum RoomProperties { /** * Bedroom. */ - BEDROOM(10000, 20, 0, 6, 7, Room.CHAMBER, new Hotspot(BuildHotspot.BED, 3, 7,4,6), - new Hotspot(BuildHotspot.FIREPLACE2, 7,4,7, 3), + BEDROOM(10000, 20, 0, 6, 7, Room.CHAMBER, new Hotspot(BuildHotspot.BED, 3, 6, 4, 7), + new Hotspot(BuildHotspot.FIREPLACE2, 7, 3, 7, 4), new Hotspot(BuildHotspot.CLOCK, 7, 0), - new Hotspot(BuildHotspot.DRESSER, 0, 7,1,7), + new Hotspot(BuildHotspot.DRESSER, 0, 7, 1, 7), new Hotspot(BuildHotspot.DRAWERS, 6, 7), new Hotspot(BuildHotspot.WINDOW, 0, 2), new Hotspot(BuildHotspot.WINDOW, 0, 5), @@ -193,9 +191,9 @@ public enum RoomProperties { new Hotspot(BuildHotspot.BEDROOM_RUG3, 6, 4)), /** - * Skill hall room. + * Skill hall. */ - SKILL_HALL(15000, 25, 0, 1, 6, Room.CHAMBER, new Hotspot(BuildHotspot.STAIRWAYS, 3, 3,4,4), + SKILL_HALL(15000, 25, 0, 1, 6, Room.CHAMBER, new Hotspot(BuildHotspot.STAIRWAYS, 3, 3, 4, 4), new Hotspot(BuildHotspot.ARMOUR_SPACE, 2, 3), new Hotspot(BuildHotspot.ARMOUR_SPACE2, 5, 3), new Hotspot(BuildHotspot.HEAD_TROPHY, 6, 7), @@ -254,7 +252,7 @@ public enum RoomProperties { new Hotspot(BuildHotspot.ATTACK_STONE, 2, 4), new Hotspot(BuildHotspot.PRIZE_CHEST, 3, 7), new Hotspot(BuildHotspot.ELEMENTAL_BALANCE, 5, 4), - new Hotspot(BuildHotspot.GAME_SPACE, 6, 0,7,1), + new Hotspot(BuildHotspot.GAME_SPACE, 6, 0, 7, 1), new Hotspot(BuildHotspot.WINDOW, 0, 2), new Hotspot(BuildHotspot.WINDOW, 0, 5), new Hotspot(BuildHotspot.WINDOW, 2, 0), @@ -318,10 +316,11 @@ public enum RoomProperties { new Hotspot(BuildHotspot.CR_INVISIBLE_WALL, 3, 5), new Hotspot(BuildHotspot.CR_INVISIBLE_WALL, 5, 3), new Hotspot(BuildHotspot.CR_INVISIBLE_WALL, 4, 2)), + /** * Quest trophy hall. */ - QUEST_HALL(25000, 35, 0, 5, 6, Room.CHAMBER, new Hotspot(BuildHotspot.QUEST_STAIRWAYS, 3, 3,4,4), + QUEST_HALL(25000, 35, 0, 5, 6, Room.CHAMBER, new Hotspot(BuildHotspot.QUEST_STAIRWAYS, 3, 3, 4, 4), new Hotspot(BuildHotspot.MAP, 7, 1), new Hotspot(BuildHotspot.SWORD, 7, 6), new Hotspot(BuildHotspot.LANDSCAPE, 6, 7), @@ -376,21 +375,11 @@ public enum RoomProperties { /** * Study. */ -/* Menagerie(30000, 37, 0, 7, 2, Room.LAND, - new Hotspot(BuildHotspot.PET_HOUSE, 1, 1), - new Hotspot(BuildHotspot.PET_FEEDER, 5, 1), - new Hotspot(BuildHotspot.OBELISK, 5, 5), - new Hotspot(BuildHotspot.HABITAT_2, 1, 5), - new Hotspot(BuildHotspot.HABITAT_1, 6, 6)),*/ - - /** - * Study. - */ - STUDY_ROOM(50000, 40, 0, 4, 5, Room.CHAMBER, new Hotspot(BuildHotspot.GLOBE, 1, 4,3,6), + STUDY_ROOM(50000, 40, 0, 4, 5, Room.CHAMBER, new Hotspot(BuildHotspot.GLOBE, 1, 4, 3, 6), new Hotspot(BuildHotspot.LECTERN, 2, 2), - new Hotspot(BuildHotspot.CRYSTAL_BALL, 5, 4), - new Hotspot(BuildHotspot.BOOKCASE3, 4, 7,3,7), - new Hotspot(BuildHotspot.BOOKCASE3, 3, 7,4,7), + new Hotspot(BuildHotspot.CRYSTAL_BALL, 5, 2), + new Hotspot(BuildHotspot.BOOKCASE3, 3, 7, 3, 7), + new Hotspot(BuildHotspot.BOOKCASE3, 4, 7, 4, 7), new Hotspot(BuildHotspot.WALL_CHART, 1, 7), new Hotspot(BuildHotspot.WALL_CHART, 6, 7), new Hotspot(BuildHotspot.WALL_CHART, 7, 1), @@ -414,22 +403,22 @@ public enum RoomProperties { new Hotspot(BuildHotspot.WINDOW, 5, 0), new Hotspot(BuildHotspot.WINDOW, 7, 2), new Hotspot(BuildHotspot.WINDOW, 7, 5), - new Hotspot(BuildHotspot.TREASURE_CHEST, 0, 3,0,4), + new Hotspot(BuildHotspot.TREASURE_CHEST, 0, 3, 0, 4), new Hotspot(BuildHotspot.ARMOUR_CASE, 2, 7), - new Hotspot(BuildHotspot.MAGIC_WARDROBE, 3, 7,5,7), + new Hotspot(BuildHotspot.MAGIC_WARDROBE, 3, 7, 5, 7), new Hotspot(BuildHotspot.CAPE_RACK, 6, 6), - new Hotspot(BuildHotspot.TOY_BOX, 7, 3,7,4), - new Hotspot(BuildHotspot.COSTUME_BOX, 3, 4,4,3)), + new Hotspot(BuildHotspot.TOY_BOX, 7, 3, 7, 4), + new Hotspot(BuildHotspot.COSTUME_BOX, 3, 3, 4, 4)), /** * Chapel room. */ CHAPEL(50000, 45, 0, 2, 5, Room.CHAMBER, - new Hotspot(BuildHotspot.ALTAR, 3, 5,4,5), + new Hotspot(BuildHotspot.ALTAR, 3, 5, 4, 5), new Hotspot(BuildHotspot.STATUE, 7, 0), new Hotspot(BuildHotspot.STATUE, 0, 0), - new Hotspot(BuildHotspot.ICON, 3,7,4, 7), - new Hotspot(BuildHotspot.MUSICAL, 7,4,7,3), + new Hotspot(BuildHotspot.ICON, 3, 7, 4, 7), + new Hotspot(BuildHotspot.MUSICAL, 7, 3, 7, 4), new Hotspot(BuildHotspot.BURNERS, 1, 5), new Hotspot(BuildHotspot.BURNERS, 6, 5), new Hotspot(BuildHotspot.CHAPEL_RUG, 3, 1), @@ -452,10 +441,10 @@ public enum RoomProperties { /** * Portal chamber. */ - PORTAL_CHAMBER(100000, 50, 0, 1, 4, Room.CHAMBER, new Hotspot(BuildHotspot.TELEPORT_FOCUS, 3, 3,4,4), - new Hotspot(BuildHotspot.PORTAL1, 0, 3,0,4), - new Hotspot(BuildHotspot.PORTAL2, 3, 7,4,7), - new Hotspot(BuildHotspot.PORTAL3, 7, 3,7,4), + PORTAL_CHAMBER(100000, 50, 0, 1, 4, Room.CHAMBER, new Hotspot(BuildHotspot.TELEPORT_FOCUS, 3, 3, 4, 4), + new Hotspot(BuildHotspot.PORTAL1, 0, 3, 0, 4), + new Hotspot(BuildHotspot.PORTAL2, 3, 7, 4, 7), + new Hotspot(BuildHotspot.PORTAL3, 7, 3, 7, 4), new Hotspot(BuildHotspot.WINDOW, 0, 2), new Hotspot(BuildHotspot.WINDOW, 0, 5), new Hotspot(BuildHotspot.WINDOW, 2, 0), @@ -469,7 +458,7 @@ public enum RoomProperties { * The formal garden. * Centrepiece should be first! */ - FORMAL_GARDEN(75000, 55, 0, 2, 1, Room.LAND, new Hotspot(BuildHotspot.CENTREPIECE_2, 3, 3,4,4), + FORMAL_GARDEN(75000, 55, 0, 2, 1, Room.LAND, new Hotspot(BuildHotspot.CENTREPIECE_2, 3, 3, 4, 4), new Hotspot(BuildHotspot.FENCING, 0, 0), new Hotspot(BuildHotspot.FENCING, 1, 0), new Hotspot(BuildHotspot.FENCING, 2, 0), @@ -621,6 +610,7 @@ public enum RoomProperties { new Hotspot(BuildHotspot.PRISON, 5, 4), new Hotspot(BuildHotspot.PRISON, 4, 5), new Hotspot(BuildHotspot.PRISON_DOOR, 3, 5)), + /** * Dungeon corridor. */ @@ -695,6 +685,9 @@ public enum RoomProperties { new Hotspot(BuildHotspot.DUNGEON_DECO, 6, 1), new Hotspot(BuildHotspot.DUNGEON_DECO, 1, 6)), + /** + * Dungeon pit. + */ DUNGEON_PIT(10000, 70, 0, 5, 2, Room.DUNGEON, new Hotspot(BuildHotspot.DUNGEON_DOOR_RIGHT3, 3, 1), new Hotspot(BuildHotspot.DUNGEON_DOOR_LEFT3, 4, 1), @@ -917,7 +910,7 @@ public enum RoomProperties { * @return The name. */ public String getName() { - return name().toLowerCase().replaceAll("_", " ").replaceAll("\\d",""); + return name().toLowerCase().replaceAll("_", " ").replaceAll("\\d", ""); } /** @@ -1023,5 +1016,4 @@ public enum RoomProperties { public int getLevel() { return level; } - -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/Servant.java b/Server/src/main/content/global/skill/construction/Servant.java index 63275330d..a3527bdad 100644 --- a/Server/src/main/content/global/skill/construction/Servant.java +++ b/Server/src/main/content/global/skill/construction/Servant.java @@ -5,8 +5,6 @@ import core.game.node.entity.npc.NPC; import core.game.node.item.Item; import org.json.simple.JSONObject; -import java.nio.ByteBuffer; - /** * Represents a player's servant. * @author Emperor @@ -44,26 +42,9 @@ public final class Servant extends NPC { } /** - * Saves the servant details. - * @param buffer The buffer to write on. - */ - public void save(ByteBuffer buffer) { - buffer.put((byte) type.ordinal()); - buffer.putShort((byte) uses); - if (item == null) { - buffer.putShort((short) -1); - } else { - buffer.putShort((short) item.getId()); - buffer.putInt(item.getAmount()); - } - buffer.put((byte) (greet ? 1 : 0)); - } - - /** - * Parses the servant from the buffer. + * Parses the servant from the save file. * @return The servant. */ - public static Servant parse(JSONObject data){ int type = Integer.parseInt( data.get("type").toString()); Servant servant = new Servant(ServantType.values()[type]); @@ -77,21 +58,6 @@ public final class Servant extends NPC { return servant; } - public static Servant parse(ByteBuffer buffer) { - int type = buffer.get(); - if (type == -1) { - return null; - } - Servant servant = new Servant(ServantType.values()[type]); - servant.uses = buffer.getShort() & 0xFFFF; - int itemId = buffer.getShort() & 0xFFFF; - if ((short) itemId != -1) { - servant.item = new Item(itemId, buffer.getInt()); - } - servant.greet = buffer.get() == 1; - return servant; - } - /** * Gets the item value. * @return The item. diff --git a/Server/src/main/content/global/skill/construction/decoration/ConstructionDoorPlugin.kt b/Server/src/main/content/global/skill/construction/decoration/ConstructionDoorPlugin.kt index 1ae0cc376..75a9e2207 100644 --- a/Server/src/main/content/global/skill/construction/decoration/ConstructionDoorPlugin.kt +++ b/Server/src/main/content/global/skill/construction/decoration/ConstructionDoorPlugin.kt @@ -1,13 +1,13 @@ package content.global.skill.construction.decoration +import content.global.skill.construction.BuildHotspot +import content.global.skill.construction.HousingStyle import core.cache.def.impl.SceneryDefinition import core.game.global.action.DoorActionHandler import core.game.interaction.OptionHandler import core.game.node.Node -import core.game.node.scenery.Scenery import core.game.node.entity.player.Player -import content.global.skill.construction.BuildHotspot -import content.global.skill.construction.HousingStyle +import core.game.node.scenery.Scenery import core.plugin.Initializable import core.plugin.Plugin @@ -38,11 +38,11 @@ class ConstructionDoorPlugin : OptionHandler() { } override fun handle(player: Player, node: Node, option: String): Boolean { - val `object` = node as Scenery - val second = DoorActionHandler.getSecondDoor(`object`, player) when (option) { "pick-lock", "force" -> return false //TODO } + val `object` = node as Scenery + val second = DoorActionHandler.getSecondDoor(`object`, player) DoorActionHandler.open(`object`, second, getReplaceId(`object`), getReplaceId(second), true, 500, false) return true } @@ -80,4 +80,4 @@ class ConstructionDoorPlugin : OptionHandler() { intArrayOf(13119, 13121) ) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberPlugin.java b/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberPlugin.java index 3c658fec2..bf1f26c54 100644 --- a/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberPlugin.java @@ -17,6 +17,8 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.ClassScanner; +import static content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners.ARDOUGNE_TELE_ATTRIBUTE; + /** * PortalChamberPlugin * @author Clayton Williams @@ -87,6 +89,12 @@ public class PortalChamberPlugin extends OptionHandler { } for (Locations l : Locations.values()) { if (l.name().contains(identifier)) { + if (l == Locations.ARDOUGNE){ + if (!player.getAttribute(ARDOUGNE_TELE_ATTRIBUTE, false)) { + player.sendMessage("You do not have the requirements to direct the portal there"); + return; + } + } Item[] runes = l.runes; if (!player.getInventory().containsItems(runes)) { player.sendMessage("You do not have the required runes to build this portal"); diff --git a/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGlory.kt b/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGlory.kt index 44c9750a9..47051e46f 100644 --- a/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGlory.kt +++ b/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGlory.kt @@ -4,11 +4,15 @@ import core.api.playGlobalAudio import core.api.teleport import core.game.interaction.IntType import core.game.interaction.InteractionListener +import core.game.node.Node import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.node.scenery.Scenery import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Items import org.rs09.consts.Sounds import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -25,27 +29,34 @@ class MountedGlory : InteractionListener { ) override fun defineListeners() { - on(MOUNTED_GLORY, IntType.SCENERY, "Edgeville") { player, _ -> - mountedGloryTeleport(player,0) + on(MOUNTED_GLORY, IntType.SCENERY, "Edgeville") { player, `object` -> + mountedGloryAction(player, `object`, 0) return@on true } - on(MOUNTED_GLORY, IntType.SCENERY, "Karamja") { player, _ -> - mountedGloryTeleport(player,1) + on(MOUNTED_GLORY, IntType.SCENERY, "Karamja") { player, `object` -> + mountedGloryAction(player, `object`, 1) return@on true } - on(MOUNTED_GLORY, IntType.SCENERY, "Draynor Village") { player, _ -> - mountedGloryTeleport(player,2) + on(MOUNTED_GLORY, IntType.SCENERY, "Draynor Village") { player, `object` -> + mountedGloryAction(player, `object`, 2) return@on true } - on(MOUNTED_GLORY, IntType.SCENERY, "Al Kharid") { player, _ -> - mountedGloryTeleport(player,3) + on(MOUNTED_GLORY, IntType.SCENERY, "Al Kharid") { player, `object` -> + mountedGloryAction(player, `object`, 3) return@on true } } - private fun mountedGloryTeleport(player : Player, int : Int) { + private fun mountedGloryAction(player : Player, `object` : Node, int : Int) { + if (player.houseManager.isBuildingMode) { + player.dialogueInterpreter.open("con:removedec", `object` as Scenery) + return + } + if (!player.zoneMonitor.teleport(1, Item(Items.AMULET_OF_GLORY_1704))) { + return + } Executors.newSingleThreadScheduledExecutor().schedule({ player.pulseManager.run(object : Pulse() { var counter = 0 @@ -63,4 +74,4 @@ class MountedGlory : InteractionListener { }) }, 0, TimeUnit.SECONDS) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/decoration/study/LecternPlugin.kt b/Server/src/main/content/global/skill/construction/decoration/study/LecternPlugin.kt index 826056ce3..eb4accf67 100644 --- a/Server/src/main/content/global/skill/construction/decoration/study/LecternPlugin.kt +++ b/Server/src/main/content/global/skill/construction/decoration/study/LecternPlugin.kt @@ -10,6 +10,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills import content.global.skill.construction.Decoration +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners.Companion.ARDOUGNE_TELE_ATTRIBUTE import core.game.node.entity.combat.spell.MagicStaff import core.game.node.item.Item import core.game.system.task.Pulse @@ -84,6 +85,11 @@ class LecternPlugin : OptionHandler() { player.sendMessages("You need the Bones to Peaches ability purchased from MTA before making these.", "This requirement doesn't apply to actually using the tabs.") return false } + if(this == ARDOUGNE && !getAttribute(player, ARDOUGNE_TELE_ATTRIBUTE, false)){ + sendMessage(player, "You need to unlock Ardougne teleport before you can make a tablet.") + return false + + } var found = false for (d in requiredDecorations) if (d.objectId == objectId) found = true if (!found) { diff --git a/Server/src/main/content/global/skill/construction/decoration/study/TelescopePlugin.kt b/Server/src/main/content/global/skill/construction/decoration/study/TelescopePlugin.kt index 6149fae5e..ea1d8c783 100644 --- a/Server/src/main/content/global/skill/construction/decoration/study/TelescopePlugin.kt +++ b/Server/src/main/content/global/skill/construction/decoration/study/TelescopePlugin.kt @@ -26,25 +26,31 @@ class TelescopePlugin : OptionHandler() { } override fun handle(player: Player?, node: Node?, option: String?): Boolean { + val obj = node?.asScenery() as Scenery val star = ShootingStarPlugin.getStar() val delay: Int = 25000 + (25000 / 3) val timeLeft = delay - star.ticks - val fakeTimeLeftBecauseFuckPlayers = TimeUnit.MILLISECONDS.toMinutes(timeLeft * 600L) + if(RandomFunction.random(0,100) % 2 == 0) 2 else -2 - val obj = node?.asScenery() as Scenery + val window = when (obj.id) { + 13657 -> 9 + 13658 -> 2 + else -> 24 + } + val fakeTimeLeft = RandomFunction.random(-window, window+1) + TimeUnit.MILLISECONDS.toMinutes(timeLeft * 600L) player?.lock() player?.animate(ANIMATION) - player?.interfaceManager?.open(Component(782)).also { player?.unlock() - Pulser.submit(object : Pulse(2, player) { - override fun pulse(): Boolean { - if (obj.isActive) { - player?.dialogueInterpreter?.sendDialogue("You see a shooting star! The star looks like it will land","in about $fakeTimeLeftBecauseFuckPlayers minutes!") + player?.interfaceManager?.open(Component(782)).also { + player?.unlock() + Pulser.submit(object : Pulse(2, player) { + override fun pulse(): Boolean { + if (obj.isActive) { + player?.dialogueInterpreter?.sendDialogue("You see a shooting star! The star looks like it will land","in about $fakeTimeLeft minutes!") + return true + } return true } - return true - } - }) - return true - } + }) + return true + } } companion object { diff --git a/Server/src/main/content/global/skill/construction/decoration/workshop/ArmourStand.kt b/Server/src/main/content/global/skill/construction/decoration/workshop/ArmourStand.kt index b30f862d1..44a050754 100644 --- a/Server/src/main/content/global/skill/construction/decoration/workshop/ArmourStand.kt +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/ArmourStand.kt @@ -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 { 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.") } diff --git a/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java b/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java index 4fd2d8c47..ddb967ebe 100644 --- a/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java +++ b/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java @@ -360,7 +360,7 @@ public class HouseServantDialogue extends DialoguePlugin { bankFetch(player, new Item(Items.MARBLE_BLOCK_8786)); break; case 3: //magic stones - bankFetch(player, new Item(Items.MAGIC_STONE_4703)); + bankFetch(player, new Item(Items.MAGIC_STONE_8788)); break; } break; diff --git a/Server/src/main/content/global/skill/cooking/CookingRecipePlugin.java b/Server/src/main/content/global/skill/cooking/CookingRecipePlugin.java index 9ef36bcaf..067217358 100644 --- a/Server/src/main/content/global/skill/cooking/CookingRecipePlugin.java +++ b/Server/src/main/content/global/skill/cooking/CookingRecipePlugin.java @@ -44,29 +44,30 @@ public final class CookingRecipePlugin extends UseWithHandler { @Override public boolean handle(NodeUsageEvent event) { Recipe recipe = null; + Item part = null; // TODO: Transitioning to a Listener would save an O(n) pass through the recipes list on every use-with + recipeloop: for (Recipe temp : Recipe.RECIPES) { if (temp.isSingular()) { if (temp.getBase().getId() == event.getUsedItem().getId() || temp.getBase().getId() == event.getBaseItem().getId()) { for (Item ingredient : temp.getIngredients()) { if (ingredient.getId() == event.getBaseItem().getId() || ingredient.getId() == event.getUsedItem().getId()) { recipe = temp; - break; + break recipeloop; } } } } else { - Item part = null; - Item ingredient = null; for (int k = 0; k < temp.getParts().length; k++) { for (int i = 0; i < temp.getIngredients().length; i++) { - part = temp.getParts()[k]; - ingredient = temp.getIngredients()[i]; - if (part.getId() == event.getUsedItem().getId() && ingredient.getId() == event.getBaseItem().getId() || part.getId() == event.getBaseItem().getId() && ingredient.getId() == event.getUsedItem().getId()) { + Item tempPart = temp.getParts()[k]; + Item ingredient = temp.getIngredients()[i]; + if (tempPart.getId() == event.getUsedItem().getId() && ingredient.getId() == event.getBaseItem().getId() || tempPart.getId() == event.getBaseItem().getId() && ingredient.getId() == event.getUsedItem().getId()) { if (k == i) {// represents that this ingredient can // mix with the other. recipe = temp; - break; + part = tempPart; + break recipeloop; } } } @@ -76,6 +77,7 @@ public final class CookingRecipePlugin extends UseWithHandler { if (recipe != null) { final Player player = event.getPlayer(); final Recipe recipe_ = recipe; + final Item part_ = part; SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, recipe.getProduct()) { @Override public void create(final int amount, int index) { @@ -91,7 +93,7 @@ public final class CookingRecipePlugin extends UseWithHandler { @Override public int getAll(int index) { - return player.getInventory().getAmount(recipe_.getBase()); + return player.getInventory().getAmount(part_ != null ? part_ : recipe_.getBase()); } }; if (player.getInventory().getAmount(recipe.getBase()) == 1) { diff --git a/Server/src/main/content/global/skill/cooking/CookingRewrite.kt b/Server/src/main/content/global/skill/cooking/CookingRewrite.kt index 76d485956..e65406487 100644 --- a/Server/src/main/content/global/skill/cooking/CookingRewrite.kt +++ b/Server/src/main/content/global/skill/cooking/CookingRewrite.kt @@ -1,17 +1,22 @@ package content.global.skill.cooking -import core.api.amountInInventory +import content.region.misc.tutisland.handlers.TutorialStage +import core.api.* import core.game.interaction.IntType import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.impl.Animator import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.node.scenery.Scenery +import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.Items.BREAD_DOUGH_2307 import org.rs09.consts.Items.RAW_BEAR_MEAT_2136 import org.rs09.consts.Items.RAW_BEEF_2132 import org.rs09.consts.Items.SEAWEED_401 import org.rs09.consts.Items.UNCOOKED_CAKE_1889 +import org.rs09.consts.Sounds /** * @author Ceikry @@ -32,8 +37,36 @@ class CookingRewrite : InteractionListener { } override fun defineListeners() { - onUseWith(IntType.SCENERY,RAW_FOODS, *COOKING_OBJs){ player, used, with -> + if (!getAttribute(player, "/save:tutorial:complete", false)) { + // On tutorial island, we don't want to show the cook-x menu and we always want to burn our first shrimp + // This requirement of a simplified and predictable cooking system means that, for the second time, I have to reinvent the wheel of cooking + // (We only need to care about shrimp here, the tutorial island range is special and has its own separate listener) + queueScript(player, 0, QueueStrength.WEAK) { stage -> + if (stage == 0) { + val FIRE_ANIMATION = Animation(897, Animator.Priority.HIGH) + lock(player, FIRE_ANIMATION.duration) + lockInteractions(player, FIRE_ANIMATION.duration) + animate(player, FIRE_ANIMATION) + playAudio(player, Sounds.FRY_2577) + return@queueScript delayScript(player, FIRE_ANIMATION.duration) + } + val tutStage = getAttribute(player, "/save:tutorial:stage", 0) + if (tutStage < 15) { + replaceSlot(player, used.asItem().slot, Item(Items.BURNT_SHRIMP_7954), used.asItem()) + setAttribute(player, "tutorial:stage", 15) + TutorialStage.load(player, 15) + } else { + replaceSlot(player, used.asItem().slot, Item(Items.SHRIMPS_315), used.asItem()) + if (tutStage == 15) { + setAttribute(player, "tutorial:stage", 16) + TutorialStage.load(player, 16) + } + } + return@queueScript stopExecuting(player) + } + return@onUseWith true + } val item = used.asItem() val obj = with.asScenery() val range = obj.name.toLowerCase().contains("range") @@ -66,7 +99,7 @@ class CookingRewrite : InteractionListener { } companion object { - val COOKING_OBJs = intArrayOf(24313,21302, 13528, 13529, 13533, 13531, 13536, 13539, 13542, 2728, 2729, 2730, 2731, 2732, 2859, 3038, 3039, 3769, 3775, 4265, 4266, 5249, 5499, 5631, 5632, 5981, 9682, 10433, 11404, 11405, 11406, 12102, 12796, 13337, 13881, 14169, 14919, 15156, 20000, 20001, 21620, 21792, 22713, 22714, 23046, 24283, 24284, 25155, 25156, 25465, 25730, 27297, 29139, 30017, 32099, 33500, 34495, 34546, 36973, 37597, 37629, 37726, 114, 4172, 5275, 8750, 16893, 22154, 34410, 34565, 114, 9085, 9086, 9087, 12269, 15398, 25440, 25441, 2724, 2725, 2726, 4618, 4650, 5165, 6093, 6094, 6095, 6096, 8712, 9374, 9439, 9440, 9441, 10824, 17640, 17641, 17642, 17643, 18039, 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) { diff --git a/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt b/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt index a60f89ff7..267d9ccfe 100644 --- a/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt +++ b/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt @@ -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 { diff --git a/Server/src/main/content/global/skill/cooking/NettleTeaListener.kt b/Server/src/main/content/global/skill/cooking/NettleTeaListener.kt new file mode 100644 index 000000000..ac1765428 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/NettleTeaListener.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/NettleTeaPlugin.java b/Server/src/main/content/global/skill/cooking/NettleTeaPlugin.java deleted file mode 100644 index 161c99991..000000000 --- a/Server/src/main/content/global/skill/cooking/NettleTeaPlugin.java +++ /dev/null @@ -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 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; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/NettleWaterListener.kt b/Server/src/main/content/global/skill/cooking/NettleWaterListener.kt new file mode 100644 index 000000000..bf52e8067 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/NettleWaterListener.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/NettleWaterPlugin.java b/Server/src/main/content/global/skill/cooking/NettleWaterPlugin.java deleted file mode 100644 index e8dbeb385..000000000 --- a/Server/src/main/content/global/skill/cooking/NettleWaterPlugin.java +++ /dev/null @@ -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 newInstance(Object arg) throws Throwable { - addHandler(4241, ITEM_TYPE, this); - return this; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java b/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java index f081bed5a..d70856a27 100644 --- a/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java +++ b/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java @@ -4,6 +4,8 @@ import content.global.skill.skillcapeperks.SkillcapePerks; import core.game.event.ResourceProducedEvent; import core.game.node.entity.impl.Animator; import core.game.node.entity.player.Player; +import core.game.node.entity.player.info.LogType; +import core.game.node.entity.player.info.PlayerMonitor; import core.game.node.entity.player.link.audio.Audio; import core.game.node.entity.skill.Skills; import core.game.node.item.GroundItemManager; @@ -15,7 +17,8 @@ import core.tools.RandomFunction; import org.rs09.consts.Items; import org.rs09.consts.Sounds; -import static core.api.ContentAPIKt.playAudio; +import static core.api.ContentAPIKt.*; +import content.data.Quests; public class StandardCookingPulse extends Pulse { //range animation @@ -38,12 +41,17 @@ public class StandardCookingPulse extends Pulse { private boolean burned = false; public CookableItems properties; + private int initialAmount; + private int processedAmount; + public StandardCookingPulse(Player player, Scenery object, int initial, int product, int amount) { this.player = player; this.object = object; this.initial = initial; this.product = product; this.amount = amount; + this.initialAmount = amountInInventory(player, initial); + this.processedAmount = 0; } @Override @@ -72,7 +80,7 @@ public class StandardCookingPulse extends Pulse { this.experience = 0; if (properties != null) { // Handle Cook's Assistant range - if (object.getId() == LUMBRIDGE_RANGE && !player.getQuestRepository().isComplete("Cook's Assistant")) { + if (object.getId() == LUMBRIDGE_RANGE && !player.getQuestRepository().isComplete(Quests.COOKS_ASSISTANT)) { player.getPacketDispatch().sendMessage("You need to have completed the Cook's Assistant quest in order to use that range."); return false; } @@ -169,6 +177,11 @@ public class StandardCookingPulse extends Pulse { player.getInventory().add(productItem); player.dispatch(new ResourceProducedEvent(productItem.getId(), 1, object, initialItem.getId())); player.getSkills().addExperience(Skills.COOKING, experience, true); + processedAmount++; + if (processedAmount > initialAmount) { + PlayerMonitor.log(player, LogType.DUPE_ALERT, "cooked item (" + player.getName() + ", " + initialItem.getName() + "): initialAmount " + initialAmount + ", processedAmount " + processedAmount); + } + player.incrementAttribute("/save:stats_manager:food_cooked", 1); } else { player.dispatch(new ResourceProducedEvent(CookableItems.getBurnt(initial).getId(), 1, object, initialItem.getId())); player.getInventory().add(CookableItems.getBurnt(initial)); diff --git a/Server/src/main/content/global/skill/cooking/recipe/HangoverRecipe.kt b/Server/src/main/content/global/skill/cooking/recipe/HangoverRecipe.kt new file mode 100644 index 000000000..063b85ce4 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/HangoverRecipe.kt @@ -0,0 +1,44 @@ +package content.global.skill.cooking.recipe + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.skill.Skills +import org.rs09.consts.Items + +class HangoverRecipe : InteractionListener { + + companion object{ + private const val SNAPE_GRASS = Items.SNAPE_GRASS_231 + private const val HANGOVER_CURE = Items.HANGOVER_CURE_1504 + private const val BUCKET_OF_MILK = Items.BUCKET_OF_MILK_1927 + private const val CHOCOLATE_DUST = Items.CHOCOLATE_DUST_1975 + private const val CHOCOLATE_MILK = Items.CHOCOLATEY_MILK_1977 + } + + override fun defineListeners() { + onUseWith(IntType.ITEM, CHOCOLATE_DUST, BUCKET_OF_MILK) { player, _, _ -> + if(hasLevelDyn(player, Skills.COOKING, 4)){ + if(removeItem(player, CHOCOLATE_DUST) and removeItem(player, BUCKET_OF_MILK)){ + addItem(player, CHOCOLATE_MILK) + sendItemDialogue(player, CHOCOLATE_MILK, "You mix the chocolate into the bucket.") + } + } + else { + sendDialogue(player, "You need a Cooking level of at least 4 to make chocolate milk.") + } + return@onUseWith true + } + + onUseWith(IntType.ITEM, SNAPE_GRASS, CHOCOLATE_MILK) { player, _, _ -> + if (removeItem(player, SNAPE_GRASS) && removeItem(player, CHOCOLATE_MILK)) + { + sendItemDialogue(player, HANGOVER_CURE, "You mix the snape grass into the bucket.") + addItem(player, HANGOVER_CURE) + return@onUseWith true + } + return@onUseWith false + } + + } +} diff --git a/Server/src/main/content/global/skill/cooking/recipe/Recipe.java b/Server/src/main/content/global/skill/cooking/recipe/Recipe.java index 2402a9772..3e2eff513 100644 --- a/Server/src/main/content/global/skill/cooking/recipe/Recipe.java +++ b/Server/src/main/content/global/skill/cooking/recipe/Recipe.java @@ -119,6 +119,9 @@ public abstract class Recipe { } } if (index != -1) { + if (!player.getInventory().containItems(event.getBaseItem().getId(), event.getUsedItem().getId())) { + return; + } if (player.getInventory().remove(event.getBaseItem()) && player.getInventory().remove(event.getUsedItem())) { player.getInventory().add(getParts()[index + 1]); String message = getMixMessage(event); diff --git a/Server/src/main/content/global/skill/crafting/BattlestaffListener.kt b/Server/src/main/content/global/skill/crafting/BattlestaffListener.kt index 54a82b557..5c65d0850 100644 --- a/Server/src/main/content/global/skill/crafting/BattlestaffListener.kt +++ b/Server/src/main/content/global/skill/crafting/BattlestaffListener.kt @@ -17,6 +17,10 @@ class BattlestaffListener : InteractionListener { onUseWith(IntType.ITEM, orbs, battlestaff) { player, used, with -> val product = BattlestaffProduct.productMap[used.id] ?: return@onUseWith true + fun getMaxAmount(_unused: Int = 0): Int { + return min(amountInInventory(player, with.id), amountInInventory(player, used.id)) + } + if (!hasLevelDyn(player, Skills.CRAFTING, product.minimumLevel)) { sendMessage(player, "You need a Crafting level of ${product.minimumLevel} to make this.") return@onUseWith true @@ -41,7 +45,7 @@ class BattlestaffListener : InteractionListener { withItems(product.producedItemId) create { _, amount -> - runTask(player, 2, amount) { + runTask(player, 2, min(amount, getMaxAmount())) { if (amount < 1) return@runTask if (removeItem(player, product.requiredOrbItemId) && removeItem(player, Items.BATTLESTAFF_1391)) { @@ -55,9 +59,7 @@ class BattlestaffListener : InteractionListener { } } - calculateMaxAmount { _ -> - min(amountInInventory(player, with.id), amountInInventory(player, used.id)) - } + calculateMaxAmount(::getMaxAmount) } return@onUseWith true diff --git a/Server/src/main/content/global/skill/crafting/PotteryPlugin.java b/Server/src/main/content/global/skill/crafting/PotteryPlugin.java index f7fa44e3a..23ddac860 100644 --- a/Server/src/main/content/global/skill/crafting/PotteryPlugin.java +++ b/Server/src/main/content/global/skill/crafting/PotteryPlugin.java @@ -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) { diff --git a/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java b/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java index f4929248e..09c2ed605 100644 --- a/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java +++ b/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java @@ -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) { diff --git a/Server/src/main/content/global/skill/crafting/TanningProduct.java b/Server/src/main/content/global/skill/crafting/TanningProduct.java index e71f370b6..30192b4e7 100644 --- a/Server/src/main/content/global/skill/crafting/TanningProduct.java +++ b/Server/src/main/content/global/skill/crafting/TanningProduct.java @@ -15,7 +15,7 @@ public enum TanningProduct { SOFT_LEATHER(1, 1739, 1741), HARD_LEATHER(2, 1739, 1743), SNAKESKIN(3, 6287, 6289), - SNAKESKIN2(4, 6287, 6289), + SNAKESKIN2(4, 7801, 6289), GREEN_DHIDE(5, 1753, 1745), BLUEDHIDE(6, 1751, 2505), REDDHIDE(7, 1749, 2507), @@ -120,9 +120,9 @@ public enum TanningProduct { } else if (def == HARD_LEATHER) { coins = 3; } else if (def == SNAKESKIN) { - coins = 20; - } else if (def == SNAKESKIN2) { coins = 15; + } else if (def == SNAKESKIN2) { + coins = 20; } else { coins = 20; } diff --git a/Server/src/main/content/global/skill/crafting/armour/DragonCraftPulse.java b/Server/src/main/content/global/skill/crafting/armour/DragonCraftPulse.java index bc8022884..924d1364a 100644 --- a/Server/src/main/content/global/skill/crafting/armour/DragonCraftPulse.java +++ b/Server/src/main/content/global/skill/crafting/armour/DragonCraftPulse.java @@ -95,9 +95,6 @@ public final class DragonCraftPulse extends SkillPulse { player.getInventory().add(item); player.getSkills().addExperience(Skills.CRAFTING, hide.getExperience(), true); LeatherCrafting.decayThread(player); - if (LeatherCrafting.isLastThread(player)) { - LeatherCrafting.removeThread(player); - } amount--; } return amount < 1; diff --git a/Server/src/main/content/global/skill/crafting/armour/HardCraftPulse.java b/Server/src/main/content/global/skill/crafting/armour/HardCraftPulse.java index 4d02be91c..5a2207b4a 100644 --- a/Server/src/main/content/global/skill/crafting/armour/HardCraftPulse.java +++ b/Server/src/main/content/global/skill/crafting/armour/HardCraftPulse.java @@ -75,9 +75,6 @@ public final class HardCraftPulse extends SkillPulse { player.getInventory().add(item); player.getSkills().addExperience(Skills.CRAFTING, 35, true); LeatherCrafting.decayThread(player); - if (LeatherCrafting.isLastThread(player)) { - LeatherCrafting.removeThread(player); - } } amount--; return amount < 1; diff --git a/Server/src/main/content/global/skill/crafting/armour/LeatherCrafting.java b/Server/src/main/content/global/skill/crafting/armour/LeatherCrafting.java index 2767fbc65..02f14d1ee 100644 --- a/Server/src/main/content/global/skill/crafting/armour/LeatherCrafting.java +++ b/Server/src/main/content/global/skill/crafting/armour/LeatherCrafting.java @@ -1,8 +1,12 @@ package content.global.skill.crafting.armour; +import core.api.Container; import core.game.component.Component; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; /** * Represents a useful class for leather crafting related information. @@ -40,52 +44,17 @@ public final class LeatherCrafting { */ private static final Component COMPONENT = new Component(154); - /** - * Checks if its the last thrad. - * @return {@code True} if so. - */ - public static boolean isLastThread(final Player player) { - final Item thread = getThread(player); - if (thread == null) { - return false; - } - int charge = thread.getCharge(); - return charge >= 1004; - } - /** * Method used to decay thread. - */ - public static void decayThread(final Player player) { - final Item thread = getThread(player); - if (thread == null) { - return; - } - int charge = thread.getCharge(); - thread.setCharge(charge + 1); - } - - /** - * Method used to remove thread. - * @param player the player. - */ - public static void removeThread(final Player player) { - if (player.getInventory().remove(THREAD)) { - player.getPacketDispatch().sendMessage("You use a reel of your thread."); - Item thread = getThread(player); - if (thread != null) { - thread.setCharge(1000); - } - } - } - - /** - * Gets the thread. - * @param player the player. - * @return the item. + * @author Player Name */ - public static Item getThread(final Player player) { - return player.getInventory().get(player.getInventory().getSlot(THREAD)); + public static void decayThread(final Player player) { + int charges = getAttribute(player, "threadCharges", 5) - 1; + if (charges <= 0 && removeItem(player, Items.THREAD_1734, Container.INVENTORY)) { + charges = 5; + sendMessage(player, "You use a reel of your thread."); + } + setAttribute(player, "/save:threadCharges", charges); } /** diff --git a/Server/src/main/content/global/skill/crafting/armour/SnakeSkinPulse.java b/Server/src/main/content/global/skill/crafting/armour/SnakeSkinPulse.java index a851caaf6..be012d01c 100644 --- a/Server/src/main/content/global/skill/crafting/armour/SnakeSkinPulse.java +++ b/Server/src/main/content/global/skill/crafting/armour/SnakeSkinPulse.java @@ -82,9 +82,6 @@ public final class SnakeSkinPulse extends SkillPulse { player.getInventory().add(item); player.getSkills().addExperience(Skills.CRAFTING, skin.getExperience(), true); LeatherCrafting.decayThread(player); - if (LeatherCrafting.isLastThread(player)) { - LeatherCrafting.removeThread(player); - } } amount--; return amount < 1; diff --git a/Server/src/main/content/global/skill/crafting/armour/SoftCraftPulse.java b/Server/src/main/content/global/skill/crafting/armour/SoftCraftPulse.java index 2d62c165a..ab49815cf 100644 --- a/Server/src/main/content/global/skill/crafting/armour/SoftCraftPulse.java +++ b/Server/src/main/content/global/skill/crafting/armour/SoftCraftPulse.java @@ -90,10 +90,6 @@ public final class SoftCraftPulse extends SkillPulse { player.getInventory().add(item); player.getSkills().addExperience(Skills.CRAFTING, soft.getExperience(), true); LeatherCrafting.decayThread(player); - if (LeatherCrafting.isLastThread(player)) { - LeatherCrafting.removeThread(player); - } - if (soft == LeatherCrafting.SoftLeather.GLOVES) { player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 3); } diff --git a/Server/src/main/content/global/skill/crafting/glass/GlassMakePulse.kt b/Server/src/main/content/global/skill/crafting/glass/GlassMakePulse.kt index 8c81c1e8e..a641e2082 100644 --- a/Server/src/main/content/global/skill/crafting/glass/GlassMakePulse.kt +++ b/Server/src/main/content/global/skill/crafting/glass/GlassMakePulse.kt @@ -25,7 +25,7 @@ class GlassMakePulse( sendMessage(player, "You heat the sand and soda ash in the furnace to make glass.") if (removeItem(player, Items.SODA_ASH_1781) && removeItem(player, Items.BUCKET_OF_SAND_1783)) { - addItem(player, Items.EMPTY_BUCKET_3727) + addItem(player, Items.BUCKET_1925) addItem(player, Items.MOLTEN_GLASS_1775) rewardXP(player, Skills.CRAFTING, 20.0) player.dispatch(ResourceProducedEvent(product, amount, player)) diff --git a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt index 357b69ca8..0df72d1f2 100644 --- a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt @@ -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 } diff --git a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt index e25677810..d636ee588 100644 --- a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt @@ -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 @@ -66,6 +70,20 @@ class LightSourceLighter : UseWithHandler(590,36,38){ lightSource ?: return false + // For Sea Slug Quest - No lighting of any torch on the fishing platform. + if(event.player.location.isInRegion(11059)) { + event.player.sendMessage("Your tinderbox is damp from the sea crossing. It won't work here.") + 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.") } diff --git a/Server/src/main/content/global/skill/crafting/pottery/FirePotteryPulse.java b/Server/src/main/content/global/skill/crafting/pottery/FirePotteryPulse.java index e7af471ef..e4cf4ec71 100644 --- a/Server/src/main/content/global/skill/crafting/pottery/FirePotteryPulse.java +++ b/Server/src/main/content/global/skill/crafting/pottery/FirePotteryPulse.java @@ -56,7 +56,7 @@ public final class FirePotteryPulse extends SkillPulse { return false; } if (!player.getInventory().containsItem(pottery.getUnfinished())) { - player.getPacketDispatch().sendMessage("You need a " + pottery.name().toLowerCase() + "in order to do this."); + player.getPacketDispatch().sendMessage("You need a " + pottery.name().toLowerCase() + " in order to do this."); return false; } return true; diff --git a/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt index 8bc77007c..338b41b47 100644 --- a/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt +++ b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt @@ -31,10 +31,8 @@ class SilverCraftingPulse( animate(player, Animations.HUMAN_FURNACE_SMELTING_3243) playAudio(player, Sounds.FURNACE_2725) - if (removeItem(player, Items.SILVER_BAR_2355, Container.INVENTORY)) { - addItem(player, product.producedItemId, product.amountProduced) + if (removeItem(player, Items.SILVER_BAR_2355, Container.INVENTORY) && addItem(player, product.producedItemId, product.amountProduced)) { rewardXP(player, Skills.CRAFTING, product.xpReward) - player.dispatch( ResourceProducedEvent( product.producedItemId, diff --git a/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt b/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt index 83becbf16..98bcc014e 100644 --- a/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt +++ b/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt @@ -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), diff --git a/Server/src/main/content/global/skill/farming/CompostBin.kt b/Server/src/main/content/global/skill/farming/CompostBin.kt index 617e800db..c7e7f5cc2 100644 --- a/Server/src/main/content/global/skill/farming/CompostBin.kt +++ b/Server/src/main/content/global/skill/farming/CompostBin.kt @@ -19,6 +19,19 @@ class CompostBin(val player: Player, val bin: CompostBins) { var finishedTime = 0L var isFinished = false + /** + * Resets the compost bin to its initial state. + */ + fun reset() { + items.clear() + isSuperCompost = true + isTomatoes = true + isClosed = false + finishedTime = 0L + isFinished = false + updateBit() + } + fun isFull() : Boolean { return items.size == 15 } diff --git a/Server/src/main/content/global/skill/farming/CropHarvester.kt b/Server/src/main/content/global/skill/farming/CropHarvester.kt index 779609bf8..e47820bb3 100644 --- a/Server/src/main/content/global/skill/farming/CropHarvester.kt +++ b/Server/src/main/content/global/skill/farming/CropHarvester.kt @@ -15,7 +15,7 @@ import core.plugin.Plugin import org.rs09.consts.Items import org.rs09.consts.Sounds -val livesBased = arrayOf(PatchType.HERB_PATCH, PatchType.CACTUS_PATCH, PatchType.BELLADONNA_PATCH, PatchType.HOPS_PATCH, PatchType.ALLOTMENT, PatchType.EVIL_TURNIP_PATCH) +val livesBased = arrayOf(PatchType.HERB_PATCH, PatchType.CACTUS_PATCH, PatchType.HOPS_PATCH, PatchType.ALLOTMENT) @Initializable class CropHarvester : OptionHandler() { @@ -43,14 +43,16 @@ class CropHarvester : OptionHandler() { override fun pulse(): Boolean { var reward = Item(crop) - val familiar = player.familiarManager.familiar - if (familiar != null && familiar is GiantEntNPC) { - familiar.modifyFarmingReward(fPatch, reward) - } if (!hasSpaceFor(player, reward)) { sendMessage(player, "You have run out of inventory space.") return true } + + val familiar = player.familiarManager.familiar + if (familiar != null && familiar is GiantEntNPC) { + familiar.modifyFarmingReward(fPatch, reward) + } + var requiredItem = when (fPatch.type) { PatchType.TREE_PATCH -> Items.SECATEURS_5329 else -> Items.SPADE_952 @@ -61,7 +63,13 @@ class CropHarvester : OptionHandler() { } } val anim = when (requiredItem) { - Items.SPADE_952 -> if (fPatch.type == PatchType.HERB_PATCH) Animation(2282) else Animation(830) + Items.SPADE_952 -> { + when (fPatch.type) { + PatchType.HERB_PATCH -> Animation(2282) + PatchType.FLOWER_PATCH -> Animation(2292) + else -> Animation(830) + } + } Items.SECATEURS_5329 -> if (fPatch.type == PatchType.TREE_PATCH) Animation(2277) else Animation(7227) Items.MAGIC_SECATEURS_7409 -> if (fPatch.type == PatchType.TREE_PATCH) Animation(3340) else Animation(7228) else -> Animation(0) @@ -76,27 +84,33 @@ class CropHarvester : OptionHandler() { sendMessage(player, "You lack the needed tool to harvest these crops.") return true } - if (firstHarvest) { + val sendHarvestMessages = if (fPatch.type == PatchType.FLOWER_PATCH) false else true + if (sendHarvestMessages && firstHarvest) { sendMessage(player, "You begin to harvest the $patchName.") firstHarvest = false } animate(player, anim) playAudio(player, sound) + // TODO: If a flower patch is being harvested, delay the clearing of the + // patch until after the animation has played - https://youtu.be/lg4GktlVNUY?t=75 delay = 2 - addItem(player, reward.id) + addItemOrDrop(player, reward.id,reward.amount) rewardXP(player, Skills.FARMING, plantable.harvestXP) if (patch.patch.type in livesBased) { patch.rollLivesDecrement( getDynLevel(player, Skills.FARMING), - requiredItem == Items.MAGIC_SECATEURS_7409 + inInventory(player, Items.MAGIC_SECATEURS_7409) //add ||inEquipment() check when Fairy Tale pt 1 has been implemented ) } else { patch.harvestAmt-- if (patch.harvestAmt <= 0 && crop == plantable.harvestItem) { patch.clear() } + else if (fPatch.type == PatchType.MUSHROOM_PATCH){ + patch.setCurrentState(patch.getCurrentState() + 1) + } } - if (patch.cropLives <= 0 || patch.harvestAmt <= 0) { + if (sendHarvestMessages && (patch.cropLives <= 0 || patch.harvestAmt <= 0)) { sendMessage(player, "The $patchName is now empty.") } return patch.cropLives <= 0 || patch.harvestAmt <= 0 diff --git a/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt b/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt index 21a773c20..ab170aa74 100644 --- a/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt +++ b/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt @@ -24,7 +24,15 @@ class DigUpPatchDialogue(player: Player? = null) : DialoguePlugin(player) { } if (patch?.patch?.type == PatchType.TREE_PATCH) { val isTreeStump = patch?.getCurrentState() == patch?.plantable!!.value + patch?.plantable!!.stages + 2 - if (!isTreeStump) { + if (patch!!.isGrown() && !isTreeStump) { + sendMessage(player, "You need to chop this tree down first.") // this message is not authentic + stage = 1000 + return true + } + } + if (patch?.patch?.type == PatchType.FRUIT_TREE_PATCH) { + val isTreeStump = patch?.getCurrentState() == patch?.plantable!!.value + 25 + if (patch!!.isGrown() && !isTreeStump) { sendMessage(player, "You need to chop this tree down first.") // this message is not authentic stage = 1000 return true diff --git a/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt index eeb8903f5..24c4dd90a 100644 --- a/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt +++ b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt @@ -1,68 +1,149 @@ package content.global.skill.farming -import core.game.node.item.Item -import org.rs09.consts.Items +import core.api.* import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.item.Item import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import org.rs09.consts.Items -class FarmerPayOptionDialogue(val patch: Patch): DialogueFile() { +class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false): DialogueFile() { var item: Item? = null - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - START_DIALOGUE -> { - item = patch.plantable?.protectionItem - val protectionText = when(item?.id){ - Items.COMPOST_6032 -> if(item?.amount == 1) "bucket of compost" else "buckets of compost" - Items.POTATOES10_5438 -> if(item?.amount == 1) "sack of potatoes" else "sacks of potatoes" - Items.ONIONS10_5458 -> if(item?.amount == 1) "sack of onions" else "sacks of onions" - Items.CABBAGES10_5478 -> if(item?.amount == 1) "sack of cabbages" else "sacks of cabbages" - Items.JUTE_FIBRE_5931 -> "jute fibres" - Items.APPLES5_5386 -> if(item?.amount == 1) "basket of apples" else "baskets of apples" - Items.MARIGOLDS_6010 -> "harvest of marigold" - Items.TOMATOES5_5968 -> if(item?.amount == 1) "basket of tomatoes" else "baskets of tomatoes" - Items.ORANGES5_5396 -> if(item?.amount == 1) "basket of oranges" else "baskets of oranges" - Items.COCONUT_5974 -> "coconuts" - Items.CACTUS_SPINE_6016 -> "cactus spines" - Items.STRAWBERRIES5_5406 -> if(item?.amount == 1) "basket of strawberries" else "baskets of strawberries" - Items.BANANAS5_5416 -> if(item?.amount == 1) "basket of bananas" else "baskets of bananas" - else -> item?.name?.toLowerCase() - } - if(item == null) npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE } - else{ - npc("I would like ${item?.amount} $protectionText","to protect that patch.") - stage++ - } - } + var itemSecondary: Item? = null + var itemTertiary: Item? = null - 1 -> options("Sure!","No, thanks.").also { stage++ } - 2 -> { - if(player!!.inventory.containsItem(item)){ - player("Here you go.").also { stage = 10 } + fun hasAllItems(): Boolean { + return (inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount)) + && (inInventory(player!!, itemSecondary!!.id, itemSecondary!!.amount) || inInventory(player!!, note(itemSecondary!!).id, note(itemSecondary!!).amount)) + && (inInventory(player!!, itemTertiary!!.id, itemTertiary!!.amount) || inInventory(player!!, note(itemTertiary!!).id, note(itemTertiary!!).amount)) + } + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> { + if (patch.patch.type == PatchType.TREE_PATCH && patch.plantable != null && patch.isGrown()) { + // This is for the right-click "Pay" option; full dialogue is in GardenerDialoguePlugin + showTopics( + Topic("Yes, get rid of the tree.", 300, true), + Topic("No thanks.", END_DIALOGUE, true), + title = "Pay 200 gp to have the tree chopped down?" + ) + } else if (patch.protectionPaid) { + npc("I don't know what you're talking about - I'm already", "looking after that patch for you.").also { stage = 100 } + } else if (patch.isDead) { + npc("That patch is dead - it's too late for me to do", "anything about it now.").also { stage = END_DIALOGUE } + } else if (patch.isDiseased) { + npc("That patch is diseased - I can't look after it", "until it has been cured.").also { stage = END_DIALOGUE } // this dialogue is not authentic + } else if (patch.isWeedy() || patch.isEmptyAndWeeded()) { + npc(FacialExpression.NEUTRAL, "You don't have anything planted in that patch. Plant", "something and I might agree to look after it for you.").also { stage = END_DIALOGUE } + } else if (patch.isGrown()) { + npc("That patch is already fully grown!", "I don't know what you want me to do with it!").also { stage = END_DIALOGUE } } else { - item = Item(item!!.noteChange,item!!.amount) - if(player!!.inventory.containsItem(item)){ - player("Here you go.").also { stage = 10 } + item = patch.plantable?.protectionItem + itemSecondary = patch.plantable?.protectionItemSecondary + itemTertiary = patch.plantable?.protectionItemTertiary + + val protectionText = when (item?.id) { + Items.COMPOST_6032 -> if (item?.amount == 1) "bucket of compost" else "buckets of compost" + Items.POTATOES10_5438 -> if (item?.amount == 1) "sack of potatoes" else "sacks of potatoes" + Items.ONIONS10_5458 -> if (item?.amount == 1) "sack of onions" else "sacks of onions" + Items.CABBAGES10_5478 -> if (item?.amount == 1) "sack of cabbages" else "sacks of cabbages" + Items.JUTE_FIBRE_5931 -> "jute fibres" + Items.APPLES5_5386 -> if (item?.amount == 1) "basket of apples" else "baskets of apples" + Items.MARIGOLDS_6010 -> "harvest of marigold" + Items.TOMATOES5_5968 -> if (item?.amount == 1) "basket of tomatoes" else "baskets of tomatoes" + Items.ORANGES5_5396 -> if (item?.amount == 1) "basket of oranges" else "baskets of oranges" + Items.COCONUT_5974 -> "coconuts" + Items.CACTUS_SPINE_6016 -> "cactus spines" + Items.STRAWBERRIES5_5406 -> if (item?.amount == 1) "basket of strawberries" else "baskets of strawberries" + Items.BANANAS5_5416 -> if (item?.amount == 1) "basket of bananas" else "baskets of bananas" + else -> item?.name?.lowercase() + } + if (item == null) { + npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE } + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && quickPay + && !hasAllItems()) { + val amount = if (item?.amount == 1) "one" else item?.amount + npc(FacialExpression.HAPPY, "I want $amount $protectionText, one monkey bar,", "and one ground tooth for that.") + stage = 200 + } else if (quickPay && !(inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount))) { + val amount = if (item?.amount == 1) "one" else item?.amount + npc(FacialExpression.HAPPY, "I want $amount $protectionText for that.") + stage = 200 + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && quickPay) { + showTopics( + //Found a 2011 source for quick-pay, but no earlier dialogue sources (https://www.youtube.com/watch?v=RdIcNH50v7I) + Topic("Yes", 20, true), + Topic("No", END_DIALOGUE, true), + title = "Pay the gnome?" + ) + } else if (quickPay) { + val amount = if (item?.amount == 1) "one" else item?.amount + showTopics( + Topic("Yes", 20, true), + Topic("No", END_DIALOGUE, true), + title = "Pay $amount $protectionText?" + ) + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH) { + val amount = if (item?.amount == 1) "one" else item?.amount + npc("If you like, but I want $amount $protectionText,", "one monkey bar, and one ground tooth for that.") + stage++ } else { - player("I don't have that to give.").also { stage = 20 } + val amount = if (item?.amount == 1) "one" else item?.amount + npc("If you like, but I want $amount $protectionText for that.") + stage++ } } } - 10 -> { - if(player!!.inventory.remove(item)){ - npc("Thank you! I'll keep an eye on this patch.").also { stage = END_DIALOGUE } - patch?.protectionPaid = true + 1 -> { + if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && !hasAllItems()) { + player("I'm afraid I don't have any of those at the moment.").also { stage = 10 } + } else if (!(inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount))) { + player("I'm afraid I don't have any of those at the moment.").also { stage = 10 } } else { - npc("That stuff just... vanished....").also { stage = END_DIALOGUE } + showTopics( + Topic(FacialExpression.NEUTRAL, "Okay, it's a deal.", 20), + Topic(FacialExpression.NEUTRAL, "No, that's too much.", 10) + ) } } + 10 -> npc("Well, I'm not wasting my time for free.").also { stage = END_DIALOGUE } + 20 -> { - npc("Come back when you do.") - stage = END_DIALOGUE + if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH + && (removeItem(player!!, item) || removeItem(player!!, note(item!!))) + && (removeItem(player!!, itemSecondary) || removeItem(player!!, note(itemSecondary!!))) + && (removeItem(player!!, itemTertiary) || removeItem(player!!, note(itemTertiary!!)))) { + patch.protectionPaid = true + npc("That'll do nicely, ${if (player!!.isMale) "sir" else "madam"}. Leave it with me - I'll make sure", "that tree grows up good and strong.").also { stage = END_DIALOGUE } + } else if (patch.patch.type != PatchType.SPIRIT_TREE_PATCH + && removeItem(player!!, item) || removeItem(player!!, note(item!!))) { + patch.protectionPaid = true + // Note: A slight change in this dialogue was seen in a December 2009 video - https://youtu.be/7gVh42ylQ48?t=138 + npc("That'll do nicely, ${if (player!!.isMale) "sir" else "madam"}. Leave it with me - I'll make sure", "those crops grow for you.").also { stage = END_DIALOGUE } + } else { + npc("This shouldn't be happening. Please report this.").also { stage = END_DIALOGUE } + } + } + + 100 -> player("Oh sorry, I forgot.").also { stage = END_DIALOGUE } + + // Right-click "Pay" - protect patch - player doesn't have payment + 200 -> player(FacialExpression.NEUTRAL, "Thanks, maybe another time.").also { stage = END_DIALOGUE } + + // Right-click "Pay" - chop down tree + 300 -> { + if (removeItem(player!!, Item(Items.COINS_995, 200))) { + patch.clear() + dialogue("The gardener obligingly removes your tree.").also { stage = END_DIALOGUE } + } else { + dialogue("You need 200 gp to pay for that.").also { stage = END_DIALOGUE } // not authentic + } } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt b/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt index 724ce6c73..52d7874ad 100644 --- a/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt +++ b/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt @@ -1,5 +1,6 @@ package content.global.skill.farming +import core.api.openDialogue import core.game.node.Node import core.game.node.entity.player.Player import core.game.interaction.InteractionListener @@ -8,35 +9,20 @@ import core.game.interaction.IntType class FarmerPayOptionHandler : InteractionListener { override fun defineListeners() { - on(IntType.NPC,"pay","pay (north)","pay (north-west)"){ player, node -> + on(IntType.NPC,"pay","pay (north)","pay (north-west)") { player, node -> return@on attemptPay(player,node,0) } - on(IntType.NPC,"pay (south)","pay (south-east)"){ player, node -> + on(IntType.NPC,"pay (south)","pay (south-east)") { player, node -> return@on attemptPay(player,node,1) } } - fun attemptPay(player: Player, node: Node, index: Int): Boolean{ + fun attemptPay(player: Player, node: Node, index: Int): Boolean { val farmer = Farmers.forId(node.id) ?: return false val patch = farmer.patches[index].getPatchFor(player) - if(patch.plantable == null){ - player.dialogueInterpreter.sendDialogue("I have nothing to protect in that patch.") - return true - } - - if(patch.protectionPaid){ - player.dialogueInterpreter.sendDialogue("I have already paid to protect that patch.") - return true - } - - if(patch.isGrown()){ - player.dialogueInterpreter.sendDialogue("This patch is already fully grown!") - return true - } - - player.dialogueInterpreter.open(FarmerPayOptionDialogue(patch),node.asNpc()) + openDialogue(player, FarmerPayOptionDialogue(patch, true), node.asNpc()) return true } } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/Farmers.kt b/Server/src/main/content/global/skill/farming/Farmers.kt index 4a57feeef..206eb254b 100644 --- a/Server/src/main/content/global/skill/farming/Farmers.kt +++ b/Server/src/main/content/global/skill/farming/Farmers.kt @@ -21,7 +21,11 @@ enum class Farmers(val id: Int, val patches: Array) { FRANCIS(2327, arrayOf(FarmingPatch.ENTRANA_HOPS)), DREVEN(2335, arrayOf(FarmingPatch.CHAMPIONS_GUILD_BUSH)), TARIA(2336, arrayOf(FarmingPatch.RIMMINGTON_BUSH)), - TORRELL(2338, arrayOf(FarmingPatch.ARDOUGNE_BUSH)); + TORRELL(2338, arrayOf(FarmingPatch.ARDOUGNE_BUSH)), + FRIZZY(4560, arrayOf(FarmingPatch.PORT_SARIM_SPIRIT_TREE)), + YULF(4561, arrayOf(FarmingPatch.ETCETERIA_SPIRIT_TREE)), + PRAISTAN(4562, arrayOf(FarmingPatch.KARAMJA_SPIRIT_TREE)) + ; companion object{ @JvmField diff --git a/Server/src/main/content/global/skill/farming/FarmingPatch.kt b/Server/src/main/content/global/skill/farming/FarmingPatch.kt index c322e4de8..6b3a1814f 100644 --- a/Server/src/main/content/global/skill/farming/FarmingPatch.kt +++ b/Server/src/main/content/global/skill/farming/FarmingPatch.kt @@ -94,6 +94,10 @@ enum class FarmingPatch(val varbit: Int, val type: PatchType) { patchNodes.addAll(8382..8383)//spirit trees patchNodes.add(8338) //spirit tree patchNodes.add(18816) //death plateau wrapper + patchNodes.add(21950) //harmony island allotment + patchNodes.add(28919) //lletya fruit patch. Was usable in 2009: https://www.youtube.com/watch?v=7sXOW4CRZ3k + //patchNodes.add(37988) Wilderness flower patch. Can only plant Limpwurt Seeds. Intentionally left out, as it is a reward from Spirit of Summer. + for (patch in patchNodes) { val def = SceneryDefinition.forId(patch) @@ -117,8 +121,8 @@ enum class FarmingPatch(val varbit: Int, val type: PatchType) { } } - fun getPatchFor(player: Player): Patch{ - var crops = getOrStartTimer (player)!! - return crops.getPatch(this) + fun getPatchFor(player: Player, addPatch : Boolean = true): Patch{ + val crops = getOrStartTimer (player) + return crops.getPatch(this, addPatch) } } diff --git a/Server/src/main/content/global/skill/farming/FarmingState.kt b/Server/src/main/content/global/skill/farming/FarmingState.kt deleted file mode 100644 index ed7f769e5..000000000 --- a/Server/src/main/content/global/skill/farming/FarmingState.kt +++ /dev/null @@ -1,43 +0,0 @@ -package content.global.skill.farming - -import core.api.* -import core.Util.clamp -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import core.game.node.entity.state.PlayerState -import core.game.node.entity.state.State -import core.tools.SystemLogger -import java.util.concurrent.TimeUnit -import content.global.skill.farming.timers.* - -@PlayerState("farming") -/** - * Kept around solely for the purpose of porting save data from this old system to the new one. - * //TODO REMOVE BY END OF 2023 -**/ -class FarmingState(player: Player? = null) : State(player) { - override fun save(root: JSONObject) {} - override fun parse(_data: JSONObject) { - player ?: return - if(_data.containsKey("farming-bins")){ - _data["bins"] = _data["farming-bins"] - val timer = getOrStartTimer (player) - timer.parse (_data, player) - } - if(_data.containsKey("farming-patches")){ - _data["patches"] = _data["farming-patches"] - val timer = getOrStartTimer (player) - timer.parse(_data, player) - } - } - - override fun newInstance(player: Player?): State { - return FarmingState(player) - } - - override fun createPulse() {} -} diff --git a/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt b/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt index f07689362..380334445 100644 --- a/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt +++ b/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt @@ -5,7 +5,9 @@ import core.cache.def.impl.SceneryDefinition import core.game.interaction.OptionHandler import core.game.node.Node import content.global.skill.summoning.familiar.GiantEntNPC +import content.global.skill.summoning.familiar.WolpertingerNPC import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.system.task.Pulse @@ -75,8 +77,15 @@ class FruitAndBerryPicker : OptionHandler() { animate(player, animation) playAudio(player, Sounds.FARMING_PICK_2437) - addItemOrDrop(player, reward.id, reward.amount) - rewardXP(player, Skills.FARMING, plantable.harvestXP) + + if (familiar != null && familiar is WolpertingerNPC && patch.patch.type == PatchType.BUSH_PATCH) { + addItemOrDrop(player, reward.id, reward.amount * 2) + rewardXP(player, Skills.FARMING, plantable.harvestXP * 2) + } + else { + addItemOrDrop(player, reward.id, reward.amount) + rewardXP(player, Skills.FARMING, plantable.harvestXP) + } patch.setCurrentState(patch.getCurrentState() - 1) if (patch.patch.type == PatchType.CACTUS_PATCH) { @@ -86,6 +95,10 @@ class FruitAndBerryPicker : OptionHandler() { sendMessage(player, "You pick $determiner ${reward.name.lowercase()}.") } + if (plantable == Plantable.POISON_IVY_SEED && patch.patch == FarmingPatch.CHAMPIONS_GUILD_BUSH){ + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 0) + } + return patch.getFruitOrBerryCount() == 0 } }) diff --git a/Server/src/main/content/global/skill/farming/FruitTreeChopper.kt b/Server/src/main/content/global/skill/farming/FruitTreeChopper.kt new file mode 100644 index 000000000..288bf1e01 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/FruitTreeChopper.kt @@ -0,0 +1,62 @@ +package content.global.skill.farming + +import content.data.skill.SkillingTool +import core.api.* +import core.cache.def.impl.SceneryDefinition +import core.game.interaction.OptionHandler +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.plugin.Initializable +import core.plugin.Plugin +import core.tools.RandomFunction +import org.rs09.consts.Sounds + +@Initializable +class FruitTreeChopper : OptionHandler() { + override fun newInstance(arg: Any?): Plugin { + SceneryDefinition.setOptionHandler("chop-down",this) + SceneryDefinition.setOptionHandler("chop down",this) + return this + } + + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + player ?: return false + node ?: return false + + val fPatch = FarmingPatch.forObject(node.asScenery()) + fPatch ?: return false + + val patch = fPatch.getPatchFor(player) + + val plantable = patch.plantable + plantable ?: return false + + if (SkillingTool.getHatchet(player) == null) { + sendMessage(player, "You do not have an axe to use.") + return true + } + + val animation = SkillingTool.getHatchet(player).animation + + submitIndividualPulse(player, object : Pulse(animation.duration) { + override fun pulse(): Boolean { + animate(player, animation) + val soundIndex = RandomFunction.random(0, woodcuttingSounds.size) + playAudio(player, woodcuttingSounds[soundIndex]) + patch.setCurrentState(patch.getCurrentState() + 19) + sendMessage(player, "You chop down the ${plantable.displayName.lowercase().removeSuffix(" sapling")}.") + return true + } + }) + return true + } + + private val woodcuttingSounds = intArrayOf( + Sounds.WOODCUTTING_HIT_3038, + Sounds.WOODCUTTING_HIT_3039, + Sounds.WOODCUTTING_HIT_3040, + Sounds.WOODCUTTING_HIT_3041, + Sounds.WOODCUTTING_HIT_3042 + ) +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/HealthChecker.kt b/Server/src/main/content/global/skill/farming/HealthChecker.kt index 43d0e24d9..eb6d03256 100644 --- a/Server/src/main/content/global/skill/farming/HealthChecker.kt +++ b/Server/src/main/content/global/skill/farming/HealthChecker.kt @@ -5,6 +5,7 @@ import core.cache.def.impl.SceneryDefinition import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills import core.plugin.Initializable import core.plugin.Plugin @@ -26,7 +27,7 @@ class HealthChecker : OptionHandler() { val patch = fPatch.getPatchFor(player) val type = patch.patch.type - if (type != PatchType.BUSH_PATCH && type != PatchType.FRUIT_TREE_PATCH && type != PatchType.TREE_PATCH && type != PatchType.CACTUS_PATCH) { + if (!patch.isCheckable()) { sendMessage(player, "This shouldn't be happening. Please report this.") return true } @@ -36,13 +37,29 @@ class HealthChecker : OptionHandler() { rewardXP(player, Skills.FARMING, patch.plantable?.checkHealthXP ?: 0.0) patch.isCheckHealth = false when (type) { - PatchType.TREE_PATCH -> patch.setCurrentState(patch.getCurrentState() + 1) - PatchType.FRUIT_TREE_PATCH -> patch.setCurrentState(patch.getCurrentState() - 14) - PatchType.BUSH_PATCH -> { - sendMessage(player, "You examine the bush for signs of disease and find that it's in perfect health.") - patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 4) + PatchType.TREE_PATCH -> { + patch.setCurrentState(patch.getCurrentState() + 1) + sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.") + } + PatchType.FRUIT_TREE_PATCH -> { + patch.setCurrentState(patch.getCurrentState() - 14) + if (fPatch == FarmingPatch.BRIMHAVEN_FRUIT_TREE) { + player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 12) + } + sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.") + } + PatchType.SPIRIT_TREE_PATCH -> { + patch.setCurrentState(patch.getCurrentState() - 24) + sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.") + } + PatchType.BUSH_PATCH -> { + patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 4) + sendMessage(player, "You examine the bush for signs of disease and find that it's in perfect health.") + } + PatchType.CACTUS_PATCH -> { + patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 3) + sendMessage(player, "You examine the cactus for signs of disease and find that it is in perfect health.") } - PatchType.CACTUS_PATCH -> patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 3) else -> log(this::class.java, Log.ERR, "Unreachable patch type from when(type) switch in HealthChecker.kt") } diff --git a/Server/src/main/content/global/skill/farming/Patch.kt b/Server/src/main/content/global/skill/farming/Patch.kt index 486f1d5bf..eacc91bde 100644 --- a/Server/src/main/content/global/skill/farming/Patch.kt +++ b/Server/src/main/content/global/skill/farming/Patch.kt @@ -16,6 +16,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl var compost = CompostType.NONE var protectionPaid = false var cropLives = 3 + val checkablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CACTUS_PATCH) fun setNewHarvestAmount() { val compostMod = when(compost) { @@ -29,7 +30,11 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl Plantable.WILLOW_SAPLING -> 0 else -> 1 } - if(plantable != null && plantable?.applicablePatch != PatchType.FLOWER_PATCH) { + if(plantable != null + && plantable?.applicablePatch != PatchType.FLOWER_PATCH + && plantable?.applicablePatch != PatchType.BELLADONNA_PATCH + && plantable?.applicablePatch != PatchType.MUSHROOM_PATCH + && plantable?.applicablePatch != PatchType.EVIL_TURNIP_PATCH) { harvestAmt += compostMod } cropLives = 3 + compostMod @@ -70,8 +75,6 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl var chance = when(patch.type){ PatchType.ALLOTMENT -> 8 //average of 8 per life times 3 lives = average 24 PatchType.HOPS_PATCH -> 6 //average of 6 per life times 3 lives = 18 - PatchType.BELLADONNA_PATCH -> 2 //average of 2 per life times 3 lives = 6 - PatchType.EVIL_TURNIP_PATCH -> 2 //average 2 per, same as BELLADONNA PatchType.CACTUS_PATCH -> 3 //average of 3 per life times 3 lives = 9 else -> 0 // nothing should go here, but if it does, do not give extra crops amd decrement cropLives } @@ -88,6 +91,11 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl return getCurrentState() in 0..2 } + fun isChoppedFruitTree(): Boolean { + return (patch.type == PatchType.FRUIT_TREE_PATCH) + && getCurrentState() == (plantable?.value ?: 0) + 25 + } + fun isEmptyAndWeeded(): Boolean { return getCurrentState() == 3 } @@ -123,6 +131,9 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl log(this::class.java, Log.DEBUG, "Patch for ${player.username} at varbit ${patch.varbit} with plantable ${plantable?.name ?: "none"} was set to diseased at stage $currentGrowthStage, which isn't valid.") return (state and (0x80.inv())) } + else if (state in listOf(0, 1, 2, 3)){ + // we're weedy (or an empty plot) as normal just continue + } else { log (this::class.java, Log.ERR, "Patch for ${player.username} at varbit ${patch.varbit} with plantable ${plantable?.name ?: "none"} was set to state $state at growth stage $currentGrowthStage, which isn't valid. We're not sure why this is happening.") } @@ -134,6 +145,13 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl return compost != CompostType.NONE } + /** + * Returns true if the patch is fully grown. + * + * Note: This returns true if the patch is fully weedy. + * Use `plantable == null` to check if a patch does + * not have anything planted. + */ fun isGrown(): Boolean{ return currentGrowthStage == (plantable?.stages ?: 0) } @@ -144,7 +162,9 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl PatchType.FRUIT_TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 20) PatchType.BUSH_PATCH -> setVarbit(player, patch.varbit, 250 + (plantable!!.ordinal - Plantable.REDBERRY_SEED.ordinal)) PatchType.CACTUS_PATCH -> setVarbit(player, patch.varbit, 31) - else -> log(this::class.java, Log.WARN, "Invalid setting of isCheckHealth for patch type: " + patch.type.name) + PatchType.TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages) + PatchType.SPIRIT_TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 24) + else -> log(this::class.java, Log.WARN, "Invalid setting of isCheckHealth for patch type: " + patch.type.name + "at" + patch.name) } } else { when(patch.type){ @@ -169,6 +189,10 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl if (state != getVarbit(player, patch.varbit)) setVisualState(state) } + PatchType.SPIRIT_TREE_PATCH -> { + if(isDead) setVisualState(getSpiritTreeDeathValue()) + else if(isDiseased && !isDead) setVisualState(getSpiritTreeDiseaseValue()) + } PatchType.FRUIT_TREE_PATCH -> { if(isDead) setVisualState(getFruitTreeDeathValue()) else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue()) @@ -187,6 +211,10 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl else if(isDiseased && !isDead) setVisualState(getHerbDiseaseValue()) else setVisualState((plantable?.value ?: 0) + currentGrowthStage) } + PatchType.MUSHROOM_PATCH -> { + if(isDead) setVisualState(getMushroomDeathValue()) + else if(isDiseased && !isDead) setVisualState(getMushroomDiseaseValue()) + } else -> {} } } @@ -210,17 +238,36 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl private fun getBushDiseaseValue(): Int{ if(plantable == Plantable.POISON_IVY_SEED){ return (plantable?.value ?: 0) + currentGrowthStage + 12 - } else { + } + else if (plantable == Plantable.REDBERRY_SEED + || plantable == Plantable.CADAVABERRY_SEED){ + return (plantable?.value ?: 0) + currentGrowthStage + 65 + } + else { return (plantable?.value ?: 0) + currentGrowthStage + 64 } } private fun getBushDeathValue(): Int{ if(plantable == Plantable.POISON_IVY_SEED){ - return (plantable?.value ?: 0) + currentGrowthStage + 22 - } else { - return (plantable?.value ?: 0) + currentGrowthStage + 126 + return (plantable?.value ?: 0) + currentGrowthStage + 20 } + else if (plantable == Plantable.REDBERRY_SEED + || plantable == Plantable.CADAVABERRY_SEED + || plantable == Plantable.WHITEBERRY_SEED){ + return (plantable?.value ?: 0) + currentGrowthStage + 129 + } + else { + return (plantable?.value ?: 0) + currentGrowthStage + 128 + } + } + + private fun getSpiritTreeDiseaseValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 12 + } + + private fun getSpiritTreeDeathValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 24 } private fun getFruitTreeDiseaseValue(): Int { @@ -247,6 +294,14 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl return (plantable?.value ?: 0) + currentGrowthStage + 16 } + private fun getMushroomDiseaseValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 11 + } + + private fun getMushroomDeathValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 16 + } + private fun getHerbDiseaseValue(): Int { return if (plantable?.value ?: -1 <= 103) { 128 + (((plantable?.ordinal ?: 0) - Plantable.GUAM_SEED.ordinal) * 3) + currentGrowthStage - 1 @@ -264,7 +319,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl } private fun grow(){ - if((isWeedy() || isEmptyAndWeeded()) && getCurrentState() > 0) { + if((isWeedy() || isEmptyAndWeeded() || (plantable == Plantable.SCARECROW && !isGrown())) && getCurrentState() > 0) { nextGrowth = System.currentTimeMillis() + 60000 setCurrentState(getCurrentState() - 1) currentGrowthStage-- @@ -276,21 +331,28 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl return } - diseaseMod = when(compost){ + // This is so a cheat can force disease + diseaseMod = if (diseaseMod < 0) -128 else when(compost){ CompostType.NONE -> 0 CompostType.COMPOST -> 8 CompostType.SUPERCOMPOST -> 13 } - if(patch != FarmingPatch.TROLL_STRONGHOLD_HERB && RandomFunction.random(128) <= (17 - diseaseMod) && !isWatered && !isGrown() && !protectionPaid && !isFlowerProtected() && patch.type != PatchType.EVIL_TURNIP_PATCH ){ - //bush, tree, fruit tree, herb and cactus can not disease on stage 1(0) of growth. - if(!((patch.type == PatchType.BUSH_PATCH || patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.CACTUS_PATCH || patch.type == PatchType.HERB_PATCH) && currentGrowthStage == 0)) { - isDiseased = true - return - } + if(patch != FarmingPatch.TROLL_STRONGHOLD_HERB + && RandomFunction.random(128) <= (17 - diseaseMod) + && !isWatered && !isGrown() + && !protectionPaid + && !isFlowerProtected() + && patch.type != PatchType.EVIL_TURNIP_PATCH + && plantable != Plantable.POISON_IVY_SEED + && currentGrowthStage != 0){ + isDiseased = true + // If we manually set disease mod reset it back to 0 so that crops can naturally grow after being treated/accidentally attempted to disease when they cannot be + if (diseaseMod < 0) diseaseMod = 0 + return } - if((patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.TREE_PATCH || patch.type == PatchType.BUSH_PATCH || patch.type == PatchType.CACTUS_PATCH) && plantable != null && plantable?.stages == currentGrowthStage + 1){ + if(isCheckable() && plantable != null && plantable?.stages == currentGrowthStage + 1){ isCheckHealth = true } @@ -311,8 +373,6 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl setCurrentState(getCurrentState() + 1) isWatered = false } - - regrowIfTreeStump() } fun regrowIfTreeStump() { @@ -364,6 +424,10 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl // restocking their fruit should take 40 minutes per fruit minutes = 40 } + else if(plantable == Plantable.WILLOW_SAPLING && isGrown()) { + // Willow Branches grow back in only 5 minutes + minutes = 5 + } return minutes } @@ -376,10 +440,16 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl FarmingPatch.CATHERBY_ALLOTMENT_S,FarmingPatch.CATHERBY_ALLOTMENT_N -> FarmingPatch.CATHERBY_FLOWER_C FarmingPatch.PORT_PHAS_ALLOTMENT_SE,FarmingPatch.PORT_PHAS_ALLOTMENT_NW -> FarmingPatch.PORT_PHAS_FLOWER_C else -> return false - }.getPatchFor(player) + }.getPatchFor(player, false) - return (fpatch.plantable != null && + if (fpatch.plantable == Plantable.SCARECROW && fpatch.plantable == plantable?.protectionFlower){ + return true + } else return (fpatch.plantable != null && (fpatch.plantable == plantable?.protectionFlower || fpatch.plantable == Plantable.forItemID(Items.WHITE_LILY_SEED_14589)) && fpatch.isGrown()) } + + fun isCheckable(): Boolean{ + return (patch.type in checkablePatches) + } } diff --git a/Server/src/main/content/global/skill/farming/PatchRaker.kt b/Server/src/main/content/global/skill/farming/PatchRaker.kt index 27bc3d8fa..21967671b 100644 --- a/Server/src/main/content/global/skill/farming/PatchRaker.kt +++ b/Server/src/main/content/global/skill/farming/PatchRaker.kt @@ -15,7 +15,7 @@ object PatchRaker { val p = patch.getPatchFor(player) val patchName = p.patch.type.displayName() var firstRake = true - if (p.isEmptyAndWeeded()) { + if (!p.isWeedy()) { sendMessage(player, "This $patchName doesn't need weeding right now.") return } @@ -35,7 +35,7 @@ object PatchRaker { } else { patch.getPatchFor(player).currentGrowthStage++ patch.getPatchFor(player).setCurrentState(++patchStage) - addItem(player, Items.WEEDS_6055) + addItem(player, Items.WEEDS_6055) //authentically destroys weeds if inventory was full rewardXP(player, Skills.FARMING, 4.0) } if (patchStage >= 3) { @@ -45,4 +45,4 @@ object PatchRaker { } }) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/Plantable.kt b/Server/src/main/content/global/skill/farming/Plantable.kt index c77d22b6e..1cf2ecdbd 100644 --- a/Server/src/main/content/global/skill/farming/Plantable.kt +++ b/Server/src/main/content/global/skill/farming/Plantable.kt @@ -3,88 +3,89 @@ package content.global.skill.farming import core.game.node.item.Item import org.rs09.consts.Items -enum class Plantable(val itemID: Int, val value: Int, val stages: Int, val plantingXP: Double, val harvestXP: Double, val checkHealthXP: Double, val requiredLevel: Int, val applicablePatch: PatchType, val harvestItem: Int, val protectionItem: Item? = null,val protectionFlower: Plantable? = null) { +enum class Plantable(val itemID: Int, val displayName: String, val value: Int, val stages: Int, val plantingXP: Double, val harvestXP: Double, val checkHealthXP: Double, val requiredLevel: Int, val applicablePatch: PatchType, val harvestItem: Int, val protectionItem: Item? = null, val protectionFlower: Plantable? = null, val protectionItemSecondary: Item? = null, val protectionItemTertiary: Item? = null) { - //Flowers - MARIGOLD_SEED(5096,8,4,8.5,47.0,0.0,2,PatchType.FLOWER_PATCH,Items.MARIGOLDS_6010), - ROSEMARY_SEED(5097,13,4,12.0,66.5,0.0,11,PatchType.FLOWER_PATCH, Items.ROSEMARY_6014), - NASTURTIUM_SEED(5098,18,4,19.5,111.0,0.0,24,PatchType.FLOWER_PATCH,Items.NASTURTIUMS_6012), - WOAD_SEED(5099,23,4,20.5,115.5,0.0,25,PatchType.FLOWER_PATCH,Items.WOAD_LEAF_1793), - LIMPWURT_SEED(5100,28,4,21.5,120.0,0.0,26,PatchType.FLOWER_PATCH,Items.LIMPWURT_ROOT_225), - WHITE_LILY_SEED(14589,37,4,42.0,250.0,0.0,52,PatchType.FLOWER_PATCH,Items.WHITE_LILY_14583), + // Flowers + MARIGOLD_SEED(Items.MARIGOLD_SEED_5096,"marigold seed",8,4,8.5,47.0,0.0,2,PatchType.FLOWER_PATCH,Items.MARIGOLDS_6010), + ROSEMARY_SEED(Items.ROSEMARY_SEED_5097,"rosemary seed",13,4,12.0,66.5,0.0,11,PatchType.FLOWER_PATCH, Items.ROSEMARY_6014), + NASTURTIUM_SEED(Items.NASTURTIUM_SEED_5098,"nasturtium seed",18,4,19.5,111.0,0.0,24,PatchType.FLOWER_PATCH,Items.NASTURTIUMS_6012), + WOAD_SEED(Items.WOAD_SEED_5099,"woad seed",23,4,20.5,115.5,0.0,25,PatchType.FLOWER_PATCH,Items.WOAD_LEAF_1793), + LIMPWURT_SEED(Items.LIMPWURT_SEED_5100,"limpwurt seed",28,4,21.5,120.0,0.0,26,PatchType.FLOWER_PATCH,Items.LIMPWURT_ROOT_225), + WHITE_LILY_SEED(Items.WHITE_LILY_SEED_14589,"white lily seed",37,4,42.0,250.0,0.0,52,PatchType.FLOWER_PATCH,Items.WHITE_LILY_14583), - //Flower(Technically) - SCARECROW(6059,33,3,0.0,0.0,0.0,23,PatchType.FLOWER_PATCH,Items.SCARECROW_6059), + // Flower (technically) + SCARECROW(Items.SCARECROW_6059,"scarecrow",36,-3,0.0,0.0,0.0,23,PatchType.FLOWER_PATCH,Items.SCARECROW_6059), - //Allotments - POTATO_SEED(5318, 6, 4, 8.0, 9.0, 0.0, 1, PatchType.ALLOTMENT, Items.POTATO_1942,Item(Items.COMPOST_6032,2),MARIGOLD_SEED), - ONION_SEED(5319, 13, 4, 9.5, 10.5,0.0, 5, PatchType.ALLOTMENT,Items.ONION_1957,Item(Items.POTATOES10_5438),MARIGOLD_SEED), - CABBAGE_SEED(5324, 20, 4, 10.0, 11.5, 0.0,7, PatchType.ALLOTMENT,Items.CABBAGE_1965,Item(Items.ONIONS10_5458),ROSEMARY_SEED), - TOMATO_SEED(5322,27,4,12.5,14.0,0.0,12,PatchType.ALLOTMENT,Items.TOMATO_1982,Item(Items.CABBAGES10_5478,2),MARIGOLD_SEED), - SWEETCORN_SEED(5320,34,6,17.0,19.0,0.0,20,PatchType.ALLOTMENT,Items.SWEETCORN_5986,Item(Items.JUTE_FIBRE_5931,10),SCARECROW), - STRAWBERRY_SEED(5323,43,6,26.0,29.0,0.0,31,PatchType.ALLOTMENT,Items.STRAWBERRY_5504,Item(Items.APPLES5_5386)), - WATERMELON_SEED(5321,52,8,48.5,54.5,0.0,47,PatchType.ALLOTMENT,Items.WATERMELON_5982,Item(Items.CURRY_LEAF_5970,10),NASTURTIUM_SEED), + // Allotments + POTATO_SEED(Items.POTATO_SEED_5318, "potato seed", 6, 4, 8.0, 9.0, 0.0, 1, PatchType.ALLOTMENT, Items.POTATO_1942,Item(Items.COMPOST_6032,2),MARIGOLD_SEED), + ONION_SEED(Items.ONION_SEED_5319, "onion seed", 13, 4, 9.5, 10.5,0.0, 5, PatchType.ALLOTMENT,Items.ONION_1957,Item(Items.POTATOES10_5438),MARIGOLD_SEED), + CABBAGE_SEED(Items.CABBAGE_SEED_5324, "cabbage seed", 20, 4, 10.0, 11.5, 0.0,7, PatchType.ALLOTMENT,Items.CABBAGE_1965,Item(Items.ONIONS10_5458),ROSEMARY_SEED), + TOMATO_SEED(Items.TOMATO_SEED_5322,"tomato seed",27,4,12.5,14.0,0.0,12,PatchType.ALLOTMENT,Items.TOMATO_1982,Item(Items.CABBAGES10_5478,2),MARIGOLD_SEED), + SWEETCORN_SEED(Items.SWEETCORN_SEED_5320,"sweetcorn seed",34,6,17.0,19.0,0.0,20,PatchType.ALLOTMENT,Items.SWEETCORN_5986,Item(Items.JUTE_FIBRE_5931,10),SCARECROW), + STRAWBERRY_SEED(Items.STRAWBERRY_SEED_5323,"strawberry seed",43,6,26.0,29.0,0.0,31,PatchType.ALLOTMENT,Items.STRAWBERRY_5504,Item(Items.APPLES5_5386)), + WATERMELON_SEED(Items.WATERMELON_SEED_5321,"watermelon seed",52,8,48.5,54.5,0.0,47,PatchType.ALLOTMENT,Items.WATERMELON_5982,Item(Items.CURRY_LEAF_5970,10),NASTURTIUM_SEED), - //Hops - BARLEY_SEED(5305,49,4,8.5,9.5,0.0,3,PatchType.HOPS_PATCH,Items.BARLEY_6006,Item(Items.COMPOST_6032,3)), - HAMMERSTONE_SEED(5307,4,4,9.0,10.0,0.0,4,PatchType.HOPS_PATCH,Items.HAMMERSTONE_HOPS_5994,Item(Items.MARIGOLDS_6010)), - ASGARNIAN_SEED(5308,11,5,10.9,12.0,0.0,8,PatchType.HOPS_PATCH,Items.ASGARNIAN_HOPS_5996,Item(Items.ONIONS10_5458)), - JUTE_SEED(5306,56,5,13.0,14.5,0.0,13,PatchType.HOPS_PATCH,Items.JUTE_FIBRE_5931,Item(Items.BARLEY_MALT_6008,6)), - YANILLIAN_SEED(5309,19,6,14.5,16.0,0.0,16,PatchType.HOPS_PATCH,Items.YANILLIAN_HOPS_5998,Item(Items.TOMATOES5_5968)), - KRANDORIAN_SEED(5310,28,7,17.5,19.5,0.0,21,PatchType.HOPS_PATCH,Items.KRANDORIAN_HOPS_6000,Item(Items.CABBAGES10_5478,3)), - WILDBLOOD_SEED(5311,38,8,23.0,26.0,0.0,28,PatchType.HOPS_PATCH,Items.WILDBLOOD_HOPS_6002,Item(Items.NASTURTIUMS_6012)), + // Hops + BARLEY_SEED(Items.BARLEY_SEED_5305,"barley seed",49,4,8.5,9.5,0.0,3,PatchType.HOPS_PATCH,Items.BARLEY_6006,Item(Items.COMPOST_6032,3)), + HAMMERSTONE_SEED(Items.HAMMERSTONE_SEED_5307,"Hammerstone hop seed",4,4,9.0,10.0,0.0,4,PatchType.HOPS_PATCH,Items.HAMMERSTONE_HOPS_5994,Item(Items.MARIGOLDS_6010)), + ASGARNIAN_SEED(Items.ASGARNIAN_SEED_5308,"Asgarnian hop seed",11,5,10.9,12.0,0.0,8,PatchType.HOPS_PATCH,Items.ASGARNIAN_HOPS_5996,Item(Items.ONIONS10_5458)), + JUTE_SEED(Items.JUTE_SEED_5306,"jute plant seed",56,5,13.0,14.5,0.0,13,PatchType.HOPS_PATCH,Items.JUTE_FIBRE_5931,Item(Items.BARLEY_MALT_6008,6)), + YANILLIAN_SEED(Items.YANILLIAN_SEED_5309,"Yanillian hop seed",19,6,14.5,16.0,0.0,16,PatchType.HOPS_PATCH,Items.YANILLIAN_HOPS_5998,Item(Items.TOMATOES5_5968)), + KRANDORIAN_SEED(Items.KRANDORIAN_SEED_5310,"Krandorian hop seed",28,7,17.5,19.5,0.0,21,PatchType.HOPS_PATCH,Items.KRANDORIAN_HOPS_6000,Item(Items.CABBAGES10_5478,3)), + WILDBLOOD_SEED(Items.WILDBLOOD_SEED_5311,"Wildblood hop seed",38,8,23.0,26.0,0.0,28,PatchType.HOPS_PATCH,Items.WILDBLOOD_HOPS_6002,Item(Items.NASTURTIUMS_6012)), - //Trees - OAK_SAPLING(5370,8,4,14.0,0.0,467.3,15,PatchType.TREE_PATCH,Items.OAK_ROOTS_6043,Item(Items.TOMATOES5_5968)), - WILLOW_SAPLING(5371,15,6,25.0,0.0,1456.5,30,PatchType.TREE_PATCH,Items.WILLOW_ROOTS_6045,Item(Items.APPLES5_5386)), - MAPLE_SAPLING(5372,24,8,45.0,0.0,3403.4,45,PatchType.TREE_PATCH,Items.MAPLE_ROOTS_6047,Item(Items.ORANGES5_5396)), - YEW_SAPLING(5373,35,10,81.0,0.0,7069.9,60,PatchType.TREE_PATCH,Items.YEW_ROOTS_6049,Item(Items.CACTUS_SPINE_6016,10)), - MAGIC_SAPLING(5374,48,12,145.5,0.0,13768.3,75,PatchType.TREE_PATCH,Items.MAGIC_ROOTS_6051,Item(Items.COCONUT_5974,25)), + // Trees + OAK_SAPLING(Items.OAK_SAPLING_5370,"oak sapling",8,4,14.0,0.0,467.3,15,PatchType.TREE_PATCH,Items.OAK_ROOTS_6043,Item(Items.TOMATOES5_5968)), + WILLOW_SAPLING(Items.WILLOW_SAPLING_5371,"willow sapling",15,6,25.0,0.0,1456.5,30,PatchType.TREE_PATCH,Items.WILLOW_ROOTS_6045,Item(Items.APPLES5_5386)), + MAPLE_SAPLING(Items.MAPLE_SAPLING_5372,"maple sapling",24,8,45.0,0.0,3403.4,45,PatchType.TREE_PATCH,Items.MAPLE_ROOTS_6047,Item(Items.ORANGES5_5396)), + YEW_SAPLING(Items.YEW_SAPLING_5373,"yew sapling",35,10,81.0,0.0,7069.9,60,PatchType.TREE_PATCH,Items.YEW_ROOTS_6049,Item(Items.CACTUS_SPINE_6016,10)), + MAGIC_SAPLING(Items.MAGIC_SAPLING_5374,"magic Tree sapling",48,12,145.5,0.0,13768.3,75,PatchType.TREE_PATCH,Items.MAGIC_ROOTS_6051,Item(Items.COCONUT_5974,25)), - //Fruit Trees - APPLE_SAPLING(5496,8,6,22.0,8.5,1199.5,27,PatchType.FRUIT_TREE_PATCH,Items.COOKING_APPLE_1955,Item(Items.SWEETCORN_5986,9)), - BANANA_SAPLING(5497,35,6,28.0,10.5,1750.5,33,PatchType.FRUIT_TREE_PATCH,Items.BANANA_1963,Item(Items.APPLES5_5386,4)), - ORANGE_SAPLING(5498,72,6,35.5,13.5,2470.2,39,PatchType.FRUIT_TREE_PATCH,Items.ORANGE_2108,Item(Items.STRAWBERRIES5_5406,3)), - CURRY_SAPLING(5499,99,6,40.0,15.0,2906.9,42,PatchType.FRUIT_TREE_PATCH,Items.CURRY_LEAF_5970,Item(Items.BANANAS5_5416,5)), - PINEAPPLE_SAPLING(5500,136,6,57.0,21.5,4605.7,51,PatchType.FRUIT_TREE_PATCH,Items.PINEAPPLE_2114,Item(Items.WATERMELON_5982,10)), - PAPAYA_SAPLING(5501,163,6,72.0,27.0,6146.4,57,PatchType.FRUIT_TREE_PATCH,Items.PAPAYA_FRUIT_5972,Item(Items.PINEAPPLE_2114,10)), - PALM_SAPLING(5502,200,6,110.5,41.5,10150.1,68,PatchType.FRUIT_TREE_PATCH,Items.COCONUT_5974,Item(Items.PAPAYA_FRUIT_5972,15)), + // Fruit Trees + APPLE_SAPLING(Items.APPLE_SAPLING_5496,"apple tree sapling",8,6,22.0,8.5,1199.5,27,PatchType.FRUIT_TREE_PATCH,Items.COOKING_APPLE_1955,Item(Items.SWEETCORN_5986,9)), + BANANA_SAPLING(Items.BANANA_SAPLING_5497,"banana tree sapling",35,6,28.0,10.5,1750.5,33,PatchType.FRUIT_TREE_PATCH,Items.BANANA_1963,Item(Items.APPLES5_5386,4)), + ORANGE_SAPLING(Items.ORANGE_SAPLING_5498,"orange tree sapling",72,6,35.5,13.5,2470.2,39,PatchType.FRUIT_TREE_PATCH,Items.ORANGE_2108,Item(Items.STRAWBERRIES5_5406,3)), + CURRY_SAPLING(Items.CURRY_SAPLING_5499,"curry tree sapling",99,6,40.0,15.0,2906.9,42,PatchType.FRUIT_TREE_PATCH,Items.CURRY_LEAF_5970,Item(Items.BANANAS5_5416,5)), + PINEAPPLE_SAPLING(Items.PINEAPPLE_SAPLING_5500,"pineapple plant",136,6,57.0,21.5,4605.7,51,PatchType.FRUIT_TREE_PATCH,Items.PINEAPPLE_2114,Item(Items.WATERMELON_5982,10)), + PAPAYA_SAPLING(Items.PAPAYA_SAPLING_5501,"papaya tree sapling",163,6,72.0,27.0,6146.4,57,PatchType.FRUIT_TREE_PATCH,Items.PAPAYA_FRUIT_5972,Item(Items.PINEAPPLE_2114,10)), + PALM_SAPLING(Items.PALM_SAPLING_5502,"palm tree sapling",200,6,110.5,41.5,10150.1,68,PatchType.FRUIT_TREE_PATCH,Items.COCONUT_5974,Item(Items.PAPAYA_FRUIT_5972,15)), - //Bushes - REDBERRY_SEED(5101,5,5,11.5,4.5,64.0,10,PatchType.BUSH_PATCH,Items.REDBERRIES_1951,Item(Items.CABBAGES10_5478,4)), - CADAVABERRY_SEED(5102,15,6,18.0,7.0,102.5,22,PatchType.BUSH_PATCH,Items.CADAVA_BERRIES_753,Item(Items.TOMATOES5_5968,3)), - DWELLBERRY_SEED(5103,26,27,31.5,12.0,177.5,36,PatchType.BUSH_PATCH,Items.DWELLBERRIES_2126,Item(Items.STRAWBERRIES5_5406,3)), - JANGERBERRY_SEED(5104,38,8,50.5,19.0,284.5,48,PatchType.BUSH_PATCH,Items.JANGERBERRIES_247,Item(Items.WATERMELON_5982,6)), - WHITEBERRY_SEED(5105,51,8,78.0,29.0,437.5,59,PatchType.BUSH_PATCH,Items.WHITE_BERRIES_239,null), - POISON_IVY_SEED(5106,197,8,120.0,45.0,675.0,70,PatchType.BUSH_PATCH,Items.POISON_IVY_BERRIES_6018,null), + // Bushes + REDBERRY_SEED(Items.REDBERRY_SEED_5101,"redberry bush seed",5,5,11.5,4.5,64.0,10,PatchType.BUSH_PATCH,Items.REDBERRIES_1951,Item(Items.CABBAGES10_5478,4)), + CADAVABERRY_SEED(Items.CADAVABERRY_SEED_5102,"cadavaberry bush seed",15,6,18.0,7.0,102.5,22,PatchType.BUSH_PATCH,Items.CADAVA_BERRIES_753,Item(Items.TOMATOES5_5968,3)), + DWELLBERRY_SEED(Items.DWELLBERRY_SEED_5103,"dwellberry bush seed",26,7,31.5,12.0,177.5,36,PatchType.BUSH_PATCH,Items.DWELLBERRIES_2126,Item(Items.STRAWBERRIES5_5406,3)), + JANGERBERRY_SEED(Items.JANGERBERRY_SEED_5104,"jangerberry bush seed",38,8,50.5,19.0,284.5,48,PatchType.BUSH_PATCH,Items.JANGERBERRIES_247,Item(Items.WATERMELON_5982,6)), + WHITEBERRY_SEED(Items.WHITEBERRY_SEED_5105,"whiteberry bush seed",51,8,78.0,29.0,437.5,59,PatchType.BUSH_PATCH,Items.WHITE_BERRIES_239,Item(Items.MUSHROOM_6004,8)), + POISON_IVY_SEED(Items.POISON_IVY_SEED_5106,"poison ivy bush seed",197,8,120.0,45.0,675.0,70,PatchType.BUSH_PATCH,Items.POISON_IVY_BERRIES_6018,null), - //Herbs - GUAM_SEED(5291,4,4,11.0,12.5,0.0,9,PatchType.HERB_PATCH,Items.GRIMY_GUAM_199), - MARRENTILL_SEED(5292,11,4,13.5,15.0,0.0,14,PatchType.HERB_PATCH,Items.GRIMY_MARRENTILL_201), - TARROMIN_SEED(5293,18,4,16.0,18.0,0.0,19,PatchType.HERB_PATCH,Items.GRIMY_TARROMIN_203), - HARRALANDER_SEED(5294,25,4,21.5,24.0,0.0,26,PatchType.HERB_PATCH,Items.GRIMY_HARRALANDER_205), - RANARR_SEED(5295,32,4,27.0,30.5,0.0,32,PatchType.HERB_PATCH,Items.GRIMY_RANARR_207), - AVANTOE_SEED(5298,39,4,54.5,61.5,0.0,50,PatchType.HERB_PATCH,Items.GRIMY_AVANTOE_211), - TOADFLAX_SEED(5296,46,4,34.0,38.5,0.0,38,PatchType.HERB_PATCH,Items.GRIMY_TOADFLAX_3049), - IRIT_SEED(5297,53,4,43.0,48.5,0.0,44,PatchType.HERB_PATCH,Items.GRIMY_IRIT_209), - KWUARM_SEED(5299,68,4,69.0,78.0,0.0,56,PatchType.HERB_PATCH,Items.GRIMY_KWUARM_213), - SNAPDRAGON_SEED(5300,75,4,87.5,98.5,0.0,62,PatchType.HERB_PATCH,Items.GRIMY_SNAPDRAGON_3051), - CADANTINE_SEED(5301,82,4,106.5,120.0,0.0,67,PatchType.HERB_PATCH,Items.GRIMY_CADANTINE_215), - LANTADYME_SEED(5302,89,4,134.5,151.5,0.0,73,PatchType.HERB_PATCH,Items.GRIMY_LANTADYME_2485), - DWARF_WEED_SEED(5303,96,4,170.5,192.0,0.0,79,PatchType.HERB_PATCH,Items.GRIMY_DWARF_WEED_217), - TORSTOL_SEED(5304,103,4,199.5,224.5,0.0,85,PatchType.HERB_PATCH,Items.GRIMY_TORSTOL_219), - GOUT_TUBER(6311,192,4,105.0,45.0,0.0,29,PatchType.HERB_PATCH,Items.GOUTWEED_3261), - SPIRIT_WEED_SEED(12176, 204, 4, 32.0, 36.0, 0.0, 36, PatchType.HERB_PATCH, Items.GRIMY_SPIRIT_WEED_12174), + // Herbs + GUAM_SEED(Items.GUAM_SEED_5291,"guam seed",4,4,11.0,12.5,0.0,9,PatchType.HERB_PATCH,Items.GRIMY_GUAM_199), + MARRENTILL_SEED(Items.MARRENTILL_SEED_5292,"marrentill seed",11,4,13.5,15.0,0.0,14,PatchType.HERB_PATCH,Items.GRIMY_MARRENTILL_201), + TARROMIN_SEED(Items.TARROMIN_SEED_5293,"tarromin seed",18,4,16.0,18.0,0.0,19,PatchType.HERB_PATCH,Items.GRIMY_TARROMIN_203), + HARRALANDER_SEED(Items.HARRALANDER_SEED_5294,"harralander seed",25,4,21.5,24.0,0.0,26,PatchType.HERB_PATCH,Items.GRIMY_HARRALANDER_205), + RANARR_SEED(Items.RANARR_SEED_5295,"ranarr seed",32,4,27.0,30.5,0.0,32,PatchType.HERB_PATCH,Items.GRIMY_RANARR_207), + AVANTOE_SEED(Items.AVANTOE_SEED_5298,"avantoe seed",39,4,54.5,61.5,0.0,50,PatchType.HERB_PATCH,Items.GRIMY_AVANTOE_211), + TOADFLAX_SEED(Items.TOADFLAX_SEED_5296,"toadflax seed",46,4,34.0,38.5,0.0,38,PatchType.HERB_PATCH,Items.GRIMY_TOADFLAX_3049), + IRIT_SEED(Items.IRIT_SEED_5297,"irit seed",53,4,43.0,48.5,0.0,44,PatchType.HERB_PATCH,Items.GRIMY_IRIT_209), + KWUARM_SEED(Items.KWUARM_SEED_5299,"kwuarm seed",68,4,69.0,78.0,0.0,56,PatchType.HERB_PATCH,Items.GRIMY_KWUARM_213), + SNAPDRAGON_SEED(Items.SNAPDRAGON_SEED_5300,"snapdragon seed",75,4,87.5,98.5,0.0,62,PatchType.HERB_PATCH,Items.GRIMY_SNAPDRAGON_3051), + CADANTINE_SEED(Items.CADANTINE_SEED_5301,"cadantine seed",82,4,106.5,120.0,0.0,67,PatchType.HERB_PATCH,Items.GRIMY_CADANTINE_215), + LANTADYME_SEED(Items.LANTADYME_SEED_5302,"lantadyme seed",89,4,134.5,151.5,0.0,73,PatchType.HERB_PATCH,Items.GRIMY_LANTADYME_2485), + DWARF_WEED_SEED(Items.DWARF_WEED_SEED_5303,"dwarf weed seed",96,4,170.5,192.0,0.0,79,PatchType.HERB_PATCH,Items.GRIMY_DWARF_WEED_217), + TORSTOL_SEED(Items.TORSTOL_SEED_5304,"torstol seed",103,4,199.5,224.5,0.0,85,PatchType.HERB_PATCH,Items.GRIMY_TORSTOL_219), + GOUT_TUBER(Items.GOUT_TUBER_6311,"gout tuber",192,4,105.0,45.0,0.0,29,PatchType.HERB_PATCH,Items.GOUTWEED_3261), + SPIRIT_WEED_SEED(Items.SPIRIT_WEED_SEED_12176,"spirit weed seed", 204, 4, 32.0, 36.0, 0.0, 36, PatchType.HERB_PATCH, Items.GRIMY_SPIRIT_WEED_12174), - //Other - BELLADONNA_SEED(5281, 4, 4, 91.0, 128.0, 0.0, 63, PatchType.BELLADONNA_PATCH, Items.CAVE_NIGHTSHADE_2398), - MUSHROOM_SPORE(Items.MUSHROOM_SPORE_5282, 6, 7, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM_PATCH, Items.MUSHROOM_6004), - CACTUS_SEED(Items.CACTUS_SEED_5280, 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS_PATCH, Items.CACTUS_SPINE_6016), - EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP_PATCH, Items.EVIL_TURNIP_12134) + // Special + BELLADONNA_SEED(Items.BELLADONNA_SEED_5281, "belladonna seed", 4, 4, 91.0, 128.0, 0.0, 63, PatchType.BELLADONNA_PATCH, Items.CAVE_NIGHTSHADE_2398), + MUSHROOM_SPORE(Items.MUSHROOM_SPORE_5282, "mushroom spore", 4, 6, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM_PATCH, Items.MUSHROOM_6004), + CACTUS_SEED(Items.CACTUS_SEED_5280, "cactus seed", 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS_PATCH, Items.CACTUS_SPINE_6016), + EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, "evil turnip seed", 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP_PATCH, Items.EVIL_TURNIP_12134), + SPIRIT_SAPLING(Items.SPIRIT_SAPLING_5375, "spirit tree sapling", 8, 12, 199.5, 0.0, 19301.0, 83, PatchType.SPIRIT_TREE_PATCH, 0, Item(Items.MONKEY_NUTS_4012, 5), null, Item(Items.MONKEY_BAR_4014, 1), Item(Items.GROUND_TOOTH_9082, 1) ) ; - constructor(itemID: Int, value: Int, stages: Int, plantingXP: Double, harvestXP: Double, checkHealthXP: Double, requiredLevel: Int, applicablePatch: PatchType, harvestItem: Int, protectionFlower: Plantable) - : this(itemID,value,stages,plantingXP,harvestXP,checkHealthXP,requiredLevel,applicablePatch,harvestItem,null,protectionFlower) + constructor(itemID: Int, displayName: String, value: Int, stages: Int, plantingXP: Double, harvestXP: Double, checkHealthXP: Double, requiredLevel: Int, applicablePatch: PatchType, harvestItem: Int, protectionFlower: Plantable) + : this(itemID,displayName,value,stages,plantingXP,harvestXP,checkHealthXP,requiredLevel,applicablePatch,harvestItem,null,protectionFlower) companion object { @JvmField val plantables = values().map { it.itemID to it }.toMap() diff --git a/Server/src/main/content/global/skill/farming/Stump.kt b/Server/src/main/content/global/skill/farming/Stump.kt new file mode 100644 index 000000000..60c9bab8a --- /dev/null +++ b/Server/src/main/content/global/skill/farming/Stump.kt @@ -0,0 +1,5 @@ +package content.global.skill.farming + +class Stump(val varbit: Int, val TTL: Long) { + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt b/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt index 980d20b07..29e0d719d 100644 --- a/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt +++ b/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt @@ -69,7 +69,7 @@ class ToolLeprechaunInterface : InterfaceListener { setHasMagicSecateurs(player,false) } } else { - sendMessage(player, "You already have one of those stored.") + sendMessage(player, "You cannot store more than one pair of secateurs in here.") } } 22 -> { @@ -80,7 +80,7 @@ class ToolLeprechaunInterface : InterfaceListener { removeItem(player, can) setWateringCan(player,can) } else { - sendMessage(player, "You already have one of those stored.") + sendMessage(player, "You cannot store more than one watering can in here.") } } 23 -> doDeposit(player, Items.GARDENING_TROWEL_5325, ::setHasGardeningTrowel, ::hasGardeningTrowel) @@ -119,7 +119,15 @@ class ToolLeprechaunInterface : InterfaceListener { depositMethod.invoke(player, true) removeItem(player, item) } else { - sendMessage(player, "You already have one of those stored.") + val itemName = when (item) { + // secateurs and watering cans are handled separately + Items.RAKE_5341 -> "rake" + Items.SEED_DIBBER_5343 -> "dibber" + Items.SPADE_952 -> "spade" + Items.GARDENING_TROWEL_5325 -> "trowel" + else -> getItemName(item).lowercase() + } + sendMessage(player, "You cannot store more than one $itemName in here.") } } diff --git a/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt index d71a7c409..eb0c5de69 100644 --- a/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt +++ b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt @@ -5,7 +5,6 @@ import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import core.game.interaction.IntType import core.game.interaction.InteractionListener @@ -13,6 +12,7 @@ import core.game.interaction.QueueStrength import core.tools.StringUtils import core.tools.prependArticle import org.rs09.consts.Sounds +import content.data.Quests class UseWithPatchHandler : InteractionListener { val RAKE = Items.RAKE_5341 @@ -21,11 +21,14 @@ class UseWithPatchHandler : InteractionListener { val SECATEURS = Items.SECATEURS_5329 val MAGIC_SECATEURS = Items.MAGIC_SECATEURS_7409 val TROWEL = Items.GARDENING_TROWEL_5325 - val pourBucketAnim = Animation(2283) - val wateringCanAnim = Animation(2293) - val plantCureAnim = Animation(2288) - val secateursTreeAnim = Animation(2277) - val magicSecateursTreeAnim = Animation(3340) + val spadeDigAnim = getAnimation(830) + val trowelDigAnim = getAnimation(2272) + val pourBucketAnim = getAnimation(2283) + val seedDibberAnim = getAnimation(2291) + val wateringCanAnim = getAnimation(2293) + val plantCureAnim = getAnimation(2288) + val secateursTreeAnim = getAnimation(2277) + val magicSecateursTreeAnim = getAnimation(3340) @JvmField val allowedNodes = ArrayList() @@ -35,9 +38,10 @@ class UseWithPatchHandler : InteractionListener { onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *FarmingPatch.patchNodes.toIntArray()) { player, used, with -> val patch = FarmingPatch.forObject(with.asScenery()) ?: return@onUseWith true val usedItem = used.asItem() + val trimmablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH) if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) { - if (!hasRequirement(player, "My Arm's Big Adventure")) + if (!hasRequirement(player, Quests.MY_ARMS_BIG_ADVENTURE)) return@onUseWith true } @@ -46,7 +50,7 @@ class UseWithPatchHandler : InteractionListener { RAKE -> PatchRaker.rake(player,patch) SEED_DIBBER -> sendMessage(player, "I should plant a seed, not the seed dibber.") SPADE -> { - val anim = getAnimation(830) + val anim = spadeDigAnim val p = patch.getPatchFor(player) if (p.isDead) { sendMessage(player, "You start digging the farming patch...") @@ -76,7 +80,7 @@ class UseWithPatchHandler : InteractionListener { } SECATEURS, MAGIC_SECATEURS -> { val p = patch.getPatchFor(player) - if (patch.type == PatchType.TREE_PATCH) { + if (patch.type in trimmablePatches) { if (p.isDiseased && !p.isDead) { submitIndividualPulse(player, object: Pulse() { override fun pulse(): Boolean { @@ -112,7 +116,7 @@ class UseWithPatchHandler : InteractionListener { return@onUseWith true } - val anim = Animation(2272) + val anim = trowelDigAnim submitIndividualPulse(player, object : Pulse(anim.duration) { override fun pulse(): Boolean { @@ -128,7 +132,7 @@ class UseWithPatchHandler : InteractionListener { Items.PLANT_CURE_6036 -> { val p = patch.getPatchFor(player) val patchName = p.patch.type.displayName() - if (p.isDiseased && !p.isDead) { + if (p.isDiseased && !p.isDead && patch.type !in trimmablePatches) { sendMessage(player, "You treat the $patchName with the plant cure.") queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> when (stage) { @@ -156,15 +160,21 @@ class UseWithPatchHandler : InteractionListener { Items.WATERING_CAN_5331,Items.WATERING_CAN1_5333,Items.WATERING_CAN2_5334,Items.WATERING_CAN3_5335,Items.WATERING_CAN4_5336,Items.WATERING_CAN5_5337,Items.WATERING_CAN6_5338,Items.WATERING_CAN7_5339,Items.WATERING_CAN8_5340 -> { val p = patch.getPatchFor(player) val t = p.patch.type - if (p.isWatered || p.isEmptyAndWeeded() || p.isGrown() || p.plantable == Plantable.SCARECROW) { - sendMessage(player, "This patch doesn't need watering.") - } else if (t == PatchType.ALLOTMENT || t == PatchType.FLOWER_PATCH || t == PatchType.HOPS_PATCH) { + if (t == PatchType.ALLOTMENT || t == PatchType.FLOWER_PATCH || t == PatchType.HOPS_PATCH) { submitIndividualPulse(player, object : Pulse() { override fun pulse(): Boolean { - if (p.isWeedy()) { + if (p.isWeedy() || p.isEmptyAndWeeded()) { sendMessage(player, "You should grow something first.") return true } + if (p.isWatered || p.isGrown() || p.plantable == Plantable.SCARECROW) { + sendMessage(player, "This patch doesn't need watering.") + return true + } + if (p.isDiseased || p.isDead) { + sendMessage(player, "Water isn't going to cure that!") + return true + } if (usedItem.id == Items.WATERING_CAN_5331) { sendMessage(player, "You need to fill the watering can first.") return true @@ -178,6 +188,8 @@ class UseWithPatchHandler : InteractionListener { return true } }) + } else { + sendMessage(player, "This patch doesn't need watering.") } } @@ -212,9 +224,9 @@ class UseWithPatchHandler : InteractionListener { val plantable = Plantable.forItemID(usedItem.id) ?: return@onUseWith false if (plantable.applicablePatch != patch.type) { - val seedNamePlural = StringUtils.plusS(plantable.name.replace("_", " ").lowercase()) + val plantableNamePlural = StringUtils.plusS(plantable.displayName) val patchType = if (plantable.applicablePatch == PatchType.ALLOTMENT) "a vegetable patch" else prependArticle(plantable.applicablePatch.displayName()) - sendMessage(player, "You can only plant $seedNamePlural in $patchType.") + sendMessage(player, "You can only plant $plantableNamePlural in $patchType.") return@onUseWith true } @@ -224,7 +236,7 @@ class UseWithPatchHandler : InteractionListener { } val p = patch.getPatchFor(player) - if (p.getCurrentState() < 3 && p.isWeedy() && plantable != Plantable.SCARECROW) { + if (p.getCurrentState() < 3 && p.isWeedy()) { sendMessage(player, "This patch needs weeding first.") return@onUseWith true } else if (p.getCurrentState() > 3) { @@ -232,66 +244,83 @@ class UseWithPatchHandler : InteractionListener { return@onUseWith true } - val plantItem = - if (patch.type == PatchType.ALLOTMENT) Item(plantable.itemID,3) else if (patch.type == PatchType.HOPS_PATCH) { - if (plantable == Plantable.JUTE_SEED) Item(plantable.itemID,3) else Item(plantable.itemID,4) - } else { - Item(plantable.itemID,1) - } - - if (patch.type == PatchType.ALLOTMENT) { - if (!player.inventory.containsItem(plantItem)) { - sendMessage(player, "You need 3 seeds to plant an allotment patch.") - return@onUseWith true - } + val plantItem = when (patch.type) { + PatchType.ALLOTMENT -> Item(plantable.itemID, 3) + PatchType.HOPS_PATCH -> if (plantable == Plantable.JUTE_SEED) Item(plantable.itemID, 3) else Item(plantable.itemID, 4) + else -> Item(plantable.itemID,1) } - if (patch.type != PatchType.FRUIT_TREE_PATCH && patch.type != PatchType.TREE_PATCH) { - if (!inInventory(player, Items.SEED_DIBBER_5343)) { - sendMessage(player, "You need a seed dibber to plant that.") - return@onUseWith true - } - } else { - if (!inInventory(player, Items.SPADE_952) && plantable != Plantable.SCARECROW) { - sendMessage(player, "You need a spade to plant that.") - return@onUseWith true - } + + if (!player.inventory.containsItem(plantItem)) { + val seedPlural = if (plantItem.amount == 1) "seed" else "seeds" + sendMessage(player, "You need ${plantItem.amount} $seedPlural to plant ${prependArticle(patch.type.displayName())}.") + return@onUseWith true } - player.lock() - if (removeItem(player, plantItem)) { - if (plantable != Plantable.SCARECROW) { - animate(player, 2291) - playAudio(player, Sounds.FARMING_DIBBING_2432) - } - val delay = if (plantable == Plantable.SCARECROW) 0 else 3 - submitIndividualPulse(player, object : Pulse(delay) { - override fun pulse(): Boolean { - if (plantable == Plantable.JUTE_SEED && patch == FarmingPatch.MCGRUBOR_HOPS && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 0, 7)) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 7) - } - p.plant(plantable) - rewardXP(player, Skills.FARMING, plantable.plantingXP) - p.setNewHarvestAmount() - if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH) { - addItem(player, Items.PLANT_POT_5350) - } - val itemAmount = if (plantItem.amount == 1) "a" else plantItem.amount - val itemName = if (plantItem.amount == 1) getItemName(plantItem.id).lowercase() else StringUtils.plusS(getItemName(plantItem.id).lowercase()) - val patchName = p.patch.type.displayName() - if (plantable == Plantable.SCARECROW) { - sendMessage(player, "You place the scarecrow in the $patchName.") - } else { - sendMessage(player, "You plant $itemAmount $itemName in the $patchName.") - } + /*if (patch == FarmingPatch.WILDERNESS_FLOWER && plantable != Plantable.LIMPWURT_SEED){ + return@onUseWith true + }*/ - player.unlock() - return true + val requiredItem = when (patch.type) { + PatchType.TREE_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH -> Items.SPADE_952 + PatchType.FLOWER_PATCH -> if (plantable == Plantable.SCARECROW) null else Items.SEED_DIBBER_5343 + else -> Items.SEED_DIBBER_5343 + } + if (requiredItem != null && !inInventory(player, requiredItem)) { + sendDialogue(player, "You need ${prependArticle(requiredItem.asItem().name.lowercase())} to plant that.") + return@onUseWith true + } + val spiritTreePlanted = (getVarbit(player, 720) > 3 || getVarbit(player, 722) > 3 || getVarbit(player, 724) > 3) + if (plantable == Plantable.SPIRIT_SAPLING && spiritTreePlanted){ + sendDialogue(player, "You already have a spirit tree. You can't plant another one.") + return@onUseWith true + } + + queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> + when (stage) { + 0 -> { + val (anim, sound) = when (requiredItem) { + Items.SPADE_952 -> Pair(spadeDigAnim, Sounds.DIGSPADE_1470) + else -> Pair(seedDibberAnim,Sounds.FARMING_DIBBING_2432) + } + animate(player, anim) + playAudio(player, sound) + val delay = if (patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.SPIRIT_TREE_PATCH || plantable == Plantable.SCARECROW) 3 else 0 + return@queueScript delayScript(player,anim.duration + delay) } - }) + 1 -> { + if (removeItem(player, plantItem)) { + if (plantable == Plantable.JUTE_SEED && patch == FarmingPatch.MCGRUBOR_HOPS && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 0, 7)) { + player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 7) + } + p.plant(plantable) + rewardXP(player, Skills.FARMING, plantable.plantingXP) + p.setNewHarvestAmount() + if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH || p.patch.type == PatchType.SPIRIT_TREE_PATCH) { + addItem(player, Items.PLANT_POT_5350) + } + + val itemAmount = + if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH || p.patch.type == PatchType.SPIRIT_TREE_PATCH) "the" + else if (plantItem.amount == 1) "a" + else plantItem.amount + val itemName = + if (plantItem.amount == 1) plantable.displayName else StringUtils.plusS( + plantable.displayName + ) + val patchName = p.patch.type.displayName() + if (plantable == Plantable.SCARECROW) { + sendMessage(player, "You place the scarecrow in the $patchName.") + } else { + sendMessage(player, "You plant $itemAmount $itemName in the $patchName.") + } + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } } } } - return@onUseWith true } } diff --git a/Server/src/main/content/global/skill/farming/timers/CropGrowth.kt b/Server/src/main/content/global/skill/farming/timers/CropGrowth.kt index ae3765a99..3cc14cd55 100644 --- a/Server/src/main/content/global/skill/farming/timers/CropGrowth.kt +++ b/Server/src/main/content/global/skill/farming/timers/CropGrowth.kt @@ -41,7 +41,7 @@ class CropGrowth : PersistTimer (500, "farming:crops", isSoft = true) { //Another more extreme example is if you planted something at 10:31 that takes 5 minutes to grow. 10:35 comes around, it hasn't been 5 minutes, so it doesn't grow, meaning //it actually grows at 10:40, an extra 4 minutes. //this code makes it so crops planted both at 10:31 and 10:34 grow at 10:35 if they are supposed to take 5 minutes for each stage. - if(patch.nextGrowth < (System.currentTimeMillis() + 240_000L) && !patch.isDead){ + if(patch.nextGrowth < (System.currentTimeMillis() + 240_000L) && !patch.isDead && !patch.isChoppedFruitTree()){ patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong()) patch.update() } @@ -57,13 +57,21 @@ class CropGrowth : PersistTimer (500, "farming:crops", isSoft = true) { private fun runOfflineCatchupLogic() { for ((_, patch) in patchMap) { val type = patch.patch.type - val shouldPlayCatchup = !patch.isGrown() || (type == PatchType.BUSH_PATCH && patch.getFruitOrBerryCount() < 4) || (type == PatchType.FRUIT_TREE_PATCH && patch.getFruitOrBerryCount() < 6) - if(shouldPlayCatchup && patch.plantable != null && !patch.isDead){ - var stagesToSimulate = if (!patch.isGrown()) patch.plantable!!.stages - patch.currentGrowthStage else 0 - if (type == PatchType.BUSH_PATCH) - stagesToSimulate += Math.min(4, 4 - patch.getFruitOrBerryCount()) - if (type == PatchType.FRUIT_TREE_PATCH) - stagesToSimulate += Math.min(6, 6 - patch.getFruitOrBerryCount()) + val shouldPlayCatchup = !patch.isGrown() || (type == PatchType.BUSH_PATCH && patch.getFruitOrBerryCount() < 4) || (type == PatchType.FRUIT_TREE_PATCH && patch.getFruitOrBerryCount() < 6) || (patch.plantable == Plantable.WILLOW_SAPLING && patch.harvestAmt < 6) + if (shouldPlayCatchup && !patch.isDead && !patch.isChoppedFruitTree()) { + var stagesToSimulate = if (!patch.isGrown()) { + if (patch.isWeedy() || patch.isEmptyAndWeeded()) patch.currentGrowthStage % 4 + else patch.plantable!!.stages - patch.currentGrowthStage + } else 0 + + if (patch.plantable != null) { + if (type == PatchType.BUSH_PATCH) + stagesToSimulate += Math.min(4, 4 - patch.getFruitOrBerryCount()) + if (type == PatchType.FRUIT_TREE_PATCH) + stagesToSimulate += Math.min(6, 6 - patch.getFruitOrBerryCount()) + if (patch.plantable == Plantable.WILLOW_SAPLING) + stagesToSimulate += Math.min(6, 6 - patch.harvestAmt) + } val nowTime = System.currentTimeMillis() var simulatedTime = patch.nextGrowth @@ -77,8 +85,8 @@ class CropGrowth : PersistTimer (500, "farming:crops", isSoft = true) { } } - fun getPatch(patch: FarmingPatch): Patch { - return patchMap[patch] ?: (Patch(player,patch).also { patchMap[patch] = it }) + fun getPatch(patch: FarmingPatch, addPatch: Boolean ): Patch { + return patchMap[patch] ?: (Patch(player,patch).also { if (addPatch) patchMap[patch] = it }) } fun getPatches(): MutableCollection{ diff --git a/Server/src/main/content/global/skill/farming/timers/StumpGrowth.kt b/Server/src/main/content/global/skill/farming/timers/StumpGrowth.kt new file mode 100644 index 000000000..616bde43a --- /dev/null +++ b/Server/src/main/content/global/skill/farming/timers/StumpGrowth.kt @@ -0,0 +1,59 @@ +package content.global.skill.farming.timers + +import core.game.node.entity.Entity +import core.game.system.timer.* +import core.game.node.entity.player.Player +import content.global.skill.farming.* +import core.tools.ticksToSeconds +import java.util.concurrent.TimeUnit +import org.json.simple.* + +class StumpGrowth : PersistTimer (1, "farming:stump", isSoft = true) { + val stumps = ArrayList() + lateinit var player: Player + + fun addStump(varbit: Int, ttl: Int){ + stumps.add( + Stump( + varbit, + System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(ticksToSeconds(ttl).toLong()) + ) + ) + } + + override fun onRegister (entity: Entity) { + player = (entity as? Player)!! + } + + override fun run (entity: Entity) : Boolean { + val removeList = ArrayList() + for (stump in stumps) { + if (System.currentTimeMillis() > stump.TTL) { + FarmingPatch.patches[stump.varbit]?.getPatchFor(player)?.regrowIfTreeStump() + removeList.add(stump) + } + } + stumps.removeAll(removeList) + return stumps.isNotEmpty() + } + + override fun save(root: JSONObject, entity: Entity) { + val stumpArray = JSONArray() + for(s in stumps){ + val seed = JSONObject() + seed["patch"] = s.varbit + seed["ttl"] = s.TTL + stumpArray.add(seed) + } + root.put("stumps",stumpArray) + } + + override fun parse(root: JSONObject, entity: Entity) { + (root["stumps"] as JSONArray).forEach { + val s = it as JSONObject + val id = s["patch"].toString().toInt() + val ttl = s["ttl"].toString().toLong() + stumps.add(Stump(id,ttl)) + } + } +} diff --git a/Server/src/main/content/global/skill/fishing/Fish.kt b/Server/src/main/content/global/skill/fishing/Fish.kt index 46b73bee2..8ccc67f24 100644 --- a/Server/src/main/content/global/skill/fishing/Fish.kt +++ b/Server/src/main/content/global/skill/fishing/Fish.kt @@ -24,7 +24,7 @@ enum class Fish(val id: Int, val level: Int, val experience: Double, val lowChan LOBSTER(Items.RAW_LOBSTER_377, 40, 90.0, 0.16, 0.375), BASS(Items.RAW_BASS_363, 46, 100.0, 0.078, 0.16), SWORDFISH(Items.RAW_SWORDFISH_371, 50, 100.0, 0.105, 0.191), - LAVA_EEL(Items.RAW_LAVA_EEL_2148, 53, 30.0, 0.227, 0.379), + LAVA_EEL(Items.RAW_LAVA_EEL_2148, 53, 60.0, 0.227, 0.379), MONKFISH(Items.RAW_MONKFISH_7944, 62, 120.0, 0.293, 0.356), KARAMBWAN(Items.RAW_KARAMBWAN_3142, 65, 105.0, 0.414, 0.629), SHARK(Items.RAW_SHARK_383, 76, 110.0, 0.121, 0.16), diff --git a/Server/src/main/content/global/skill/fishing/FishingOption.kt b/Server/src/main/content/global/skill/fishing/FishingOption.kt index 2cf25e5e9..b75ccf9b4 100644 --- a/Server/src/main/content/global/skill/fishing/FishingOption.kt +++ b/Server/src/main/content/global/skill/fishing/FishingOption.kt @@ -20,7 +20,8 @@ enum class FishingOption(val tool: Int, val level: Int, val animation: Animation SHARK_HARPOON(Items.HARPOON_311, 76, Animation(618), null, "harpoon", Fish.SHARK), MONKFISH_NET(Items.SMALL_FISHING_NET_303, 62, Animation(621), null, "net", Fish.MONKFISH), MORTMYRE_ROD(Items.FISHING_ROD_307, 5, Animation(622), intArrayOf(Items.FISHING_BAIT_313), "bait", Fish.SLIMY_EEL), - LUMBDSWAMP_ROD(Items.FISHING_ROD_307, 5, Animation(622), intArrayOf(Items.FISHING_BAIT_313), "bait", Fish.SLIMY_EEL, Fish.FROG_SPAWN), + LUMBDSWAMP_ROD(Items.FISHING_ROD_307, 5, Animation(622), intArrayOf(Items.FISHING_BAIT_313), "bait", Fish.SLIMY_EEL, Fish.CAVE_EEL), + LUMBDSWAMP_NET(Items.SMALL_FISHING_NET_303, 1, Animation(621), null, "net", Fish.FROG_SPAWN), KBWANJI_NET(Items.SMALL_FISHING_NET_303, 5, Animation(621), null, "net", Fish.KARAMBWANJI), KARAMBWAN_VES(Items.KARAMBWAN_VESSEL_3157, 65, Animation(1193), intArrayOf((Items.RAW_KARAMBWANJI_3150)), "fish", Fish.KARAMBWAN), OILY_FISHING_ROD(Items.OILY_FISHING_ROD_1585, 53, Animation(622), intArrayOf(Items.FISHING_BAIT_313), "bait", Fish.LAVA_EEL); diff --git a/Server/src/main/content/global/skill/fishing/FishingSpot.kt b/Server/src/main/content/global/skill/fishing/FishingSpot.kt index cbfda947b..e60dcb98d 100644 --- a/Server/src/main/content/global/skill/fishing/FishingSpot.kt +++ b/Server/src/main/content/global/skill/fishing/FishingSpot.kt @@ -109,6 +109,7 @@ enum class FishingSpot(val ids: IntArray, vararg val options: FishingOption) { NPCs.FISHING_SPOT_2067, NPCs.FISHING_SPOT_2068 ), + FishingOption.LUMBDSWAMP_NET, FishingOption.LUMBDSWAMP_ROD ), SPOT_KBWANJI( diff --git a/Server/src/main/content/global/skill/fletching/AchievementDiaryAttributeKeys.kt b/Server/src/main/content/global/skill/fletching/AchievementDiaryAttributeKeys.kt new file mode 100644 index 000000000..8e3d7fe33 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/AchievementDiaryAttributeKeys.kt @@ -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" +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/BoltCreatePlugin.java b/Server/src/main/content/global/skill/fletching/BoltCreatePlugin.java deleted file mode 100644 index 711e9b626..000000000 --- a/Server/src/main/content/global/skill/fletching/BoltCreatePlugin.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import core.game.dialogue.SkillDialogueHandler; -import core.game.dialogue.SkillDialogueHandler.SkillDialogue; -import core.game.node.entity.skill.fletching.items.bolts.Bolt; -import core.game.node.entity.skill.fletching.items.bolts.BoltPulse; -import org.crandor.game.interaction.NodeUsageEvent; -import org.crandor.game.interaction.UseWithHandler; -import org.crandor.game.node.entity.player.Player; -import org.crandor.net.packet.PacketRepository; -import org.crandor.net.packet.context.ChildPositionContext; -import org.crandor.net.packet.out.RepositionChild; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.plugin.Plugin; - -*/ -/** - * Represents the bolt creating plugin. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class BoltCreatePlugin extends UseWithHandler { - - */ -/** - * Constructs a new {@code BoltCreatePlugin} {@code Object}. - *//* - - public BoltCreatePlugin() { - super(314); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (Bolt bolt : Bolt.values()) { - addHandler(bolt.getItem().getId(), ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - final Player player = event.getPlayer(); - - } - -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/BoltGemPlugin.java b/Server/src/main/content/global/skill/fletching/BoltGemPlugin.java deleted file mode 100644 index 70c38b30a..000000000 --- a/Server/src/main/content/global/skill/fletching/BoltGemPlugin.java +++ /dev/null @@ -1,45 +0,0 @@ -//package core.game.node.entity.skill.fletching; -// -//import core.game.dialogue.SkillDialogueHandler; -//import core.game.dialogue.SkillDialogueHandler.SkillDialogue; -//import core.game.node.entity.skill.fletching.items.gem.GemBolt; -//import core.game.node.entity.skill.fletching.items.gem.GemBoltPulse; -//import org.crandor.game.interaction.NodeUsageEvent; -//import org.crandor.game.interaction.UseWithHandler; -//import org.crandor.game.node.entity.player.Player; -//import org.crandor.net.packet.PacketRepository; -//import org.crandor.net.packet.context.ChildPositionContext; -//import org.crandor.net.packet.out.RepositionChild; -//import org.crandor.plugin.InitializablePlugin; -//import org.crandor.plugin.Plugin; -// -///** -// * Represents the plugin used to handle gem bolt making. -// * @author 'Vexia -// * @version 1.0 -// */ -//@InitializablePlugin -//public class BoltGemPlugin extends UseWithHandler { -// -// /** -// * Constructs a new {@code BoltGemPlugin} {@code Object}. -// */ -// public BoltGemPlugin() { -// super(45, 46, 9187, 9188, 9189, 9190, 9191, 9192, 9193, 9194); -// } -// -// @Override -// public Plugin newInstance(Object arg) throws Throwable { -// for (GemBolt bolt : GemBolt.values()) { -// addHandler(bolt.getBase().getId(), ITEM_TYPE, this); -// } -// return this; -// } -// -// @Override -// public boolean handle(final NodeUsageEvent event) { -// final Player player = event.getPlayer(); -// -// } -// -//} diff --git a/Server/src/main/content/global/skill/fletching/DartCreatePlugin.java b/Server/src/main/content/global/skill/fletching/DartCreatePlugin.java deleted file mode 100644 index c43104095..000000000 --- a/Server/src/main/content/global/skill/fletching/DartCreatePlugin.java +++ /dev/null @@ -1,49 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import core.game.node.entity.skill.fletching.items.darts.Dart; -import org.crandor.game.interaction.NodeUsageEvent; -import org.crandor.game.interaction.UseWithHandler; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.plugin.Plugin; -import core.game.content.quest.touristrap.TouristTrap; - -*/ -/** - * Represents the plugin used to create a dart. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class DartCreatePlugin extends UseWithHandler { - - */ -/** - * Constructs a new {@code DartCreatePlugin} {@code Object}. - *//* - - public DartCreatePlugin() { - super(314); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (Dart dart : Dart.values()) { - addHandler(dart.getItem().getId(), ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - if (event.getPlayer().getQuestRepository().getQuest(TouristTrap.NAME).getStage(event.getPlayer()) < 60) { - event.getPlayer().getPacketDispatch().sendMessage("You need to start Tourist Trap in order to do this."); - return true; - } - event.getPlayer().getDialogueInterpreter().open(328933, event.getUsedItem(), event.getBaseItem()); - return true; - } - -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/Feathers.kt b/Server/src/main/content/global/skill/fletching/Feathers.kt new file mode 100644 index 000000000..f76b00a72 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/Feathers.kt @@ -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 +} diff --git a/Server/src/main/content/global/skill/fletching/FletchItem.java b/Server/src/main/content/global/skill/fletching/FletchItem.java deleted file mode 100644 index fee42d6c1..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchItem.java +++ /dev/null @@ -1,137 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents a fletching item to make. - * @author 'Vexia - *//* - -public enum FletchItem { - ARROW_SHAFT(FletchType.LOG, new Item(52, 15), 1, 5), SHORTBOW(FletchType.LOG, new Item(50), 5, 5), - LONGBOW(FletchType.LOG, new Item(48), 10, 10), - OAK_SHORTBOW(FletchType.OAK, new Item(54), 20, 16.5), - OAK_LONGBOW(FletchType.OAK, new Item(56), 25, 25), - WILLOW_SHORTBOW(FletchType.WILLOW, new Item(60), 35, 33.3), - WILLOW_LONGBOW(FletchType.WILLOW, new Item(58), 40, 41.5), - MAPLE_SHORTBOW(FletchType.MAPLE, new Item(64), 50, 50), - MAPLE_LONGBOW(FletchType.MAPLE, new Item(62), 55, 58.3), - YEW_SHORTBOW(FletchType.YEW, new Item(68), 65, 55), - YEW_LONGBOW(FletchType.YEW, new Item(66), 70, 75), - MAGIC_SHORTBOW(FletchType.MAGIC, new Item(72), 80, 83.3), - MAGIC_LONGBOW(FletchType.MAGIC, new Item(70), 85, 91.5), - WOODEN_STOCK(FletchType.LOG, new Item(9440), 9, 6), - OAK_STOCK(FletchType.OAK, new Item(9442), 24, 16), - WILLOW_STOCK(FletchType.WILLOW, new Item(9444), 39, 22), - TEAK_STOCK(FletchType.TEAK, new Item(9446), 46, 27), - MAPLE_STOCK(FletchType.MAPLE, new Item(9448), 54, 32), - MAHOGANY_STOCK(FletchType.MAHOGANY, new Item(9450), 61, 41), - YEW_STOCK(FletchType.YEW, new Item(9452), 69, 50); - - */ -/** - * Constructs a new {@code FletchItem.java} {@code Object}. - * @param type the type. - * @param product the product. - * @param level the level. - * @param experience the experience. - *//* - - FletchItem(final FletchType type, final Item product, final int level, final double experience) { - this.type = type; - this.product = product; - this.level = level; - this.experience = experience; - } - - */ -/** - * Represents the type this item pertaint o. - *//* - - private FletchType type; - - */ -/** - * Represents the product of this item. - *//* - - private Item product; - - */ -/** - * Represents the level. - *//* - - private final int level; - - */ -/** - * Represents the experience. - *//* - - private final double experience; - - */ -/** - * Gets the type. - * @return The type. - *//* - - public FletchType getType() { - return type; - } - - */ -/** - * Sets the type. - * @param type The type to set. - *//* - - public void setType(FletchType type) { - this.type = type; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Sets the product. - * @param product The product to set. - *//* - - public void setProduct(Item product) { - this.product = product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/FletchType.java b/Server/src/main/content/global/skill/fletching/FletchType.java deleted file mode 100644 index 33509c1ab..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchType.java +++ /dev/null @@ -1,88 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import org.crandor.game.component.Component; -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the multiple fletching types(log types) - * @author 'Vexia - *//* - -public enum FletchType { - LOG(new Item(1511), FletchItem.ARROW_SHAFT, FletchItem.SHORTBOW, FletchItem.LONGBOW, FletchItem.WOODEN_STOCK), OAK(new Item(1521), FletchItem.OAK_SHORTBOW, FletchItem.OAK_LONGBOW, FletchItem.OAK_STOCK), WILLOW(new Item(1519), FletchItem.WILLOW_SHORTBOW, FletchItem.WILLOW_LONGBOW, FletchItem.WILLOW_STOCK), MAPLE(new Item(1517), FletchItem.MAPLE_SHORTBOW, FletchItem.MAPLE_LONGBOW, FletchItem.MAPLE_STOCK), TEAK(new Item(6333), FletchItem.TEAK_STOCK), MAHOGANY(new Item(6332), FletchItem.MAHOGANY_STOCK), YEW(new Item(1515), FletchItem.YEW_SHORTBOW, FletchItem.YEW_LONGBOW, FletchItem.YEW_STOCK), MAGIC(new Item(1513), FletchItem.MAGIC_SHORTBOW, FletchItem.MAGIC_LONGBOW); - - */ -/** - * Constructs a new {@code FletchType} {@code Object}. - * @param log the log. - * @param items the item.s - *//* - - FletchType(final Item log, final FletchItem... items) { - this.log = log; - this.items = items; - } - - */ -/** - * Represents the log of this type. - *//* - - private final Item log; - - */ -/** - * Represents the fletching items of this type. - *//* - - private FletchItem[] items; - - */ -/** - * Gets the items. - * @return The items. - *//* - - public FletchItem[] getItems() { - return items; - } - - */ -/** - * Gets the log. - * @return The log. - *//* - - public Item getLog() { - return log; - } - - */ -/** - * Method used to get the component based on type. - * @return the component. - *//* - - public Component getComponent() { - return items.length > 1 ? new Component(301 + items.length) : new Component(309); - } - - */ -/** - * Method used to get the FletchType based on the log item. - * @param item the item. - * @return the fletch type. - *//* - - public static FletchType forItem(final Item item) { - for (FletchType type : FletchType.values()) { - if (type.getLog().getId() == item.getId()) { - return type; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/Fletching.java b/Server/src/main/content/global/skill/fletching/Fletching.java deleted file mode 100644 index 4dea01354..000000000 --- a/Server/src/main/content/global/skill/fletching/Fletching.java +++ /dev/null @@ -1,353 +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 HashMaplogMap = new HashMap<>(); - public static HashMap boltMap = new HashMap<>(); - public static HashMap dartMap = new HashMap<>(); - public static HashMap arrowHeadMap = new HashMap<>(); - public static HashMap gemMap = new HashMap<>(); - public static HashMap tipMap = new HashMap<>(); - public static HashMap stringMap = new HashMap<>(); - public static HashMap 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)), - - //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, 2.6), - IRON_ARROW(40, 884, 15, 3.8), - STEEL_ARROW(41, 886, 30, 6.3), - MITHRIL_ARROW(42, 888, 45, 8.8), - ADAMANT_ARROW(43, 890, 60, 11.3), - RUNE_ARROW(44, 892, 75, 13.8), - DRAGON_ARROW(11237, 11212, 90, 16.3), - 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), - 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), - - //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); - } - } - -} diff --git a/Server/src/main/content/global/skill/fletching/FletchingListeners.kt b/Server/src/main/content/global/skill/fletching/FletchingListeners.kt deleted file mode 100644 index 54ac72b5d..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchingListeners.kt +++ /dev/null @@ -1,139 +0,0 @@ -package content.global.skill.fletching - -import core.game.node.entity.skill.Skills -import content.global.skill.fletching.Fletching -import content.global.skill.fletching.items.arrow.ArrowHeadPulse -import content.global.skill.fletching.items.arrow.HeadlessArrowPulse -import content.global.skill.fletching.items.bow.StringPulse -import content.global.skill.fletching.items.crossbow.LimbPulse -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 - -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 FLETCHED_SHAFT = Items.HEADLESS_ARROW_53 - 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(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,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 - } - - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/FletchingPlugin.java b/Server/src/main/content/global/skill/fletching/FletchingPlugin.java deleted file mode 100644 index 3ec122866..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchingPlugin.java +++ /dev/null @@ -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 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; - } -} diff --git a/Server/src/main/content/global/skill/fletching/FletchingPulse.java b/Server/src/main/content/global/skill/fletching/FletchingPulse.java deleted file mode 100644 index 9c7d0d638..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchingPulse.java +++ /dev/null @@ -1,117 +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; - -/** - * fletching skill pulse - * @author ceik - */ -public final class FletchingPulse extends SkillPulse { - - /** - * 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; - - /** - * 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("Big Chompy Bird Hunting").getStage(player) == 0) { - player.getPacketDispatch().sendMessage("You must have started Big Chompy Bird Hunting to make those."); - 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 ) { - item.setAmount(RandomFunction.random(3,6)); - } - 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."; - default: - return "You carefully cut the wood into " + (StringUtils.isPlusN(fletch.getItem().getName()) ? "an" : "a") + " " + fletch.getItem().getName().replace("(u)", "").trim() + "."; - } - } -} diff --git a/Server/src/main/content/global/skill/fletching/GemBoltListener.kt b/Server/src/main/content/global/skill/fletching/GemBoltListener.kt deleted file mode 100644 index 94c09c65b..000000000 --- a/Server/src/main/content/global/skill/fletching/GemBoltListener.kt +++ /dev/null @@ -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 - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/Zones.kt b/Server/src/main/content/global/skill/fletching/Zones.kt new file mode 100644 index 000000000..8c10f0c4f --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/Zones.kt @@ -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): Boolean { + for (zone in zones) { + if (zone.insideBorder(node)) { + return true + } + } + return false + } +} diff --git a/Server/src/main/content/global/skill/fletching/arrow/ArrowCraftInfo.kt b/Server/src/main/content/global/skill/fletching/arrow/ArrowCraftInfo.kt new file mode 100644 index 000000000..087c688a9 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/ArrowCraftInfo.kt @@ -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] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/arrow/ArrowListeners.kt b/Server/src/main/content/global/skill/fletching/arrow/ArrowListeners.kt new file mode 100644 index 000000000..2c061b3cb --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/ArrowListeners.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/arrow/FlightedOgreArrowCraftScript.kt b/Server/src/main/content/global/skill/fletching/arrow/FlightedOgreArrowCraftScript.kt new file mode 100644 index 000000000..126b72aee --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/FlightedOgreArrowCraftScript.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/arrow/HeadlessArrowCraftScript.kt b/Server/src/main/content/global/skill/fletching/arrow/HeadlessArrowCraftScript.kt new file mode 100644 index 000000000..40ef4c235 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/HeadlessArrowCraftScript.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/arrow/TippedArrowCraftScript.kt b/Server/src/main/content/global/skill/fletching/arrow/TippedArrowCraftScript.kt new file mode 100644 index 000000000..d4249b660 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/TippedArrowCraftScript.kt @@ -0,0 +1,79 @@ +package content.global.skill.fletching.arrow + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import kotlin.math.min + +/** + * Represents the arrow head pulse to complete the headless arrow. + * @author 'Vexia + * @param player the player. + * @param arrowCraftInfo provides information about the arrow we're crafting. + * @param sets the amount of sets to complete. + */ +class TippedArrowCraftScript( + private val player: Player, + private val arrowCraftInfo: ArrowCraftInfo, + private val sets: Int +) { + + private val headlessArrow = Items.HEADLESS_ARROW_53 + private val maximumArrowsCraftableInOneStage = 15 + private val initialDelay = 1 + private val subsequentDelay = 3 + + private fun getAmountToCraftForThisStage(headlessArrowsInInventory: Int, tipsInInventory: Int): Int { + val smallerItemAmount = min(headlessArrowsInInventory, tipsInInventory) + return if (smallerItemAmount > maximumArrowsCraftableInOneStage) { + maximumArrowsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < arrowCraftInfo.level) { + ArrowListeners.sendLevelCheckFailDialog(player, arrowCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val tipsInInventory = amountInInventory(player, arrowCraftInfo.tipItemId) + val headlessArrowsInInventory = amountInInventory(player, headlessArrow) + + val amountToCraft = getAmountToCraftForThisStage(headlessArrowsInInventory, tipsInInventory) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(headlessArrow, amountToCraft), + Item(arrowCraftInfo.tipItemId, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach an arrow head to an arrow shaft.") + else -> { + sendMessage(player, "You attach arrow heads to $amountToCraft arrow shafts.") + } + } + + rewardXP(player, Skills.FLETCHING, arrowCraftInfo.experience * amountToCraft) + addItem(player, arrowCraftInfo.arrowItemId, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/bolts/BoltCraftInfo.kt b/Server/src/main/content/global/skill/fletching/bolts/BoltCraftInfo.kt new file mode 100644 index 000000000..497332a59 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/bolts/BoltCraftInfo.kt @@ -0,0 +1,39 @@ +package content.global.skill.fletching.bolts + +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different bolts + * @property unfinishedBoltItemId the unfinished bolt (unf) Item id used to craft the dart. + * @property finishedItemId the resulting finished bolt Item id. + * @property level the level required to craft. + * @property experience the experience gained by crafting. + */ +enum class BoltCraftInfo( + val unfinishedBoltItemId: Int, + val finishedItemId: Int, + val level: Int, + val experience: Double +) { + BRONZE_BOLT(Items.BRONZE_BOLTS_UNF_9375, Items.BRONZE_BOLTS_877, 9, 0.5), + BLURITE_BOLT(Items.BLURITE_BOLTS_UNF_9376, Items.BLURITE_BOLTS_9139, 24, 1.0), + IRON_BOLT(Items.IRON_BOLTS_UNF_9377, Items.IRON_BOLTS_9140, 39, 1.5), + SILVER_BOLT(Items.SILVER_BOLTS_UNF_9382, Items.SILVER_BOLTS_9145, 43, 2.5), + STEEL_BOLT(Items.STEEL_BOLTS_UNF_9378, Items.STEEL_BOLTS_9141, 46, 3.5), + MITHRIL_BOLT(Items.MITHRIL_BOLTS_UNF_9379, Items.MITHRIL_BOLTS_9142, 54, 5.0), + ADAMANTITE_BOLT(Items.ADAMANT_BOLTSUNF_9380, Items.ADAMANT_BOLTS_9143, 61, 7.0), + RUNITE_BOLT(Items.RUNITE_BOLTS_UNF_9381, Items.RUNE_BOLTS_9144, 69, 10.0), + BROAD_BOLT(Items.UNFINISHED_BROAD_BOLTS_13279, Items.BROAD_TIPPED_BOLTS_13280, 55, 3.0); + + companion object { + private val boltCraftInfoByUnfinishedBoltIds = values().associateBy { it.unfinishedBoltItemId } + + val unfinishedBoltIds: IntArray = BoltCraftInfo.values() + .map { unfinishedBoltToBoltMapping: BoltCraftInfo -> unfinishedBoltToBoltMapping.unfinishedBoltItemId } + .toIntArray() + + fun fromUnfinishedBoltId(unfinishedBoltId: Int): BoltCraftInfo? { + return boltCraftInfoByUnfinishedBoltIds[unfinishedBoltId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/bolts/BoltCraftScript.kt b/Server/src/main/content/global/skill/fletching/bolts/BoltCraftScript.kt new file mode 100644 index 000000000..e525e7055 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/bolts/BoltCraftScript.kt @@ -0,0 +1,79 @@ +package content.global.skill.fletching.bolts + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +/** + * Represents the bolt pulse class to make bolts. + * @author ceik + * @param player the player. + * @param boltCraftInfo contains information about what bolt we're crafting + * @param feather the feather we're using to craft + * @param sets the amount of sets to craft + */ +class BoltCraftScript( + private val player: Player, + private val boltCraftInfo: BoltCraftInfo, + private val feather: Item, + private val sets: Int +) { + + private val maximumBoltsCraftableInOneStage = 10 + private val initialDelay = 1 + private val subsequentDelay = 3 + + private fun getAmountToCraftForThisSet(feathersInInventory: Int, unfinishedBoltsInInventory: Int): Int { + val smallerItemAmount = min(feathersInInventory, unfinishedBoltsInInventory) + return if (smallerItemAmount > maximumBoltsCraftableInOneStage) { + maximumBoltsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < boltCraftInfo.level) { + BoltListeners.sendLevelCheckFailDialog(player, boltCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val featherAmount = amountInInventory(player, feather.id) + val unfinishedBoltAmount = amountInInventory(player, boltCraftInfo.unfinishedBoltItemId) + + val amountToCraft = getAmountToCraftForThisSet(featherAmount, unfinishedBoltAmount) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(feather.id, amountToCraft), + Item(boltCraftInfo.unfinishedBoltItemId, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach a feather to a bolt.") + else -> { + sendMessage(player, "You fletch $amountToCraft bolts") + } + } + + rewardXP(player, Skills.FLETCHING, boltCraftInfo.experience * amountToCraft) + addItem(player, boltCraftInfo.finishedItemId, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/bolts/BoltListeners.kt b/Server/src/main/content/global/skill/fletching/bolts/BoltListeners.kt new file mode 100644 index 000000000..ad9d806d8 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/bolts/BoltListeners.kt @@ -0,0 +1,65 @@ +package content.global.skill.fletching.bolts + +import content.global.skill.fletching.Feathers +import core.api.* +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +@Suppress("unused") // Reflectively loaded +class BoltListeners : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Feathers.all, *BoltCraftInfo.unfinishedBoltIds) { player, feather, unfinishedBolt -> + val boltCraftInfo = BoltCraftInfo.fromUnfinishedBoltId(unfinishedBolt.id) ?: return@onUseWith false + val handler: SkillDialogueHandler = + object : SkillDialogueHandler( + player, + SkillDialogue.MAKE_SET_ONE_OPTION, + Item(boltCraftInfo.finishedItemId) + ) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + BoltCraftScript(player, boltCraftInfo, feather.asItem(), amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (boltCraftInfo == BoltCraftInfo.BROAD_BOLT && !getSlayerFlags(player).isBroadsUnlocked()) { + sendDialogue(player, "You need to unlock the ability to create broad bolts.") + return false + } + if (getDynLevel(player, Skills.FLETCHING) < boltCraftInfo.level) { + sendLevelCheckFailDialog(player, boltCraftInfo.level) + return false + } + if (!hasSpaceFor(player, Item(boltCraftInfo.finishedItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + + return true + } + + override fun getAll(index: Int): Int { + return min( + amountInInventory(player, feather.id), + amountInInventory(player, unfinishedBolt.id) + ) + } + } + handler.open() + return@onUseWith true + } + } + companion object { + fun sendLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a fletching level of $level in order to do this." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/crossbow/LimbingListener.kt b/Server/src/main/content/global/skill/fletching/crossbow/LimbingListener.kt new file mode 100644 index 000000000..0876d54c1 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/crossbow/LimbingListener.kt @@ -0,0 +1,80 @@ +package content.global.skill.fletching.crossbow + +import core.api.getDynLevel +import core.api.hasSpaceFor +import core.api.sendDialogue +import core.api.sendMessage +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.net.packet.PacketRepository +import core.net.packet.context.ChildPositionContext +import core.net.packet.out.RepositionChild +import org.rs09.consts.Components + +@Suppress("unused") // Reflectively loaded +class LimbingListener : InteractionListener { + override fun defineListeners() { + onUseWith( + IntType.ITEM, + UnfinishedCrossbowCraftInfo.limbIds, + *UnfinishedCrossbowCraftInfo.stockIds + ) { player, limb, stock -> + val unfinishedCrossbowCraftInfo = UnfinishedCrossbowCraftInfo.forStockId(stock.id) ?: return@onUseWith false + if (unfinishedCrossbowCraftInfo.limbItemId != limb.id) { + sendMessage(player, "That's not the right limb to attach to that stock.") + return@onUseWith true + } + val handler: SkillDialogueHandler = object : SkillDialogueHandler( + player, + SkillDialogue.ONE_OPTION, + Item(unfinishedCrossbowCraftInfo.unstrungCrossbowItemId) + ) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + LimbingScript(player, unfinishedCrossbowCraftInfo, amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < unfinishedCrossbowCraftInfo.level) { + sendLevelCheckFailDialog(player, unfinishedCrossbowCraftInfo.level) + return false + } + if (!hasSpaceFor(player, Item(unfinishedCrossbowCraftInfo.unstrungCrossbowItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + + return true + } + + override fun getAll(index: Int): Int { + return player.inventory.getAmount(stock.asItem()) + } + } + handler.open() + fixTextOverlappingTheCrossbowIcon(player) + return@onUseWith true + } + } + + + private fun fixTextOverlappingTheCrossbowIcon(player: Player) { + PacketRepository.send( + RepositionChild::class.java, + ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10) + ) + } + + companion object { + fun sendLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a fletching level of $level to attach these limbs." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/crossbow/LimbingScript.kt b/Server/src/main/content/global/skill/fletching/crossbow/LimbingScript.kt new file mode 100644 index 000000000..07fb8ac43 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/crossbow/LimbingScript.kt @@ -0,0 +1,56 @@ +package content.global.skill.fletching.crossbow + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item + +/** + * The queue script for attaching limbs. + * @author Ceikry + * @param player The player + * @param unfinishedCrossbowCraftInfo info about the unfinished (unstrung) crossbow we're crafting + * @param amount to create + */ +class LimbingScript( + private val player: Player, + private val unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo, + private val amount: Int +) { + + private val initialDelay = 1 + private val subsequentDelay = 6 + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < unfinishedCrossbowCraftInfo.level) { + LimbingListener.sendLevelCheckFailDialog(player, unfinishedCrossbowCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(unfinishedCrossbowCraftInfo.stockItemId), + Item(unfinishedCrossbowCraftInfo.limbItemId) + ) + ) { + addItem(player, unfinishedCrossbowCraftInfo.unstrungCrossbowItemId, 1) + rewardXP(player, Skills.FLETCHING, unfinishedCrossbowCraftInfo.experience) + sendMessage(player, "You attach the metal limbs to the stock.") + animate(player, unfinishedCrossbowCraftInfo.animation) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= amount - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/crossbow/UnfinishedCrossbowCraftInfo.kt b/Server/src/main/content/global/skill/fletching/crossbow/UnfinishedCrossbowCraftInfo.kt new file mode 100644 index 000000000..7250b02e4 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/crossbow/UnfinishedCrossbowCraftInfo.kt @@ -0,0 +1,55 @@ +package content.global.skill.fletching.crossbow + +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different unfinished (unstrung) crossbows + * @author 'Vexia + * @param stockItemId item id of the stock. + * @param limbItemId item id of the limb. + * @param unstrungCrossbowItemId item id of the product. + * @param level the level. + * @param experience the experience. + * @param animation the animation. + */ +enum class UnfinishedCrossbowCraftInfo + ( + val stockItemId: Int, + val limbItemId: Int, + val unstrungCrossbowItemId: Int, + val level: Int, + val experience: Double, + val animation: Animation +) { + WOODEN_STOCK(Items.WOODEN_STOCK_9440, Items.BRONZE_LIMBS_9420, Items.BRONZE_CBOW_U_9454, 9, 12.0, Animation(4436)), + OAK_STOCK(Items.OAK_STOCK_9442, Items.BLURITE_LIMBS_9422, Items.BLURITE_CROSSBOW_9176, 24, 32.0, Animation(4437)), + WILLOW_STOCK(Items.WILLOW_STOCK_9444, Items.IRON_LIMBS_9423, Items.IRON_CBOW_U_9457, 39, 44.0, Animation(4438)), + TEAK_STOCK(Items.TEAK_STOCK_9446, Items.STEEL_LIMBS_9425, Items.STEEL_CBOW_U_9459, 46, 54.0, Animation(4439)), + MAPLE_STOCK(Items.MAPLE_STOCK_9448, Items.MITHRIL_LIMBS_9427, Items.MITHRIL_CBOW_U_9461, 54, 64.0, Animation(4440)), + MAHOGANY_STOCK( + Items.MAHOGANY_STOCK_9450, + Items.ADAMANTITE_LIMBS_9429, + Items.ADAMANT_CBOW_U_9463, + 61, + 82.0, + Animation(4441) + ), + YEW_STOCK(Items.YEW_STOCK_9452, Items.RUNITE_LIMBS_9431, Items.RUNITE_CBOW_U_9465, 69, 100.0, Animation(4442)); + + companion object { + private val unfinishedCrossbowCraftInfoByStockId = values().associateBy { it.stockItemId } + + val limbIds: IntArray = + values().map { unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo -> unfinishedCrossbowCraftInfo.limbItemId } + .toIntArray() + + val stockIds: IntArray = + values().map { unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo -> unfinishedCrossbowCraftInfo.stockItemId } + .toIntArray() + + fun forStockId(stockId: Int): UnfinishedCrossbowCraftInfo? { + return unfinishedCrossbowCraftInfoByStockId[stockId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/darts/DartCraftInfo.kt b/Server/src/main/content/global/skill/fletching/darts/DartCraftInfo.kt new file mode 100644 index 000000000..488075b80 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/darts/DartCraftInfo.kt @@ -0,0 +1,36 @@ +package content.global.skill.fletching.darts + +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different darts + * @author 'Vexia + * @property tipItemId the tip Item id used to craft the dart. + * @property dartItemId the resulting dart Item id. + * @property level the level required to craft. + * @property experience the experience gained by crafting. + */ +enum class DartCraftInfo( + val tipItemId: Int, + val dartItemId: Int, + val level: Int, + val experience: Double) { + + BRONZE_DART(Items.BRONZE_DART_TIP_819, Items.BRONZE_DART_806, 1, 1.8), + IRON_DART(Items.IRON_DART_TIP_820, Items.IRON_DART_807, 22, 3.8), + STEEL_DART(Items.STEEL_DART_TIP_821, Items.STEEL_DART_808, 37, 7.5), + MITHRIL_DART(Items.MITHRIL_DART_TIP_822, Items.MITHRIL_DART_809, 52, 11.2), + ADAMANT_DART(Items.ADAMANT_DART_TIP_823, Items.ADAMANT_DART_810, 67, 15.0), + RUNE_DART(Items.RUNE_DART_TIP_824, Items.RUNE_DART_811, 81, 18.8), + DRAGON_DART(Items.DRAGON_DART_TIP_11232, Items.DRAGON_DART_11230, 95, 25.0); + + companion object { + private val dartCraftInfoByTipIds = values().associateBy { it.tipItemId } + + val tipIDs: IntArray = values().map { dartCraftInfo: DartCraftInfo -> dartCraftInfo.tipItemId }.toIntArray() + + fun fromTipID(dartTipId: Int): DartCraftInfo? { + return dartCraftInfoByTipIds[dartTipId] + } + } +} diff --git a/Server/src/main/content/global/skill/fletching/darts/DartCraftScript.kt b/Server/src/main/content/global/skill/fletching/darts/DartCraftScript.kt new file mode 100644 index 000000000..baecff08c --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/darts/DartCraftScript.kt @@ -0,0 +1,78 @@ +package content.global.skill.fletching.darts + +import content.global.skill.fletching.Feathers +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +/** + * Represents the queueScript to craft a dart. + * @author ceikry + * @param player the player. + * @param dartCraftInfo contains info about the dart we're crafting. + * @param sets count of sets to make + */ +class DartCraftScript( + private val player: Player, + private val dartCraftInfo: DartCraftInfo, + private var sets: Int +) { + + private val initialDelay = 1 + private val subsequentDelay = 3 + private val maximumDartsCraftableInOneStage = 10 + + private fun getAmountToCraftForThisStage(feathersInInventory: Int, dartTipsInInventory: Int): Int { + val smallerItemAmount = min(feathersInInventory, dartTipsInInventory) + return if (smallerItemAmount > maximumDartsCraftableInOneStage) { + maximumDartsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < dartCraftInfo.level) { + DartListeners.sendLevelCheckFailDialog(player, dartCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val dartTipsInInventory = amountInInventory(player, dartCraftInfo.tipItemId) + val feathersInInventory = amountInInventory(player, Feathers.STANDARD) + + val amountToCraft = getAmountToCraftForThisStage(feathersInInventory, dartTipsInInventory) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(Feathers.STANDARD, amountToCraft), + Item(dartCraftInfo.tipItemId, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach a feather to a dart.") + else -> { + sendMessage(player, "You attach feathers to $amountToCraft darts.") + } + } + + rewardXP(player, Skills.FLETCHING, dartCraftInfo.experience * amountToCraft) + addItem(player, dartCraftInfo.dartItemId, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/darts/DartListeners.kt b/Server/src/main/content/global/skill/fletching/darts/DartListeners.kt new file mode 100644 index 000000000..93b96d2ab --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/darts/DartListeners.kt @@ -0,0 +1,59 @@ +package content.global.skill.fletching.darts + +import content.data.Quests +import content.global.skill.fletching.Feathers +import core.api.* +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +@Suppress("unused") // Reflectively loaded +class DartListeners : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Feathers.STANDARD, *DartCraftInfo.tipIDs) { player, feather, dartTip -> + val dartCraftInfo = DartCraftInfo.fromTipID(dartTip.id) ?: return@onUseWith false + val handler: SkillDialogueHandler = + object : + SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(dartCraftInfo.dartItemId)) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + DartCraftScript(player, dartCraftInfo, amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < dartCraftInfo.level) { + sendLevelCheckFailDialog(player, dartCraftInfo.level) + return false + } + if (!isQuestComplete(player, Quests.THE_TOURIST_TRAP)) { + sendDialogue(player, "You need to have completed Tourist Trap to fletch darts.") + return false + } + if (!hasSpaceFor(player, Item(dartCraftInfo.dartItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + return true + } + + override fun getAll(index: Int): Int { + return min( + amountInInventory(player, feather.id), + amountInInventory(player, dartTip.id) + ) + } + } + handler.open() + return@onUseWith true + } + } + companion object { + fun sendLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue(player, "You need a fletching level of $level to do this.") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/gem/AttachGemTipToBoltScript.kt b/Server/src/main/content/global/skill/fletching/gem/AttachGemTipToBoltScript.kt new file mode 100644 index 000000000..c6b1cfdfd --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/gem/AttachGemTipToBoltScript.kt @@ -0,0 +1,77 @@ +package content.global.skill.fletching.gem + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +/** + * Represents the attaching of a gem tip to a premade bolt. + * @author Ceikry + * @param player the player. + * @param gemBoltCraftInfo crafting info for adding the gem to the bolt + * @param sets the number of sets to craft. + */ +class AttachGemTipToBoltScript( + private val player: Player, + private val gemBoltCraftInfo: GemBoltsCraftInfo, + private val sets: Int +) { + + private val initialDelay = 1 + private val subsequentDelay = 3 + private val maximumGemBoltsCraftableInOneStage = 10 + + private fun getAmountToCraftForThisPulse(untippedBoltsInInventory: Int, tipsInInventory: Int): Int { + val smallerItemAmount = min(untippedBoltsInInventory, tipsInInventory) + return if (smallerItemAmount > maximumGemBoltsCraftableInOneStage) { + maximumGemBoltsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) { + GemBoltListeners.sendGemTipAttachLevelCheckFailDialog(player, gemBoltCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val untippedBoltsInInventory = amountInInventory(player, gemBoltCraftInfo.untippedBoltItemId) + val tipsInInventory = amountInInventory(player, gemBoltCraftInfo.tipItemId) + + val amountToCraft = getAmountToCraftForThisPulse(untippedBoltsInInventory, tipsInInventory) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(gemBoltCraftInfo.untippedBoltItemId, amountToCraft), + Item(gemBoltCraftInfo.tipItemId, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach the tip to the bolt.") + else -> { + sendMessage(player, "You fletch $amountToCraft bolts.") + } + } + + rewardXP(player, Skills.FLETCHING, gemBoltCraftInfo.experience * amountToCraft) + addItem(player, gemBoltCraftInfo.tippedBoltItemId, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/gem/CutGemsIntoBoltTipsScript.kt b/Server/src/main/content/global/skill/fletching/gem/CutGemsIntoBoltTipsScript.kt new file mode 100644 index 000000000..55ea058d0 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/gem/CutGemsIntoBoltTipsScript.kt @@ -0,0 +1,79 @@ +package content.global.skill.fletching.gem + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item + +/** + * Represents the queue script for cutting gems into bolt tips + * @author Ceikry + * @param player the player. + * @param gemBoltCraftInfo represents the crafting info for the gem we're cutting. + * @param amount the amount to make. + */ +class CutGemsIntoBoltTipsScript( + private val player: Player, + private val gemBoltCraftInfo: GemBoltsCraftInfo, + private val amount: Int +) { + + private val initialDelay = 1 + private val craftDelay = 5 + private val animationDelay = 6 + + private var craftingFinished = false + + private fun invokeAnimationLoop() { + queueScript(player, 0) { + if (craftingFinished) { + return@queueScript stopExecuting(player) + } + animate(player, gemBoltCraftInfo.gemCutAnimationId) + return@queueScript delayScript(player, animationDelay) + } + } + + private fun invokeCraftLoop() { + queueScript(player, 0) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) { + craftingFinished = true + GemBoltListeners.sendGemTipCutLevelCheckFailDialog(player, gemBoltCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val amountOfTipsToCraft = when (gemBoltCraftInfo) { + GemBoltsCraftInfo.PEARLS -> 24 + GemBoltsCraftInfo.PEARL -> 6 + GemBoltsCraftInfo.ONYX -> 24 + else -> 12 + } + + if (removeItem(player, Item(gemBoltCraftInfo.gemItemId))) { + addItem(player, gemBoltCraftInfo.tipItemId, amountOfTipsToCraft) + rewardXP(player, Skills.FLETCHING, gemBoltCraftInfo.experience) + } else { + craftingFinished = true + return@queueScript stopExecuting(player) + } + + if (stage >= amount - 1) { + craftingFinished = true + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, craftDelay, true) + } + } + + fun invoke() { + queueScript(player, initialDelay) { + invokeAnimationLoop() + invokeCraftLoop() + return@queueScript stopExecuting(player) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/gem/GemBoltListeners.kt b/Server/src/main/content/global/skill/fletching/gem/GemBoltListeners.kt new file mode 100644 index 000000000..15ef1d38f --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/gem/GemBoltListeners.kt @@ -0,0 +1,90 @@ +package content.global.skill.fletching.gem + +import core.api.amountInInventory +import core.api.getDynLevel +import core.api.hasSpaceFor +import core.api.sendDialogue +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import kotlin.math.min + +@Suppress("unused") // Reflectively loaded +class GemBoltListeners : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.CHISEL_1755, *GemBoltsCraftInfo.gemIds) { player, _, gem -> + val gemBoltCraftInfo = GemBoltsCraftInfo.forGemId(gem.id) ?: return@onUseWith true + + object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(gemBoltCraftInfo.gemItemId)) { + override fun create(amount: Int, index: Int) { + if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) { + sendGemTipCutLevelCheckFailDialog(player, gemBoltCraftInfo.level) + return + } + CutGemsIntoBoltTipsScript(player, gemBoltCraftInfo, amount).invoke() + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, gemBoltCraftInfo.gemItemId) + } + }.open() + return@onUseWith true + } + + onUseWith( + IntType.ITEM, + GemBoltsCraftInfo.untippedBoltIds, + *GemBoltsCraftInfo.boltTipIds + ) { player, untippedBolt, boltTip -> + val bolt = GemBoltsCraftInfo.forTipId(boltTip.id) ?: return@onUseWith false + if (untippedBolt.id != bolt.untippedBoltItemId) return@onUseWith false + + val handler: SkillDialogueHandler = + object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(bolt.tippedBoltItemId)) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + AttachGemTipToBoltScript(player, bolt, amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < bolt.level) { + sendGemTipAttachLevelCheckFailDialog(player, bolt.level) + return false + } + if (!hasSpaceFor(player, Item(bolt.tippedBoltItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + return true + } + + override fun getAll(index: Int): Int { + return min(amountInInventory(player, untippedBolt.id), amountInInventory(player, boltTip.id)) + } + } + handler.open() + return@onUseWith true + } + } + + companion object { + fun sendGemTipCutLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a Fletching level of $level or above to do that." + ) + } + + fun sendGemTipAttachLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a Fletching level of $level or above to do that." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/gem/GemBoltsCraftInfo.kt b/Server/src/main/content/global/skill/fletching/gem/GemBoltsCraftInfo.kt new file mode 100644 index 000000000..d0e2b0f11 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/gem/GemBoltsCraftInfo.kt @@ -0,0 +1,106 @@ +package content.global.skill.fletching.gem + +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different gem tipped bolts + * @property untippedBoltItemId the base untipped bolt item id + * @property gemItemId the gem item id used to craft the bolt tips + * @property gemCutAnimationId the animation to play when the player cuts the gem + * @property tipItemId the item id of the resulting bolt tips + * @property tippedBoltItemId the item id of the finished gem-tipped bolt + * @property level the required level to craft the gem tipped bolt or tips themselves + * @property experience gained creating one set of gem tips, or per bolt tipped + */ +enum class GemBoltsCraftInfo( + var untippedBoltItemId: Int, + var gemItemId: Int, + var gemCutAnimationId: Int, + var tipItemId: Int, + var tippedBoltItemId: Int, + var level: Int, + var experience: Double +) { + OPAL(Items.BRONZE_BOLTS_877, Items.OPAL_1609, 890, Items.OPAL_BOLT_TIPS_45, Items.OPAL_BOLTS_879, 11, 1.6), + JADE(Items.BLURITE_BOLTS_9139, Items.JADE_1611, 891, Items.JADE_BOLT_TIPS_9187, Items.JADE_BOLTS_9335, 26, 2.4), + PEARL(Items.IRON_BOLTS_9140, Items.OYSTER_PEARL_411, 4470, Items.PEARL_BOLT_TIPS_46, Items.PEARL_BOLTS_880, 41, 3.2), + PEARLS( + Items.IRON_BOLTS_9140, + Items.OYSTER_PEARLS_413, + 4470, + Items.PEARL_BOLT_TIPS_46, + Items.PEARL_BOLTS_880, + 41, + 3.2 + ), + RED_TOPAZ( + Items.STEEL_BOLTS_9141, + Items.RED_TOPAZ_1613, + 892, + Items.TOPAZ_BOLT_TIPS_9188, + Items.TOPAZ_BOLTS_9336, + 48, + 3.9 + ), + SAPPHIRE( + Items.MITHRIL_BOLTS_9142, + Items.SAPPHIRE_1607, + 888, + Items.SAPPHIRE_BOLT_TIPS_9189, + Items.SAPPHIRE_BOLTS_9337, + 56, + 4.7 + ), + EMERALD( + Items.MITHRIL_BOLTS_9142, + Items.EMERALD_1605, + 889, + Items.EMERALD_BOLT_TIPS_9190, + Items.EMERALD_BOLTS_9338, + 58, + 5.5 + ), + RUBY(Items.ADAMANT_BOLTS_9143, Items.RUBY_1603, 887, Items.RUBY_BOLT_TIPS_9191, Items.RUBY_BOLTS_9339, 63, 6.3), + DIAMOND( + Items.ADAMANT_BOLTS_9143, + Items.DIAMOND_1601, + 886, + Items.DIAMOND_BOLT_TIPS_9192, + Items.DIAMOND_BOLTS_9340, + 65, + 7.0 + ), + DRAGONSTONE( + Items.RUNE_BOLTS_9144, + Items.DRAGONSTONE_1615, + 885, + Items.DRAGON_BOLT_TIPS_9193, + Items.DRAGON_BOLTS_9341, + 71, + 8.2 + ), + ONYX(Items.RUNE_BOLTS_9144, Items.ONYX_6573, 2717, Items.ONYX_BOLT_TIPS_9194, Items.ONYX_BOLTS_9342, 73, 9.4); + + companion object { + private val gemBoltCraftInfoByGemId = values().associateBy { it.gemItemId } + private val gemBoltCraftInfoByBoltTipId = values().associateBy { it.tipItemId } + + val untippedBoltIds: IntArray = + values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.untippedBoltItemId }.distinct() + .toIntArray() + + val gemIds: IntArray = + values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.gemItemId }.toIntArray() + + val boltTipIds: IntArray = + values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.tipItemId }.toIntArray() + + fun forGemId(gemId: Int): GemBoltsCraftInfo? { + return gemBoltCraftInfoByGemId[gemId] + } + + fun forTipId(tipId: Int): GemBoltsCraftInfo? { + return gemBoltCraftInfoByBoltTipId[tipId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/grapple/GrappleListeners.kt b/Server/src/main/content/global/skill/fletching/grapple/GrappleListeners.kt new file mode 100644 index 000000000..46373cda0 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/grapple/GrappleListeners.kt @@ -0,0 +1,67 @@ +package content.global.skill.fletching.grapple + +import core.api.* +import core.game.interaction.Clocks +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items + +@Suppress("unused") // Reflectively loaded +class GrappleListeners : InteractionListener { + private val mithrilBolt = Items.MITHRIL_BOLTS_9142 + private val mithrilGrappleTip = Items.MITH_GRAPPLE_TIP_9416 + private val rope = Items.ROPE_954 + private val mithrilGrapple = Items.MITH_GRAPPLE_9418 + private val mithrilGrappleWithRope = Items.MITH_GRAPPLE_9419 + + override fun defineListeners() { + onUseWith(IntType.ITEM, mithrilBolt, mithrilGrappleTip) { player, bolt, tip -> + if (getDynLevel(player, Skills.FLETCHING) < 59) { + sendMessage(player, "You need a fletching level of 59 to make this.") + return@onUseWith true + } + queueScript(player, 0) { _ -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + if (removeItemsIfPlayerHasEnough( + player, + Item(mithrilBolt, 1), + tip.asItem() + ) + ) { + addItem(player, mithrilGrapple, 1) + } + + delayClock(player, Clocks.SKILLING, 3) + return@queueScript stopExecuting(player) + } + + + return@onUseWith true + } + + onUseWith(IntType.ITEM, rope, mithrilGrapple) { player, rope, grapple -> + if (getDynLevel(player, Skills.FLETCHING) < 59) { + sendMessage(player, "You need a fletching level of 59 to make this.") + return@onUseWith true + } + queueScript(player, 0) { _ -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + if (removeItemsIfPlayerHasEnough( + player, + rope.asItem(), + grapple.asItem() + ) + ) { + addItem(player, mithrilGrappleWithRope, 1) + } + delayClock(player, Clocks.SKILLING, 3) + return@queueScript stopExecuting(player) + } + return@onUseWith true + } + + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHead.java b/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHead.java deleted file mode 100644 index c7ab5ec7c..000000000 --- a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHead.java +++ /dev/null @@ -1,123 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.arrow; - -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the enum storing the arrow head information. - * @author 'Vexia - * @note brutal arrows after quest. - *//* - -public enum ArrowHead { - BRONZE_ARROW(new Item(39), new Item(882), 1, 2.6), - IRON_ARROW(new Item(40), new Item(884), 15, 3.8), - STEEL_ARROW(new Item(41), new Item(886), 30, 6.3), - MITHRIL_ARROW(new Item(42), new Item(888), 45, 8.8), - ADAMANT_ARROW(new Item(43), new Item(890), 60, 10), - RUNE_ARROW(new Item(44), new Item(892), 75, 13.8), - DRAGON_ARROW(new Item(11237), new Item(11212), 90, 16.3), - BROAD_ARROW(new Item(13278), new Item(4160), 52, 10); - - */ -/** - * Constructs a new {@code ArrowHead.java} {@code Object}. - * @param item the item. - * @param product the product. - * @param level the level. - * @param experience the experience. - *//* - - ArrowHead(Item item, Item product, int level, double experience) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - } - - */ -/** - * Represents the arrow tip. - *//* - - private final Item item; - - */ -/** - * Represents the product item. - *//* - - private final Item product; - - */ -/** - * Represents the level required. - *//* - - private final int level; - - */ -/** - * Represents the experience gained. - *//* - - private final double experience; - - */ -/** - * Gets the item. - * @return The item. - *//* - - public Item getTips() { - return item; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Gets the arrow head. - * @param item the item. - * @return the arrow head. - *//* - - public static ArrowHead forItem(final Item item) { - for (ArrowHead arrow : ArrowHead.values()) { - if (arrow.getTips().getId() == item.getId()) { - return arrow; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHeadPulse.java b/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHeadPulse.java deleted file mode 100644 index 5ea27c2cc..000000000 --- a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHeadPulse.java +++ /dev/null @@ -1,110 +0,0 @@ -package content.global.skill.fletching.items.arrow; - -import content.global.skill.slayer.SlayerManager; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -import static core.api.ContentAPIKt.*; - -/** - * Represents the arrow head pulse to complete the headless arrow. - * @author 'Vexia - */ -public class ArrowHeadPulse extends SkillPulse { - - /** - * Represents the headless arrow item. - */ - private static final Item HEADLESS_ARROW = new Item(53); - - /** - * Represents the arrow head. - */ - private final Fletching.ArrowHeads arrow; - - /** - * Represents the sets to do. - */ - private int sets; - - /** - * Constructs a new {@code ArrowHeadPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - * @param arrow the arrow. - * @param sets the sets. - */ - public ArrowHeadPulse(final Player player, final Item node, final Fletching.ArrowHeads arrow, final int sets) { - super(player, node); - this.arrow = arrow; - this.sets = sets; - } - - @Override - public boolean checkRequirements() { - if (arrow.unfinished == 4160) { - if (!SlayerManager.getInstance(player).flags.isBroadsUnlocked()) { - player.getDialogueInterpreter().sendDialogue("You need to unlock the ability to create broad arrows."); - return false; - } - } - if (player.getSkills().getLevel(Skills.FLETCHING) < arrow.level) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + arrow.level + " to do this."); - return false; - } - if (!hasSpaceFor(player, arrow.getFinished())) { - sendDialogue(player, "You do not have enough inventory space."); - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - super.setDelay(3); - } - Item tip = arrow.getUnfinished(); - int tipAmount = player.getInventory().getAmount(arrow.unfinished); - int shaftAmount = player.getInventory().getAmount(HEADLESS_ARROW); - if (tipAmount >= 15 && shaftAmount >= 15) { - HEADLESS_ARROW.setAmount(15); - tip.setAmount(15); - player.getPacketDispatch().sendMessage("You attach arrow heads to 15 arrow shafts."); - } else { - int amount = tipAmount > shaftAmount ? shaftAmount : tipAmount; - HEADLESS_ARROW.setAmount(amount); - tip.setAmount(amount); - player.getPacketDispatch().sendMessage(amount == 1 ? "You attach an arrow head to an arrow shaft." : "You attach arrow heads to " + amount + " arrow shafts."); - } - if (player.getInventory().remove(HEADLESS_ARROW, tip)) { - player.getSkills().addExperience(Skills.FLETCHING, arrow.experience * tip.getAmount(), true); - Item product = arrow.getFinished(); - product.setAmount(tip.getAmount()); - player.getInventory().add(product); - } - HEADLESS_ARROW.setAmount(1); - tip.setAmount(1); - if (!player.getInventory().containsItem(HEADLESS_ARROW)) { - return true; - } - if (!player.getInventory().containsItem(tip)) { - return true; - } - sets--; - return sets == 0; - } - - @Override - public void message(int type) { - - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessArrowPulse.java b/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessArrowPulse.java deleted file mode 100644 index 3125b5669..000000000 --- a/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessArrowPulse.java +++ /dev/null @@ -1,145 +0,0 @@ -package content.global.skill.fletching.items.arrow; - -import org.rs09.consts.Items; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -import static core.api.ContentAPIKt.*; - -/** - * Represents the arrow pulse for creating unfinished arrows. - * @author 'Vexia - */ -public final class HeadlessArrowPulse extends SkillPulse { - - /** - * Represents the headless arrow item. - */ - private final Item HEADLESS_ARROW = new Item(Items.HEADLESS_ARROW_53); - - /** - * Represents the arrow shaft item. - */ - private final Item ARROW_SHAFT = new Item(Items.ARROW_SHAFT_52); - - /** - * Represents the feather items. - */ - private static final Item[] FEATHER = new Item[] { - new Item(Items.FEATHER_314), - new Item(Items.STRIPY_FEATHER_10087), - new Item(Items.RED_FEATHER_10088), - new Item(Items.BLUE_FEATHER_10089), - new Item(Items.YELLOW_FEATHER_10090), - new Item(Items.ORANGE_FEATHER_10091) - }; - - /** - * The feather being used. - */ - private Item feather; - - /** - * Represents the amount to make. - */ - private int sets; - - /** - * Represents if we should use sets, meaning we have 15 & 15 arrow shafts and feathers. - */ - private boolean useSets = false; - - /** - * Constructs a new {@code ArrowPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - */ - public HeadlessArrowPulse(Player player, Item node, Item feather, int sets) { - super(player, node); - this.sets = sets; - this.feather = feather; - } - - @Override - public boolean checkRequirements() { - if (!player.getInventory().containsItem(ARROW_SHAFT)) { - player.getDialogueInterpreter().sendDialogue("You don't have any arrow shafts."); - return false; - } - if (feather == null || !player.getInventory().containsItem(feather)) { - player.getDialogueInterpreter().sendDialogue("You don't have any feathers."); - return false; - } - if (player.getInventory().contains(ARROW_SHAFT.getId(), 15) && player.getInventory().contains(feather.getId(), 15)) { - useSets = true; - } else { - useSets = false; - } - if (!hasSpaceFor(player, HEADLESS_ARROW.asItem())) { - sendDialogue(player, "You do not have enough inventory space."); - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - int featherAmount = player.getInventory().getAmount(feather); - int shaftAmount = player.getInventory().getAmount(ARROW_SHAFT); - if (getDelay() == 1) { - super.setDelay(3); - } - if (featherAmount >= 15 && shaftAmount >= 15) { - feather.setAmount(15); - ARROW_SHAFT.setAmount(15); - player.getPacketDispatch().sendMessage("You attach feathers to 15 arrow shafts."); - } else { - int amount = Math.min(featherAmount, shaftAmount); - feather.setAmount(amount); - ARROW_SHAFT.setAmount(amount); - player.getPacketDispatch().sendMessage(amount == 1 - ? "You attach a feathers to a shaft." : "You attach feathers to " + amount + " arrow shafts."); - } - if (player.getInventory().remove(feather, ARROW_SHAFT)) { - HEADLESS_ARROW.setAmount(feather.getAmount()); - player.getSkills().addExperience(Skills.FLETCHING, HEADLESS_ARROW.getAmount(), true); - player.getInventory().add(HEADLESS_ARROW); - } - HEADLESS_ARROW.setAmount(1); - feather.setAmount(1); - ARROW_SHAFT.setAmount(1); - if (!player.getInventory().containsItem(ARROW_SHAFT)) { - return true; - } - if (!player.getInventory().containsItem(feather)) { - return true; - } - sets--; - return sets <= 0; - } - - @Override - public void message(int type) { - } - - /** - * Gets the feather item. - * @return the item. - */ - private Item getFeather() { - int length = FEATHER.length; - for (int i = 0; i < length; i++) { - Item f = FEATHER[i]; - if (player.getInventory().containsItem(f)) { - return f; - } - } - return null; - } -} diff --git a/Server/src/main/content/global/skill/fletching/items/bolts/Bolt.java b/Server/src/main/content/global/skill/fletching/items/bolts/Bolt.java deleted file mode 100644 index f877050db..000000000 --- a/Server/src/main/content/global/skill/fletching/items/bolts/Bolt.java +++ /dev/null @@ -1,123 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.bolts; - -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents an enum of bolts. - * @author 'Vexia - *//* - -public enum Bolt { - BRONZE_BOLT(new Item(9375), new Item(877), 9, 0.5), - BLURITE_BOLT(new Item(9376), new Item(9139), 24, 1), - IRON_BOLT(new Item(9377), new Item(9140), 39, 1.5), - SILVER_BOLT(new Item(9382), new Item(9145), 43, 2.5), - STEEL_BOLT(new Item(9378), new Item(9141), 46, 3.5), - MITHRIL_BOLT(new Item(9379), new Item(9142), 54, 5), - ADAMANTITE_BOLT(new Item(9380), new Item(9143), 61, 7), - RUNITE_BOLT(new Item(9381), new Item(9144), 69, 10), - BROAD_BOLT(new Item(13279), new Item(13280), 55, 3); - - */ -/** - * The item required. - *//* - - private final Item item; - - */ -/** - * The product recieved. - *//* - - private final Item product; - - */ -/** - * The level required. - *//* - - private final int level; - - */ -/** - * The experience gained. - *//* - - private final double experience; - - */ -/** - * Constructs a new {@code Bolt} {@code Object}. - * @param item the item. - * @param product the product. - * @param level the level. - * @param experience the experienece. - *//* - - Bolt(Item item, Item product, int level, double experience) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - } - - */ -/** - * Gets the item. - * @return The item. - *//* - - public Item getItem() { - return item; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Method used to get the bolt for the item. - * @param item the item. - * @return the bolt. - *//* - - public static Bolt forItem(final Item item) { - for (Bolt bolt : Bolt.values()) { - if (bolt.getItem().getId() == item.getId()) { - return bolt; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java b/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java deleted file mode 100644 index b345c4276..000000000 --- a/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java +++ /dev/null @@ -1,130 +0,0 @@ -package content.global.skill.fletching.items.bolts; - -import content.global.skill.slayer.SlayerManager; -import org.rs09.consts.Items; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the bolt pulse class to make bolts. - * @author ceik - */ -public final class BoltPulse extends SkillPulse { - - /** - * Represents the feather item. - */ - private Item feather; - - /** - * Represents possible feather Items - */ - private static final Item[] FEATHER = new Item[] { - new Item(Items.FEATHER_314), - new Item(Items.STRIPY_FEATHER_10087), - new Item(Items.RED_FEATHER_10088), - new Item(Items.BLUE_FEATHER_10089), - new Item(Items.YELLOW_FEATHER_10090), - new Item(Items.ORANGE_FEATHER_10091) - }; - - /** - * Represents the bolt. - */ - private final Fletching.Bolts bolt; - - /** - * Represents the sets to do. - */ - private int sets; - - /** - * Represents if we're using sets. - */ - private boolean useSets = false; - - /** - * Constructs a new {@code BoltPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - */ - public BoltPulse(Player player, Item node, final Fletching.Bolts bolt, final Item feather, final int sets) { - super(player, node); - this.bolt = bolt; - this.sets = sets; - this.feather = feather; - } - - @Override - public boolean checkRequirements() { - if (bolt.getUnfinished().getId() == 13279) { - if (!SlayerManager.getInstance(player).flags.isBroadsUnlocked()) { - player.getDialogueInterpreter().sendDialogue("You need to unlock the ability to create broad bolts."); - return false; - } - } - if (player.getSkills().getLevel(Skills.FLETCHING) < bolt.level) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bolt.level + " in order to do this."); - return false; - } - if (!player.getInventory().containsItem(feather)) { - return false; - } - if (!player.getInventory().containsItem(bolt.getUnfinished())) { - return false; - } - if (!player.getInventory().hasSpaceFor(bolt.getFinished())) { - player.getDialogueInterpreter().sendDialogue("You do not have enough inventory space."); - return false; - } - - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - int featherAmount = player.getInventory().getAmount(feather); - int boltAmount = player.getInventory().getAmount(bolt.unfinished); - if (getDelay() == 1) { - super.setDelay(3); - } - final Item unfinished = bolt.getUnfinished(); - if (featherAmount >= 10 && boltAmount >= 10) { - feather.setAmount(10); - unfinished.setAmount(10); - player.getPacketDispatch().sendMessage("You fletch 10 bolts."); - } else { - int amount = featherAmount > boltAmount ? boltAmount : featherAmount; - feather.setAmount(amount); - unfinished.setAmount(amount); - player.getPacketDispatch().sendMessage(amount == 1 ? "You attach a feather to a bolt." : "You fletch " + amount + " bolts"); - } - if (player.getInventory().remove(feather, unfinished)) { - Item product = bolt.getFinished(); - product.setAmount(feather.getAmount()); - player.getSkills().addExperience(Skills.FLETCHING, product.getAmount() * bolt.experience, true); - player.getInventory().add(product); - } - feather.setAmount(1); - if (!player.getInventory().containsItem(feather)) { - return true; - } - if (!player.getInventory().containsItem(bolt.getUnfinished())) { - return true; - } - sets--; - return sets <= 0; - } - - @Override - public void message(int type) { - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/bow/StringBow.java b/Server/src/main/content/global/skill/fletching/items/bow/StringBow.java deleted file mode 100644 index 0187f17bb..000000000 --- a/Server/src/main/content/global/skill/fletching/items/bow/StringBow.java +++ /dev/null @@ -1,136 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.bow; - -import org.crandor.game.node.item.Item; -import org.crandor.game.world.update.flag.context.Animation; - -*/ -/** - * Represents the enum of stringing bows. - * @author 'Vexia - *//* - -public enum StringBow { - SHORT_BOW(new Item(50), new Item(841), 5, 5, new Animation(6678)), - LONG_BOW(new Item(48), new Item(839), 10, 10, new Animation(6684)), - OAK_SHORTBOW(new Item(54), new Item(843), 20, 16.5, new Animation(6679)), - OAK_LONGBOW(new Item(56), new Item(845), 25, 25, new Animation(6685)), - WILLOW_SHORTBOW(new Item(60), new Item(849), 35, 33.3, new Animation(6680)), - WILLOW_LONGBOW(new Item(58), new Item(847), 40, 41.5, new Animation(6686)), - MAPLE_SHORTBOW(new Item(64), new Item(853), 50, 50, new Animation(6681)), - MAPLE_LONGBOW(new Item(62), new Item(851), 55, 58.3, new Animation(6687)), - YEW_SHORTBOW(new Item(68), new Item(857), 65, 66, new Animation(6682)), - YEW_LONGBOW(new Item(66), new Item(855), 70, 75, new Animation(6688)), - MAGIC_SHORTBOW(new Item(72), new Item(861), 80, 83.3, new Animation(6683)), - MAGIC_LONGBOW(new Item(70), new Item(859), 85, 91.5, new Animation(6689)); - - - StringBow(final Item item, final Item product, final int level, final double experience, final Animation animation) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - this.animation = animation; - } - - */ -/** - * The item required. - *//* - - private final Item item; - - */ -/** - * The item product. - *//* - - private final Item product; - - */ -/** - * The level required. - *//* - - private final int level; - - */ -/** - * The experience required. - *//* - - private final double experience; - - */ -/** - * The animation of stringing. - *//* - - private final Animation animation; - - */ -/** - * Gets the item. - * @return The item. - *//* - - public Item getItem() { - return item; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Method used to get the animation. - * @return the animation. - *//* - - public Animation getAnimation() { - return animation; - } - - */ -/** - * Method used to get the string bow for the item. - * @param item the item. - * @return the string bow. - *//* - - public static StringBow forItem(final int id) { - for (StringBow bw : StringBow.values()) { - if (bw.getItem().getId() == id) { - return bw; - } - } - return null; - } -}*/ diff --git a/Server/src/main/content/global/skill/fletching/items/bow/StringPulse.java b/Server/src/main/content/global/skill/fletching/items/bow/StringPulse.java deleted file mode 100644 index fd1b2c560..000000000 --- a/Server/src/main/content/global/skill/fletching/items/bow/StringPulse.java +++ /dev/null @@ -1,92 +0,0 @@ -package content.global.skill.fletching.items.bow; - -import core.api.Container; -import core.api.ContentAPIKt; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.world.map.zone.ZoneBorders; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the skill pulse of stringing. - * - * @author Ceikry - */ -public class StringPulse extends SkillPulse { - - /** - * Represents the string bow. - */ - private final Fletching.String bow; - - /** - * The amount. - */ - private int amount; - - /** - * Constructs a new {@code StringbowPlugin.java} {@code Object}. - * - * @param player the player. - * @param node the node. - */ - public StringPulse(Player player, Item node, final Fletching.String bow, int amount) { - super(player, node); - this.bow = bow; - this.amount = amount; - } - - @Override - public boolean checkRequirements() { - if (getDelay() == 1) { - setDelay(2); - } - if (player.getSkills().getLevel(Skills.FLETCHING) < bow.level) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bow.level + " to string this bow."); - return false; - } - if (!player.getInventory().containsItem(new Item(bow.unfinished))) { - return false; - } - if (!player.getInventory().containsItem(new Item(bow.string))) { - player.getDialogueInterpreter().sendDialogue("You seem to have run out of bow strings."); - return false; - } - animate(); - return true; - } - - @Override - public void animate() { - player.animate(bow.animation); - } - - @Override - public boolean reward() { - if (player.getInventory().remove(new Item(bow.unfinished), new Item(bow.string))) { - player.getInventory().add(new Item(bow.product)); - player.getSkills().addExperience(Skills.FLETCHING, bow.experience, true); - player.getPacketDispatch().sendMessage("You add a string to the bow."); - - if (bow == Fletching.String.MAGIC_SHORTBOW - && (new ZoneBorders(2721, 3489, 2724, 3493, 0).insideBorder(player) - || new ZoneBorders(2727, 3487, 2730, 3490, 0).insideBorder(player)) - && player.getAttribute("diary:seers:fletch-magic-short-bow", false)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 2); - } - } - if (!player.getInventory().containsItem(new Item(bow.string)) || !player.getInventory().containsItem(new Item(bow.unfinished))) { - return true; - } - amount--; - return amount == 0; - } - - @Override - public void message(int type) { - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/crossbow/CrossbowPulse.java b/Server/src/main/content/global/skill/fletching/items/crossbow/CrossbowPulse.java deleted file mode 100644 index cfe0cf53b..000000000 --- a/Server/src/main/content/global/skill/fletching/items/crossbow/CrossbowPulse.java +++ /dev/null @@ -1,97 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.crossbow; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the skill pulse of stringing. - * @author 'Vexia - *//* - -public class CrossbowPulse extends SkillPulse { - - */ -/** - * Represents the bow string item. - *//* - - private final Item BOW_STRING = new Item(9438); - - */ -/** - * Represents the string bow. - *//* - - private final StringCross bow; - - */ -/** - * Represents the amount. - *//* - - private int amount; - - */ -/** - * Constructs a new {@code StringcrossbowPlugin.java} {@code Object}. - * @param player the player. - * @param node the node. - *//* - - public CrossbowPulse(Player player, Item node, final StringCross bow, int amount) { - super(player, node); - this.bow = bow; - this.amount = amount; - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(Skills.FLETCHING) < bow.getLevel()) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bow.getLevel() + " to string this crossbow."); - return false; - } - if (!player.getInventory().containsItem(BOW_STRING)) { - player.getDialogueInterpreter().sendDialogue("You seem to have run out of bow strings."); - return false; - } - return true; - } - - @Override - public void animate() { - player.animate(bow.getAnimation()); - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - super.setDelay(5); - return false; - } - if (player.getInventory().remove(bow.getItem(), BOW_STRING)) { - player.getInventory().add(bow.getProduct()); - player.getSkills().addExperience(Skills.FLETCHING, bow.getExperience(), true); - player.getPacketDispatch().sendMessage("You add a string to the crossbow."); - } - if (!player.getInventory().containsItem(BOW_STRING) || !player.getInventory().containsItem(bow.getItem())) { - return true; - } - amount--; - return amount == 0; - } - - @Override - public void message(int type) { - switch (type) { - case 0: - break; - case 1: - break; - } - } - -}*/ diff --git a/Server/src/main/content/global/skill/fletching/items/crossbow/Limb.java b/Server/src/main/content/global/skill/fletching/items/crossbow/Limb.java deleted file mode 100644 index 6ffc01413..000000000 --- a/Server/src/main/content/global/skill/fletching/items/crossbow/Limb.java +++ /dev/null @@ -1,128 +0,0 @@ -package content.global.skill.fletching.items.crossbow; - -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; - -/** - * Represents the enum for limbs. - * @author 'Vexia - */ -public enum Limb { - WOODEN_STOCK(new Item(9440), new Item(9420), new Item(9454), 9, 12, new Animation(4436)), - OAK_STOCK(new Item(9442), new Item(9422), new Item(9176), 24, 32, new Animation(4437)), - WILLOW_STOCK(new Item(9444), new Item(9423), new Item(9457), 39, 44, new Animation(4438)), - TEAK_STOCK(new Item(9446), new Item(9425), new Item(9459), 46, 54, new Animation(4439)), - MAPLE_STOCK(new Item(9448), new Item(9427), new Item(9461), 54, 64, new Animation(4440)), - MAHOGANY_STOCK(new Item(9450), new Item(9429), new Item(9463), 61, 82, new Animation(4441)), - YEW_STOCK(new Item(9452), new Item(9431), new Item(9465), 69, 100, new Animation(4442)); - - /** - * Constructs a new {@code StringcrosbowPlugin.java} {@code Object}. - * @param stock the stock. - * @param limb the limb. - * @param product the product. - * @param level the level. - * @param experience the experience. - * @param animation the animation. - */ - Limb(Item stock, Item limb, Item product, int level, double experience, Animation animation) { - this.stock = stock; - this.limb = limb; - this.product = product; - this.level = level; - this.experience = experience; - this.animation = animation; - } - - /** - * The stock. - */ - private final Item stock; - - /** - * The limb. - */ - private final Item limb; - - /** - * The product. - */ - private final Item product; - - /** - * The level. - */ - private final int level; - - /** - * The experience. - */ - private final double experience; - - /** - * The animation. - */ - private final Animation animation; - - /** - * Gets the stock. - * @return The stock. - */ - public Item getStock() { - return stock; - } - - /** - * Gets the limb. - * @return The limb. - */ - public Item getLimb() { - return limb; - } - - /** - * Gets the product. - * @return The product. - */ - public Item getProduct() { - return product; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Gets the animation. - * @return The animation. - */ - public Animation getAnimation() { - return animation; - } - - /** - * Method used to get the {@link Limb} for the item. - * @param item the item. - * @return the limb. - */ - public static Limb forItems(final Item item, final Item second) { - for (Limb l : Limb.values()) { - if (l.getLimb().getId() == item.getId() && l.getStock().getId() == second.getId() || l.getLimb().getId() == second.getId() && l.getStock().getId() == item.getId()) { - return l; - } - } - return null; - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/crossbow/LimbPulse.kt b/Server/src/main/content/global/skill/fletching/items/crossbow/LimbPulse.kt deleted file mode 100644 index 2d6d35562..000000000 --- a/Server/src/main/content/global/skill/fletching/items/crossbow/LimbPulse.kt +++ /dev/null @@ -1,52 +0,0 @@ -package content.global.skill.fletching.items.crossbow - -import core.game.node.entity.player.Player -import core.game.node.entity.skill.SkillPulse -import core.game.node.entity.skill.Skills -import content.global.skill.fletching.Fletching -import core.game.node.item.Item - -/** - * Represents the skill pulse of attaching limbs. - * @author Ceikry - */ -class LimbPulse(player: Player?, node: Item, private val limb: Fletching.Limb, private var amount: Int) : SkillPulse(player, node) { - override fun checkRequirements(): Boolean { - if (player.skills.getLevel(Skills.FLETCHING) < limb.level) { - player.dialogueInterpreter.sendDialogue("You need a fletching level of " + limb.level + " to attach these limbs.") - return false - } - if (!player.inventory.containsItem(Item(limb.limb))) { - player.dialogueInterpreter.sendDialogue("That's not the correct limb to attach.") - return false - } - if(!player.inventory.containsItem(Item(limb.stock))){ - player.dialogueInterpreter.sendDialogue("That's not the correct stock for that limb.") - return false - } - return player.inventory.containsItem(Item(limb.stock)) - } - - override fun animate() { - player.animate(limb.animation) - } - - override fun reward(): Boolean { - if (delay == 1) { - super.setDelay(6) - return false - } - if (player.inventory.remove(Item(limb.stock), Item(limb.limb))) { - player.inventory.add(Item(limb.product)) - player.skills.addExperience(Skills.FLETCHING, limb.experience, true) - player.packetDispatch.sendMessage("You attach the metal limbs to the stock.") - } - if (!player.inventory.containsItem(Item(limb.limb))) { - return true - } - amount-- - return amount == 0 - } - - override fun message(type: Int) {} -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/crossbow/StringCross.java b/Server/src/main/content/global/skill/fletching/items/crossbow/StringCross.java deleted file mode 100644 index 32671c480..000000000 --- a/Server/src/main/content/global/skill/fletching/items/crossbow/StringCross.java +++ /dev/null @@ -1,139 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.crossbow; - -import org.crandor.game.node.item.Item; -import org.crandor.game.world.update.flag.context.Animation; - -*/ -/** - * Represents the enum of stringing crossbows. - * @author 'Vexia - *//* - -public enum StringCross { - BRONZE_CBOW(new Item(9454), new Item(9174), 9, 6, new Animation(6671)), - BLURITE_CBOW(new Item(9456), new Item(9176), 24, 16, new Animation(6672)), - IRON_CBOW(new Item(9457), new Item(9177), 39, 22, new Animation(6673)), - STEEL_CBOW(new Item(9459), new Item(9179), 46, 27, new Animation(6674)), - MITHIRIL_CBOW(new Item(9461), new Item(9181), 54, 32, new Animation(6675)), - ADAMANT_CBOW(new Item(9463), new Item(9183), 61, 41, new Animation(6676)), - RUNITE_CBOW(new Item(9465), new Item(9185), 69, 50, new Animation(6677)); - */ -/** - * Constructs a new {@code StringcrossbowPlugin.java} {@code Object}. - * @param item the item. - * @param product the product. - * @param level the level. - * @param experience the experience. - *//* - - StringCross(final Item item, final Item product, final int level, final double experience, final Animation animation) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - this.animation = animation; - } - - */ -/** - * The item required. - *//* - - private final Item item; - - */ -/** - * The item product. - *//* - - private final Item product; - - */ -/** - * The level required. - *//* - - private final int level; - - */ -/** - * The experience required. - *//* - - private final double experience; - - */ -/** - * The animation of stringing. - *//* - - private final Animation animation; - - */ -/** - * Gets the item. - * @return The item. - *//* - - public Item getItem() { - return item; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Method used to get the animation. - * @return the animation. - *//* - - public Animation getAnimation() { - return animation; - } - - */ -/** - * Method used to get the string bow for the item. - * @param item the item. - * @return the string bow. - *//* - - public static StringCross forItem(final Item item) { - for (StringCross bw : StringCross.values()) { - if (bw.getItem().getId() == item.getId()) { - return bw; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/items/darts/Dart.java b/Server/src/main/content/global/skill/fletching/items/darts/Dart.java deleted file mode 100644 index 235849496..000000000 --- a/Server/src/main/content/global/skill/fletching/items/darts/Dart.java +++ /dev/null @@ -1,96 +0,0 @@ -package content.global.skill.fletching.items.darts; - -import core.game.node.item.Item; - -/** - * Represents the enum to hold dart info. - * @author 'Vexia - */ -public enum Dart { - BRONZE_DART(new Item(819), new Item(806), 1, 1.8), - IRON_DART(new Item(820), new Item(807), 22, 3.8), - STEEL_DART(new Item(821), new Item(808), 37, 7.5), - MITHRIL_DART(new Item(822), new Item(809), 52, 11.2), - ADAMANT_DART(new Item(823), new Item(810), 67, 15), - RUNE_DART(new Item(824), new Item(811), 81, 18.8), - DRAGON_DART(new Item(11232), new Item(11230), 95, 25); - /** - * Constructs a new {@code Dart} {@code Object}. - * @param item the item. - * @param product the product. - * @param level the level. - * @param experience the experience. - */ - Dart(final Item item, final Item product, final int level, final double experience) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - } - - /** - * Represents the item required. - */ - private final Item item; - - /** - * Represents the product gained. - */ - private final Item product; - - /** - * Represents the level required. - */ - private final int level; - - /** - * Represents the experience gained. - */ - private final double experience; - - /** - * Gets the item. - * @return The item. - */ - public Item getItem() { - return item; - } - - /** - * Gets the product. - * @return The product. - */ - public Item getProduct() { - return product; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Method used to get the dart for the item. - * @param item the item. - * @return the dart. - */ - public static Dart forItem(final Item item) { - for (Dart dart : Dart.values()) { - if (dart.getItem().getId() == item.getId()) { - return dart; - } - } - return null; - } -} diff --git a/Server/src/main/content/global/skill/fletching/items/darts/DartPulse.java b/Server/src/main/content/global/skill/fletching/items/darts/DartPulse.java deleted file mode 100644 index 6f21ba8d7..000000000 --- a/Server/src/main/content/global/skill/fletching/items/darts/DartPulse.java +++ /dev/null @@ -1,104 +0,0 @@ -package content.global.skill.fletching.items.darts; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -import static core.api.ContentAPIKt.*; - -/** - * Represents the dart pulse. - * @author ceikry - */ -public final class DartPulse extends SkillPulse { - - /** - * Represents the feather item. - */ - private static final Item FEATHER = new Item(314); - - /** - * Represents the dart. - */ - private final Fletching.Darts dart; - - /** - * Represents the sets to make. - */ - private int sets; - - /** - * Constructs a new {@code DartPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - */ - public DartPulse(Player player, Item node, Fletching.Darts dart, int sets) { - super(player, node); - this.dart = dart; - this.sets = sets; - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(Skills.FLETCHING) < dart.level) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + dart.level + " to do this."); - return false; - } - if (!player.getQuestRepository().isComplete("The Tourist Trap")){ - player.getDialogueInterpreter().sendDialogue("You need to have completed Tourist Trap to fletch darts."); - return false; - } - if (!hasSpaceFor(player, dart.getFinished())) { - sendDialogue(player, "You do not have enough inventory space."); - return false; - } - return true; - } - - @Override - public void animate() { - - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - super.setDelay(3); - } - final Item unfinished = dart.getUnfinished(); - final int dartAmount = player.getInventory().getAmount(unfinished); - final int featherAmount = player.getInventory().getAmount(FEATHER); - if (dartAmount >= 10 && featherAmount >= 10) { - FEATHER.setAmount(10); - unfinished.setAmount(10); - player.getPacketDispatch().sendMessage("You attach feathers to 10 darts."); - } else { - int amount = featherAmount > dartAmount ? dartAmount : featherAmount; - FEATHER.setAmount(amount); - unfinished.setAmount(amount); - player.getPacketDispatch().sendMessage(amount == 1 ? "You attach a feather to a dart." : "You attach feathers to " + amount + " darts."); - } - if (player.getInventory().remove(FEATHER, unfinished)) { - Item product = dart.getFinished(); - product.setAmount(FEATHER.getAmount()); - player.getSkills().addExperience(Skills.FLETCHING, dart.experience * product.getAmount(), true); - player.getInventory().add(product); - } - FEATHER.setAmount(1); - if (!player.getInventory().containsItem(FEATHER)) { - return true; - } - if (!player.getInventory().containsItem(dart.getUnfinished())) { - return true; - } - sets--; - return sets == 0; - } - - @Override - public void message(int type) { - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/gem/Gem.java b/Server/src/main/content/global/skill/fletching/items/gem/Gem.java deleted file mode 100644 index 6a3377aa4..000000000 --- a/Server/src/main/content/global/skill/fletching/items/gem/Gem.java +++ /dev/null @@ -1,101 +0,0 @@ -package content.global.skill.fletching.items.gem; - -import core.game.node.item.Item; - -/** - * Represents gems to cut into bolt tips. - * @author 'Vexia - * @date 01/12/2013 - */ -public enum Gem { - OPAL(new Item(1609), new Item(45, 12), 11, 1.5), - JADE(new Item(1611), new Item(9187, 12), 26, 2.4), - RED_TOPAZ(new Item(1613), new Item(9188, 12), 48, 3.9), - SAPPHIRE(new Item(1607), new Item(9189, 12), 56, 4), - EMERALD(new Item(1605), new Item(9190, 12), 58, 5.5), - RUBY(new Item(1603), new Item(9191, 12), 63, 6.3), - DIAMOND(new Item(1601), new Item(9192, 12), 65, 7), - DRAGONSTONE(new Item(1615), new Item(9193, 12), 71, 8.2), - ONYX(new Item(6573), new Item(9194, 24), 73, 9.4); - - /** - * Constructs a new {@code Gem.java} {@code Object}. - * @param gem the gem. - * @param bolt the bolt. - * @param level the level. - * @param experience the experience. - */ - Gem(Item gem, Item bolt, int level, double experience) { - this.gem = gem; - this.bolt = bolt; - this.level = level; - this.experience = experience; - } - - /** - * Represents the gem. - */ - private final Item gem; - - /** - * Represents the bolt. - */ - private final Item bolt; - - /** - * Represents the level required. - */ - private final int level; - - /** - * Represents the experience gained. - */ - private final double experience; - - /** - * Gets the gem. - * @return The gem. - */ - public Item getGem() { - return gem; - } - - /** - * Gets the bolt. - * @return The bolt. - */ - public Item getBolt() { - return bolt; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Method used to get a gem for the item. - * @param item the item. - * @return the gem. - */ - public static Gem forItem(final Item item) { - for (Gem gem : values()) { - if (gem.getGem().getId() == item.getId()) { - return gem; - } - } - return null; - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/gem/GemBolt.java b/Server/src/main/content/global/skill/fletching/items/gem/GemBolt.java deleted file mode 100644 index b21e6a164..000000000 --- a/Server/src/main/content/global/skill/fletching/items/gem/GemBolt.java +++ /dev/null @@ -1,144 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.gem; - -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents a gem bolt. - * @author 'Vexia - * @date 01/12/2013 - *//* - -public enum GemBolt { - OPAL(new Item(877, 10), new Item(45, 10), new Item(879, 10), 11, 1.5), - PEARL(new Item(9140, 10), new Item(46, 10), new Item(880, 10), 41, 3.2), - JADE(new Item(9139, 10), new Item(9187, 10), new Item(9335, 10), 26, 2.4), - RED_TOPAZ(new Item(9141, 10), new Item(9188, 10), new Item(9336, 10), 48, 3.9), - SAPPHIRE(new Item(9142, 10), new Item(9189, 10), new Item(9337, 10), 56, 4), - EMERALD(new Item(9142, 10), new Item(9190, 10), new Item(9338, 10), 58, 5.5), - RUBY(new Item(9143, 10), new Item(9191, 10), new Item(9339, 10), 63, 6.3), - DIAMOND(new Item(9143, 10), new Item(9192, 10), new Item(9340, 10), 65, 7), - DRAGONSTONE(new Item(9144, 10), new Item(9193, 10), new Item(9341, 10), 71, 8.2), - ONYX(new Item(9144, 10), new Item(9194, 10), new Item(9342, 10), 73, 9.4); - - */ -/** - * Constructs a new {@code GemBolt} {@code Object}. - * @param base the base. - * @param tip the tip. - * @param level the level. - * @param experience the experience. - *//* - - GemBolt(Item base, Item tip, Item product, int level, double experience) { - this.base = base; - this.tip = tip; - this.product = product; - this.level = level; - this.experience = experience; - } - - */ -/** - * Represents the base item. - *//* - - private final Item base; - - */ -/** - * Represents the tip to attach. - *//* - - private final Item tip; - - */ -/** - * Represents the product. - *//* - - private final Item product; - - */ -/** - * Represents the level. - *//* - - private final int level; - - */ -/** - * Represents the experience. - *//* - - private final double experience; - - */ -/** - * Gets the base. - * @return The base. - *//* - - public Item getBase() { - return base; - } - - */ -/** - * Gets the tip. - * @return The tip. - *//* - - public Item getTip() { - return tip; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Method used to get the gem bolt from the id. - * @param boltt the boltt. - * @param tip the tip. - * @return the bolt. - *//* - - public static GemBolt forItems(final Item boltt, final Item tip) { - for (GemBolt bolt : values()) { - if (bolt.getBase().getId() == boltt.getId() && bolt.getTip().getId() == tip.getId() || bolt.getBase().getId() == tip.getId() && bolt.getTip().getId() == boltt.getId()) { - return bolt; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltCutPulse.kt b/Server/src/main/content/global/skill/fletching/items/gem/GemBoltCutPulse.kt deleted file mode 100644 index e03781529..000000000 --- a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltCutPulse.kt +++ /dev/null @@ -1,68 +0,0 @@ -package content.global.skill.fletching.items.gem - -import core.game.node.entity.player.Player -import core.game.node.entity.skill.SkillPulse -import core.game.node.entity.skill.Skills -import content.global.skill.fletching.Fletching.GemBolts -import core.game.node.item.Item -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items - -/** - * Represents the gem cutting pulse(gem to bolt). - * @author Ceikry - */ -class GemBoltCutPulse -/** - * Constructs a new `GemCutPulse.java` `Object`. - * @param player the player. - * @param node the node. - * @param amount the amount. - */(player: Player?, node: Item?, - /** - * Represents the gem we're cutting. - */ - private val gem: GemBolts, - /** - * Represents the amount to make. - */ - private var amount: Int) : SkillPulse(player, node) { - /** - * Represents the ticks passed. - */ - private var ticks = 0 - - override fun checkRequirements(): Boolean { - if (player.skills.getLevel(Skills.FLETCHING) < gem.level) { - player.dialogueInterpreter.sendDialogue("You need a Fletching level of " + gem.level + " or above to do that.") - return false - } - return player.inventory.containsItem(Item(gem.gem)) - } - - override fun animate() { - if (ticks % 6 == 0) { - player.animate(ANIMATION) - } - } - - override fun reward(): Boolean { - if (++ticks % 5 != 0) { - return false - } - val reward = if (gem.gem == Items.OYSTER_PEARLS_413) Item(gem.tip, 24) else Item(gem.tip, 12) - if (player.inventory.remove(Item(gem.gem))) { - player.inventory.add(reward) - player.skills.addExperience(Skills.FLETCHING, gem.experience, true) - } - amount-- - return amount <= 0 - } - - companion object { - /** - * Represents the cutting animation. - */ - private val ANIMATION = Animation(6702) - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltPulse.java b/Server/src/main/content/global/skill/fletching/items/gem/GemBoltPulse.java deleted file mode 100644 index 4cd856e8a..000000000 --- a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltPulse.java +++ /dev/null @@ -1,91 +0,0 @@ -package content.global.skill.fletching.items.gem; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the attaching of a gem bolt to a premade bolt. - * @author Ceikry - */ -public final class GemBoltPulse extends SkillPulse { - - /** - * Represents the gem bolt being made. - */ - private Fletching.GemBolts bolt; - - /** - * Represents the sets to make. - */ - private int sets = 0; - - /** - * Represents the ticks passed. - */ - private int ticks; - - /** - * Constructs a new {@code GemBoltPulse} {@code Object}. - * @param player the player. - * @param node the node. - * @param sets the sets. - */ - public GemBoltPulse(Player player, Item node, Fletching.GemBolts bolt, int sets) { - super(player, node); - this.bolt = bolt; - this.sets = sets; - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(Skills.FLETCHING) < bolt.level) { - player.getDialogueInterpreter().sendDialogue("You need a Fletching level of " + bolt.level + " or above to do that."); - return false; - } - if (!player.getInventory().containsItem(new Item(bolt.base)) || !player.getInventory().containsItem(new Item(bolt.tip))) { - return false; - } - if (!player.getInventory().hasSpaceFor(new Item(bolt.product))){ - player.getDialogueInterpreter().sendDialogue("You do not have enough inventory space."); - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (++ticks % 3 != 0) { - return false; - } - int baseAmount = player.getInventory().getAmount(bolt.base); - int tipAmount = player.getInventory().getAmount(bolt.tip); - Item base = new Item(bolt.base); - Item tip = new Item(bolt.tip); - Item product = new Item(bolt.product); - if(baseAmount >= 10 && tipAmount >= 10){ - base.setAmount(10); - tip.setAmount(10); - product.setAmount(10); - } else { - int amount = baseAmount > tipAmount ? tipAmount : baseAmount; - base.setAmount(amount); - tip.setAmount(amount); - product.setAmount(amount); - } - if (player.getInventory().remove(base,tip)) { - player.getInventory().add(product); - player.getSkills().addExperience(Skills.FLETCHING, bolt.experience * product.getAmount(), true); - player.getPacketDispatch().sendMessage(product.getAmount() == 1 ? "You attach the tip to the bolt." : "You fletch " + product.getAmount() + " bolts."); - } - sets--; - return sets <= 0; - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/grapple/GrapplePulse.java b/Server/src/main/content/global/skill/fletching/items/grapple/GrapplePulse.java deleted file mode 100644 index cbba3820f..000000000 --- a/Server/src/main/content/global/skill/fletching/items/grapple/GrapplePulse.java +++ /dev/null @@ -1,85 +0,0 @@ -package content.global.skill.fletching.items.grapple; - -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; - -/** - * Represents the skill pulse used to create a mith grapple. - * @author 'Vexia - * @date 21/12/2013 - */ -public final class GrapplePulse extends SkillPulse { - - /** - * Represents the mith grapple tip. - */ - private static final Item MITH_GRAPPLE = new Item(9418); - - /** - * Represents the mithril grapple tio. - */ - private static final Item GRAPPLE_TIP = new Item(9416); - - /** - * Represents the mithril bolt. - */ - private static final Item MITHRIL_BOLT = new Item(9142); - - /** - * Represents the amount of the grapple to make. - */ - private int amount; - - /** - * Constructs a new {@code GrapplePulse} {@code Object}. - * @param player the player. - * @param node the node. - * @param amount the amount. - */ - public GrapplePulse(Player player, Item node, int amount) { - super(player, node); - this.amount = amount; - } - - @Override - public boolean checkRequirements() { - int inventoryAmount = player.getInventory().getAmount(GRAPPLE_TIP); - if (amount > inventoryAmount) { - amount = inventoryAmount; - } - if (!player.getInventory().containsItem(GRAPPLE_TIP) || !player.getInventory().containsItem(MITHRIL_BOLT)) { - return false; - } - if (player.getSkills().getLevel(Skills.FLETCHING) < 59) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of at least 59 in order to do this."); - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - setDelay(3); - return false; - } - if (player.getInventory().remove(GRAPPLE_TIP) && player.getInventory().remove(MITHRIL_BOLT)) { - player.getInventory().add(MITH_GRAPPLE); - player.getSkills().addExperience(Skills.FLETCHING, 5, true); - } - amount--; - return amount < 1; - } - - @Override - public void message(int type) { - - } - -} diff --git a/Server/src/main/content/global/skill/fletching/log/CraftItemWithLogScript.kt b/Server/src/main/content/global/skill/fletching/log/CraftItemWithLogScript.kt new file mode 100644 index 000000000..33e6c5524 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/log/CraftItemWithLogScript.kt @@ -0,0 +1,113 @@ +package content.global.skill.fletching.log + +import content.global.skill.fletching.AchievementDiaryAttributeKeys +import content.global.skill.fletching.Zones +import content.global.skill.fletching.log.GrammarHelpers.aOrAn +import content.global.skill.fletching.log.GrammarHelpers.makeFriendlyName +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.Items + +/** + * Represents the queueScript used to craft things by carving a log + * @author ceik + * @param player the player. + * @param logCraftInfo crafting info about what we're making out of the log + * @param amount iterations of craft to run + */ +class CraftItemWithLogScript( + private val player: Player, + private val logCraftInfo: LogCraftInfo, + private val amount: Int +) { + + private val initialDelay = 1 + + // src https://gitlab.com/2009scape/2009scape/-/merge_requests/1960#note_2702231552 + private val subsequentDelay = 3 + + private val carveLogAnimation = Animation(1248) + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < logCraftInfo.level) { + LogCraftableListeners.sendLevelCheckFailDialog(player, logCraftInfo) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + if (removeItem(player, logCraftInfo.logItemId.asItem())) { + player.animate(carveLogAnimation) + + val finishedItemName = makeFriendlyName(Item(logCraftInfo.finishedItemId).name) + + val amountToCraft = when (logCraftInfo) { + LogCraftInfo.OGRE_ARROW_SHAFT -> RandomFunction.random(2, 6) + LogCraftInfo.ARROW_SHAFT -> 15 + else -> 1 + } + + if (logCraftInfo == LogCraftInfo.OGRE_COMP_BOW) { + if (!removeItem(player, Items.WOLF_BONES_2859)) { + return@queueScript stopExecuting(player) + } + } + + sendCraftMessageToPlayer(amountToCraft, finishedItemName) + + addItem(player, logCraftInfo.finishedItemId, amountToCraft) + rewardXP(player, Skills.FLETCHING, logCraftInfo.experience) + + if (logCraftInfo == LogCraftInfo.MAGIC_SHORTBOW) { + handleSeersMagicShortbowAchievement() + } + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= amount - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } + + private fun handleSeersMagicShortbowAchievement() { + if (Zones.inAnyZone(player, Zones.seersMagicShortbowAchievementZones) && + !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 2) + ) { + setAttribute( + player, + "/save:${AchievementDiaryAttributeKeys.FLETCHED_UNSTRUNG_MAGIC_SHORTBOW}", + true + ) + } + } + + private fun sendCraftMessageToPlayer(amountToCraft: Int, finishedItemName: String) { + when (logCraftInfo) { + LogCraftInfo.ARROW_SHAFT -> sendMessage( + player, + "You carefully cut the wood into $amountToCraft arrow shafts." + ) + + LogCraftInfo.OGRE_ARROW_SHAFT -> sendMessage( + player, + "You carefully cut the wood into $amountToCraft arrow shafts." + ) + + else -> sendMessage( + player, + "You carefully cut the wood into ${aOrAn(finishedItemName)} $finishedItemName." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/log/GrammarHelpers.kt b/Server/src/main/content/global/skill/fletching/log/GrammarHelpers.kt new file mode 100644 index 000000000..5863c51ea --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/log/GrammarHelpers.kt @@ -0,0 +1,20 @@ +package content.global.skill.fletching.log + +import core.tools.StringUtils + +object GrammarHelpers { + fun makeFriendlyName(name: String): String { + return name.replace("(u)", "").trim { it <= ' ' } + } + + /** + * Returns "a" or "an" depending on whether the word begins with a vowel + */ + @Suppress("GrazieInspection") + fun aOrAn(word: String): String { + return when (StringUtils.isPlusN(word)) { + true -> "an" + false -> "a" + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/log/LogCraftInfo.kt b/Server/src/main/content/global/skill/fletching/log/LogCraftInfo.kt new file mode 100644 index 000000000..2111606dc --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/log/LogCraftInfo.kt @@ -0,0 +1,63 @@ +package content.global.skill.fletching.log + +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different items from logs + * @property logItemId the log item id that can be used to craft this item + * @property finishedItemId the resulting finished bolt Item id. + * @property level the level required to craft. + * @property experience the experience gained by crafting. + */ +enum class LogCraftInfo(val logItemId: Int, val finishedItemId: Int, val experience: Double, val level: Int) { + ARROW_SHAFT(Items.LOGS_1511, Items.ARROW_SHAFT_52, 5.0, 1), + SHORT_BOW(Items.LOGS_1511, Items.SHORTBOW_U_50, 5.0, 5), + LONG_BOW(Items.LOGS_1511, Items.LONGBOW_U_48, 10.0, 10), + WOODEN_STOCK(Items.LOGS_1511, Items.WOODEN_STOCK_9440, 6.0, 9), + + OGRE_ARROW_SHAFT(Items.ACHEY_TREE_LOGS_2862, Items.OGRE_ARROW_SHAFT_2864, 6.4, 5), + OGRE_COMP_BOW(Items.ACHEY_TREE_LOGS_2862, Items.UNSTRUNG_COMP_BOW_4825, 45.0, 30), + + OAK_SHORTBOW(Items.OAK_LOGS_1521, Items.OAK_SHORTBOW_U_54, 16.5, 20), + OAK_LONGBOW(Items.OAK_LOGS_1521, Items.OAK_LONGBOW_U_56, 25.0, 25), + OAK_STOCK(Items.OAK_LOGS_1521, Items.OAK_STOCK_9442, 16.0, 24), + + WILLOW_SHORTBOW(Items.WILLOW_LOGS_1519, Items.WILLOW_SHORTBOW_U_60, 33.3, 35), + WILLOW_LONGBOW(Items.WILLOW_LOGS_1519, Items.WILLOW_LONGBOW_U_58, 41.5, 40), + WILLOW_STOCK(Items.WILLOW_LOGS_1519, Items.WILLOW_STOCK_9444, 22.0, 39), + + MAPLE_SHORTBOW(Items.MAPLE_LOGS_1517, Items.MAPLE_SHORTBOW_U_64, 50.0, 50), + MAPLE_LONGBOW(Items.MAPLE_LOGS_1517, Items.MAPLE_LONGBOW_U_62, 58.3, 55), + MAPLE_STOCK(Items.MAPLE_LOGS_1517, Items.MAPLE_STOCK_9448, 32.0, 54), + + YEW_SHORTBOW(Items.YEW_LOGS_1515, Items.YEW_SHORTBOW_U_68, 67.5, 65), + YEW_LONGBOW(Items.YEW_LOGS_1515, Items.YEW_LONGBOW_U_66, 75.0, 70), + YEW_STOCK(Items.YEW_LOGS_1515, Items.YEW_STOCK_9452, 50.0, 69), + + MAGIC_SHORTBOW(Items.MAGIC_LOGS_1513, Items.MAGIC_SHORTBOW_U_72, 83.3, 80), + MAGIC_LONGBOW(Items.MAGIC_LOGS_1513, Items.MAGIC_LONGBOW_U_70, 91.5, 85), + + TEAK_STOCK(Items.TEAK_LOGS_6333, Items.TEAK_STOCK_9446, 27.0, 46), + + MAHOGANY_STOCK(Items.MAHOGANY_LOGS_6332, Items.MAHOGANY_STOCK_9450, 41.0, 61); + + + companion object { + val logIds: IntArray = LogCraftInfo.values().map { logCraftInfo: LogCraftInfo -> logCraftInfo.logItemId }.distinct().toIntArray() + + private val logCraftablesByLogId = associateCraftInfoByLogIds() + private fun associateCraftInfoByLogIds(): Map> { + val map = HashMap>() + + for (logId in logIds) { + map[logId] = LogCraftInfo.values().filter { logCraftInfo: LogCraftInfo -> logCraftInfo.logItemId == logId } + } + + return map + } + + fun forLogId(logId: Int): List? { + return logCraftablesByLogId[logId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/log/LogCraftableListeners.kt b/Server/src/main/content/global/skill/fletching/log/LogCraftableListeners.kt new file mode 100644 index 000000000..fde0d5ff4 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/log/LogCraftableListeners.kt @@ -0,0 +1,83 @@ +package content.global.skill.fletching.log + +import content.data.Quests +import content.global.skill.fletching.log.GrammarHelpers.aOrAn +import content.global.skill.fletching.log.GrammarHelpers.makeFriendlyName +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 LogCraftableListeners : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.KNIFE_946, *LogCraftInfo.logIds) { player, knife, log -> + val applicableCraftInfosForLog = LogCraftInfo.forLogId(log.id) ?: return@onUseWith false + val craftableItems = + applicableCraftInfosForLog.map { logCraftable -> Item(logCraftable.finishedItemId) }.toTypedArray() + + val handler: SkillDialogueHandler = object : SkillDialogueHandler( + player, + SkillDialogue.forLength(applicableCraftInfosForLog.size), + *craftableItems + ) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements(applicableCraftInfosForLog[index])) return + CraftItemWithLogScript(player, applicableCraftInfosForLog[index], amount).invoke() + } + + private fun playerMeetsInitialRequirements(logCraftInfo: LogCraftInfo): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < logCraftInfo.level) { + sendLevelCheckFailDialog(player, logCraftInfo) + return false + } + if (logCraftInfo == LogCraftInfo.OGRE_ARROW_SHAFT && + !isQuestStarted(player, Quests.BIG_CHOMPY_BIRD_HUNTING) + ) { + sendDialogue(player, "You must have started ${Quests.BIG_CHOMPY_BIRD_HUNTING} to make those.") + return false + } + if (logCraftInfo == LogCraftInfo.OGRE_COMP_BOW) { + if (getQuestStage(player, Quests.ZOGRE_FLESH_EATERS) < 8) { + sendMessage( + player, + "You must have started Zogre Flesh Eaters and asked Grish to string this." + ) + return false + } + if (amountInInventory(player, Items.WOLF_BONES_2859) < 1) { + sendMessage(player, "You need to have Wolf Bones in order to make this.") + return false + } + } + return true + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, log.id) + } + } + handler.open() + return@onUseWith true + } + } + + companion object { + fun sendLevelCheckFailDialog(player: Player, logCraftInfo: LogCraftInfo) { + val finishedItem = Item(logCraftInfo.finishedItemId) + val friendlyCraftedItemName = makeFriendlyName(finishedItem.name) + sendDialogue( + player, + "You need a Fletching skill of ${logCraftInfo.level} or above to make ${ + aOrAn( + friendlyCraftedItemName + ) + } $friendlyCraftedItemName." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/stringing/StringItemScript.kt b/Server/src/main/content/global/skill/fletching/stringing/StringItemScript.kt new file mode 100644 index 000000000..633e29667 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/stringing/StringItemScript.kt @@ -0,0 +1,71 @@ +package content.global.skill.fletching.stringing + +import content.global.skill.fletching.AchievementDiaryAttributeKeys +import content.global.skill.fletching.Zones +import content.global.skill.fletching.stringing.StringableCraftInfo.Companion.applicableStringId +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.node.item.Item + +/** + * Represents queueScript to string a bow/crossbow + * @author Ceikry + * @param player the player. + * @param stringableCraftInfo contains crafting information about what we're stringing + * @param amount the amount of items to string + */ +class StringItemScript( + private val player: Player, + private val stringableCraftInfo: StringableCraftInfo, + private var amount: Int +) { + private val initialDelay = 1 + private val subsequentDelay = 2 + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < stringableCraftInfo.level) { + StringingListeners.sendLevelCheckFailDialogue(player, stringableCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(stringableCraftInfo.unstrungItemId), + Item(stringableCraftInfo.applicableStringId) + ) + ) { + player.animate(stringableCraftInfo.animation) + + addItem(player, stringableCraftInfo.strungItemId, 1) + rewardXP(player, Skills.FLETCHING, stringableCraftInfo.experience) + sendMessage(player, "You add a string to the bow.") + if (stringableCraftInfo == StringableCraftInfo.MAGIC_SHORTBOW) { + handleSeersMagicShortbowAchievement() + } + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= amount - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } + + private fun handleSeersMagicShortbowAchievement() { + if (Zones.inAnyZone(player, Zones.seersMagicShortbowAchievementZones) && + getAttribute(player, AchievementDiaryAttributeKeys.FLETCHED_UNSTRUNG_MAGIC_SHORTBOW, false) + ) { + player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 2) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/stringing/StringableCraftInfo.kt b/Server/src/main/content/global/skill/fletching/stringing/StringableCraftInfo.kt new file mode 100644 index 000000000..38c089621 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/stringing/StringableCraftInfo.kt @@ -0,0 +1,66 @@ +package content.global.skill.fletching.stringing + +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different strung items + * @property isCrossbow is this enum for a crossbow? + * @property unstrungItemId the unstrung item Id + * @property strungItemId the resulting strung item Id + * @property level the level required to string the item + * @property experience experience gained from stringing + * @property animation the stringing animation to play + */ +enum class StringableCraftInfo( + val isCrossbow: Boolean, + val unstrungItemId: Int, + val strungItemId: Int, + val level: Int, + val experience: Double, + val animation: Animation +) { + SHORT_BOW(false, Items.SHORTBOW_U_50, Items.SHORTBOW_841, 5, 5.0, Animation(6678)), + LONG_BOW(false, Items.LONGBOW_U_48, Items.LONGBOW_839, 10, 10.0, Animation(6684)), + OAK_SHORTBOW(false, Items.OAK_SHORTBOW_U_54, Items.OAK_SHORTBOW_843, 20, 16.5, Animation(6679)), + OAK_LONGBOW(false, Items.OAK_LONGBOW_U_56, Items.OAK_LONGBOW_845, 25, 25.0, Animation(6685)), + OGRE_COMP_BOW(false, Items.UNSTRUNG_COMP_BOW_4825, Items.COMP_OGRE_BOW_4827, 30, 45.0, Animation(-1)), + WILLOW_SHORTBOW(false, Items.WILLOW_SHORTBOW_U_60, Items.WILLOW_SHORTBOW_849, 35, 33.3, Animation(6680)), + WILLOW_LONGBOW(false, Items.WILLOW_LONGBOW_U_58, Items.WILLOW_LONGBOW_847, 40, 41.5, Animation(6686)), + MAPLE_SHORTBOW(false, Items.MAPLE_SHORTBOW_U_64, Items.MAPLE_SHORTBOW_853, 50, 50.0, Animation(6681)), + MAPLE_LONGBOW(false, Items.MAPLE_LONGBOW_U_62, Items.MAPLE_LONGBOW_851, 55, 58.3, Animation(6687)), + YEW_SHORTBOW(false, Items.YEW_SHORTBOW_U_68, Items.YEW_SHORTBOW_857, 65, 67.5, Animation(6682)), + YEW_LONGBOW(false, Items.YEW_LONGBOW_U_66, Items.YEW_LONGBOW_855, 70, 75.0, Animation(6688)), + MAGIC_SHORTBOW(false, Items.MAGIC_SHORTBOW_U_72, Items.MAGIC_SHORTBOW_861, 80, 83.3, Animation(6683)), + MAGIC_LONGBOW(false, Items.MAGIC_LONGBOW_U_70, Items.MAGIC_LONGBOW_859, 85, 91.5, Animation(6689)), + + BRONZE_CBOW(true, Items.BRONZE_CBOW_U_9454, Items.BRONZE_CROSSBOW_9174, 9, 6.0, Animation(6671)), + BLURITE_CBOW(true, Items.BLURITE_CBOW_U_9456, Items.BLURITE_CROSSBOW_9176, 24, 16.0, Animation(6672)), + IRON_CBOW(true, Items.IRON_CBOW_U_9457, Items.IRON_CROSSBOW_9177, 39, 22.0, Animation(6673)), + STEEL_CBOW(true, Items.STEEL_CBOW_U_9459, Items.STEEL_CROSSBOW_9179, 46, 27.0, Animation(6674)), + MITHIRIL_CBOW(true, Items.MITHRIL_CBOW_U_9461, Items.MITH_CROSSBOW_9181, 54, 32.0, Animation(6675)), + ADAMANT_CBOW(true, Items.ADAMANT_CBOW_U_9463, Items.ADAMANT_CROSSBOW_9183, 61, 41.0, Animation(6676)), + RUNITE_CBOW(true, Items.RUNITE_CBOW_U_9465, Items.RUNE_CROSSBOW_9185, 69, 50.0, Animation(6677)); + + companion object { + val unstrungBowIds: IntArray = values().map { stringableCraftInfo: StringableCraftInfo -> stringableCraftInfo.unstrungItemId }.toIntArray() + val stringItemIds: IntArray = intArrayOf(Items.BOW_STRING_1777,Items.CROSSBOW_STRING_9438) + + private val stringableCraftInfoByUnstrungItemId = values().associateBy { it.unstrungItemId } + + fun forUnstrungBowItemId(unstrungItemId: Int): StringableCraftInfo? { + return stringableCraftInfoByUnstrungItemId[unstrungItemId] + } + + /** + * Returns the item id that should be used for stringing this enums unstrung bow + */ + val StringableCraftInfo.applicableStringId: Int + get() { + return when { + this.isCrossbow -> Items.CROSSBOW_STRING_9438 + else -> Items.BOW_STRING_1777 + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/stringing/StringingListeners.kt b/Server/src/main/content/global/skill/fletching/stringing/StringingListeners.kt new file mode 100644 index 000000000..051f8102b --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/stringing/StringingListeners.kt @@ -0,0 +1,69 @@ +package content.global.skill.fletching.stringing + +import content.data.Quests +import content.global.skill.fletching.stringing.StringableCraftInfo.Companion.applicableStringId +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 core.net.packet.PacketRepository +import core.net.packet.context.ChildPositionContext +import core.net.packet.out.RepositionChild +import org.rs09.consts.Components + +@Suppress("unused") // Reflectively loaded +class StringingListeners : InteractionListener { + override fun defineListeners() { + onUseWith( + IntType.ITEM, + StringableCraftInfo.stringItemIds, + *StringableCraftInfo.unstrungBowIds + ) { player, string, unstrungBow -> + val stringableCraftInfo = StringableCraftInfo.forUnstrungBowItemId(unstrungBow.id) ?: return@onUseWith false + if (string.id != stringableCraftInfo.applicableStringId) { + sendMessage(player, "That's not the right kind of string for this.") + return@onUseWith true + } + if (stringableCraftInfo == StringableCraftInfo.OGRE_COMP_BOW && getQuestStage(player, Quests.ZOGRE_FLESH_EATERS) < 8) { + sendMessage(player, "You must have started Zogre Flesh Eaters and asked Grish to string this.") + return@onUseWith true + } + val handler: SkillDialogueHandler = + object : + SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(stringableCraftInfo.strungItemId)) { + override fun create(amount: Int, index: Int) { + if (getDynLevel(player, Skills.FLETCHING) < stringableCraftInfo.level) { + sendLevelCheckFailDialogue(player, stringableCraftInfo.level) + return + } + StringItemScript(player, stringableCraftInfo, amount).invoke() + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, string.id) + } + } + handler.open() + fixSpacingBetweenTextAndCraftedItemIcon(player) + return@onUseWith true + } + } + + private fun fixSpacingBetweenTextAndCraftedItemIcon(player: Player) { + PacketRepository.send( + RepositionChild::class.java, + ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 215, 10) + ) + } + companion object { + fun sendLevelCheckFailDialogue(player: Player, level: Int) { + sendDialogue( + player, + "You need a fletching level of $level to string this bow." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/gather/SkillingResource.java b/Server/src/main/content/global/skill/gather/SkillingResource.java index 178f6679b..2d2b43e65 100644 --- a/Server/src/main/content/global/skill/gather/SkillingResource.java +++ b/Server/src/main/content/global/skill/gather/SkillingResource.java @@ -612,9 +612,6 @@ public enum SkillingResource { */ RUNITE_ORE_0(2107, 85, 0.95, 1250 | 2500 << 16, 125.0, 451, 1, "runite rocks", null, 452, Skills.MINING), RUNITE_ORE_1(2106, 85, 0.95, 1250 | 2500 << 16, 125.0, 451, 1, "runite rocks", null, 450, Skills.MINING), - RUNITE_ORE_2(6669, 85, 0.95, 1250 | 2500 << 16, 125.0, 451, 1, "runite rocks", null, 21296, Skills.MINING), - RUNITE_ORE_3(6671, 85, 0.95, 1250 | 2500 << 16, 125.0, 451, 1, "runite rocks", null, 21298, Skills.MINING), - RUNITE_ORE_4(6670, 85, 0.95, 1250 | 2500 << 16, 125.0, 451, 1, "runite rocks", null, 21297, Skills.MINING), RUNITE_ORE_5(14861, 85, 0.95, 1250 | 2500 << 16, 125.0, 451, 1, "runite rocks", null, 25373, Skills.MINING), RUNITE_ORE_6(14860, 85, 0.95, 1250 | 2500 << 16, 125.0, 451, 1, "runite rocks", null, 25372, Skills.MINING), RUNITE_ORE_7(14859, 85, 0.95, 1250 | 2500 << 16, 125.0, 451, 1, "runite rocks", null, 25371, Skills.MINING), @@ -632,7 +629,15 @@ public enum SkillingResource { */ GEM_ROCK_0(23567, 40, 0.95, 166 | 175 << 16, 65, 1625, 1, "gem rocks", null, 21297, Skills.MINING), GEM_ROCK_1(23566, 40, 0.95, 166 | 175 << 16, 65, 1625, 1, "gem rocks", null, 21296, Skills.MINING), - GEM_ROCK_2(23568, 40, 0.95, 166 | 175 << 16, 65, 1625, 1, "gem rocks", null, 21298, Skills.MINING); + GEM_ROCK_2(23568, 40, 0.95, 166 | 175 << 16, 65, 1625, 1, "gem rocks", null, 21298, Skills.MINING), + + /** + * Magic stone. + */ + MAGIC_STONE_0(6669, 20, 0.3, 100 | 200 << 16, 0.0, 4703, 1, "magic stone", null, 21296, Skills.MINING), + MAGIC_STONE_1(6671, 20, 0.3, 100 | 200 << 16, 0.0, 4703, 1, "magic stone", null, 21298, Skills.MINING), + MAGIC_STONE_2(6670, 20, 0.3, 100 | 200 << 16, 0.0, 4703, 1, "magic stone", null, 21297, Skills.MINING); + /** * The resources mapping. diff --git a/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt b/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt index ea254e311..3e309fb65 100644 --- a/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt +++ b/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt @@ -1,6 +1,5 @@ package content.global.skill.gather.fishing -import content.data.skill.SkillingPets import content.global.skill.fishing.Fish import content.global.skill.fishing.FishingOption import content.global.skill.skillcapeperks.SkillcapePerks @@ -124,13 +123,12 @@ class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption? updateSkillTask() } player.dispatch(ResourceProducedEvent(fish!!.id, 1, node!!)) - SkillingPets.checkPetDrop(player, SkillingPets.HERON) val item = fish!! if (isActive(SkillcapePerks.GREAT_AIM, player) && RandomFunction.random(100) <= 5) { - addItem(player, item.id) + addItemOrDrop(player, item.id) player.sendMessage(colorize("%RYour expert aim catches you a second fish.")) } - addItem(player, item.id) + addItemOrDrop(player, item.id) var fishCaught = player.getAttribute(STATS_BASE + ":" + STATS_FISH, 0) player.setAttribute("/save:$STATS_BASE:$STATS_FISH", ++fishCaught) player.skills.addExperience(Skills.FISHING, fish!!.experience, true) diff --git a/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishingPulse.kt b/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishingPulse.kt index 2a66a2f1f..a44387209 100644 --- a/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishingPulse.kt +++ b/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishingPulse.kt @@ -48,6 +48,10 @@ class BarbFishingPulse(player: Player) : SkillPulse(player,NPC(1176)) { } override fun reward(): Boolean { + if (delay == 1){ + super.setDelay(5) + return false + } val stragiXP = arrayOf(5,6,7) val fishXP = arrayOf(50,70,80) val reward = getRandomFish() diff --git a/Server/src/main/content/global/skill/gather/mining/MiningListener.kt b/Server/src/main/content/global/skill/gather/mining/MiningListener.kt index d744b0a8f..ea9de8c49 100644 --- a/Server/src/main/content/global/skill/gather/mining/MiningListener.kt +++ b/Server/src/main/content/global/skill/gather/mining/MiningListener.kt @@ -1,6 +1,5 @@ package content.global.skill.gather.mining -import content.data.skill.SkillingPets import content.data.skill.SkillingTool import content.global.skill.skillcapeperks.SkillcapePerks import content.global.activity.shootingstar.StarBonus @@ -27,11 +26,11 @@ import org.rs09.consts.Items class MiningListener : InteractionListener { override fun defineListeners() { defineInteraction( - IntType.SCENERY, - MiningNode.values().map { it.id }.toIntArray(), - "mine", - persistent = true, allowedDistance = 1, - handler = ::handleMining + IntType.SCENERY, + MiningNode.values().map { it.id }.toIntArray(), + "mine", + persistent = true, allowedDistance = 1, + handler = ::handleMining ) } private val GEM_REWARDS = arrayOf(ChanceItem(1623, 1, DropFrequency.COMMON), ChanceItem(1621, 1, DropFrequency.COMMON), ChanceItem(1619, 1, DropFrequency.UNCOMMON), ChanceItem(1617, 1, DropFrequency.RARE)) @@ -39,25 +38,25 @@ class MiningListener : InteractionListener { private fun handleMining(player: Player, node: Node, state: Int) : Boolean { val resource = MiningNode.forId(node.id) val tool = SkillingTool.getPickaxe(player) - val isEssence = resource.id == 2491 + val isEssence = resource == MiningNode.RUNE_ESSENCE val isGems = resource.identifier == MiningNode.GEM_ROCK_0.identifier if (!finishedMoving(player)) return true if (state == 0) { - if (!checkRequirements(player, resource, node)) { - player.scripts.reset() - return true - } + if (!checkRequirements(player, resource, node)) { + player.scripts.reset() + return true + } anim(player, tool) - sendMessage(player, "You swing your pickaxe at the rock...") - return delayScript(player, getDelay(resource)) + sendMessage(player, "You swing your pickaxe at the rock.") + return delayScript(player, getDelay(resource, tool)) } anim(player, tool) if (!checkReward(player, resource, tool)) - return delayScript(player, getDelay(resource)) + return delayScript(player, getDelay(resource, tool)) // Reward logic var reward = resource!!.reward @@ -67,7 +66,6 @@ class MiningListener : InteractionListener { rewardAmount = calculateRewardAmount(player, isEssence, reward) // calculate amount player.dispatch(ResourceProducedEvent(reward, rewardAmount, node)) - SkillingPets.checkPetDrop(player, SkillingPets.GOLEM) // roll for pet // Reward mining experience val experience = resource!!.experience * rewardAmount @@ -96,20 +94,29 @@ class MiningListener : InteractionListener { if (reward == Items.GOLD_ORE_444 && inBorders(player, familyCrestGoldOreArea)) { reward = Items.PERFECT_GOLD_ORE_446 } - val rewardName = getItemName(reward).lowercase() + + // Prepare reward name - gems stay lowercase without "Uncut", others are lowercase + val rewardName = if (isGems) { + getItemName(reward).replace("Uncut ", "").lowercase() + } else { + getItemName(reward).substringBefore(" (").lowercase().removeSuffix(" ore") + } // Send the message for the resource reward if (isGems) { - sendMessage(player, "You get ${prependArticle(rewardName)}.") - } else { - sendMessage(player, "You get some ${rewardName.lowercase()}.") + val withArticle = prependArticle(rewardName) // "an emerald" or "a red topaz" + val parts = withArticle.split(" ", limit = 2) // ["an", "emerald"] or ["a", "red topaz"] + val article = parts[0] // "an" or "a" + val gemName = parts[1].replaceFirstChar { it.uppercase() } // "Emerald" or "Red topaz" + sendMessage(player, "You just mined $article $gemName!") + } else if (!isEssence) { + sendMessage(player, "You manage to mine some ${rewardName}.") } // Give the mining reward, increment 'rocks mined' attribute - if(addItem(player, reward, rewardAmount)) { - var rocksMined = getAttribute(player, "$STATS_BASE:$STATS_ROCKS", 0) - setAttribute(player, "/save:$STATS_BASE:$STATS_ROCKS", ++rocksMined) - } + addItemOrDrop(player, reward, rewardAmount) + var rocksMined = getAttribute(player, "$STATS_BASE:$STATS_ROCKS", 0) + setAttribute(player, "/save:$STATS_BASE:$STATS_ROCKS", rocksMined + rewardAmount) // Calculate bonus gem chance while mining if (!isEssence) { @@ -127,7 +134,7 @@ class MiningListener : InteractionListener { } if (RandomFunction.roll(chance)) { val gem = GEM_REWARDS.random() - sendMessage(player,"You find a ${gem.name}!") + sendMessage(player,"You just found ${prependArticle(gem.name.replace("Uncut ", "").lowercase())}!") if (freeSlots(player) == 0) { sendMessage(player,"You do not have enough space in your inventory, so you drop the gem on the floor.") } @@ -141,6 +148,14 @@ class MiningListener : InteractionListener { node.setActive(false) return true } + + // For essence, check inventory and continue mining + if (isEssence) { + if (freeSlots(player) == 0) { + return true + } + return delayScript(player, getDelay(resource, tool)) + } } return true } @@ -192,6 +207,10 @@ class MiningListener : InteractionListener { } private fun checkReward(player: Player, resource: MiningNode?, tool: SkillingTool): Boolean { + // Essence mining always succeeds + if (resource == MiningNode.RUNE_ESSENCE) { + return true + } val level = 1 + getDynLevel(player, Skills.MINING) + getFamiliarBoost(player, Skills.MINING) val hostRatio = Math.random() * (100.0 * resource!!.rate) var toolRatio = tool.ratio @@ -202,8 +221,20 @@ class MiningListener : InteractionListener { return hostRatio < clientRatio } - fun getDelay(resource: MiningNode) : Int { - return if (resource.id == 2491) 3 else 4 + fun getDelay(resource: MiningNode, tool: SkillingTool) : Int { + if (resource == MiningNode.RUNE_ESSENCE) { // Essence mining - speed varies by pickaxe + return when (tool) { + SkillingTool.BRONZE_PICKAXE -> 7 + SkillingTool.IRON_PICKAXE -> 6 + SkillingTool.STEEL_PICKAXE -> 5 + SkillingTool.MITHRIL_PICKAXE -> 4 + SkillingTool.ADAMANT_PICKAXE -> 3 + SkillingTool.RUNE_PICKAXE -> 2 + SkillingTool.INFERNO_ADZE2 -> if (RandomFunction.random(2) == 0) 1 else 2 //https://www.youtube.com/watch?v=9XhjSdJ4qro + else -> 4 // fallback + } + } + return 4 // normal rocks } fun anim(player: Player, tool: SkillingTool) { @@ -225,9 +256,13 @@ class MiningListener : InteractionListener { sendDialogue(player,"Your inventory is too full to hold any more gems.") return false } + if (resource == MiningNode.RUNE_ESSENCE) { + sendDialogue(player,"Your inventory is too full to hold any more essence.") + return false + } sendDialogue(player,"Your inventory is too full to hold any more ${ItemDefinition.forId(resource!!.reward).name.lowercase()}.") return false } return node.isActive } -} +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/gather/mining/MiningNode.java b/Server/src/main/content/global/skill/gather/mining/MiningNode.java index 5f7f00cf3..b271fdee2 100644 --- a/Server/src/main/content/global/skill/gather/mining/MiningNode.java +++ b/Server/src/main/content/global/skill/gather/mining/MiningNode.java @@ -403,9 +403,6 @@ public enum MiningNode{ //Runite RUNITE_ORE_0( 2107, 452, (byte) 12), RUNITE_ORE_1( 2106, 450, (byte) 12), - RUNITE_ORE_2( 6669, 21296, (byte) 12), - RUNITE_ORE_3( 6671, 21298, (byte) 12), - RUNITE_ORE_4( 6670, 21297, (byte) 12), RUNITE_ORE_5( 14861,25373, (byte) 12), RUNITE_ORE_6( 14860,25372, (byte) 12), RUNITE_ORE_7( 14859,25371, (byte) 12), @@ -437,8 +434,14 @@ public enum MiningNode{ GRANITE(10947,10945, (byte) 16), //Rubium? - RUBIUM(29746,29747, (byte) 17); + RUBIUM(29746,29747, (byte) 17), + //Magic stone (Tears of Guthix) + MAGIC_STONE_0( 6669, 21296, (byte) 18), // Was mistaken for RUNITE_ORE_2 + MAGIC_STONE_1( 6671, 21298, (byte) 18), // Was mistaken for RUNITE_ORE_3 + MAGIC_STONE_2( 6670, 21297, (byte) 18), // Was mistaken for RUNITE_ORE_4 + + ; public static List gemRockGems = new ArrayList<>(20); @@ -573,6 +576,13 @@ public enum MiningNode{ reward = 12630; level = 46; break; + case 18: + respawnRate = 100 | 200 << 16; + experience = 0.0; + rate = 0.3; + reward = 4703; + level = 20; + break; } } private static HashMap NODE_MAP = new HashMap<>(); diff --git a/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt b/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt index aa7ef70e6..4c26fac4d 100644 --- a/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt +++ b/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt @@ -3,7 +3,6 @@ package content.global.skill.gather.mining import core.api.* import core.game.event.ResourceProducedEvent import core.cache.def.impl.ItemDefinition -import content.data.skill.SkillingPets import core.game.node.Node import core.game.node.entity.impl.Animator import core.game.node.entity.npc.drop.DropFrequency @@ -11,6 +10,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills import content.data.skill.SkillingTool +import content.global.activity.shootingstar.StarBonus import content.global.skill.skillcapeperks.SkillcapePerks import core.game.node.item.ChanceItem import core.game.node.scenery.Scenery @@ -131,7 +131,6 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul rewardAmount = calculateRewardAmount(reward) // calculate amount player.dispatch(ResourceProducedEvent(reward, rewardAmount, node)) - SkillingPets.checkPetDrop(player, SkillingPets.GOLEM) // roll for pet // Reward mining experience val experience = resource!!.experience * rewardAmount @@ -164,10 +163,9 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul } // Give the mining reward, increment 'rocks mined' attribute - if(addItem(player, reward, rewardAmount)) { - var rocksMined = getAttribute(player, "$STATS_BASE:$STATS_ROCKS", 0) - setAttribute(player, "/save:$STATS_BASE:$STATS_ROCKS", ++rocksMined) - } + addItemOrDrop(player, reward, rewardAmount) + var rocksMined = getAttribute(player, "$STATS_BASE:$STATS_ROCKS", 0) + setAttribute(player, "/save:$STATS_BASE:$STATS_ROCKS", rocksMined + rewardAmount) // Calculate bonus gem chance while mining if (!isMiningEssence) { @@ -225,7 +223,7 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul } // If player has mining boost from Shooting Star, roll chance at extra ore - if (player.hasActiveState("shooting-star")) { + if (hasTimerActive(player)) { if (RandomFunction.getRandom(5) == 3) { sendMessage(player, "...you manage to mine a second ore thanks to the Star Sprite.") amount += 1 diff --git a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt index 4f7c9276f..6b1be0ab0 100644 --- a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt @@ -1,9 +1,10 @@ package content.global.skill.gather.woodcutting -import content.data.skill.SkillingPets +import content.data.Quests import content.data.skill.SkillingTool import content.data.tables.BirdNest import content.global.skill.farming.FarmingPatch.Companion.forObject +import content.global.skill.farming.timers.StumpGrowth import content.global.skill.firemaking.Log import content.global.skill.skillcapeperks.SkillcapePerks import content.global.skill.skillcapeperks.SkillcapePerks.Companion.isActive @@ -72,7 +73,18 @@ class WoodcuttingListener : InteractionListener { if (clockReady(player, Clocks.SKILLING)) { animateWoodcutting(player) - if (!checkReward(player, resource, tool)) + + if (resource == WoodcuttingNode.DRAMEN_TREE) { + // Reward after one chop and then abort chopping (this is authentic) + queueScript(player, 1, QueueStrength.STRONG) { + sendMessage(player, "You cut a branch from the Dramen tree.") + addItem(player, resource.getReward()) + return@queueScript clearScripts(player) + } + return delayClock(player, Clocks.SKILLING, 1) + } + + if (!checkReward(player, resource, tool) && !getAttribute(player, "instachop", false)) return delayClock(player, Clocks.SKILLING, 3) val reward = resource.getReward() @@ -93,7 +105,9 @@ class WoodcuttingListener : InteractionListener { ).send() //add woodcutting experience - player.getSkills().addExperience(Skills.WOODCUTTING, resource.getExperience()) + rewardAmount = calculateRewardAmount(player, reward) // calculate amount + val experience: Double = calculateExperience(player, resource, rewardAmount) + player.getSkills().addExperience(Skills.WOODCUTTING, experience, true) //nullcheck the fire, and only if it exists award the firemaking XP val fire = Log.forId(reward) @@ -107,16 +121,13 @@ class WoodcuttingListener : InteractionListener { if (reward > 0) { rewardAmount = calculateRewardAmount(player, reward) // calculate amount - SkillingPets.checkPetDrop(player, SkillingPets.BEAVER) // roll for pet //add experience val experience: Double = calculateExperience(player, resource, rewardAmount) player.getSkills().addExperience(Skills.WOODCUTTING, experience, true) //send the message for the resource reward - if (resource == WoodcuttingNode.DRAMEN_TREE) { - player.packetDispatch.sendMessage("You cut a branch from the Dramen tree.") - } else if (reward == Items.BARK_3239 && rewardAmount == 0) { + if (reward == Items.BARK_3239 && rewardAmount == 0) { player.packetDispatch.sendMessage("You chop away some bark, but it falls to pieces before you can pick it up.") } else { player.packetDispatch.sendMessage("You get some " + ItemDefinition.forId(reward).name.lowercase(Locale.getDefault()) + ".") @@ -156,7 +167,7 @@ class WoodcuttingListener : InteractionListener { //OSRS: https://oldschool.runescape.wiki/w/Woodcutting scroll down to the mechanics section //RS3 : https://runescape.wiki/w/Woodcutting scroll down to the mechanics section, and expand the tree felling chances table if (resource.getRespawnRate() > 0) { - if (RandomFunction.roll(8) || resource.identifier.toInt() == 1 || resource.identifier.toInt() == 2 || resource.identifier.toInt() == 3 || resource.identifier.toInt() == 6) { + if (RandomFunction.roll(8) || listOf(1, 2, 3, 4, 6).contains(resource.identifier.toInt()) || getAttribute(player, "instachop", false)){ if (resource.isFarming()) { val fPatch = forObject(node.asScenery()) if (fPatch != null) { @@ -168,6 +179,8 @@ class WoodcuttingListener : InteractionListener { node.isActive = true return@queueScript stopExecuting(player) } + val stumps = getOrStartTimer (player) + stumps.addStump(fPatch.varbit, resource.respawnDuration) } return true } @@ -226,6 +239,10 @@ class WoodcuttingListener : InteractionListener { player.packetDispatch.sendMessage("You do not have an axe to use.") return false } + if (node.id == org.rs09.consts.Scenery.DRAMEN_TREE_1292 && getQuestStage(player, Quests.LOST_CITY) <= 20) { + //TODO: find out if there is any authentic message to be shown + return false + } if (player.inventory.freeSlots() < 1 && node.isActive) { player.sendMessage("Your inventory is too full to hold any more " + ItemDefinition.forId(resource.getReward()).name.lowercase(Locale.getDefault()) + ".") return false diff --git a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java index 70bd09d38..d69c7fea1 100644 --- a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java @@ -8,7 +8,6 @@ import core.game.event.ResourceProducedEvent; import core.cache.def.impl.ItemDefinition; import core.game.container.impl.EquipmentContainer; import core.game.dialogue.FacialExpression; -import content.data.skill.SkillingPets; import core.game.node.entity.impl.Animator; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; @@ -157,20 +156,14 @@ public class WoodcuttingSkillPulse extends Pulse { if (reward > 0) { reward = calculateReward(reward); // calculate rewards rewardAmount = calculateRewardAmount(reward); // calculate amount - SkillingPets.checkPetDrop(player, SkillingPets.BEAVER); // roll for pet //add experience double experience = calculateExperience(resource.reward, rewardAmount); player.getSkills().addExperience(Skills.WOODCUTTING, experience, true); - //send the message for the resource reward, and in the case of the dramen tree, authentically abort the chopping action - if (resource == WoodcuttingNode.DRAMEN_TREE) { - player.getPacketDispatch().sendMessage("You cut a branch from the Dramen tree."); - stop(); - } else { - player.getPacketDispatch().sendMessage("You get some " + ItemDefinition.forId(reward).getName().toLowerCase() + "."); - } + //send the message for the resource reward + player.getPacketDispatch().sendMessage("You get some " + ItemDefinition.forId(reward).getName().toLowerCase() + "."); //give the reward player.getInventory().add(new Item(reward, rewardAmount)); player.dispatch(new ResourceProducedEvent(reward, rewardAmount, node, -1)); @@ -204,7 +197,7 @@ public class WoodcuttingSkillPulse extends Pulse { if (resource.isFarming()) { FarmingPatch fPatch = FarmingPatch.forObject(node.asScenery()); if(fPatch != null) { - Patch patch = fPatch.getPatchFor(player); + Patch patch = fPatch.getPatchFor(player, true); patch.setCurrentState(patch.getCurrentState() + 1); } return true; diff --git a/Server/src/main/content/global/skill/herblore/BarbarianPotion.kt b/Server/src/main/content/global/skill/herblore/BarbarianPotion.kt index 5876c4018..ce8e7202e 100644 --- a/Server/src/main/content/global/skill/herblore/BarbarianPotion.kt +++ b/Server/src/main/content/global/skill/herblore/BarbarianPotion.kt @@ -6,7 +6,7 @@ package content.global.skill.herblore; * @author treevar */ public enum class BarbarianPotion { - ATTACK_POTION(123, 4, 11429, 8.0, true), ANTI_POISION_POTION(177, 6, 11433, 12.0, true), RELIC(4846, 9, 11437, 14.0, true), STRENGTH_POTION(117, 14, 11443, 17.0, true), RESTORE_POTION(129, 24, 11449, 21.0, true), ENERGY_POTION(3012, 29, 11453, 23.0, false), DEFENCE_POTION(135, 33, 11457, 25.0, false), AGILITY_POTION(3036, 37, 11461, 27.0, false), COMBAT_POTION(9743, 40, 11445, 28.0, false), PRAYER_POTION(141, 42, 11465, 29.0, false), SUPER_ATTACK_POTION(147, 47, 11469, 33.0, false), SUPER_ANTIPOISION_POTION(183, 51, 11473, 35.0, false), FISHING_POTION(153, 53, 11477, 38.0, false), SUPER_ENERGY_POTION(3020, 56, 11481, 42.0, false), HUNTER_POTION(10002, 58, 11517, 40.0, false), SUPER_STRENGTH_POTION(159, 59, 11485, 42.0, false), SUPER_RESTORE(3028, 67, 11493, 48.0, false), SUPER_DEFENCE_POTION(165, 71, 11497, 50.0, false), ANTIDOTE_PLUS(5947, 74, 11501, 52.0, false), ANTIFIRE_POTION(2456, 75, 11505, 53.0, false), RANGING_POTION(171, 80, 11509, 54.0, false), MAGIC_POTION(3044, 83, 11513, 57.0, false), ZAMORAK_BREW(191, 85, 11521, 58.0, false); + ATTACK_POTION(123, 4, 11429, 8.0, true), ANTI_POISION_POTION(177, 6, 11433, 12.0, true), RELIC(4846, 9, 11437, 14.0, true), STRENGTH_POTION(117, 14, 11443, 17.0, true), RESTORE_POTION(129, 24, 11449, 21.0, true), ENERGY_POTION(3012, 29, 11453, 23.0, false), DEFENCE_POTION(135, 33, 11457, 25.0, false), AGILITY_POTION(3036, 37, 11461, 27.0, false), COMBAT_POTION(9743, 40, 11445, 28.0, false), PRAYER_POTION(141, 42, 11465, 29.0, false), SUPER_ATTACK_POTION(147, 47, 11469, 33.0, false), SUPER_ANTIPOISION_POTION(183, 51, 11473, 35.0, false), FISHING_POTION(153, 53, 11477, 38.0, false), SUPER_ENERGY_POTION(3020, 56, 11481, 42.0, false), HUNTER_POTION(10002, 58, 11517, 40.0, false), SUPER_STRENGTH_POTION(159, 59, 11485, 42.0, false), MAGIC_ESSENCE_POTION(9023, 61, 11489, 43.0, false), SUPER_RESTORE(3028, 67, 11493, 48.0, false), SUPER_DEFENCE_POTION(165, 71, 11497, 50.0, false), ANTIDOTE_PLUS(5947, 74, 11501, 52.0, false), ANTIFIRE_POTION(2456, 75, 11505, 53.0, false), RANGING_POTION(171, 80, 11509, 54.0, false), MAGIC_POTION(3044, 83, 11513, 57.0, false), ZAMORAK_BREW(191, 85, 11521, 58.0, false); /** * Constructs a new {@code BarbarianPotion} {@Code Object}. diff --git a/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt b/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt index ec9cd70c4..b87ead3f4 100644 --- a/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt +++ b/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt @@ -61,8 +61,7 @@ class GrindItemPlugin : UseWithHandler(233) { override fun reward(): Boolean { if (node.id == Items.FISHING_BAIT_313) { - var quantity = 0 - quantity = if (amountInInventory(player, FISHING_BAIT) >= 10) { + val quantity: Int = if (amountInInventory(player, FISHING_BAIT) >= 10) { 10 } else { amountInInventory(player, FISHING_BAIT) diff --git a/Server/src/main/content/global/skill/herblore/HerbCleanListener.kt b/Server/src/main/content/global/skill/herblore/HerbCleanListener.kt index 7ba8cfe5a..6807e5d9e 100644 --- a/Server/src/main/content/global/skill/herblore/HerbCleanListener.kt +++ b/Server/src/main/content/global/skill/herblore/HerbCleanListener.kt @@ -6,6 +6,7 @@ import core.game.interaction.InteractionListener import core.game.node.entity.skill.Skills import core.game.node.item.Item import java.util.* +import content.data.Quests /** * Dirty herb cleaning listener @@ -15,7 +16,7 @@ class HerbCleanListener : InteractionListener { override fun defineListeners() { on(IntType.ITEM, "clean") { player, node -> lock(player, 1) - if (!requireQuest(player, "Druidic Ritual", "before you can use Herblore.")) return@on true + if (!requireQuest(player, Quests.DRUIDIC_RITUAL, "before you can use Herblore.")) return@on true val herb: Herbs = Herbs.forItem(node as Item) ?: return@on true if (getDynLevel(player, Skills.HERBLORE) < herb.level) { diff --git a/Server/src/main/content/global/skill/herblore/HerbTarPulse.java b/Server/src/main/content/global/skill/herblore/HerbTarPulse.java index 45793be82..247f69f62 100644 --- a/Server/src/main/content/global/skill/herblore/HerbTarPulse.java +++ b/Server/src/main/content/global/skill/herblore/HerbTarPulse.java @@ -5,6 +5,7 @@ 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 content.data.Quests; /** * Represents the pulse used to create herb tars. @@ -52,7 +53,7 @@ public final class HerbTarPulse extends SkillPulse { @Override public boolean checkRequirements() { - if (!player.getQuestRepository().isComplete("Druidic Ritual")) { + if (!player.getQuestRepository().isComplete(Quests.DRUIDIC_RITUAL)) { player.getPacketDispatch().sendMessage("You must complete the Druidic Ritual quest before you can use Herblore."); return false; } diff --git a/Server/src/main/content/global/skill/herblore/HerblorePulse.java b/Server/src/main/content/global/skill/herblore/HerblorePulse.java index fbf1a8c73..0f859ad23 100644 --- a/Server/src/main/content/global/skill/herblore/HerblorePulse.java +++ b/Server/src/main/content/global/skill/herblore/HerblorePulse.java @@ -12,6 +12,7 @@ import core.tools.RandomFunction; import org.rs09.consts.Sounds; import static core.api.ContentAPIKt.playAudio; +import content.data.Quests; /** @@ -69,7 +70,7 @@ public final class HerblorePulse extends SkillPulse { @Override public boolean checkRequirements() { - if (!player.getQuestRepository().isComplete("Druidic Ritual")) { + if (!player.getQuestRepository().isComplete(Quests.DRUIDIC_RITUAL)) { player.getPacketDispatch().sendMessage("You must complete the Druidic Ritual quest before you can use Herblore."); return false; } diff --git a/Server/src/main/content/global/skill/herblore/Herbs.java b/Server/src/main/content/global/skill/herblore/Herbs.java index 5ee4959eb..4226fb436 100644 --- a/Server/src/main/content/global/skill/herblore/Herbs.java +++ b/Server/src/main/content/global/skill/herblore/Herbs.java @@ -7,7 +7,7 @@ import core.game.node.item.Item; * @author 'Vexia */ public enum Herbs { - GUAM(new Item(199), 2.5, 3, new Item(249)), MARRENTILL(new Item(201), 3.8, 5, new Item(251)), TARROMIN(new Item(203), 5, 11, new Item(253)), HARRALANDER(new Item(205), 6.3, 20, new Item(255)), RANARR(new Item(207), 7.5, 25, new Item(257)), TOADFLAX(new Item(3049), 8, 30, new Item(2998)), SPIRIT_WEED(new Item(12174), 7.8, 35, new Item(12172)), IRIT(new Item(209), 8.8, 40, new Item(259)), AVANTOE(new Item(211), 10, 48, new Item(261)), KWUARM(new Item(213), 11.3, 54, new Item(263)), SNAPDRAGON(new Item(3051), 11.8, 59, new Item(3000)), CADANTINE(new Item(215), 12.5, 65, new Item(265)), LANTADYME(new Item(2485), 13.1, 67, new Item(2481)), DWARF_WEED(new Item(217), 13.8, 70, new Item(267)), TORSTOL(new Item(219), 15, 75, new Item(269)), SNAKE_WEED(new Item(1525), 2.5, 3, new Item(1526)), ARDRIGAL(new Item(1527), 2.5, 3, new Item(1528)), SITO_FOIL(new Item(1529), 2.5, 3, new Item(1530)), VOLENCIA_MOSS(new Item(1531), 2.5, 3, new Item(1532)), ROGUES_PUSE(new Item(1533), 2.5, 3, new Item(1534)); + GUAM(new Item(199), 2.5, 3, new Item(249)), MARRENTILL(new Item(201), 3.8, 5, new Item(251)), TARROMIN(new Item(203), 5, 11, new Item(253)), HARRALANDER(new Item(205), 6.3, 20, new Item(255)), RANARR(new Item(207), 7.5, 25, new Item(257)), TOADFLAX(new Item(3049), 8, 30, new Item(2998)), SPIRIT_WEED(new Item(12174), 7.8, 35, new Item(12172)), IRIT(new Item(209), 8.8, 40, new Item(259)), AVANTOE(new Item(211), 10, 48, new Item(261)), KWUARM(new Item(213), 11.3, 54, new Item(263)), SNAPDRAGON(new Item(3051), 11.8, 59, new Item(3000)), CADANTINE(new Item(215), 12.5, 65, new Item(265)), LANTADYME(new Item(2485), 13.1, 67, new Item(2481)), DWARF_WEED(new Item(217), 13.8, 70, new Item(267)), TORSTOL(new Item(219), 15, 75, new Item(269)), SNAKE_WEED(new Item(1525), 2.5, 3, new Item(1526)), ARDRIGAL(new Item(1527), 2.5, 3, new Item(1528)), SITO_FOIL(new Item(1529), 2.5, 3, new Item(1530)), VOLENCIA_MOSS(new Item(1531), 2.5, 3, new Item(1532)), ROGUES_PURSE(new Item(1533), 2.5, 3, new Item(1534)); /** * Represents the herb item. diff --git a/Server/src/main/content/global/skill/herblore/Tars.java b/Server/src/main/content/global/skill/herblore/Tars.java index 06484ee60..c63d783bc 100644 --- a/Server/src/main/content/global/skill/herblore/Tars.java +++ b/Server/src/main/content/global/skill/herblore/Tars.java @@ -1,13 +1,18 @@ package content.global.skill.herblore; import core.game.node.item.Item; +import org.rs09.consts.Items; /** * Represents a tar to create. * @author 'Vexia */ public enum Tars { - GUAM_TAR(Herbs.GUAM.getProduct(), 19, 30, new Item(10142)), MARRENTILL_TAR(Herbs.MARRENTILL.getProduct(), 31, 42.5, new Item(10143)), TARROMIN_TAR(Herbs.TARROMIN.getProduct(), 39, 55, new Item(10144)), HARRALANDER_TAR(Herbs.HARRALANDER.getProduct(), 44, 72.5, new Item(10145)); + GUAM_TAR(Herbs.GUAM.getProduct(), 19, 30, new Item(10142)), + GROUND_GUAM_TAR(new Item(Items.GROUND_GUAM_6681), 19, 30, new Item(10142)), + MARRENTILL_TAR(Herbs.MARRENTILL.getProduct(), 31, 42.5, new Item(10143)), + TARROMIN_TAR(Herbs.TARROMIN.getProduct(), 39, 55, new Item(10144)), + HARRALANDER_TAR(Herbs.HARRALANDER.getProduct(), 44, 72.5, new Item(10145)); /** * Represents the ingredient. diff --git a/Server/src/main/content/global/skill/herblore/UnfinishedPotion.java b/Server/src/main/content/global/skill/herblore/UnfinishedPotion.java index 699a03b74..aa6e48d27 100644 --- a/Server/src/main/content/global/skill/herblore/UnfinishedPotion.java +++ b/Server/src/main/content/global/skill/herblore/UnfinishedPotion.java @@ -9,6 +9,7 @@ import core.game.node.item.Item; public enum UnfinishedPotion { GUAM(Herbs.GUAM.getProduct(), 3, new Item(91)), MARRENTILL(Herbs.MARRENTILL.getProduct(), 5, new Item(93)), + ROGUES_PURSE(Herbs.ROGUES_PURSE.getProduct(), 5, new Item(4840)), TARROMIN(Herbs.TARROMIN.getProduct(), 12, new Item(95)), HARRALANDER(Herbs.HARRALANDER.getProduct(), 22, new Item(97)), RANARR(Herbs.RANARR.getProduct(), 30, new Item(99)), diff --git a/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java b/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java index 0492f937b..8d3493c28 100644 --- a/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java +++ b/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java @@ -1,6 +1,5 @@ package content.global.skill.hunter; -import content.data.skill.SkillingPets; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; @@ -95,13 +94,6 @@ public final class TrapDismantlePulse extends SkillPulse { if (wrapper.getType().getSettings().clear(wrapper, 1)) { instance.deregister(wrapper); if (wrapper.isCaught()) { - if (wrapper.getType().equals(Traps.BOX_TRAP)) { - for (int i : wrapper.getReward().getNpcIds()) { - if (i == 5080 || i == 5079) { - SkillingPets.checkPetDrop(player, i == 5080 ? SkillingPets.BABY_RED_CHINCHOMPA : SkillingPets.BABY_GREY_CHINCHOMPA); - } - } - } player.getSkills().addExperience(Skills.HUNTER, wrapper.getReward().getExperience(), true); } player.getPacketDispatch().sendMessage("You dismantle the trap."); diff --git a/Server/src/main/content/global/skill/hunter/TrapNode.java b/Server/src/main/content/global/skill/hunter/TrapNode.java index ad8608779..d2cc48e01 100644 --- a/Server/src/main/content/global/skill/hunter/TrapNode.java +++ b/Server/src/main/content/global/skill/hunter/TrapNode.java @@ -1,6 +1,5 @@ package content.global.skill.hunter; -import content.data.skill.SkillingPets; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; @@ -123,8 +122,4 @@ public class TrapNode { return objectIds; } - public SkillingPets getPet() { - return null; - } - } diff --git a/Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt b/Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt index f4959cbae..bdc9611a9 100644 --- a/Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt +++ b/Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt @@ -21,6 +21,7 @@ import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.node.entity.player.link.diary.DiaryType import core.game.world.GameWorld import org.rs09.consts.Sounds @@ -262,6 +263,7 @@ class PitfallListeners : InteractionListener { } on(GRAAHK_PIT, IntType.SCENERY, "dismantle") { player, node -> lootCorpse(player, node as Scenery, 240.0, Items.GRAAHK_FUR_10099, Items.TATTY_GRAAHK_FUR_10097) + player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 13) sendMessage(player, "You've caught a horned graahk!") return@on true } diff --git a/Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt b/Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt new file mode 100644 index 000000000..1b67a38e9 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt @@ -0,0 +1,46 @@ +package content.global.skill.magic + +import core.api.delayScript +import core.api.playGlobalAudio +import core.api.queueScript +import core.api.sendMessage +import core.api.stopExecuting +import core.game.event.TeleportEvent +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.ZoneRestriction + +val HOME_ANIMATIONS = arrayOf(1722, 1723, 1724, 1725, 2798, 2799, 2800, 3195, 4643, 4645, 4646, 4847, 4848, 4849, 4850, 4851, 4852, 65535) +val HOME_GRAPHICS = arrayOf(775, 800, 801, 802, 803, 804, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 65535) +fun getAudio(count: Int): Int { + return when (count) { + 0 -> 193 + 4 -> 194 + 11 -> 195 + else -> -1 + } +} + +fun homeTeleport(player: Player, dest: Location) { + if (player.timers.getTimer("teleblock") != null) { + sendMessage(player, "A magical force prevents you from teleporting.") + return + } + if (player.locks.isTeleportLocked || player.zoneMonitor.isRestricted(ZoneRestriction.TELEPORT)) { + sendMessage(player, "A magical force has stopped you from teleporting.") + return + } + queueScript(player, 0, QueueStrength.WEAK) { stage -> + if (stage == 18) { + player.properties.teleportLocation = dest + player.dispatch(TeleportEvent(TeleportManager.TeleportType.NORMAL, TeleportMethod.SPELL, -1, dest)) + return@queueScript stopExecuting(player) + } + playGlobalAudio(player.location, getAudio(stage)) + player.packetDispatch.sendGraphic(HOME_GRAPHICS[stage]) + player.packetDispatch.sendAnimation(HOME_ANIMATIONS[stage]) + return@queueScript delayScript(player, 1) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/MagicAltarListener.kt b/Server/src/main/content/global/skill/magic/MagicAltarListener.kt new file mode 100644 index 000000000..eb4e61fc5 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/MagicAltarListener.kt @@ -0,0 +1,64 @@ +package content.global.skill.magic + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.SpellBookManager.SpellBook +import core.game.node.entity.skill.Skills +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds +import content.data.Quests + +class MagicAltarListener : InteractionListener { + override fun defineListeners() { + on(intArrayOf(ANCIENT_ALTAR, LUNAR_ALTAR), IntType.SCENERY, "pray-at", "pray") { player, node -> + if (meetsRequirements(player, node)) { + swapSpellBook(player, node) + } + + return@on true + } + } + + private fun meetsRequirements(player: Player, altar: Node): Boolean { + val level = if (altar.id == ANCIENT_ALTAR) 50 else 65 + + if (!hasRequirement(player, if (altar.id == ANCIENT_ALTAR) Quests.DESERT_TREASURE else Quests.LUNAR_DIPLOMACY)) { + return false + } + + if (!hasLevelStat(player, Skills.MAGIC, level)) { + sendMessage(player, "You need a Magic level of at least $level in order to do this.") + return false + } + + return true + } + + private fun swapSpellBook(player: Player, altar: Node) { + lock(player, 3) + playAudio(player, Sounds.PRAYER_RECHARGE_2674) + animate(player, 645) + + if (altar.id == ANCIENT_ALTAR) { + player.skills.decrementPrayerPoints(player.skills.prayerPoints) + } + + if (SpellBook.forInterface(player.spellBookManager.spellBook) == if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) { + sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange drain upon your memory..." else "Modern spells activated!") + player.spellBookManager.setSpellBook(SpellBook.MODERN) + player.spellBookManager.update(player) + } else { + sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange wisdom fill your mind..." else "Lunar spells activated!") + player.spellBookManager.setSpellBook(if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) + player.spellBookManager.update(player) + } + } + + companion object { + private const val ANCIENT_ALTAR = Scenery.ALTAR_6552 + private const val LUNAR_ALTAR = Scenery.ALTAR_17010 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/SpellListeners.kt b/Server/src/main/content/global/skill/magic/SpellListeners.kt index 95ff686a0..e390516e3 100644 --- a/Server/src/main/content/global/skill/magic/SpellListeners.kt +++ b/Server/src/main/content/global/skill/magic/SpellListeners.kt @@ -1,14 +1,14 @@ package content.global.skill.magic +import core.api.hasLineOfSight +import core.api.log import core.game.event.SpellCastEvent -import core.api.* +import core.game.interaction.MovementPulse import core.game.node.Node import core.game.node.entity.player.Player import core.game.node.entity.player.link.SpellBookManager -import core.tools.Log -import core.tools.SystemLogger -import core.game.interaction.* import core.game.world.map.path.Pathfinder +import core.tools.Log object SpellListeners { val castMap = HashMap Unit>() @@ -44,7 +44,7 @@ object SpellListeners { range = next.first method = next.second ?: return } - + player.scripts.removeWeakScripts() if (type in intArrayOf (SpellListener.NPC, SpellListener.OBJECT, SpellListener.PLAYER, SpellListener.GROUND_ITEM)) { player.pulseManager.run (object : MovementPulse (player, node, Pathfinder.SMART) { override fun pulse() : Boolean { diff --git a/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java b/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java index 3944c3c7e..8b8e2c693 100644 --- a/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java +++ b/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java @@ -1,5 +1,6 @@ package content.global.skill.magic.ancient; +import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.combat.spell.MagicSpell; import core.game.node.entity.combat.spell.Runes; @@ -13,8 +14,12 @@ import core.game.world.GameWorld; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import core.tools.Log; import core.tools.RandomFunction; +import static content.global.skill.magic.HomeTeleportHelperKt.homeTeleport; +import static core.api.ContentAPIKt.log; + /** * Represents the plugin used to handle all ancient teleporting plugins. * @author 'Vexia @@ -55,7 +60,14 @@ public final class AncientTeleportPlugin extends MagicSpell { entity.asPlayer().sendMessage("A magical force has stopped you from teleporting."); return false; } - if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), getSpellId() == 28 ? TeleportType.HOME : TeleportType.ANCIENT)) { + boolean isHomeTeleport = getSpellId() == 28; + if (isHomeTeleport) { + if (!entity.isPlayer()) { + log(this.getClass(), Log.ERR, "Why the fuck is a non-player entity trying to cast ancient-magick home teleport?!"); + return false; + } + homeTeleport((Player) entity, Location.create(3087, 3495, 0)); + } else if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.ANCIENT)) { if (!super.meetsRequirements(entity, true, true)) { entity.getTeleporter().getCurrentTeleport().stop(); return false; @@ -86,7 +98,7 @@ public final class AncientTeleportPlugin extends MagicSpell { // dareeyak teleport SpellBook.ANCIENT.register(24, new AncientTeleportPlugin(78, 88, Location.create(2966, 3696, 0), new Item(Runes.LAW_RUNE.getId(), 2), new Item(Runes.FIRE_RUNE.getId(), 3), new Item(Runes.AIR_RUNE.getId(), 2))); // carralangar teleport - SpellBook.ANCIENT.register(25, new AncientTeleportPlugin(84, 82, Location.create(3163, 3664, 0), new Item(Runes.SOUL_RUNE.getId(), 2), new Item(Runes.LAW_RUNE.getId(), 2))); + SpellBook.ANCIENT.register(25, new AncientTeleportPlugin(84, 82, Location.create(3217, 3676, 0), new Item(Runes.SOUL_RUNE.getId(), 2), new Item(Runes.LAW_RUNE.getId(), 2))); // annakarl teleport SpellBook.ANCIENT.register(26, new AncientTeleportPlugin(90, 100, Location.create(3287, 3883, 0), new Item(Runes.BLOOD_RUNE.getId(), 2), new Item(Runes.LAW_RUNE.getId(), 2))); // ghorrock teleport diff --git a/Server/src/main/content/global/skill/magic/ancient/IceSpells.java b/Server/src/main/content/global/skill/magic/ancient/IceSpells.java index e4f287574..4b0e29ff4 100644 --- a/Server/src/main/content/global/skill/magic/ancient/IceSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/IceSpells.java @@ -1,162 +1,160 @@ -package content.global.skill.magic.ancient; - -import java.util.List; - -import core.game.node.entity.combat.spell.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.spell.CombatSpell; -import core.game.node.entity.combat.spell.SpellType; -import core.game.node.entity.impl.Projectile; -import core.game.node.entity.impl.Animator.Priority; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; -import org.rs09.consts.Sounds; - -import static core.api.ContentAPIKt.*; - -/** - * Handles the Ice spells from the Ancient spellbook. - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class IceSpells extends CombatSpell { - - /** - * The barrage orb GFX. - */ - private static final Graphics BARRAGE_ORB = new Graphics(1677, 96); //119 - - /** - * The projectile for Ice rush. - */ - private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 360, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Ice rush. - */ - private static final Graphics RUSH_END = new Graphics(361, 96); - - /** - * The projectile for Ice barrage. - */ - private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 362, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Ice barrage. - */ - private static final Graphics BURST_END = new Graphics(363, 0); - - /** - * The start graphic for Ice rush. - */ - private static final Graphics BLITZ_START = new Graphics(366, 96); - - /** - * The end graphic for Ice rush. - */ - private static final Graphics BLITZ_END = new Graphics(367, 96); - - /** - * The projectile for Ice barrage. - */ - private static final Projectile BARRAGE_PROJECTILE = Projectile.create((Entity) null, null, 368, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Ice barrage. - */ - private static final Graphics BARRAGE_END = new Graphics(369, 0); - - /** - * Constructs a new {@code IceSpells} {@code Object}. - */ - public IceSpells() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code IceSpells} {@Code Object} - * @param type The spell type. - * @param impactSound The impact sound id. - * @param anim The animation. - * @param start The start graphics. - * @param projectile The projectile. - * @param end The end graphics. - */ - private IceSpells(SpellType type, int level, double baseExperience, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { - super(type, SpellBook.ANCIENT, level, baseExperience, Sounds.ICE_CAST_171, impactSound, anim, start, projectile, end, runes); - } - - @Override - public void visualize(Entity entity, Node target) { - entity.graphics(graphic); - if (projectile != null) { - projectile.transform(entity, (Entity) target, false, 58, 10).send(); - } - entity.animate(animation); - playGlobalAudio(entity.getLocation(), audio.id, 20); - - } - - @Override - public void visualizeImpact(Entity entity, Entity target, BattleState state) { - if (state.isFrozen()) { - playGlobalAudio(target.getLocation(), impactAudio, 20); - target.graphics(BARRAGE_ORB); - return; - } - super.visualizeImpact(entity, target, state); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.ANCIENT.register(0, new IceSpells(SpellType.RUSH, 58, 34.0, Sounds.ICE_RUSH_IMPACT_173, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2), Runes.WATER_RUNE.getItem(2))); - SpellBook.ANCIENT.register(2, new IceSpells(SpellType.BURST, 70, 40.0, Sounds.ICE_BURST_IMPACT_170, new Animation(1979, Priority.HIGH), null, BURST_PROJECTILE, BURST_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4), Runes.WATER_RUNE.getItem(4))); - SpellBook.ANCIENT.register(1, new IceSpells(SpellType.BLITZ, 82, 46.0, Sounds.ICE_BLITZ_IMPACT_169, new Animation(1978, Priority.HIGH), BLITZ_START, null, BLITZ_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.WATER_RUNE.getItem(3))); - SpellBook.ANCIENT.register(3, new IceSpells(SpellType.BARRAGE, 94, 52.0, Sounds.ICE_BARRAGE_IMPACT_168, new Animation(1979, Priority.HIGH), null, BARRAGE_PROJECTILE, BARRAGE_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(4), Runes.WATER_RUNE.getItem(6))); - return this; - } - - @Override - public void fireEffect(Entity entity, Entity victim, BattleState state) { - if (state.getEstimatedHit() == -1) { - return; - } - int ticks = (1 + (type.ordinal() - SpellType.RUSH.ordinal())) * 8; - if (state.getEstimatedHit() > -1) { - if (!hasTimerActive(victim, "frozen:immunity")) { - registerTimer(victim, spawnTimer("frozen", ticks, true)); - } else if (type == SpellType.BARRAGE) { - state.setFrozen(true); - } - } - } - - @Override - public BattleState[] getTargets(Entity entity, Entity target) { - if (animation.getId() == 1978 || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { - return super.getTargets(entity, target); - } - List list = getMultihitTargets(entity, target, 9); - BattleState[] targets = new BattleState[list.size()]; - int index = 0; - for (Entity e : list) { - targets[index++] = new BattleState(entity, e); - } - return targets; - } - - @Override - public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { - return getType().getImpactAmount(entity, victim, 4); - } - -} +package content.global.skill.magic.ancient; + +import java.util.List; + +import core.game.node.entity.combat.spell.Runes; +import core.game.node.Node; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.spell.CombatSpell; +import core.game.node.entity.combat.spell.SpellType; +import core.game.node.entity.impl.Projectile; +import core.game.node.entity.impl.Animator.Priority; +import core.game.node.entity.player.link.SpellBookManager.SpellBook; +import core.game.node.item.Item; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; +import core.plugin.Initializable; +import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; + +/** + * Handles the Ice spells from the Ancient spellbook. + * @author Emperor + * @version 1.0 + */ +@Initializable +public final class IceSpells extends CombatSpell { + + /** + * The barrage orb GFX. + */ + private static final Graphics BARRAGE_ORB = new Graphics(1677, 96); //119 + + /** + * The projectile for Ice rush. + */ + private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 360, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice rush. + */ + private static final Graphics RUSH_END = new Graphics(361, 96); + + /** + * The projectile for Ice barrage. + */ + private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 362, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice barrage. + */ + private static final Graphics BURST_END = new Graphics(363, 0); + + /** + * The start graphic for Ice rush. + */ + private static final Graphics BLITZ_START = new Graphics(366, 96); + + /** + * The end graphic for Ice rush. + */ + private static final Graphics BLITZ_END = new Graphics(367, 96); + + /** + * The projectile for Ice barrage. + */ + private static final Projectile BARRAGE_PROJECTILE = Projectile.create((Entity) null, null, 368, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice barrage. + */ + private static final Graphics BARRAGE_END = new Graphics(369, 0); + + /** + * Constructs a new {@code IceSpells} {@code Object}. + */ + public IceSpells() { + /* + * empty. + */ + } + + /** + * Constructs a new {@code IceSpells} {@Code Object} + * @param type The spell type. + * @param impactSound The impact sound id. + * @param anim The animation. + * @param start The start graphics. + * @param projectile The projectile. + * @param end The end graphics. + */ + private IceSpells(SpellType type, int level, double baseExperience, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.ANCIENT, level, baseExperience, Sounds.ICE_CAST_171, impactSound, anim, start, projectile, end, runes); + } + + @Override + public void visualize(Entity entity, Node target) { + entity.graphics(graphic); + if (projectile != null) { + projectile.transform(entity, (Entity) target, false, 58, 10).send(); + } + entity.animate(animation); + playGlobalAudio(entity.getLocation(), audio.id, 20); + + } + + @Override + public void visualizeImpact(Entity entity, Entity target, BattleState state) { + if (state.isFrozen()) { + playGlobalAudio(target.getLocation(), impactAudio, 20); + target.graphics(BARRAGE_ORB); + return; + } + super.visualizeImpact(entity, target, state); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + SpellBook.ANCIENT.register(0, new IceSpells(SpellType.RUSH, 58, 34.0, Sounds.ICE_RUSH_IMPACT_173, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2), Runes.WATER_RUNE.getItem(2))); + SpellBook.ANCIENT.register(2, new IceSpells(SpellType.BURST, 70, 40.0, Sounds.ICE_BURST_IMPACT_170, new Animation(1979, Priority.HIGH), null, BURST_PROJECTILE, BURST_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4), Runes.WATER_RUNE.getItem(4))); + SpellBook.ANCIENT.register(1, new IceSpells(SpellType.BLITZ, 82, 46.0, Sounds.ICE_BLITZ_IMPACT_169, new Animation(1978, Priority.HIGH), BLITZ_START, null, BLITZ_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.WATER_RUNE.getItem(3))); + SpellBook.ANCIENT.register(3, new IceSpells(SpellType.BARRAGE, 94, 52.0, Sounds.ICE_BARRAGE_IMPACT_168, new Animation(1979, Priority.HIGH), null, BARRAGE_PROJECTILE, BARRAGE_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(4), Runes.WATER_RUNE.getItem(6))); + return this; + } + + @Override + public void fireEffect(Entity entity, Entity victim, BattleState state) { + if (state.getEstimatedHit() == -1) { + return; + } + int ticks = (type.ordinal() - 4) * 8; + if (hasTimerActive(victim, "frozen") || hasTimerActive(victim, "frozen:immunity")) { + if (type == SpellType.BARRAGE) { state.setFrozen(true); } + return; + } + registerTimer(victim, spawnTimer("frozen", ticks, true)); + } + + @Override + public BattleState[] getTargets(Entity entity, Entity target) { + if (animation.getId() == 1978 || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { + return super.getTargets(entity, target); + } + List list = getMultihitTargets(entity, target, 9); + BattleState[] targets = new BattleState[list.size()]; + int index = 0; + for (Entity e : list) { + targets[index++] = new BattleState(entity, e); + } + return targets; + } + + @Override + public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { + return getType().getImpactAmount(entity, victim, 4); + } + +} diff --git a/Server/src/main/content/global/skill/magic/lunar/DreamSpell.java b/Server/src/main/content/global/skill/magic/lunar/DreamSpell.java deleted file mode 100644 index e35542f0a..000000000 --- a/Server/src/main/content/global/skill/magic/lunar/DreamSpell.java +++ /dev/null @@ -1,104 +0,0 @@ -package content.global.skill.magic.lunar; - -import core.game.node.entity.combat.spell.MagicSpell; -import core.game.node.entity.combat.spell.Runes; -import core.game.node.entity.skill.Skills; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.spell.SpellType; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the dream magic spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DreamSpell extends MagicSpell { - - /** - * Represents the starting animation. - */ - private static final Animation START = Animation.create(6295); - - /** - * Represents the dreaming animation. - */ - private static final Animation DREAMING = Animation.create(6296); - - /** - * Represents the end animation. - */ - private static final Animation END = Animation.create(6297); - - /** - * Represents the graphics of this spell. - */ - private static final Graphics GRAPHIC = new Graphics(1056); - - /** - * Constructs a new {@code CureOtherSpell} {@code Object}. - */ - public DreamSpell() { - super(SpellBook.LUNAR, 79, 82, null, null, null, new Item[] { new Item(Runes.COSMIC_RUNE.getId(), 1), new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.BODY_RUNE.getId(), 5) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(10, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - final Player p = (Player) entity; - if (p.getSkills().getLifepoints() == p.getSkills().getStaticLevel(Skills.HITPOINTS)) { - p.getPacketDispatch().sendMessage("You have no need to cast this spell since your hitpoints are already full."); - return false; - } - if (!meetsRequirements(entity, true, true)) { - return false; - } - p.animate(START); - p.lock(); - GameWorld.getPulser().submit(new Pulse(4, p) { - @Override - public boolean pulse() { - p.animate(DREAMING); - p.graphics(GRAPHIC); - p.unlock(); - return true; - } - - }); - p.getPulseManager().run(new Pulse(18, p) { - @Override - public boolean pulse() { - p.graphics(GRAPHIC); - p.getSkills().heal(1); - if (p.getSkills().getLifepoints() == p.getSkills().getStaticLevel(Skills.HITPOINTS)) { - stop(); - return true; - } - return false; - } - - @Override - public void stop() { - super.stop(); - p.graphics(new Graphics(-1)); - p.animate(END); - } - }); - return true; - } - -} diff --git a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt index 6ed1cfebc..b89b1a3c7 100644 --- a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt +++ b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt @@ -5,10 +5,12 @@ import content.global.skill.farming.CompostBins import content.global.skill.farming.CompostType import content.global.skill.farming.FarmingPatch import content.global.skill.magic.SpellListener +import content.global.skill.magic.homeTeleport import content.global.skill.magic.spellconsts.Lunar import core.api.* import core.game.component.CloseEvent import core.game.component.Component +import core.game.interaction.QueueStrength import core.game.node.Node import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.npc.NPC @@ -20,6 +22,8 @@ import core.game.node.scenery.Scenery import core.game.system.command.Privilege import core.game.system.config.NPCConfigParser import core.game.system.task.Pulse +import core.game.system.timer.impl.PoisonImmunity +import core.game.system.timer.impl.SkillRestore import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.repository.Repository @@ -34,7 +38,7 @@ class LunarListeners : SpellListener("lunar"), Commands { // Level 0 onCast(Lunar.HOME_TELEPORT, NONE) { player, _ -> requires(player) - player.teleporter.send(Location.create(2100, 3914, 0),TeleportManager.TeleportType.HOME) + homeTeleport(player, Location.create(2100, 3914, 0)) setDelay(player,true) } @@ -169,9 +173,10 @@ class LunarListeners : SpellListener("lunar"), Commands { } // Level 79 - /** - * Dream - */ + onCast(Lunar.DREAM, NONE) { player, _ -> + requires(player, 79, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.BODY_RUNE_559, 5), Item(Items.COSMIC_RUNE_564, 1))) + dream(player) + } // Level 80 onCast(Lunar.STRING_JEWELLERY, NONE) { player, _ -> @@ -295,7 +300,7 @@ class LunarListeners : SpellListener("lunar"), Commands { if(playerPies.isEmpty()){ player.sendMessage("You have no pies which you have the level to cook.") - return + throw IllegalStateException() } player.pulseManager.run(object : Pulse(){ @@ -320,30 +325,38 @@ class LunarListeners : SpellListener("lunar"), Commands { } // Level 66 - fun curePlant(player: Player, obj: Scenery){ + fun curePlant(player: Player, obj: Scenery) { + if (CompostBins.forObject(obj) != null) { + sendMessage(player, "Bins don't often get diseased.") + throw IllegalStateException() + } val fPatch = FarmingPatch.forObject(obj) - if(fPatch == null){ - sendMessage(player, "You attempt to cast Cure Plant on ${obj.definition.name}!") - sendMessage(player, "Nothing interesting happens.") - return + if (fPatch == null) { + sendMessage(player, "Umm... this spell won't cure that!") + throw IllegalStateException() } val patch = fPatch.getPatchFor(player) - if(!patch.isDiseased && !patch.isWeedy() && !patch.isEmptyAndWeeded()){ - sendMessage(player, "It is growing just fine.") - return - } - if(patch.isWeedy()){ + if (patch.isWeedy()) { sendMessage(player, "The weeds are healthy enough already.") - return + throw IllegalStateException() } - if(patch.isDead){ - sendMessage(player, "It says 'Cure' not 'Resurrect'. Although death may arise from disease, it is not in itself a disease and hence cannot be cured. So there.") - return + if (patch.isEmptyAndWeeded()) { + sendMessage(player, "There's nothing there to cure.") + throw IllegalStateException() } - if(patch.isGrown()){ + if (patch.isGrown()) { sendMessage(player, "That's not diseased.") - return + throw IllegalStateException() } + if (patch.isDead) { + sendMessage(player, "It says 'Cure' not 'Resurrect'. Although death may arise from disease, it is not in itself a disease and hence cannot be cured. So there.") + throw IllegalStateException() + } + if (!patch.isDiseased) { + sendMessage(player, "It is growing just fine.") + throw IllegalStateException() + } + patch.cureDisease() removeRunes(player) addXP(player,60.0) @@ -355,7 +368,7 @@ class LunarListeners : SpellListener("lunar"), Commands { private fun monsterExamine(player: Player, npc: NPC){ if(!npc.location.withinDistance(player.location)){ sendMessage(player, "You must get closer to use this spell.") - return + throw IllegalStateException() } face(player, npc) visualizeSpell(player, Animations.LUNAR_SPELLBOOK_STATSPY_6293, Graphics.LUNAR_SPELLBOOK_STAT_SPY_OVER_PLAYER_1060, soundID = Sounds.LUNAR_STAT_SPY_3620) @@ -369,7 +382,7 @@ class LunarListeners : SpellListener("lunar"), Commands { setInterfaceText(player, "Hitpoints : ${npc.definition.handlers[NPCConfigParser.LIFEPOINTS] ?: 0}", Components.DREAM_MONSTER_STAT_522, 2) setInterfaceText(player, "Max hit : ${npc.getSwingHandler(false).calculateHit(npc, player, 1.0)}", Components.DREAM_MONSTER_STAT_522, 3) - val poisonStatus = if(npc.definition.handlers.getOrDefault(NPCConfigParser.POISON_IMMUNE,false) == true){ + val poisonStatus = if(npc.isPoisonImmune){ "This creature is immune to poison." } else "This creature is not immune to poison." @@ -391,20 +404,20 @@ class LunarListeners : SpellListener("lunar"), Commands { private fun cureOther(player: Player, target: Node) { if(!isPlayer(target)) { sendMessage(player, "You can only cast this spell on other players.") - return + throw IllegalStateException() } val p = target.asPlayer() if(!p.isActive || p.locks.isInteractionLocked) { sendMessage(player, "This player is busy.") - return + throw IllegalStateException() } if(!p.settings.isAcceptAid) { sendMessage(player, "This player is not accepting any aid.") - return + throw IllegalStateException() } if(!isPoisoned(p)) { sendMessage(player, "This player is not poisoned.") - return + throw IllegalStateException() } player.face(p) visualizeSpell(player, Animations.LUNAR_SPELLBOOK_CURE_OTHER_4411, Graphics.LUNAR_SPELLBOOK_CURE_OTHER_736, 130, Sounds.LUNAR_CURE_OTHER_2886) @@ -427,7 +440,7 @@ class LunarListeners : SpellListener("lunar"), Commands { if(playerEmpties.isEmpty()) { sendMessage(player, "You have nothing in your inventory that this spell can humidify.") - return + throw IllegalStateException() } removeRunes(player) @@ -449,7 +462,7 @@ class LunarListeners : SpellListener("lunar"), Commands { private fun cureMe(player: Player) { if(!isPoisoned(player)) { sendMessage(player, "You are not poisoned.") - return + throw IllegalStateException() } removeRunes(player, true) visualizeSpell(player, Animations.LUNAR_SPELLBOOK_CURE_ME_4411, Graphics.LUNAR_SPELLBOOK_CURE_ME_742, 90, Sounds.LUNAR_CURE_2884) @@ -492,7 +505,7 @@ class LunarListeners : SpellListener("lunar"), Commands { private fun statSpy(player: Player, target: Node) { if(target !is Player) { sendMessage(player, "You can only cast this spell on players.") - return + throw IllegalStateException() } val stat = Components.DREAM_PLAYER_STATS_523 val statCloseEvent = CloseEvent { p, _ -> @@ -568,9 +581,41 @@ class LunarListeners : SpellListener("lunar"), Commands { } // Level 79 - /** - * Dream - */ + private fun dream(player: Player) { + if(player.skills.lifepoints >= getStatLevel(player, Skills.HITPOINTS)) { + sendMessage(player, "You have no need to cast this spell since your hitpoints are already full.") + throw IllegalStateException() + } + + // https://runescape.wiki/w/Dream?oldid=880976 claims Dream makes you heal 1 hp every 20 seconds + // https://oldschool.runescape.wiki/w/Dream claims that Dream has its own timer, so the Dream heals don't need + // to align with the natural heals + val timer = getOrStartTimer(player) + animate(player, Animations.LUNAR_SPELLBOOK_DREAM_START_6295) + removeRunes(player, true) + addXP(player, 82.0) + delayEntity(player, 4) + queueScript(player, 4, QueueStrength.WEAK) { stage: Int -> + if (stage == 0) { + sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location) + playAudio(player, Sounds.LUNAR_SLEEP_3619) + return@queueScript delayScript(player, 5) + } + animate(player, Animations.LUNAR_SPELLBOOK_DREAM_MID_6296) + sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location) + // This heals 2 HP every min. Naturally you heal 1 for a total of 3 + // The script steps every 5 ticks and we want 50 ticks before a heal + if (stage.mod(10) == 0) { + val amt = timer.getHealAmount(player) //accounts for regen brace + heal(player, amt) + if (player.skills.lifepoints >= getStatLevel(player, Skills.HITPOINTS)) { + animate(player, Animations.LUNAR_SPELLBOOK_DREAM_END_6297) + return@queueScript stopExecuting(player) + } + } + return@queueScript delayScript(player, 5) + } + } private fun stringJewellery(player: Player) { val playerJewellery = ArrayDeque() @@ -620,23 +665,23 @@ class LunarListeners : SpellListener("lunar"), Commands { private fun fertileSoil(player: Player, target: Scenery) { if (CompostBins.forObjectID(target.id) != null) { sendMessage(player, "No, that would be silly.") - return + throw IllegalStateException() } val fPatch = FarmingPatch.forObject(target) if(fPatch == null) { sendMessage(player, "Um... I don't want to fertilize that!") - return + throw IllegalStateException() } val patch = fPatch.getPatchFor(player) if (patch.isGrown()) { sendMessage(player, "Composting isn't going to make it get any bigger.") - return + throw IllegalStateException() } if (patch.isFertilized()) { sendMessage(player, "This patch has already been composted.") - return + throw IllegalStateException() } removeRunes(player, true) animate(player, Animations.LUNAR_SPELLBOOK_FERTILE_SOIL_4413) @@ -657,11 +702,11 @@ class LunarListeners : SpellListener("lunar"), Commands { val plankType = PlankType.getForLog(item) if (plankType == null) { sendMessage(player, "You need to use this spell on logs.") - return + throw IllegalStateException() } if (amountInInventory(player, Items.COINS_995) < plankType.price || !removeItem(player, Item(Items.COINS_995, plankType.price))) { sendMessage(player, "You need ${plankType.price} coins to convert that log into a plank.") - return + throw IllegalStateException() } lock(player, 3) setDelay(player, false) @@ -676,20 +721,20 @@ class LunarListeners : SpellListener("lunar"), Commands { private fun energyTransfer(player: Player, target: Node) { if(!isPlayer(target)) { sendMessage(player, "You can only cast this spell on other players.") - return + throw IllegalStateException() } val targetPlayer = target.asPlayer() if(!targetPlayer.isActive || targetPlayer.locks.isInteractionLocked) { sendMessage(player, "This player is busy.") - return + throw IllegalStateException() } if(!targetPlayer.settings.isAcceptAid) { sendMessage(player, "This player is not accepting any aid.") - return + throw IllegalStateException() } if(10 >= player.skills.lifepoints) { sendMessage(player, "You need more hitpoints to cast this spell.") - return + throw IllegalStateException() } player.face(targetPlayer) visualizeSpell(player, Animations.LUNAR_SPELLBOOK_ENERGY_TRANSFER_4411, Graphics.LUNAR_SPELLBOOK_ENERGY_TRANSFER_738, 90, Sounds.LUNAR_ENERGY_TRANSFER_2885) @@ -779,7 +824,10 @@ class LunarListeners : SpellListener("lunar"), Commands { return@define } if(dmg != null) { - p.let { applyPoison(it, it, dmg) } + p.let { + removeTimer(it) + applyPoison(it, it, dmg) + } } else { sendMessage(player, "Damage must be an integer. Format:") sendMessage(player, "::poison username damage") diff --git a/Server/src/main/content/global/skill/magic/modern/ChargeOrbSpells.java b/Server/src/main/content/global/skill/magic/modern/ChargeOrbSpells.java deleted file mode 100644 index 837a44991..000000000 --- a/Server/src/main/content/global/skill/magic/modern/ChargeOrbSpells.java +++ /dev/null @@ -1,206 +0,0 @@ -package content.global.skill.magic.modern; - -import core.game.node.entity.combat.spell.Runes; -import core.game.node.entity.combat.spell.MagicSpell; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.spell.SpellType; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; -import org.rs09.consts.Sounds; - -/** - * Represents the the charging orb magic spell. - * - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class ChargeOrbSpells extends MagicSpell { - - /** - * The animation. - */ - private static final Animation ANIMATION = Animation.create(791); - - /** - * The unpowered orb item. - */ - private static final Item UNPOWERED_ORB = new Item(567); - - /** - * The object id. - */ - private int objectId; - - /** - * The item id. - */ - private int itemId; - - /** - * The button that was clicked. - */ - private int buttonId; - - /** - * Constructs a new {@code ChargeOrbSpells} {@code Object} - */ - public ChargeOrbSpells() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code ChargeOrbSpells} {@code Object}. - * - * @param level The level required. - * @param objectId The object id. - * @param itemId The item to add. - * @param buttonId the button clicked - * @param g The graphics. - * @param runes The runes required. - */ - public ChargeOrbSpells(int level, int objectId, int itemId, int buttonId, Graphics g, Audio a, Item... runes) { - super(SpellBook.MODERN, level, level + 10, ANIMATION, g, a, runes); - this.objectId = objectId; - this.itemId = itemId; - this.buttonId = buttonId; - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.MODERN.register(35, new ChargeOrbSpells(56, 2151, 571, 35, new Graphics(149, 96), new Audio(Sounds.CHARGE_WATER_ORB_118), Runes.WATER_RUNE.getItem(30), Runes.COSMIC_RUNE.getItem(3), UNPOWERED_ORB)); - SpellBook.MODERN.register(39, new ChargeOrbSpells(60, 29415, 575, 39, new Graphics(151, 96), new Audio(Sounds.CHARGE_EARTH_ORB_115), Runes.EARTH_RUNE.getItem(30), Runes.COSMIC_RUNE.getItem(3), UNPOWERED_ORB)); - SpellBook.MODERN.register(46, new ChargeOrbSpells(63, 2153, 569, 46, new Graphics(152, 96), new Audio(Sounds.CHARGE_FIRE_ORB_117), Runes.FIRE_RUNE.getItem(30), Runes.COSMIC_RUNE.getItem(3), UNPOWERED_ORB)); - SpellBook.MODERN.register(49, new ChargeOrbSpells(66, 2152, 573, 49, new Graphics(150, 96), new Audio(Sounds.CHARGE_AIR_ORB_116), Runes.AIR_RUNE.getItem(30), Runes.COSMIC_RUNE.getItem(3), UNPOWERED_ORB)); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - if (!(target instanceof Scenery)) { - return false; - } - Player p = (Player) entity; - Scenery obj = (Scenery) target; - if (obj == null || obj.getId() != objectId) { - p.getPacketDispatch().sendMessage("You can't cast this spell on this object!"); - return false; - } - if (obj.getLocation().getDistance(p.getLocation()) > 3) { - p.getPacketDispatch().sendMessage("You're standing too far from the obelisk's reach."); - return false; - } - if (!p.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 9) - && objectId == 2151 - && p.getInventory().containsItems(Runes.COSMIC_RUNE.getItem(15), Runes.WATER_RUNE.getItem(150), new Item(UNPOWERED_ORB.getId(), 5))) { - p.setAttribute("/save:diary:seers:water-orb-can-earn", true); - - p.setAttribute("/save:diary:seers:water-orb", 1); - } - if (!meetsRequirements(entity, true, true)) { - return false; - } - p.faceLocation(obj.getLocation()); - visualize(p, target); - if (!p.getPulseManager().hasPulseRunning()) { - p.getPulseManager().run(new ChargeOrbPulse(p, new Item(itemId), target, buttonId)); - } - p.getInventory().add(new Item(itemId)); - return true; - } - - /** - * Represents the pulse for automatically charging orbs on obelisks. - * - * @author Splinter - * @version 1.0 - */ - public static class ChargeOrbPulse extends SkillPulse { - - /** - * The item we are going to recieve (always an orb). - */ - public Item item; - - /** - * The target of the spell. - */ - public Node target; - - /** - * The button (spell) that we clicked. - */ - public int buttonId; - - /** - * The unpowered orb item. - */ - private static final Item UNPOWERED_ORB = new Item(567, 1); - - /** - * Constructs a new {@code ChargeOrbPulse} {@code Object}. - * - * @param player the player. - * @param target the node. - * @param item the item. - * @param buttonId the clicked button - */ - public ChargeOrbPulse(Player player, Item item, Node target, int buttonId) { - super(player, item); - this.item = item; - this.target = target; - this.buttonId = buttonId; - } - - @Override - public boolean checkRequirements() { - if (player.getInventory().contains(UNPOWERED_ORB.getId(), 1)) { - return true; - } - return false; - } - - @Override - public void animate() { - player.animate(new Animation(791)); - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - super.setDelay(4); - return false; - } - if (ChargeOrbSpells.castSpell(player, SpellBook.MODERN, buttonId, target)) { - if (!player.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 9) - && player.getAttribute("diary:seers:water-orb-can-earn", false)) { - player.setAttribute("/save:diary:seers:water-orb", 1 + player.getAttribute("diary:seers:water-orb", 0)); - } - if (player.getAttribute("diary:seers:water-orb", 0) >= 5) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 9); - player.removeAttribute("diary:seers:water-orb-can-earn"); - } - return false; - } else { - return true; - } - } - - @Override - public void stop() { - player.removeAttribute("diary:seers:water-orb-can-earn"); - } - } -} diff --git a/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java b/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java index ec23e258f..582795a81 100644 --- a/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java +++ b/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java @@ -1,5 +1,7 @@ package content.global.skill.magic.modern; +import content.region.kandarin.feldip.quest.zogreflesheaters.SkogreBehavior; +import content.region.kandarin.feldip.quest.zogreflesheaters.ZogreBehavior; import core.game.node.entity.combat.spell.Runes; import core.game.node.Node; import core.game.node.entity.Entity; @@ -15,8 +17,12 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.NPCs; import org.rs09.consts.Sounds; +import java.util.Arrays; +import java.util.List; + /** * Handles the crumble undead spell. * @author Emperor @@ -35,6 +41,15 @@ public final class CrumbleUndead extends CombatSpell { @Override public boolean cast(Entity entity, Node target) { NPC npc = target instanceof NPC ? (NPC) target : null; + // Exception for Zogres and Skogres because they are classified as OGRES in npc.getTask() as not undead, + // so you couldn't use crumble undead on it. This bypasses that. + if ( + Arrays.stream(ZogreBehavior.zogreIds).anyMatch(i -> i == npc.getId()) || + Arrays.stream(SkogreBehavior.skogreIds).anyMatch(i -> i == npc.getId()) || + npc.getId() == NPCs.SLASH_BASH_2060 + ) { + return super.cast(entity, target); + } if (npc == null || npc.getTask() == null || !npc.getTask().undead) { ((Player) entity).getPacketDispatch().sendMessage("This spell only affects the undead."); return false; diff --git a/Server/src/main/content/global/skill/magic/modern/ModernData.kt b/Server/src/main/content/global/skill/magic/modern/ModernData.kt new file mode 100644 index 000000000..793a757b5 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/ModernData.kt @@ -0,0 +1,63 @@ +package content.global.skill.magic.modern + +import core.game.node.item.Item +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +enum class ChargeOrbData( + val obelisk: Int, + val requiredRunes: Array, + val level: Int, + val experience: Double, + val graphics: Graphics, + val sound: Int, + val chargedOrb: Int +) { + CHARGE_WATER_ORB( + Scenery.OBELISK_OF_WATER_2151, + arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.WATER_RUNE_555, 30), Item(Items.UNPOWERED_ORB_567)), + 56, + 66.0, + Graphics(149, 90), + Sounds.CHARGE_WATER_ORB_118, + Items.WATER_ORB_571 + ), + CHARGE_EARTH_ORB( + Scenery.OBELISK_OF_EARTH_29415, + arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.EARTH_RUNE_557, 30), Item(Items.UNPOWERED_ORB_567)), + 60, + 70.0, + Graphics(151, 90), + Sounds.CHARGE_EARTH_ORB_115, + Items.EARTH_ORB_575 + ), + CHARGE_FIRE_ORB( + Scenery.OBELISK_OF_FIRE_2153, + arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.FIRE_RUNE_554, 30), Item(Items.UNPOWERED_ORB_567)), + 63, + 73.0, + Graphics(152, 90), + Sounds.CHARGE_FIRE_ORB_117, + Items.FIRE_ORB_569 + ), + CHARGE_AIR_ORB( + Scenery.OBELISK_OF_AIR_2152, + arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.AIR_RUNE_556, 30), Item(Items.UNPOWERED_ORB_567)), + 66, + 76.0, + Graphics(150, 90), + Sounds.CHARGE_AIR_ORB_116, + Items.AIR_ORB_573 + ); + companion object{ + val spellMap = HashMap() + + init { + for (spell in ChargeOrbData.values()) { + spellMap[spell.obelisk] = spell + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt index f0076c83f..d817cd31c 100644 --- a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt +++ b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt @@ -1,70 +1,46 @@ package content.global.skill.magic.modern +import content.data.Quests import content.global.skill.magic.SpellListener +import content.global.skill.magic.SpellUtils.hasRune import content.global.skill.magic.TeleportMethod +import content.global.skill.magic.homeTeleport import content.global.skill.magic.spellconsts.Modern -import core.game.event.ItemAlchemizationEvent -import core.game.event.TeleportEvent import content.global.skill.prayer.Bones +import content.global.skill.smithing.smelting.Bar +import content.global.skill.smithing.smelting.SmeltingPulse +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners +import core.ServerConstants +import core.api.* +import core.game.event.ItemAlchemizationEvent +import core.game.event.ResourceProducedEvent +import core.game.event.TeleportEvent import core.game.interaction.MovementPulse +import core.game.node.Node import core.game.node.entity.Entity +import core.game.node.entity.combat.spell.MagicStaff +import core.game.node.entity.impl.Animator import core.game.node.entity.impl.Projectile import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager -import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills -import content.global.skill.smithing.smelting.Bar -import content.global.skill.smithing.smelting.SmeltingPulse import core.game.node.item.Item import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import org.rs09.consts.Items -import core.ServerConstants -import core.api.* -import core.game.node.entity.combat.spell.MagicStaff -import core.game.node.entity.impl.Animator +import org.rs09.consts.Scenery import org.rs09.consts.Sounds class ModernListeners : SpellListener("modern"){ - - private val CONFUSE_START = Graphics(102, 96) - private val CONFUSE_PROJECTILE = Projectile.create(null as Entity?, null, 103, 40, 36, 52, 75, 15, 11) - private val CONFUSE_END = Graphics(104, 96) - private val WEAKEN_START = Graphics(105, 96) - private val WEAKEN_PROJECTILE = Projectile.create(null as Entity?, null, 106, 40, 36, 52, 75, 15, 11) - private val WEAKEN_END = Graphics(107, 96) - private val CURSE_START = Graphics(108, 96) - private val CURSE_PROJECTILE = Projectile.create(null as Entity?, null, 109, 40, 36, 52, 75, 15, 11) - private val CURSE_END = Graphics(110, 96) - private val VULNER_START = Graphics(167, 96) - private val VULNER_PROJECTILE = Projectile.create(null as Entity?, null, 168, 40, 36, 52, 75, 15, 11) - private val VULNER_END = Graphics(169, 96) - private val ENFEEBLE_START = Graphics(170, 96) - private val ENFEEBLE_PROJECTILE = Projectile.create(null as Entity?, null, 171, 40, 36, 52, 75, 15, 11) - private val ENFEEBLE_END = Graphics(172, 96) - private val STUN_START = Graphics(173, 96) - private val STUN_PROJECTILE = Projectile.create(null as Entity?, null, 174, 40, 36, 52, 75, 15, 11) - private val STUN_END = Graphics(107, 96) - private val LOW_ANIMATION = Animation(716, Animator.Priority.HIGH) - private val HIGH_ANIMATION = Animation(729, Animator.Priority.HIGH) - private val LOW_ALCH_ANIM = Animation(712) - private val LOW_ALCH_GFX = Graphics(112,5) - private val HIGH_ALCH_ANIM = Animation(713) - private val HIGH_ALCH_GFX = Graphics(113,5) - private val BONE_CONVERT_GFX = Graphics(141, 96) - private val BONE_CONVERT_ANIM = Animation(722) - - override fun defineListeners() { - onCast(Modern.HOME_TELEPORT, NONE){ player, _ -> if (!getAttribute(player, "tutorial:complete", false)) { return@onCast } requires(player) - player.teleporter.send(ServerConstants.HOME_LOCATION,TeleportManager.TeleportType.HOME) + homeTeleport(player, ServerConstants.HOME_LOCATION ?: Location(3222, 3218, 0)) setDelay(player,true) } @@ -92,31 +68,36 @@ class ModernListeners : SpellListener("modern"){ } onCast(Modern.ARDOUGNE_TELEPORT, NONE){ player, _ -> - if (!hasRequirement(player, "Plague City")) - return@onCast - requires(player,51, arrayOf(Item(Items.WATER_RUNE_555,2),Item(Items.LAW_RUNE_563,2))) - sendTeleport(player,61.0, Location.create(2662, 3307, 0)) + if (getAttribute(player, PlagueCityListeners.ARDOUGNE_TELE_ATTRIBUTE, false)){ + requires(player,51, arrayOf(Item(Items.WATER_RUNE_555,2),Item(Items.LAW_RUNE_563,2))) + sendTeleport(player,61.0, Location.create(2662, 3307, 0)) + } + else { + // source https://runescape.salmoneus.net/forums/topic/289818-ardougne-teleport-help/ + sendDialogue(player, "You haven\'t learnt how to cast this spell yet") + } + return@onCast } onCast(Modern.WATCHTOWER_TELEPORT, NONE){ player, _ -> - if (!hasRequirement(player, "Watchtower")) + if (!hasRequirement(player, Quests.WATCHTOWER)) return@onCast requires(player,58, arrayOf(Item(Items.EARTH_RUNE_557,2),Item(Items.LAW_RUNE_563,2))) sendTeleport(player,68.0, Location.create(2549, 3112, 0)) } onCast(Modern.TROLLHEIM_TELEPORT, NONE){ player, _ -> - if (!hasRequirement(player, "Eadgar's Ruse")) + if (!hasRequirement(player, Quests.EADGARS_RUSE)) return@onCast requires(player,61, arrayOf(Item(Items.FIRE_RUNE_554,2),Item(Items.LAW_RUNE_563,2))) sendTeleport(player,68.0, Location.create(2891, 3678, 0)) } onCast(Modern.APE_ATOLL_TELEPORT, NONE){ player, _ -> - if (!hasRequirement(player, "Monkey Madness")) + if (!hasRequirement(player, Quests.MONKEY_MADNESS)) return@onCast requires(player,64, arrayOf(Item(Items.FIRE_RUNE_554,2),Item(Items.WATER_RUNE_555,2),Item(Items.LAW_RUNE_563,2),Item(Items.BANANA_1963))) - sendTeleport(player,74.0, Location.create(2754, 2784, 0)) + sendTeleport(player,74.0, Location.create(2795, 2798, 1)) } onCast(Modern.TELEPORT_TO_HOUSE, NONE){ player, _ -> @@ -151,6 +132,11 @@ class ModernListeners : SpellListener("modern"){ requires(player,60, arrayOf(Item(Items.EARTH_RUNE_557,4), Item(Items.WATER_RUNE_555,4), Item(Items.NATURE_RUNE_561,2))) boneConvert(player,false) } + + onCast(Modern.CHARGE_WATER_ORB, OBJECT, Scenery.OBELISK_OF_WATER_2151, 3, method = ::chargeOrb) + onCast(Modern.CHARGE_EARTH_ORB, OBJECT, Scenery.OBELISK_OF_EARTH_29415, 3, method = ::chargeOrb) + onCast(Modern.CHARGE_FIRE_ORB, OBJECT, Scenery.OBELISK_OF_FIRE_2153, 3, method = ::chargeOrb) + onCast(Modern.CHARGE_AIR_ORB, OBJECT, Scenery.OBELISK_OF_AIR_2152, 3, method = ::chargeOrb) } private fun boneConvert(player: Player,bananas: Boolean){ @@ -194,7 +180,7 @@ class ModernListeners : SpellListener("modern"){ } private fun superheat(player: Player,item: Item){ - if(!item.name.contains("ore") && !item.name.toLowerCase().equals("coal")){ + if(!item.name.contains("ore") && !item.name.equals("coal", true)){ player.sendMessage("You can only cast this spell on ore.") return } @@ -205,21 +191,27 @@ class ModernListeners : SpellListener("modern"){ return } - var bar = Bar.forOre(item.id) ?: return - if(bar == Bar.IRON && player.inventory.getAmount(Items.COAL_453) >= 2 && player.skills.getLevel(Skills.SMITHING) >= Bar.STEEL.level && player.inventory.contains(Items.IRON_ORE_441,1)) bar = Bar.STEEL + fun returnBar(player: Player,item: Item): Bar? { + // Loop through all metal bars starting with the highest tier + for (potentialBar in Bar.values().reversed()) { + // Check if the ore being cast on is needed for the current bar being considered + val inputOreInBar = potentialBar.ores.map{it.id}.contains(item.id) + // Check the player has all the required ores (and corresponding quantities) to make the current bar being considered + val playerHasNecessaryOres = potentialBar.ores.all{ore -> inInventory(player, ore.id, ore.amount)} + // If both tests pass return the current bar being considered as the one the spell should try to make + if (inputOreInBar && playerHasNecessaryOres) return potentialBar + } + // If none of the bars passed both tests the player must be missing a required ore + player.packetDispatch.sendMessage("You do not have the required ores to make this bar.") + return null + } + var bar = returnBar(player,item)?: return if(player.skills.getLevel(Skills.SMITHING) < bar.level){ player.sendMessage("You need a smithing level of ${bar.level} to superheat that ore.") return } - for (items in bar.ores) { - if (!player.inventory.contains(items.id, items.amount)) { - player.packetDispatch.sendMessage("You do not have the required ores to make this bar.") - return - } - } - player.lock(3) removeRunes(player) addXP(player,53.0) @@ -229,7 +221,7 @@ class ModernListeners : SpellListener("modern"){ setDelay(player,false) } - public fun alchemize(player: Player, item: Item, high: Boolean) : Boolean { + fun alchemize(player: Player, item: Item, high: Boolean, explorersRing: Boolean = false): Boolean { if(item.name == "Coins") player.sendMessage("You can't alchemize something that's already gold!").also { return false } if((!item.definition.isTradeable) && (!item.definition.isAlchemizable)) player.sendMessage("You can't cast this spell on something like that.").also { return false } @@ -239,7 +231,7 @@ class ModernListeners : SpellListener("modern"){ } val coins = Item(995, item.definition.getAlchemyValue(high)) - if (coins.amount > 0 && !player.inventory.hasSpaceFor(coins)) { + if (item.amount > 1 && coins.amount > 0 && !player.inventory.hasSpaceFor(coins)) { player.sendMessage("Not enough space in your inventory!") return false } @@ -248,21 +240,21 @@ class ModernListeners : SpellListener("modern"){ player.pulseManager.clear() } - val weapon = player.equipment.getItem(getItemFromEquipment(player, EquipmentSlot.WEAPON)) - if (weapon != null && !weapon.equals(MagicStaff.FIRE_RUNE)) { - player.animate(Animation(if (high) 9633 else 9625)) - player.graphics(Graphics(if (high) 1693 else 1692)) + if (explorersRing) { + visualize(player, LOW_ALCH_ANIM, EXPLORERS_RING_GFX) } else { - player.animate(Animation(if (high) 713 else 712)) - player.graphics(Graphics(if (high) 113 else 112)) + val weapon = getItemFromEquipment(player, EquipmentSlot.WEAPON) + if (weapon != null && weapon.id in MagicStaff.FIRE_RUNE.staves) { + visualize(player, if (high) HIGH_ALCH_STAFF_ANIM else LOW_ALCH_STAFF_ANIM, if (high) HIGH_ALCH_STAFF_GFX else LOW_ALCH_STAFF_GFX) + } else { + visualize(player, if (high) HIGH_ALCH_ANIM else LOW_ALCH_ANIM, if (high) HIGH_ALCH_GFX else LOW_ALCH_GFX) + } } playAudio(player, if (high) Sounds.HIGH_ALCHEMY_97 else Sounds.LOW_ALCHEMY_98) - - if (coins.amount > 0) - player.inventory.add(coins) - player.dispatch(ItemAlchemizationEvent(item.id, high)) - player.inventory.remove(Item(item.id, 1)) + if (player.inventory.remove(Item(item.id, 1)) && coins.amount > 0) { + player.inventory.add(coins) + } removeRunes(player) addXP(player, if (high) 65.0 else 31.0) showMagicTab(player) @@ -309,4 +301,82 @@ class ModernListeners : SpellListener("modern"){ addXP(player,30.0) setDelay(player,true) } -} + + private fun chargeOrb(player: Player, node: Node?) { + if (node == null) return + val spell = ChargeOrbData.spellMap[node.id] ?: return + requires(player, spell.level, spell.requiredRunes) + removeAttribute(player, "spell:runes") + face(player, node) + sendSkillDialogue(player) { + withItems(spell.chargedOrb) + calculateMaxAmount { return@calculateMaxAmount amountInInventory(player, Items.UNPOWERED_ORB_567) } + create { _, amount -> + var crafted = 0 + queueScript(player, 0) { + if (!hasLevelDyn(player, Skills.MAGIC, spell.level)) { + sendMessage(player, "You need a magic level of ${spell.level} to cast this spell.") + return@queueScript stopExecuting(player) + } + for (rune in spell.requiredRunes) { + if(!hasRune(player,rune)){ + sendMessage(player, "You don't have enough ${rune.name.lowercase()}s to cast this spell.") + return@queueScript stopExecuting(player) + } + } + visualizeSpell(player, CHARGE_ORB_ANIM, spell.graphics, spell.sound) + removeRunes(player) + val success = addItem(player, spell.chargedOrb) + if (!success) { + return@queueScript stopExecuting(player) + } + addXP(player, spell.experience) + setDelay(player, 3) + crafted++ + + if (crafted == 5 && spell.chargedOrb == Items.WATER_ORB_571) { + player.dispatch(ResourceProducedEvent(spell.chargedOrb, crafted, node)) + } + if (amount == crafted) { return@queueScript stopExecuting(player) } + setCurrentScriptState(player, 0) + return@queueScript delayScript(player, 6) + } + } + } + return + } + companion object { + private val CONFUSE_START = Graphics(102, 96) + private val CONFUSE_PROJECTILE = Projectile.create(null as Entity?, null, 103, 40, 36, 52, 75, 15, 11) + private val CONFUSE_END = Graphics(104, 96) + private val WEAKEN_START = Graphics(105, 96) + private val WEAKEN_PROJECTILE = Projectile.create(null as Entity?, null, 106, 40, 36, 52, 75, 15, 11) + private val WEAKEN_END = Graphics(107, 96) + private val CURSE_START = Graphics(108, 96) + private val CURSE_PROJECTILE = Projectile.create(null as Entity?, null, 109, 40, 36, 52, 75, 15, 11) + private val CURSE_END = Graphics(110, 96) + private val VULNER_START = Graphics(167, 96) + private val VULNER_PROJECTILE = Projectile.create(null as Entity?, null, 168, 40, 36, 52, 75, 15, 11) + private val VULNER_END = Graphics(169, 96) + private val ENFEEBLE_START = Graphics(170, 96) + private val ENFEEBLE_PROJECTILE = Projectile.create(null as Entity?, null, 171, 40, 36, 52, 75, 15, 11) + private val ENFEEBLE_END = Graphics(172, 96) + private val STUN_START = Graphics(173, 96) + private val STUN_PROJECTILE = Projectile.create(null as Entity?, null, 174, 40, 36, 52, 75, 15, 11) + private val STUN_END = Graphics(107, 96) + private val LOW_ANIMATION = Animation(716, Animator.Priority.HIGH) + private val HIGH_ANIMATION = Animation(729, Animator.Priority.HIGH) + private val LOW_ALCH_ANIM = Animation(9623) + private val LOW_ALCH_STAFF_ANIM = Animation(9625) + private val HIGH_ALCH_ANIM = Animation(9631) + private val HIGH_ALCH_STAFF_ANIM = Animation(9633) + private val LOW_ALCH_GFX = Graphics(763) + private val HIGH_ALCH_GFX = Graphics(1691) + private val LOW_ALCH_STAFF_GFX = Graphics(1692) + private val HIGH_ALCH_STAFF_GFX = Graphics(1693) + private val EXPLORERS_RING_GFX = Graphics(1698) + private val BONE_CONVERT_GFX = Graphics(141, 96) + private val BONE_CONVERT_ANIM = Animation(722) + private val CHARGE_ORB_ANIM = Animation(726) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/spellconsts/Modern.kt b/Server/src/main/content/global/skill/magic/spellconsts/Modern.kt index 6696eb19b..fa3d3f036 100644 --- a/Server/src/main/content/global/skill/magic/spellconsts/Modern.kt +++ b/Server/src/main/content/global/skill/magic/spellconsts/Modern.kt @@ -17,10 +17,14 @@ object Modern { const val SNARE = 30 const val ARDOUGNE_TELEPORT = 32 const val HIGH_ALCHEMY = 34 + const val CHARGE_WATER_ORB = 35 const val WATCHTOWER_TELEPORT = 37 + const val CHARGE_EARTH_ORB = 39 const val BONES_TO_PEACHES = 40 const val TROLLHEIM_TELEPORT = 44 + const val CHARGE_FIRE_ORB = 46 const val APE_ATOLL_TELEPORT = 47 + const val CHARGE_AIR_ORB = 49 const val VULNERABILITY = 50 const val ENFEEBLE = 53 const val ENTANGLE = 56 diff --git a/Server/src/main/content/global/skill/prayer/BoneBuryListener.kt b/Server/src/main/content/global/skill/prayer/BoneBuryListener.kt index e8b93e25c..5b41927aa 100644 --- a/Server/src/main/content/global/skill/prayer/BoneBuryListener.kt +++ b/Server/src/main/content/global/skill/prayer/BoneBuryListener.kt @@ -5,6 +5,7 @@ import core.game.event.BoneBuryEvent import core.game.interaction.Clocks import core.game.interaction.IntType import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.game.node.item.Item @@ -13,62 +14,37 @@ import org.rs09.consts.Sounds class BoneBuryListener : InteractionListener { override fun defineListeners() { - /** + + /* * Handles the bury options for bones in Bones.kt */ on(Bones.array, IntType.ITEM, "bury") { player, node -> val bones = Bones.forId(node.id) ?: return@on true + if (!clockReady(player, Clocks.SKILLING)) return@on true + if (!inInventory(player, node.id)) return@on true - // Checks if the player is delayed from burying a bone and begins the clock if none exists. - if (!clockReady(player, Clocks.SKILLING)) { - return@on true - } else { - delayClock(player, Clocks.SKILLING, 2) + stopWalk(player) + lock(player, 2) + delayClock(player, Clocks.SKILLING, 2) + sendMessage(player, "You dig a hole in the ground.") + animate(player, Animations.HUMAN_BURYING_BONES_827) + playAudio(player, Sounds.BONES_DOWN_2738) + + // A strong queue is required in the event a player moves immediately after clicking the bones + queueScript(player, 1, QueueStrength.STRONG) { + if (removeBones(player, node.asItem())) { + sendMessage(player, "You bury the bones.") + rewardXP(player, Skills.PRAYER, bones.experience) + player.dispatch(BoneBuryEvent(bones.itemId)) + } + return@queueScript stopExecuting(player) } - - // Verifies the bones are in the player's inventory. - if (!inInventory(player, bones.itemId)) { - return@on true - } - - // Replaces the bones slot in the inventory with nothing and checks that the bones are removed. - if (replaceSlot(player, node.asItem().slot, Item()) != node.asItem()) { - sendMessage(player, "The gods intervene and you keep your bones!") - return@on true - } - - buryBones(player, bones) return@on true } } - /** - * Buries the bones - * @param player the player burying the bones - * @param bones the bones being buried - */ - private fun buryBones(player: Player, bones: Bones): Boolean { - queueScript(player, 0) { stage -> - when (stage) { - 0 -> { - stopWalk(player) - lock(player, 2) - animate(player, Animations.HUMAN_BURYING_BONES_827) - playAudio(player, Sounds.BONES_DOWN_2738) - sendMessage(player, "You dig a hole in the ground.") - return@queueScript delayScript(player, 2) - } - - 1 -> { - sendMessage(player, "You bury the bones.") - rewardXP(player, Skills.PRAYER, bones.experience) - player.dispatch(BoneBuryEvent(bones.itemId)) - return@queueScript stopExecuting(player) - } - - else -> return@queueScript stopExecuting(player) - } - } - return true + private fun removeBones(player: Player, item: Item): Boolean { + val removedBones = replaceSlot(player, item.slot, Item()) + return removedBones == item && removedBones.slot == item.slot } } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/prayer/PrayerAltarListener.kt b/Server/src/main/content/global/skill/prayer/PrayerAltarListener.kt new file mode 100644 index 000000000..71982f578 --- /dev/null +++ b/Server/src/main/content/global/skill/prayer/PrayerAltarListener.kt @@ -0,0 +1,166 @@ +package content.global.skill.prayer + +import core.api.* +import core.game.event.PrayerPointsRechargeEvent +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds +import content.data.Quests + +class PrayerAltarListener : InteractionListener { + override fun defineListeners() { + on(altars, IntType.SCENERY, "pray", "pray-at") { player, node -> + if (node.id == Scenery.TRIBAL_STATUE_3863 && !hasRequirement(player, Quests.TAI_BWO_WANNAI_TRIO)) { + // https://runescape.wiki/w/Tribal_Statue?oldid=1940922 + return@on true + } + + if (!pray(player, node)) { + if (node.id != Scenery.ELIDINIS_STATUETTE_10439) return@on true + } + + if (node.id == Scenery.ELIDINIS_STATUETTE_10439) { + // https://youtu.be/kEwYhnrQCu8?t=727 & https://youtu.be/b9Wy1JWDes8?t=413 + setTempLevel(player, Skills.HITPOINTS, getStatLevel(player, Skills.HITPOINTS).plus(7)) + sendMessage(player, "You feel much healthier after praying in the shrine.") + } + + if (node.id == Scenery.CHAOS_ALTAR_412) { + lock(player, 4) + queueScript(player, 4, QueueStrength.STRONG) { + sendMessage(player, "It's a trap!") + teleport(player, Location(2583, 9576, 0), TeleportManager.TeleportType.INSTANT) + return@queueScript stopExecuting(player) + } + } + return@on true + } + + on(Scenery.CHAOS_ALTAR_61, IntType.SCENERY, "check") { player, _ -> + if (getQuestStage(player, Quests.MERLINS_CRYSTAL) == 70) { + sendDialogue(player, "You find a small inscription at the bottom of the altar. It reads: 'Snarthon Candtrick Termanto'.") + setQuestStage(player, Quests.MERLINS_CRYSTAL, 80) + } else { + sendMessage(player, "An altar of the evil god Zamorak.") + } + return@on true + } + } + + private fun pray(player: Player, node: Node): Boolean { + val prayerLevel = getStatLevel(player, Skills.PRAYER).plus(if (node.id in boostedAltars) 2 else 0) + + if (player.skills.prayerPoints >= prayerLevel.toDouble()) { + sendMessage(player, "You already have full prayer points.") + return false + } + + lock(player, 3) + animate(player, 645) + playAudio(player, Sounds.PRAYER_RECHARGE_2674) + setTempLevel(player, Skills.PRAYER, prayerLevel) + sendMessage(player, "You recharge your prayer points.") + player.dispatch(PrayerPointsRechargeEvent(node)) + return true + } + + companion object { + private val altars = intArrayOf( + Scenery.ALTAR_409, + Scenery.ALTAR_2478, + Scenery.ALTAR_2479, + Scenery.ALTAR_2480, + Scenery.ALTAR_2481, + Scenery.ALTAR_2482, + Scenery.ALTAR_2483, + Scenery.ALTAR_2484, + Scenery.ALTAR_2485, + Scenery.ALTAR_2486, + Scenery.ALTAR_2487, + Scenery.ALTAR_2488, + Scenery.ALTAR_2489, + Scenery.ALTAR_2640, + Scenery.ALTAR_4008, + Scenery.ALTAR_8749, + Scenery.ALTAR_10639, + Scenery.ALTAR_10640, + Scenery.ALTAR_13179, + Scenery.ALTAR_13180, + Scenery.ALTAR_13181, + Scenery.ALTAR_13182, + Scenery.ALTAR_13183, + Scenery.ALTAR_13184, + Scenery.ALTAR_13185, + Scenery.ALTAR_13186, + Scenery.ALTAR_13187, + Scenery.ALTAR_13188, + Scenery.ALTAR_13189, + Scenery.ALTAR_13190, + Scenery.ALTAR_13191, + Scenery.ALTAR_13192, + Scenery.ALTAR_13193, + Scenery.ALTAR_13194, + Scenery.ALTAR_13195, + Scenery.ALTAR_13196, + Scenery.ALTAR_13197, + Scenery.ALTAR_13198, + Scenery.ALTAR_13199, + Scenery.ALTAR_15050, + Scenery.ALTAR_15051, + Scenery.ALTAR_18254, + Scenery.ALTAR_19145, + Scenery.ALTAR_20377, + Scenery.ALTAR_20378, + Scenery.ALTAR_20379, + Scenery.ALTAR_24343, + Scenery.ALTAR_27306, + Scenery.ALTAR_27307, + Scenery.ALTAR_27308, + Scenery.ALTAR_27309, + Scenery.ALTAR_27334, + Scenery.ALTAR_27338, + Scenery.ALTAR_27339, + Scenery.ALTAR_27661, + Scenery.ALTAR_30624, + Scenery.ALTAR_30726, + Scenery.ALTAR_34616, + Scenery.ALTAR_36972, + Scenery.ALTAR_37630, + Scenery.ALTAR_37901, + Scenery.ALTAR_37902, + Scenery.ALTAR_37903, + Scenery.ALTAR_37904, + Scenery.ALTAR_37905, + Scenery.ALTAR_37906, + Scenery.ALTAR_37907, + Scenery.ALTAR_37908, + Scenery.ALTAR_37909, + Scenery.ALTAR_37910, + Scenery.ALTAR_37911, + Scenery.ALTAR_37912, + Scenery.ALTAR_39547, + Scenery.ALTAR_39842, + Scenery.CHAOS_ALTAR_61, + Scenery.CHAOS_ALTAR_411, + Scenery.CHAOS_ALTAR_412, + Scenery.CHAOS_ALTAR_32079, + Scenery.CHAOS_ALTAR_37990, + Scenery.GORILLA_STATUE_4858, + Scenery.GORILLA_STATUE_4859, + Scenery.ALTAR_OF_GUTHIX_410, + Scenery.ALTAR_OF_GUTHIX_28698, + Scenery.ALTAR_OF_NATURE_3521, + Scenery.TRIBAL_STATUE_3863, + Scenery.ELIDINIS_STATUETTE_10439, + Scenery.DECAYED_ALTAR_37985 + ) + private val boostedAltars = intArrayOf(Scenery.ALTAR_2640, Scenery.ALTAR_OF_NATURE_3521) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/prayer/PrayerAltarPlugin.java b/Server/src/main/content/global/skill/prayer/PrayerAltarPlugin.java deleted file mode 100644 index c9cb114d7..000000000 --- a/Server/src/main/content/global/skill/prayer/PrayerAltarPlugin.java +++ /dev/null @@ -1,254 +0,0 @@ -package content.global.skill.prayer; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.player.link.quest.Quest; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; -import org.rs09.consts.Sounds; - -import static core.api.ContentAPIKt.hasRequirement; -import static core.api.ContentAPIKt.playAudio; - -/** - * Handles the praying at an alter. - * @author Vexia - */ -@Initializable -public class PrayerAltarPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.setOptionHandler("pray-at", this); - SceneryDefinition.setOptionHandler("pray", this); - SceneryDefinition.forId(61).getHandlers().put("option:check", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (option.equalsIgnoreCase("check")) { - final Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); - if (quest.getStage(player) == 70) { - player.getDialogueInterpreter().sendDialogue("You find a small inscription at the bottom of the altar. It reads:", "'Snarthon Candtrick Termanto'."); - quest.setStage(player, 80); - return true; - } - player.getPacketDispatch().sendMessage("An altar of the evil god Zamorak."); - return true; - } - Altar altar = Altar.forId(node.getId()); - if (altar != null) { - altar.pray(player); - visualize(player); - return true; - } - if (player.getSkills().getPrayerPoints() == player.getSkills().getStaticLevel(Skills.PRAYER)) { - player.getPacketDispatch().sendMessage("You already have full prayer points."); - return true; - } - visualize(player); - player.getSkills().rechargePrayerPoints(); - player.getPacketDispatch().sendMessage("You recharge your Prayer points."); - if (node.getId() == 2640) { - player.getSkills().setLevel(Skills.PRAYER, player.getSkills().getStaticLevel(Skills.PRAYER) + 2); - } - if (node.getId() == 409 - && player.getLocation().withinDistance(new Location(3209, 3495, 1)) - && player.getPrayer().getActive().contains(PrayerType.SMITE)) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.VARROCK,2, 4); - } - if (node.getId() == 39842 - && player.getLocation().withinDistance(new Location(2995, 3177, 0))) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR,0, 13); - } - // Seers task can be completed with either of camelot altar or seers church altar - if (node.getId() == 19145 - && player.getLocation().withinDistance(new Location(2749, 3496, 1))) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.SEERS_VILLAGE,0, 10); - } - if (node.getId() == 409 - && player.getLocation().withinDistance(new Location(2694, 3462, 0))) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.SEERS_VILLAGE,0, 10); - } - - if (node.getLocation().equals(new Location(2571, 9499, 0))) { - player.teleport(new Location(2583, 9576, 0)); - player.sendMessage("It's a trap!"); - return true; - } - return true; - } - - /** - * Visualizes the prayer. - * @param player the player. - */ - public void visualize(Player player) { - player.lock(3); - playAudio(player, Sounds.PRAYER_RECHARGE_2674); - player.animate(Animation.create(645)); - } - - /** - * An altar. - * @author Vexia - */ - public enum Altar { - ANCIENT(6552, SpellBook.ANCIENT.getInterfaceId(), "You feel a strange wisdom fill your mind...", "You feel a strange drain upon your memory...") { - @Override - public void pray(Player player) { - if (!hasRequirement(player, "Desert Treasure")) - return; - if (player.getSkills().getStaticLevel(Skills.MAGIC) < 50) { - player.sendMessage("You need a Magic level of at least 50 in order to do this."); - return; - } - drain(player); - if (!isPrayerType(player)) { - switchToBook(player); - player.sendMessage(getMessages()[0]); - } else { - revert(player); - player.sendMessage(getMessages()[1]); - } - } - }, - LUNAR(17010, SpellBook.LUNAR.getInterfaceId(), "Lunar spells activated!", "Modern spells activated!") { - @Override - public void pray(Player player) { - if (!hasRequirement(player, "Lunar Diplomacy")) - return; - if (player.getSkills().getStaticLevel(Skills.MAGIC) < 65) { - player.sendMessage("You need a Magic level of at least 65 in order to do this."); - return; - } - if (!isPrayerType(player)) { - switchToBook(player); - player.sendMessage(getMessages()[0]); - } else { - revert(player); - player.sendMessage(getMessages()[1]); - } - } - }; - - /** - * The id. - */ - private int id; - - /** - * The book. - */ - private int book; - - /** - * The messages. - */ - private String[] messages; - - /** - * Constructs a new {@Code Altar} {@Code Object} - * @param id the id. - * @param book the book. - * @param messages the messages. - */ - private Altar(int id, int book, String... messages) { - this.id = id; - this.book = book; - this.messages = messages; - } - - /** - * Prays at the altar. - * @param player the player. - */ - public void pray(Player player) { - - } - - /** - * Reverts the book. - * @param player the player. - */ - public void revert(Player player) { - player.getSpellBookManager().setSpellBook(SpellBook.MODERN); - player.getInterfaceManager().openTab(new Component(SpellBook.values()[SpellBook.MODERN.ordinal()].getInterfaceId())); - } - - /** - * Drains the player. - * @param player the player. - */ - public void drain(Player player) { - player.getSkills().decrementPrayerPoints(player.getSkills().getPrayerPoints()); - } - - /** - * Switches to the book. - * @param player the player. - */ - public void switchToBook(Player player) { - player.getSpellBookManager().setSpellBook(SpellBook.forInterface(book)); - player.getInterfaceManager().openTab(new Component(book)); - } - - /** - * Checks if it is the prayer type. - * @param player the player. - * @return true if so. - */ - public boolean isPrayerType(Player player) { - return player.getSpellBookManager().getSpellBook() == book; - } - - /** - * Gets an altar. - * @param id the id. - * @return the altar. - */ - public static Altar forId(int id) { - for (Altar altar : values()) { - if (id == altar.getId()) { - return altar; - } - } - return null; - } - - /** - * Gets the id. - * @return the id - */ - public int getId() { - return id; - } - - /** - * Gets the book. - * @return the book - */ - public int getBook() { - return book; - } - - /** - * Gets the messages. - * @return the messages - */ - public String[] getMessages() { - return messages; - } - } - -} diff --git a/Server/src/main/content/global/skill/runecrafting/Altar.java b/Server/src/main/content/global/skill/runecrafting/Altar.java index 29cc03953..487d61e6f 100644 --- a/Server/src/main/content/global/skill/runecrafting/Altar.java +++ b/Server/src/main/content/global/skill/runecrafting/Altar.java @@ -5,6 +5,7 @@ import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Represents an altar an it's relative information(corresponding ruin, etc) @@ -72,24 +73,24 @@ public enum Altar { */ public void enterRift(Player player) { if (this == ASTRAL) { - if (!hasRequirement(player, "Lunar Diplomacy")) + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return; } if (this == DEATH) { - if (!hasRequirement(player, "Mourning's End Part II")) + if (!hasRequirement(player, Quests.MOURNINGS_END_PART_II)) return; } if (this == BLOOD) { - if (!hasRequirement(player, "Legacy of Seergaze")) + if (!hasRequirement(player, Quests.LEGACY_OF_SEERGAZE)) return; } if (this == LAW) { if (!ItemDefinition.canEnterEntrana(player)) { - player.sendMessage("You can't take weapons and armour into the law rift."); + player.sendMessage("The power of Saradomin prevents you from taking armour or weaponry to Entrana."); return; } } - if (this == COSMIC && !player.getQuestRepository().isComplete("Lost City")) { + if (this == COSMIC && !player.getQuestRepository().isComplete(Quests.LOST_CITY)) { player.getPacketDispatch().sendMessage("You need to have completed the Lost City quest in order to do that."); return; } diff --git a/Server/src/main/content/global/skill/runecrafting/MysteriousRuinListener.kt b/Server/src/main/content/global/skill/runecrafting/MysteriousRuinListener.kt index 68fcf10c7..7bf55dd7f 100644 --- a/Server/src/main/content/global/skill/runecrafting/MysteriousRuinListener.kt +++ b/Server/src/main/content/global/skill/runecrafting/MysteriousRuinListener.kt @@ -1,5 +1,6 @@ package content.global.skill.runecrafting +import content.data.Quests import content.region.misthalin.varrock.diary.VarrockAchivementDiary.Companion.EasyTasks.ENTER_EARTH_ALTAR import core.api.* import core.game.container.impl.EquipmentContainer.SLOT_HAT @@ -19,7 +20,7 @@ class MysteriousRuinListener : InteractionListener { private val animation = Animation(827) private val allowedUsed = arrayOf(1438, 1448, 1444, 1440, 1442, 5516, 1446, 1454, 1452, 1462, 1458, 1456, 1450, 1460).toIntArray() private val allowedWith = allRuins() - private val nothingInteresting = "Nothing interesting happens" + private val nothingInteresting = "Nothing interesting happens." override fun defineListeners() { onUseWith(IntType.SCENERY, allowedUsed, *allowedWith) { player, used, with -> @@ -76,9 +77,9 @@ class MysteriousRuinListener : InteractionListener { private fun checkQuestCompletion(player: Player, ruin: MysteriousRuin): Boolean { return when (ruin) { - MysteriousRuin.DEATH -> hasRequirement(player, QuestReq(MEP_2), true) - MysteriousRuin.BLOOD -> hasRequirement(player, QuestReq(SEERGAZE), true) - else -> hasRequirement(player, QuestReq(RUNE_MYSTERIES), true) + MysteriousRuin.DEATH -> hasRequirement(player, Quests.MOURNINGS_END_PART_II, true) + MysteriousRuin.BLOOD -> hasRequirement(player, Quests.LEGACY_OF_SEERGAZE, true) + else -> hasRequirement(player, Quests.RUNE_MYSTERIES, true) } } @@ -102,4 +103,4 @@ class MysteriousRuinListener : InteractionListener { }) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/runecrafting/PouchManager.kt b/Server/src/main/content/global/skill/runecrafting/PouchManager.kt index 52aba0d4b..22cc1865f 100644 --- a/Server/src/main/content/global/skill/runecrafting/PouchManager.kt +++ b/Server/src/main/content/global/skill/runecrafting/PouchManager.kt @@ -1,5 +1,6 @@ package content.global.skill.runecrafting +import core.api.* import core.game.container.Container import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills @@ -7,109 +8,131 @@ import core.game.node.item.Item import org.json.simple.JSONArray import org.json.simple.JSONObject import org.rs09.consts.Items -import core.tools.colorize /** * A class for managing rune pouches. * @param player the player this manager instance belongs to. - * @author Ceikry + * @author Ceikry, Player Name */ class PouchManager(val player: Player) { - val pouches = mapOf( - Items.SMALL_POUCH_5509 to RCPouch(3,1), - Items.MEDIUM_POUCH_5510 to RCPouch(6,25), - Items.LARGE_POUCH_5512 to RCPouch(9,50), - Items.GIANT_POUCH_5514 to RCPouch(12,75) + Items.SMALL_POUCH_5509 to RCPouch(3, 3, 1), + Items.MEDIUM_POUCH_5510 to RCPouch(6, 264, 25), + Items.LARGE_POUCH_5512 to RCPouch(9, 186, 50), + Items.GIANT_POUCH_5514 to RCPouch(12,140, 75) ) /** * Method to add essence to a pouch - * @param pouchId the id of the pouch we are adding to + * @param itemId the item ID of the pouch we are adding to * @param amount the amount of essence to add * @param essence the ID of the essence item we are trying to add - * @author Ceikry + * @author Ceikry, Player Name */ - fun addToPouch(pouchId: Int, amount: Int, essence: Int){ - if(!checkRequirement(pouchId)){ - player.sendMessage(colorize("%RYou lack the required level to use this pouch.")) + fun addToPouch(itemId: Int, amount: Int, essence: Int) { + val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId + if (!checkRequirement(pouchId)) { + sendMessage(player, "You lack the required level to use this pouch.") return } var amt = amount val pouch = pouches[pouchId] - val otherEssence = when(essence){ + val otherEssence = when(essence) { Items.RUNE_ESSENCE_1436 -> Items.PURE_ESSENCE_7936 Items.PURE_ESSENCE_7936 -> Items.RUNE_ESSENCE_1436 else -> 0 } pouch ?: return - if(amount > pouch.container.freeSlots()){ + if (amount > pouch.container.freeSlots()) { amt = pouch.container.freeSlots() } - if(amt == 0){ - player.sendMessage("This pouch is already full.") + if (amt == pouch.container.freeSlots()) { + sendMessage(player, "Your pouch is full.") //https://www.youtube.com/watch?v=wbYtRwODKTo } - if(pouch.container.contains(otherEssence,1)){ - player.sendMessage("You can only store one type of essence in each pouch.") + if (pouch.container.contains(otherEssence,1)) { + sendMessage(player, "You can only store one type of essence in each pouch.") return } - player.inventory.remove(Item(essence,amt)) - pouch.container.add(Item(essence,amt)) - } + var disappeared = false + if (itemId != Items.SMALL_POUCH_5509) { + pouch.charges -= amt + } + if (pouch.charges <= 0) { + pouch.currentCap -= when (pouchId) { + Items.MEDIUM_POUCH_5510 -> 1 + Items.LARGE_POUCH_5512 -> 2 + Items.GIANT_POUCH_5514 -> 3 + else /*small pouch*/ -> 0 + } + if (pouch.currentCap <= 0) { + // The pouch will disappear: https://runescape.wiki/w/Runecrafting_pouches?oldid=708494, https://oldschool.runescape.wiki/w/Essence_pouch + // "Degraded pouches will continue to degrade and lose essence capacity until they disappear or are repaired." implies that this is the end result of a gradual decay process + if (removeItem(player, itemId)) { + disappeared = true + sendMessage(player, "Your pouch has degraded completely.") + // Reset the pouch for when the player obtains a new one + pouch.currentCap = pouch.capacity + pouch.charges = pouch.maxCharges + pouch.remakeContainer() + replaceAllItems(player, itemId, itemId - 1) //in case the player had more copies + } + } else { + if (!isDecayedPouch(itemId)) { + replaceAllItems(player, itemId, itemId + 1) + } + sendMessage(player, "Your pouch has decayed through use.") //https://www.youtube.com/watch?v=FUcPYrgPUlQ + pouch.charges = 9 * pouch.currentCap //implied by multiple contemporaneous sources, quantified only by https://oldschool.runescape.wiki/w/Large_pouch + pouch.remakeContainer() + if (amt > pouch.currentCap) { + amt = pouch.currentCap + } + } + } + val essItem = Item(essence, amt) + if (!disappeared && removeItem(player, essItem)) { + pouch.container.add(essItem) + } + } /** * Method to withdraw rune essence from a pouch. - * @param pouchId the item ID of the pouch to withdraw from - * @author Ceikry + * @param itemId the item ID of the pouch to withdraw from + * @author Ceikry, Player Name */ - fun withdrawFromPouch(pouchId: Int){ + fun withdrawFromPouch(itemId: Int) { + val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId val pouch = pouches[pouchId] pouch ?: return - val playerFree = player.inventory.freeSlots() + val playerFree = freeSlots(player) var amount = pouch.currentCap - pouch.container.freeSlots() - if (amount > playerFree) amount = playerFree - player.debug("$amount") - if(amount == 0) return - val essence = Item(pouch.container.get(0).id,amount) - pouch.container.remove(essence) - pouch.container.shift() - player.inventory.add(essence) - if(pouch.charges-- <= 0){ - pouch.currentCap -= when(pouchId){ - 5510 -> 1 - 5512 -> 2 - 5514 -> 3 - else -> 0 - } - if(pouch.currentCap <= 0){ - player.inventory.remove(Item(pouchId)) - player.inventory.add(Item(pouchId + 1)) - player.sendMessage(colorize("%RYour ${Item(pouchId).name} has degraded completely.")) - } - pouch.remakeContainer() - pouch.charges = 10 - if(pouchId != 5509) { - player.sendMessage(colorize("%RYour ${Item(pouchId).name.toLowerCase()} has degraded slightly from use.")) + if (amount > playerFree) { + amount = playerFree + } else { + sendMessage(player, "Your pouch has no essence left in it.") //https://www.youtube.com/watch?v=wbYtRwODKTo + if (amount == 0) { + return } } - + val essence = Item(pouch.container.get(0).id, amount) + pouch.container.remove(essence) + pouch.container.shift() + addItem(player, essence.id, essence.amount) } - /** * Method to save pouches to a root JSONObject * @param root the JSONObject we are adding the "pouches" JSONArray to * @author Ceikry */ - fun save(root: JSONObject){ + fun save(root: JSONObject) { val pouches = JSONArray() - for(i in this.pouches){ + for(i in this.pouches) { val pouch = JSONObject() pouch.put("id",i.key.toString()) val items = JSONArray() - for(item in i.value.container.toArray()){ + for(item in i.value.container.toArray()) { item ?: continue val it = JSONObject() it.put("itemId",item.id.toString()) @@ -124,14 +147,13 @@ class PouchManager(val player: Player) { root.put("pouches",pouches) } - /** * Method to parse save data from a JSONArray * @param data the JSONArray that contains the data to parse * @author Ceikry */ - fun parse(data: JSONArray){ - for(e in data){ + fun parse(data: JSONArray) { + for (e in data){ val pouch = e as JSONObject val id = pouch["id"].toString().toInt() val p = pouches[id] @@ -141,7 +163,7 @@ class PouchManager(val player: Player) { p.charges = charges p.currentCap = currentCap p.remakeContainer() - for(i in pouch["container"] as JSONArray){ + for (i in pouch["container"] as JSONArray) { val it = i as JSONObject it["itemId"] ?: continue val item = it["itemId"].toString().toInt() @@ -151,33 +173,31 @@ class PouchManager(val player: Player) { } } - /** * Method for checking the level requirement for a given pouch. * @param pouchId the item ID of the pouch to check * @author Ceikry */ - fun checkRequirement(pouchId: Int): Boolean{ + fun checkRequirement(pouchId: Int): Boolean { val p = pouches[pouchId] p ?: return false return player.skills.getLevel(Skills.RUNECRAFTING) >= p.levelRequirement } - /** * Method for sending the player a message about how much space is left in a pouch - * @param pouchId the item ID of the pouch to check - * @author Ceikry + * @param itemId the item ID of the pouch to check + * @author Ceikry, Player Name */ - fun checkAmount(pouchId: Int){ + fun checkAmount(itemId: Int) { + val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId val p = pouches[pouchId] p ?: return player.sendMessage("This pouch has space for ${p.container.freeSlots()} more essence.") } - - fun isDecayedPouch(pouchId: Int): Boolean{ - if(pouchId == 5510) return false + fun isDecayedPouch(pouchId: Int): Boolean { + if (pouchId == Items.MEDIUM_POUCH_5510) return false return pouches[pouchId - 1] != null } @@ -185,12 +205,12 @@ class PouchManager(val player: Player) { * A class that represents a runecrafting pouch. * @author Ceikry */ - class RCPouch(val capacity: Int, val levelRequirement: Int){ + class RCPouch(val capacity: Int, val maxCharges: Int, val levelRequirement: Int) { var container = Container(capacity) var currentCap = capacity - var charges = 10 - fun remakeContainer(){ + var charges = maxCharges + fun remakeContainer() { this.container = Container(currentCap) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java b/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java index dd48fe6b1..c9fa23ef5 100644 --- a/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java +++ b/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java @@ -2,6 +2,7 @@ package content.global.skill.runecrafting; import content.global.handlers.item.equipment.fistofguthixgloves.FOGGlovesManager; import core.ServerConstants; +import core.api.Container; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; @@ -23,11 +24,13 @@ import org.rs09.consts.Sounds; import java.util.ArrayList; import java.util.Arrays; +import content.data.Quests; /** * A class used to craft runes. * * @author Vexia + * @author Player Name */ public final class RuneCraftPulse extends SkillPulse { @@ -100,47 +103,47 @@ public final class RuneCraftPulse extends SkillPulse { @Override public boolean checkRequirements() { if (altar == Altar.ASTRAL) { - if (!hasRequirement(player, "Lunar Diplomacy")) + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return false; } if (altar == Altar.DEATH) { - if (!hasRequirement(player, "Mourning's End Part II")) + if (!hasRequirement(player, Quests.MOURNINGS_END_PART_II)) return false; } if (altar == Altar.BLOOD) { - if (!hasRequirement(player, "Legacy of Seergaze")) + if (!hasRequirement(player, Quests.LEGACY_OF_SEERGAZE)) return false; } - if (!altar.isOurania() && player.getSkills().getLevel(Skills.RUNECRAFTING) < rune.getLevel()) { - player.getPacketDispatch().sendMessage("You need a Runecrafting level of at least " + rune.getLevel() + " to craft this rune."); + if (!altar.isOurania() && getDynLevel(player, Skills.RUNECRAFTING) < rune.getLevel()) { + sendMessage(player, "You need a Runecrafting level of at least " + rune.getLevel() + " to craft this rune."); return false; } - if (combination && !player.getInventory().containsItem(PURE_ESSENCE)) { - player.getPacketDispatch().sendMessage("You need pure essence to craft this rune."); + if (combination && amountInInventory(player, PURE_ESSENCE.getId()) == 0) { + sendMessage(player, "You need pure essence to craft this rune."); return false; } - if (!altar.isOurania() && !rune.isNormal() && !player.getInventory().containsItem(PURE_ESSENCE)) { - player.getPacketDispatch().sendMessage("You need pure essence to craft this rune."); + if (!altar.isOurania() && !rune.isNormal() && amountInInventory(player, PURE_ESSENCE.getId()) == 0) { + sendMessage(player, "You need pure essence to craft this rune."); return false; } - if (!altar.isOurania() && rune.isNormal() && !player.getInventory().containsItem(PURE_ESSENCE) && !player.getInventory().containsItem(RUNE_ESSENCE)) { - player.getPacketDispatch().sendMessage("You need rune essence or pure essence in order to craft this rune."); + if (!altar.isOurania() && rune.isNormal() && amountInInventory(player, PURE_ESSENCE.getId()) == 0 && amountInInventory(player, RUNE_ESSENCE.getId()) == 0) { + sendMessage(player, "You need rune essence or pure essence in order to craft this rune."); return false; } - if (altar.isOurania() && !player.getInventory().containsItem(PURE_ESSENCE)) { - player.getPacketDispatch().sendMessage("You need pure essence to craft this rune."); + if (altar.isOurania() && amountInInventory(player, PURE_ESSENCE.getId()) == 0) { + sendMessage(player, "You need pure essence to craft this rune."); return false; } - if (combination && player.getSkills().getLevel(Skills.RUNECRAFTING) < combo.getLevel()) { - player.getPacketDispatch().sendMessage("You need a Runecrafting level of at least " + combo.getLevel() + " to combine this rune."); + if (combination && getDynLevel(player, Skills.RUNECRAFTING) < combo.getLevel()) { + sendMessage(player, "You need a Runecrafting level of at least " + combo.getLevel() + " to combine this rune."); return false; } if (node != null) { if (node.getName().contains("rune") && !hasSpellImbue()) { final Rune r = Rune.forItem(node); final Talisman t = Talisman.forName(r.name()); - if (!player.getInventory().containsItem(t.getTalisman())) { - player.getPacketDispatch().sendMessage("You don't have the correct talisman to combine this rune."); + if (amountInInventory(player, t.getTalisman().getId()) == 0) { + sendMessage(player, "You don't have the correct talisman to combine this rune."); return false; } talisman = t; @@ -167,37 +170,63 @@ public final class RuneCraftPulse extends SkillPulse { return true; } - @Override - public void message(int type) { - switch (type) { - case 1: - if (altar != Altar.OURANIA) { - player.getPacketDispatch().sendMessage("You bind the temple's power into " + (combination ? combo.getRune().getName().toLowerCase() : rune.getRune().getName().toLowerCase()) + "s."); - } else { - player.getPacketDispatch().sendMessage("You bind the temple's power into runes."); - } - break; - } - } + private static final int[][] OuraniaTable = { //https://x.com/JagexAsh/status/1312893446395506688/photo/1 + /*level up to 9*/ { 2, 7, 15, 30, 60, 105, 165, 250, 400, 700,1300,2500,5000,10000}, + /*level up to 19*/ { 3, 9, 21, 45, 85, 145, 225, 400,1000,2200,4600,6700,8500,10000}, + /*level up to 29*/ { 8, 23, 55, 110, 220, 430, 850,1650,3250,4750,6150,7500,8800,10000}, + /*level up to 39*/ { 20, 60, 120, 250, 500,1000,2000,4000,5300,6500,7600,8500,9300,10000}, + /*level up to 49*/ { 40, 120, 240, 500,1000,2000,4000,5500,6500,7300,8050,8750,9400,10000}, + /*level up to 59*/ { 80, 250, 600,1300,2650,4150,5250,6250,7000,7700,8350,8950,9500,10000}, + /*level up to 69*/ {100, 300, 700,1500,3050,4450,5500,6450,7200,7900,8500,9050,9550,10000}, + /*level up to 79*/ {200, 700,1700,3500,5000,6200,7100,7800,8300,8700,9100,9400,9700,10000}, + /*level up to 89*/ {400,1000,2450,3900,5250,6300,7100,7800,8400,8900,9300,9600,9800,10000}, + /*level up to 98*/ {650,1650,3300,4750,6100,7100,7800,8400,8900,9300,9600,9800,9900,10000}, + /*level up to 99*/ {900,2200,3750,5200,6550,7500,8100,8600,9000,9300,9600,9800,9900,10000} + }; /** * Method used to craft runes. */ private void craft() { - final Item item = new Item(getEssence().getId(), getEssenceAmount()); + final Item item = getEssenceItem(); int amount = player.getInventory().getAmount(item); - if (!altar.isOurania()) { + if (altar.isOurania()) { + if (removeItem(player, item, Container.INVENTORY)) { + sendMessage(player, "You bind the temple's power into runes."); + player.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_RC, amount); + + int[] OuraniaValues; + if (getDynLevel(player, Skills.RUNECRAFTING) == 99) { + OuraniaValues = OuraniaTable[10]; + } else { + int index = getDynLevel(player, Skills.RUNECRAFTING) / 10; + OuraniaValues = OuraniaTable[index]; + } + for (int i = 0; i < amount; i++) { + int roll = RandomFunction.random(10000); + Rune rune = null; + for (int j = 0; j < 14; j++) { + if (roll < OuraniaValues[j]) { + rune = Rune.values()[13 - j]; + break; + } + } + rewardXP(player, Skills.RUNECRAFTING, rune.getExperience() * 2); + addItemOrDrop(player, rune.getRune().getId(), 1); + } + } + } else { int total = 0; for(int j = 0; j < amount; j++) { // since getMultiplier is stochastic, roll `amount` independent copies total += getMultiplier(); } - Item i = new Item(rune.getRune().getId(), total); - if (player.getInventory().remove(item) && player.getInventory().hasSpaceFor(i)) { - player.getInventory().add(i); + if (removeItem(player, item, Container.INVENTORY)) { + sendMessage(player, "You bind the temple's power into " + (combination ? combo.getRune().getName().toLowerCase() : rune.getRune().getName().toLowerCase()) + "s."); + addItemOrDrop(player, rune.getRune().getId(), total); player.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_RC, amount); - + // Fist of guthix gloves double xp = rune.getExperience() * amount; if ((altar == Altar.AIR && inEquipment(player, Items.AIR_RUNECRAFTING_GLOVES_12863, 1)) @@ -205,7 +234,7 @@ public final class RuneCraftPulse extends SkillPulse { || (altar == Altar.EARTH && inEquipment(player, Items.EARTH_RUNECRAFTING_GLOVES_12865, 1))) { xp += xp * FOGGlovesManager.updateCharges(player, amount) / amount; } - player.getSkills().addExperience(Skills.RUNECRAFTING, xp, true); + rewardXP(player, Skills.RUNECRAFTING, xp); // Achievement Diary handling // Craft some nature runes @@ -213,7 +242,7 @@ public final class RuneCraftPulse extends SkillPulse { player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 3); } // Craft 196 or more air runes simultaneously - if (altar == Altar.AIR && i.getAmount() >= 196) { + if (altar == Altar.AIR && total >= 196) { player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 2); } // Craft a water rune at the Water Altar @@ -222,57 +251,34 @@ public final class RuneCraftPulse extends SkillPulse { } } - } else { - if (player.getInventory().remove(item)) { - player.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_RC, amount); - for (int i = 0; i < amount; i++) { - Rune rune = null; - while (rune == null) { - final Rune temp = Rune.values()[RandomFunction.random(Rune.values().length)]; - if (player.getSkills().getLevel(Skills.RUNECRAFTING) >= temp.getLevel()) { - rune = temp; - } else { - if (RandomFunction.random(3) == 1) { - rune = temp; - } - } - } - player.getSkills().addExperience(Skills.RUNECRAFTING, rune.getExperience() * 2, true); - Item runeItem = rune.getRune(); - player.getInventory().add(runeItem); - } - } } } /** * Method used to combine runes. */ - private final void combine() { + private void combine() { final Item remove = node.getName().contains("talisman") ? node : talisman != null ? talisman.getTalisman() : Talisman.forName(Rune.forItem(node).name()).getTalisman(); boolean imbued = hasSpellImbue(); - if (!imbued ? player.getInventory().remove(remove) : imbued) { + if (!imbued ? removeItem(player, remove, Container.INVENTORY) : imbued) { int amount = 0; int essenceAmt = player.getInventory().getAmount(PURE_ESSENCE); final Item rune = node.getName().contains("rune") ? Rune.forItem(node).getRune() : Rune.forName(Talisman.forItem(node).name()).getRune(); int runeAmt = player.getInventory().getAmount(rune); - if (essenceAmt > runeAmt) { - amount = runeAmt; - } else { - amount = essenceAmt; - } - if (player.getInventory().remove(new Item(PURE_ESSENCE.getId(), amount)) && player.getInventory().remove(new Item(rune.getId(), amount))) { + amount = Math.min(essenceAmt, runeAmt); + if (removeItem(player, new Item(PURE_ESSENCE.getId(), amount), Container.INVENTORY) && removeItem(player, new Item(rune.getId(), amount), Container.INVENTORY)) { for (int i = 0; i < amount; i++) { if (RandomFunction.random(1, 3) == 1 || hasBindingNecklace()) { - player.getInventory().add(new Item(combo.getRune().getId(), 1)); - player.getSkills().addExperience(Skills.RUNECRAFTING, combo.getExperience(), true); + addItemOrDrop(player, combo.getRune().getId(), 1); + rewardXP(player, Skills.RUNECRAFTING, combo.getExperience()); } } if (hasBindingNecklace()) { player.getEquipment().get(EquipmentContainer.SLOT_AMULET).setCharge(player.getEquipment().get(EquipmentContainer.SLOT_AMULET).getCharge() - 1); if (1000 - player.getEquipment().get(EquipmentContainer.SLOT_AMULET).getCharge() > 14) { - player.getEquipment().remove(BINDING_NECKLACE, true); - player.getPacketDispatch().sendMessage("Your binding necklace crumbles into dust."); + if (player.getEquipment().remove(BINDING_NECKLACE, true)) { + sendMessage(player, "Your binding necklace crumbles into dust."); + } } } } @@ -289,39 +295,21 @@ public final class RuneCraftPulse extends SkillPulse { } /** - * Gets the essence amount. + * Gets the rune essence item. * - * @return the amount of essence. + * @return the rune essence item. */ - private int getEssenceAmount() { - if (altar.isOurania() && player.getInventory().containsItem(PURE_ESSENCE)) { - return player.getInventory().getAmount(PURE_ESSENCE); + private Item getEssenceItem() { + if (altar.isOurania() && amountInInventory(player, PURE_ESSENCE.getId()) > 0) { + return new Item(PURE_ESSENCE.getId(), amountInInventory(player, PURE_ESSENCE.getId())); } - if (!rune.isNormal() && player.getInventory().containsItem(PURE_ESSENCE)) { - return player.getInventory().getAmount(PURE_ESSENCE); - } else if (rune.isNormal() && player.getInventory().containsItem(PURE_ESSENCE)) { - return player.getInventory().getAmount(PURE_ESSENCE); - } else { - return player.getInventory().getAmount(RUNE_ESSENCE); + if (!rune.isNormal() && amountInInventory(player, PURE_ESSENCE.getId()) > 0) { + return new Item(PURE_ESSENCE.getId(), amountInInventory(player, PURE_ESSENCE.getId())); } - } - - /** - * Gets the rune essence that needs to be defined. - * - * @return the item. - */ - private Item getEssence() { - if (altar.isOurania() && player.getInventory().containsItem(PURE_ESSENCE)) { - return PURE_ESSENCE; - } - if (!rune.isNormal() && player.getInventory().containsItem(PURE_ESSENCE)) { - return PURE_ESSENCE; - } else if (rune.isNormal() && player.getInventory().containsItem(PURE_ESSENCE)) { - return PURE_ESSENCE; - } else { - return RUNE_ESSENCE; + if (rune.isNormal() && amountInInventory(player, RUNE_ESSENCE.getId()) > 0) { + return new Item(RUNE_ESSENCE.getId(), amountInInventory(player, RUNE_ESSENCE.getId())); } + return new Item(PURE_ESSENCE.getId(), amountInInventory(player, PURE_ESSENCE.getId())); } /** @@ -333,7 +321,7 @@ public final class RuneCraftPulse extends SkillPulse { if (altar.isOurania()) { return 1; } - int rcLevel = player.getSkills().getLevel(Skills.RUNECRAFTING); + int rcLevel = getDynLevel(player, Skills.RUNECRAFTING); int runecraftingFormulaRevision = ServerConstants.RUNECRAFTING_FORMULA_REVISION; boolean lumbridgeDiary = player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE).isComplete(1); return RuneCraftPulse.getMultiplier(rcLevel, rune, runecraftingFormulaRevision, lumbridgeDiary); @@ -348,7 +336,7 @@ public final class RuneCraftPulse extends SkillPulse { } } - if(multipleLevels.length > i && runecraftingFormulaRevision >= 573) { + if (multipleLevels.length > i && runecraftingFormulaRevision >= 573) { int a = Math.max(multipleLevels[i-1], rune.getLevel()); int b = multipleLevels[i]; if(b <= 99 || runecraftingFormulaRevision >= 581) { @@ -385,5 +373,4 @@ public final class RuneCraftPulse extends SkillPulse { public Altar getAltar() { return altar; } - } diff --git a/Server/src/main/content/global/skill/runecrafting/RunePouchPlugin.kt b/Server/src/main/content/global/skill/runecrafting/RunePouchPlugin.kt index 41779cf1f..599ca05b0 100644 --- a/Server/src/main/content/global/skill/runecrafting/RunePouchPlugin.kt +++ b/Server/src/main/content/global/skill/runecrafting/RunePouchPlugin.kt @@ -11,7 +11,7 @@ import core.tools.colorize /** * Handles the rune pouches. - * @author Ceikry + * @author Ceikry, Player Name */ class RunePouchPlugin : OptionHandler() { @Throws(Throwable::class) @@ -37,21 +37,11 @@ class RunePouchPlugin : OptionHandler() { if(preferenceFlag == 0) rEssAmt else pEssAmt ) - - if(player.pouchManager.isDecayedPouch(node.id)){ - player.debug("E2") - when(option) { //Handling for IF the pouch has already completely decayed - "drop" -> player.dialogueInterpreter.open(9878,Item(node.id)) - else -> player.sendMessage(colorize("%RThis pouch has completely decayed and needs to be repaired.")) - } - } else { - player.debug("E") - when (option) { //Normal handling - "fill" -> player.pouchManager.addToPouch(node.id, essence.amount, essence.id) - "empty" -> player.pouchManager.withdrawFromPouch(node.id) - "check" -> player.pouchManager.checkAmount(node.id) - "drop" -> player.dialogueInterpreter.open(9878,Item(node.id)) - } + when (option) { + "fill" -> player.pouchManager.addToPouch(node.id, essence.amount, essence.id) + "empty" -> player.pouchManager.withdrawFromPouch(node.id) + "check" -> player.pouchManager.checkAmount(node.id) + "drop" -> player.dialogueInterpreter.open(9878,Item(node.id)) } return true } @@ -59,4 +49,4 @@ class RunePouchPlugin : OptionHandler() { override fun isWalk(): Boolean { return false } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java b/Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java index 6bb69109f..954948eaf 100644 --- a/Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java +++ b/Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java @@ -24,6 +24,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles runecraftign related options. @@ -52,7 +53,7 @@ public class RunecraftingPlugin extends OptionHandler { @Override public boolean handle(final Player player, Node node, String option) { - if (!player.getQuestRepository().isComplete("Rune Mysteries") && player.getDetails().getRights() != Rights.ADMINISTRATOR) { + if (!player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES) && player.getDetails().getRights() != Rights.ADMINISTRATOR) { player.getPacketDispatch().sendMessage("You need to finish the Rune Mysteries Quest in order to do this."); return true; } @@ -97,7 +98,7 @@ public class RunecraftingPlugin extends OptionHandler { } Altar a = Altar.forObject(((Scenery) node)); if (a == Altar.ASTRAL) { - if (!hasRequirement(player, "Lunar Diplomacy")) + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return true; } player.getPulseManager().run(new RuneCraftPulse(player, null, a, false, null)); diff --git a/Server/src/main/content/global/skill/runecrafting/abyss/DarkMageDialogue.java b/Server/src/main/content/global/skill/runecrafting/abyss/DarkMageDialogue.java index b7297429e..a7b6450cd 100644 --- a/Server/src/main/content/global/skill/runecrafting/abyss/DarkMageDialogue.java +++ b/Server/src/main/content/global/skill/runecrafting/abyss/DarkMageDialogue.java @@ -4,6 +4,9 @@ import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.replaceAllItems; /** * Handles the dark mages dialogue. @@ -140,26 +143,19 @@ public final class DarkMageDialogue extends DialoguePlugin { private boolean repair() { player.pouchManager.getPouches().forEach((id, pouch) -> { pouch.setCurrentCap(pouch.getCapacity()); - pouch.setCharges(10); - Item essence = null; - if(!pouch.getContainer().isEmpty()){ - int ess = pouch.getContainer().get(0).getId(); - int amount = pouch.getContainer().getAmount(ess); - essence = new Item(ess,amount); + pouch.setCharges(pouch.getMaxCharges()); + Item essItem = null; + if (!pouch.getContainer().isEmpty()) { + int essence = pouch.getContainer().get(0).getId(); + int amount = pouch.getContainer().getAmount(essence); + essItem = new Item(essence, amount); } pouch.remakeContainer(); - if(essence != null){ - pouch.getContainer().add(essence); + if (essItem != null) { + pouch.getContainer().add(essItem); } - if(id != 5509) { - if (player.getInventory().contains(id + 1, 1)) { - player.getInventory().remove(new Item(id + 1, 1)); - player.getInventory().add(new Item(id, 1)); - } - if (player.getBank().contains(id + 1, 1)) { - player.getBank().remove(new Item(id + 1, 1)); - player.getBank().add(new Item(id, 1)); - } + if (id != Items.SMALL_POUCH_5509) { + replaceAllItems(player, id + 1, id); } }); return true; diff --git a/Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java b/Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java index 1b46aa4e0..ab06e4843 100644 --- a/Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java +++ b/Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** @@ -51,7 +52,7 @@ public final class ZamorakMageDialogue extends DialoguePlugin { public boolean open(Object... args) { npc = (NPC) args[0]; varrockMage = npc.getId() == 2261 || npc.getId() == 2260; - if (!player.getQuestRepository().isComplete("Rune Mysteries")) { + if (!player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES)) { end(); player.sendMessage("The mage doesn't seem interested in talking to you."); return true; @@ -190,7 +191,7 @@ public final class ZamorakMageDialogue extends DialoguePlugin { stage++; break; case 201: - npc("And there is an ubundant supply of this 'essence' there", "you say?"); + npc("And there is an abundant supply of this 'essence' there", "you say?"); stage++; break; case 202: @@ -299,7 +300,7 @@ public final class ZamorakMageDialogue extends DialoguePlugin { switch (stage) { case 0: if (!player.hasItem(ORBS[0]) && !player.getInventory().containsItem(ORBS[1])) { - player("Uh...", "No...", "I kinda lost that orb thingy that you have me."); + player("Uh...", "No...", "I kinda lost that orb thingy that you gave me."); stage++; break; } @@ -313,14 +314,14 @@ public final class ZamorakMageDialogue extends DialoguePlugin { break; case 1: player.getInventory().add(ORBS[0], player); - npc("What?", "Incompetent fool. Take this.", "And do not make me refret allying myself with you."); + npc("What?", "Incompetent fool. Take this.", "And do not make me regret allying myself with you."); stage++; break; case 2: end(); break; case 3: - npc("I assume the task to be self-explainatory.", "What is it you wish to know?"); + npc("I assume the task to be self-explanatory.", "What is it you wish to know?"); stage++; break; case 4: @@ -328,7 +329,7 @@ public final class ZamorakMageDialogue extends DialoguePlugin { stage++; break; case 5: - npc("All I wish for you to do is to teleport to this 'rune", "essence' location from three different locations wile", "carrying the scrying orb I gave you.", "It will collect the data as you teleport."); + npc("All I wish for you to do is to teleport to this 'rune", "essence' location from three different locations while", "carrying the scrying orb I gave you.", "It will collect the data as you teleport."); stage++; break; case 6: diff --git a/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt index 0ad090fcd..36a76e8cd 100644 --- a/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt @@ -1,6 +1,8 @@ package content.global.skill.skillcapeperks +import core.api.sendMessage import core.game.interaction.InteractionListener +import core.game.world.GameWorld class SkillcapeEquipmentPlugin : InteractionListener { override fun defineListeners() { @@ -19,6 +21,13 @@ class SkillcapeEquipmentPlugin : InteractionListener { onUnequip(capes){player, node -> val skillcape = Skillcape.forId(node.id) + + // For Temple of Ikov. Do not let player unequip firemaking skillcape in the dark basement (need to keep an active light source). + if(player.location.isInRegion(10648) && (node.id == 9804 || node.id == 9805) && GameWorld.settings?.skillcape_perks == true) { + sendMessage(player, "Unequipping that skillcape would leave you without a light source.") + return@onUnequip false + } + val perk = SkillcapePerks.forSkillcape(skillcape) perk.deactivate(player) return@onUnequip true diff --git a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt index 7ee273098..eae541c70 100644 --- a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt @@ -15,7 +15,9 @@ import core.ServerStore.Companion.getBoolean import core.ServerStore.Companion.getInt import core.api.* import core.cache.def.impl.ItemDefinition +import core.tools.END_DIALOGUE import org.rs09.consts.Items +import content.data.Quests enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) { BAREFISTED_SMITHING("cape_perks:barefisted-smithing"), @@ -47,7 +49,6 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? player.dialogueInterpreter.sendDialogue("Your cape is still on cooldown.") } else { player.dialogueInterpreter.open(509871233) - store[player.name] = used + 1 } }), SEED_ATTRACTION("cape_perks:seed_attract",{player -> @@ -130,8 +131,15 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? player.setAttribute("/save:$attribute",true) } player.debug("Activated ${this.name}") - if(this == CONSTANT_GLOW) + if(this == CONSTANT_GLOW) { DarkZone.checkDarkArea(player) + // For Temple of Ikov - if you are in the dark basement and put on the firemaking cape with its 2009Scape light source perk, switch to the light basement. + // For the listener that teleports if you light a normal light source, see content.global.skill.crafting.lightsources.LightSourceLighter.kt + if(player.location.isInRegion(10648) && player.location.withinDistance(Location(2639,9738,0), 8)) { + teleport(player, Location.create(player.getLocation().getX(), player.getLocation().getY() + 23, player.getLocation().getZ())) + closeDialogue(player) + } + } } fun operate(player: Player){ @@ -195,11 +203,11 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? end() if(spellbook != null){ if (spellbook == SpellBookManager.SpellBook.ANCIENT) { - if (!hasRequirement(player, "Desert Treasure")) + if (!hasRequirement(player, Quests.DESERT_TREASURE)) return true } else if (spellbook == SpellBookManager.SpellBook.LUNAR) { - if (!hasRequirement(player, "Lunar Diplomacy")) + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return true } player.spellBookManager.setSpellBook(spellbook) @@ -222,54 +230,69 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? } override fun open(vararg args: Any?): Boolean { - options("Air","Mind","Water","Earth","More...") + altarList(0) stage = 0 return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> when(buttonId){ + when(stage) { + 0 -> when(buttonId) { 1 -> sendAltar(player, Altar.AIR) 2 -> sendAltar(player, Altar.MIND) 3 -> sendAltar(player, Altar.WATER) 4 -> sendAltar(player, Altar.EARTH) - 5 -> options("Fire","Body","Cosmic","Chaos","More...").also { stage++ } + 5 -> altarList(++stage) } - 1 -> when(buttonId){ - 1 -> sendAltar(player, Altar.FIRE) - 2 -> sendAltar(player, Altar.BODY) - 3 -> sendAltar(player, Altar.COSMIC) - 4 -> sendAltar(player, Altar.CHAOS) - 5 -> options("Astral","Nature","Law","Death","More...").also { stage++ } + 1 -> when(buttonId) { + 1 -> altarList(--stage) + 2 -> sendAltar(player, Altar.FIRE) + 3 -> sendAltar(player, Altar.BODY) + 4 -> sendAltar(player, Altar.COSMIC) + 5 -> altarList(++stage) } - 2 -> when(buttonId){ - 1 -> sendAltar(player, Altar.ASTRAL) - 2 -> sendAltar(player, Altar.NATURE) - 3 -> sendAltar(player, Altar.LAW) - 4 -> sendAltar(player, Altar.DEATH) - 5 -> options("Blood","Nevermind").also { stage++ } + 2 -> when(buttonId) { + 1 -> altarList(--stage) + 2 -> sendAltar(player, Altar.CHAOS) + 3 -> sendAltar(player, Altar.ASTRAL) + 4 -> sendAltar(player, Altar.NATURE) + 5 -> altarList(++stage) } - 3 -> when(buttonId){ - 1 -> sendAltar(player, Altar.BLOOD) - 2 -> end() + 3 -> when(buttonId) { + 1 -> altarList(--stage) + 2 -> sendAltar(player, Altar.LAW) + 3 -> sendAltar(player, Altar.DEATH) + 4 -> sendAltar(player, Altar.BLOOD) + 5 -> altarList(0).also { stage = 0 } } } return true } - fun sendAltar(player: Player,altar: Altar){ + fun altarList(stage: Int) { + when (stage) { + 0 -> options("Air", "Mind", "Water", "Earth", "More...") + 1 -> options("Back...", "Fire", "Body", "Cosmic", "More...") + 2 -> options("Back...", "Chaos", "Astral", "Nature", "More...") + 3 -> options("Back...", "Law", "Death", "Blood", "More...") + } + } + + fun sendAltar(player: Player,altar: Altar) { end() - if (altar == Altar.DEATH && !hasRequirement(player, "Mourning's End Part II")) return - if (altar == Altar.ASTRAL && !hasRequirement(player, "Lunar Diplomacy")) return - if (altar == Altar.BLOOD && !hasRequirement(player, "Legacy of Seergaze")) return + if (altar == Altar.DEATH && !hasRequirement(player, Quests.MOURNINGS_END_PART_II)) return + if (altar == Altar.ASTRAL && !hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return + if (altar == Altar.BLOOD && !hasRequirement(player, Quests.LEGACY_OF_SEERGAZE)) return if (altar == Altar.LAW && !ItemDefinition.canEnterEntrana(player)) { - sendItemDialogue(player, Items.SARADOMIN_SYMBOL_8055, "No weapons or armour are permitted on holy Entrana.") + sendMessage(player, "The power of Saradomin prevents you from taking armour or weaponry to Entrana."); return } var endLoc = if (altar == Altar.ASTRAL) Location.create(2151, 3864, 0) else altar.ruin.end + val store = ServerStore.getArchive("daily-abyss-warp") + val used = store.getInt(player.name,0) + store[player.name] = used + 1 player.teleporter.send(endLoc, TeleportManager.TeleportType.TELE_OTHER) player.incrementAttribute("/save:cape_perks:abyssal_warp",-1) } diff --git a/Server/src/main/content/global/skill/slayer/FishingExplosivePlugin.java b/Server/src/main/content/global/skill/slayer/FishingExplosivePlugin.java deleted file mode 100644 index 196a4c62d..000000000 --- a/Server/src/main/content/global/skill/slayer/FishingExplosivePlugin.java +++ /dev/null @@ -1,228 +0,0 @@ -package content.global.skill.slayer; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.OptionHandler; -import core.game.interaction.UseWithHandler; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.ImpactHandler.HitsplatType; -import core.game.node.entity.impl.Projectile; -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.HintIconManager; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.RandomFunction; - -/** - * Represents the plugin used to handle the fishing expolosive on a omnious - * fishing spot. - * @author 'Vexia - */ -@Initializable -public final class FishingExplosivePlugin extends OptionHandler { - - /** - * Represents the ominous fishing spot ids. - */ - private final static int[] IDS = new int[] { 10087, 10088, 10089 }; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : IDS) { - SceneryDefinition.forId(id).getHandlers().put("option:lure", this); - SceneryDefinition.forId(id).getHandlers().put("option:bait", this); - } - new FishingExplosiveHandler().newInstance(arg); - new MogreNPC().newInstance(arg); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getPacketDispatch().sendMessage("Something seems to have scared all the fishes away..."); - return true; - } - - @Override - public Location getDestination(Node node, Node n) { - return node.getLocation(); - } - - /** - * Represents the handler for the fishing expolsive on a fishing spot. - * @author 'Vexia - * @version 1.0 - */ - public static final class FishingExplosiveHandler extends UseWithHandler { - - /** - * Represents the throwing animation. - */ - private static final Animation ANIMATION = new Animation(385); - - /** - * Represents the splashing graphics. - */ - private static final Graphics SPLASH_GRAPHIC = new Graphics(68); - - /** - * Represents the npc mogre id. - */ - private static final int MOGRE_ID = 114; - - /** - * Represents the messages used to send for the mogres. - */ - private static final String[] MESSAGES = new String[] { "Da boom-boom kill all da fishies!", "I smack you good!", "Smash stupid human!", "Tasty human!", "Human hit me on the head!", "I get you!", "Human scare all da fishies!" }; - - /** - * Constructs a new {@code FishingExplosiveHandler} {@code Object}. - */ - public FishingExplosiveHandler() { - super(6664, 12633); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i : IDS) { - addHandler(i, OBJECT_TYPE, this); - } - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getAttributes().containsKey("hasMogre")) { - player.getDialogueInterpreter().sendDialogues(player, null, "Sinister as that fishing spot is, why would I want to", "explode it?"); - return true; - } - if (!player.getInventory().remove(new Item(event.getUsedItem().getId(), 1))) { - return true; - } - final int delay = (int) (2 + (player.getLocation().getDistance(event.getUsedWith().getLocation())) * 0.5); - player.animate(ANIMATION); - player.getPacketDispatch().sendMessage("You hurl the shuddering vial into the water..."); - sendProjectile(player, (Scenery) event.getUsedWith()); - GameWorld.getPulser().submit(new Pulse(delay, player) { - @Override - public boolean pulse() { - Direction dir = event.getUsedWith().getDirection(); - NPC mogre = NPC.create(MOGRE_ID, event.getUsedWith().getLocation().transform(dir.getStepX() << 1, dir.getStepY() << 1, 0)); - mogre.init(); - mogre.moveStep(); - mogre.setRespawn(false); - mogre.getProperties().getCombatPulse().attack(player); - mogre.setAttribute("player", player); - mogre.sendChat(MESSAGES[RandomFunction.random(MESSAGES.length)]); - HintIconManager.registerHintIcon(player, mogre); - if (event.getUsedItem().getId() == 12633) { - mogre.getImpactHandler().manualHit(player, 15, HitsplatType.NORMAL); - } - player.setAttribute("hasMogre", true); - mogre.graphics(SPLASH_GRAPHIC); - player.getPacketDispatch().sendMessage("...and a Mogre appears!"); - return true; - } - }); - return true; - } - - /** - * Method used to send the fishign explosive projectile. - * @param player the player. - * @param object the object. - */ - private void sendProjectile(final Player player, final Scenery object) { - Projectile p = Projectile.create(player, null, 49, 30, 20, 30, Projectile.getSpeed(player, object.getLocation())); - p.setEndLocation(object.getLocation()); - p.send(); - } - - @Override - public Location getDestination(Player player, Node with) { - return player.getLocation(); - } - } - - /** - * Represents a mogre npc. - * @author 'Vexia - * @version 1.0 - */ - public final class MogreNPC extends AbstractNPC { - - /** - * Constructs a new {@code MogreNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public MogreNPC(int id, Location location) { - super(id, location, true); - } - - /** - * Constructs a new {@code MogreNPC} {@code Object}. - */ - public MogreNPC() { - super(0, null); - } - - @Override - public void tick() { - super.tick(); - final Player pl = getAttribute("player", null); - if (pl == null || pl.getLocation().getDistance(getLocation()) > 15) { - clear(); - } - } - - @Override - public void clear() { - super.clear(); - final Player pl = getAttribute("player", null); - if (pl != null) { - pl.removeAttribute("hasMogre"); - } - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - final Player pl = getAttribute("player", null); - return pl != null && pl == entity && super.isAttackable(entity, style, message); - } - - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - final Player player = killer.asPlayer(); - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR, 2, 7); - } - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new MogreNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] { 114 }; - } - - } -} diff --git a/Server/src/main/content/global/skill/slayer/Master.java b/Server/src/main/content/global/skill/slayer/Master.java index 0a3d3619d..0ce93eb54 100644 --- a/Server/src/main/content/global/skill/slayer/Master.java +++ b/Server/src/main/content/global/skill/slayer/Master.java @@ -10,270 +10,240 @@ import java.util.List; /** * A non-garbage way of representing slayer masters + * Source for task amounts: ... + * Need to add a source for weights * @author ceik + * @author gregf */ public enum Master { - TURAEL(8273, 0, 0, new int[]{15, 50}, new int[]{0, 0, 0}, + TURAEL(8273, 3, 0, new int[]{15, 50}, new int[]{0, 0, 0}, new Task(Tasks.BANSHEE, 8), new Task(Tasks.BATS, 7), - new Task(Tasks.BIRDS, 6), new Task(Tasks.BEARS, 7), - new Task(Tasks.CAVE_BUG, 8), + new Task(Tasks.BIRDS, 6), + new Task(Tasks.CAVE_BUG, 8), new Task(Tasks.CAVE_CRAWLERS, 8), new Task(Tasks.CAVE_SLIMES, 8), - new Task(Tasks.COWS, 8), + new Task(Tasks.COWS, 8), new Task(Tasks.CRAWLING_HAND, 8), + new Task(Tasks.DESERT_LIZARDS, 8), new Task(Tasks.DOG, 7), new Task(Tasks.DWARF, 7), new Task(Tasks.GHOSTS, 7), new Task(Tasks.GOBLINS, 7), new Task(Tasks.ICE_FIENDS, 8), new Task(Tasks.KALPHITES, 6), - new Task(Tasks.DESERT_LIZARDS, 8), new Task(Tasks.MINOTAURS, 7), new Task(Tasks.MONKEYS, 6), - new Task(Tasks.RATS, 7), - new Task(Tasks.SCORPIONS, 7), + new Task(Tasks.SCORPIONS, 7), new Task(Tasks.SKELETONS, 7), - new Task(Tasks.SPIDERS, 6), + new Task(Tasks.SPIDERS, 6), new Task(Tasks.WOLVES, 7), new Task(Tasks.ZOMBIES, 7)), - MAZCHNA(8274, 20, 0, new int[]{30, 70}, new int[]{2, 5, 15}, + MAZCHNA(8274, 20, 0, new int[]{40, 70}, new int[]{2, 5, 15}, new Task(Tasks.BANSHEE, 8), - new Task(Tasks.BATS,7), - new Task(Tasks.BEARS,6), - new Task(Tasks.CATABLEPONS,8), - new Task(Tasks.CAVE_BUG,8), + new Task(Tasks.BATS, 7), + new Task(Tasks.BEARS, 6), + new Task(Tasks.CATABLEPONS, 8), + new Task(Tasks.CAVE_BUG, 8), new Task(Tasks.CAVE_CRAWLERS, 8), new Task(Tasks.CAVE_SLIMES, 8), new Task(Tasks.COCKATRICES, 8), new Task(Tasks.CRAWLING_HAND, 8), new Task(Tasks.DESERT_LIZARDS, 8), - new Task(Tasks.DOG,7), - new Task(Tasks.EARTH_WARRIORS,6), - new Task(Tasks.FLESH_CRAWLERS,7), - new Task(Tasks.GHOSTS,7), + new Task(Tasks.DOG, 7), + new Task(Tasks.EARTH_WARRIORS, 6), + new Task(Tasks.FLESH_CRAWLERS, 7), + new Task(Tasks.GHOSTS, 7), new Task(Tasks.GHOULS, 7), - new Task(Tasks.HILL_GIANTS,7), + new Task(Tasks.HILL_GIANTS, 7), new Task(Tasks.HOBGOBLINS, 7), new Task(Tasks.ICE_WARRIOR, 7), - new Task(Tasks.KALPHITES,6), - //new Task(Tasks.KILLERWATTS, 6), - //new Task(Tasks.MOGRES, 8), + new Task(Tasks.KALPHITES, 6), + new Task(Tasks.MOGRES, 8), new Task(Tasks.PYREFIENDS, 8), - new Task(Tasks.ROCK_SLUGS,8), - new Task(Tasks.SCORPIONS,7), - new Task(Tasks.SHADE,8), - new Task(Tasks.SKELETONS, 7), + new Task(Tasks.ROCK_SLUGS, 8), + new Task(Tasks.SHADE, 8), + new Task(Tasks.SKELETONS, 7), new Task(Tasks.VAMPIRES, 6), - //new Task(Tasks.WALL_BEASTS,7), + // new Task(Tasks.WALL_BEASTS, 7), new Task(Tasks.WOLVES, 7), - new Task(Tasks.ZOMBIES,7)), + new Task(Tasks.ZOMBIES, 7)), - VANNAKA(1597, 40, 0, new int[]{30, 80}, new int[]{4, 20, 60}, + VANNAKA(1597, 40, 0, new int[]{60, 120}, new int[]{4, 20, 60}, new Task(Tasks.ABERRANT_SPECTRES, 8), - new Task(Tasks.ABYSSAL_DEMONS, 5), - new Task(Tasks.ANKOU,7), - new Task(Tasks.BANSHEE,6), - new Task(Tasks.BASILISKS,8), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.BLUE_DRAGONS,7), - new Task(Tasks.BRINE_RATS,7), - new Task(Tasks.BRONZE_DRAGONS,7), - new Task(Tasks.CAVE_BUG,7), - new Task(Tasks.CAVE_CRAWLERS,7), - new Task(Tasks.CAVE_SLIMES,7), - new Task(Tasks.COCKATRICES,8), - new Task(Tasks.CRAWLING_HAND,6), - new Task(Tasks.CROCODILES,6), - new Task(Tasks.DAGANNOTHS, 7), - new Task(Tasks.DUST_DEVILS,8), - new Task(Tasks.EARTH_WARRIORS,6), - new Task(Tasks.ELVES, 7), - //new Task(Tasks.FEVER_SPIDERS,7), - new Task(Tasks.FIRE_GIANTS,7), - new Task(Tasks.GARGOYLES, 5), - new Task(Tasks.GHOULS,7), - new Task(Tasks.GREEN_DRAGONS,6), - new Task(Tasks.HARPIE_BUG_SWARMS,8), - new Task(Tasks.HELLHOUNDS,7), - new Task(Tasks.HILL_GIANTS,7), - new Task(Tasks.HOBGOBLINS,7), - new Task(Tasks.ICE_GIANTS,7), - new Task(Tasks.ICE_WARRIOR,7), - new Task(Tasks.INFERNAL_MAGES,8), - new Task(Tasks.JELLIES,8), + new Task(Tasks.ANKOU, 7), + new Task(Tasks.BANSHEE, 6), + new Task(Tasks.BASILISKS, 8), + new Task(Tasks.BLUE_DRAGONS, 7), + new Task(Tasks.BLOODVELDS, 8), + new Task(Tasks.BRINE_RATS, 7), + new Task(Tasks.CAVE_BUG, 7), + new Task(Tasks.CAVE_CRAWLERS, 7), + new Task(Tasks.CAVE_SLIMES, 7), + new Task(Tasks.COCKATRICES, 8), + new Task(Tasks.CRAWLING_HAND, 6), + new Task(Tasks.CROCODILES, 6, new Integer[]{30, 60}), + new Task(Tasks.DAGANNOTHS, 7), + new Task(Tasks.DESERT_LIZARDS, 7, new Integer[]{30, 60}), + new Task(Tasks.DUST_DEVILS, 8), + new Task(Tasks.EARTH_WARRIORS, 6, new Integer[]{30, 60}), + new Task(Tasks.ELVES, 7, new Integer[]{30, 60}), + //new Task(Tasks.FEVER_SPIDERS, 7), + new Task(Tasks.FIRE_GIANTS, 7), + new Task(Tasks.GHOULS, 7), + new Task(Tasks.GREEN_DRAGONS, 6, new Integer[]{30, 60}), + new Task(Tasks.HARPIE_BUG_SWARMS, 8), + new Task(Tasks.HELLHOUNDS, 7), + new Task(Tasks.HILL_GIANTS, 7), + new Task(Tasks.ICE_GIANTS, 7, new Integer[]{30, 60}), + new Task(Tasks.ICE_WARRIOR, 7), + new Task(Tasks.INFERNAL_MAGES, 8), + new Task(Tasks.JELLIES, 8), new Task(Tasks.JUNGLE_HORRORS, 8), - new Task(Tasks.KALPHITES,7), - //new Task(Tasks.KILLERWATTS,6), - new Task(Tasks.KURASKS,7), - new Task(Tasks.DESERT_LIZARDS,7), - new Task(Tasks.LESSER_DEMONS,7), - //new Task(Tasks.MOGRES,7), - //new Task(Tasks.MOLANISKS,7), - new Task(Tasks.MOSS_GIANTS,7), - new Task(Tasks.NECHRYAELS, 5), - new Task(Tasks.OGRES,7), - new Task(Tasks.OTHERWORDLY_BEING,8), - new Task(Tasks.PYREFIENDS,8), - new Task(Tasks.ROCK_SLUGS,7), - new Task(Tasks.SHADE,8), - //new Task(Tasks.SEA_SNAKES,6), - //new Task(Tasks.SHADOW_WARRIORS, 8), - new Task(Tasks.SPIRTUAL_MAGES,3), - new Task(Tasks.SPIRTUAL_RANGERS, 3), - new Task(Tasks.SPIRTUAL_WARRIORS,3), - //new Task(Tasks.TERROR_DOGS,6), - new Task(Tasks.TROLLS,7), + new Task(Tasks.KALPHITES, 7), + // new Task(Tasks.KILLERWATTS, 6), + new Task(Tasks.KURASKS, 7), + new Task(Tasks.LESSER_DEMONS, 7), + new Task(Tasks.MOGRES, 7), + // new Task(Tasks.MOLANISKS, 7), + new Task(Tasks.MOSS_GIANTS, 7), + new Task(Tasks.OGRES, 7), + new Task(Tasks.OTHERWORDLY_BEING, 8), + new Task(Tasks.PYREFIENDS, 8), + new Task(Tasks.ROCK_SLUGS, 7), + // new Task(Tasks.SEA_SNAKES, 6), + new Task(Tasks.SHADE, 8), + // new Task(Tasks.SHADOW_WARRIORS, 8), + new Task(Tasks.TROLLS, 7), new Task(Tasks.TUROTHS, 8), - new Task(Tasks.VAMPIRES,7), - //new Task(Tasks.WALL_BEAST,6), - new Task(Tasks.WEREWOLFS,7)), + new Task(Tasks.VAMPIRES, 7), + // new Task(Tasks.WALL_BEASTS, 6, , new Integer[]{10, 20}), + new Task(Tasks.WEREWOLVES, 7)), CHAELDAR(1598, 70, 0, new int[]{110, 170}, new int[]{10, 50, 150}, - new Task(Tasks.ABERRANT_SPECTRES,8), - new Task(Tasks.ABYSSAL_DEMONS,12), - new Task(Tasks.AVIANSIES,9), + new Task(Tasks.ABERRANT_SPECTRES, 8), + new Task(Tasks.ABYSSAL_DEMONS, 12), new Task(Tasks.BANSHEE, 5), - new Task(Tasks.BASILISKS,7), - new Task(Tasks.BLACK_DEMONS,10), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.BLUE_DRAGONS,8), - new Task(Tasks.BRINE_RATS,7), - new Task(Tasks.BRONZE_DRAGONS,11), - new Task(Tasks.CAVE_CRAWLERS, 5), - new Task(Tasks.CAVE_HORRORS,10), - new Task(Tasks.CAVE_SLIMES,6), - new Task(Tasks.COCKATRICES,6), - new Task(Tasks.DAGANNOTHS,11), - new Task(Tasks.DUST_DEVILS,9), - new Task(Tasks.ELVES,8), - //new Task(Tasks.FEVER_SPIDERS,7), + new Task(Tasks.BASILISKS, 7), + new Task(Tasks.BLUE_DRAGONS, 8), + new Task(Tasks.BLOODVELDS, 8), + new Task(Tasks.BRINE_RATS, 7), + new Task(Tasks.BRONZE_DRAGONS, 11, new Integer[]{30, 60}), + new Task(Tasks.CAVE_BUG, 5), + new Task(Tasks.CAVE_CRAWLERS, 5), + new Task(Tasks.CAVE_HORRORS, 10), + new Task(Tasks.CAVE_SLIMES, 6), + new Task(Tasks.COCKATRICES, 6), + new Task(Tasks.CRAWLING_HAND, 5), + new Task(Tasks.CROCODILES, 5, new Integer[]{30, 60}), + new Task(Tasks.DAGANNOTHS, 11), + new Task(Tasks.DESERT_LIZARDS, 5, new Integer[]{30, 60}), + new Task(Tasks.DUST_DEVILS, 9), + new Task(Tasks.ELVES, 8, new Integer[]{60, 90}), + //new Task(Tasks.FEVER_SPIDERS, 7), new Task(Tasks.FIRE_GIANTS, 12), - new Task(Tasks.GARGOYLES,11), - new Task(Tasks.GREATER_DEMONS,9), - new Task(Tasks.HARPIE_BUG_SWARMS,6), - new Task(Tasks.HELLHOUNDS,9), - new Task(Tasks.INFERNAL_MAGES,7), - new Task(Tasks.IRON_DRAGONS,12), + new Task(Tasks.GARGOYLES, 11), + new Task(Tasks.GREATER_DEMONS, 9), + new Task(Tasks.HARPIE_BUG_SWARMS, 6), + new Task(Tasks.HELLHOUNDS, 9), + new Task(Tasks.IRON_DRAGONS, 12, new Integer[]{30, 60}), + new Task(Tasks.INFERNAL_MAGES, 7), new Task(Tasks.JELLIES, 10), - new Task(Tasks.JUNGLE_HORRORS,10), - new Task(Tasks.KALPHITES,11), + new Task(Tasks.JUNGLE_HORRORS, 10), + new Task(Tasks.KALPHITES, 11), new Task(Tasks.KURASKS, 12), - new Task(Tasks.LESSER_DEMONS,9), - new Task(Tasks.DESERT_LIZARDS, 5), - //new Task(Tasks.MOGRES,6), - //new Task(Tasks.MOLANISKS,6), - //new Task(Tasks.MUTATED_ZYGOMITES,7), + new Task(Tasks.LESSER_DEMONS, 9), + new Task(Tasks.MOGRES, 6), + // new Task(Tasks.MOLANISKS, 6), + //new Task(Tasks.MUTATED_ZYGOMITES, 7, new Integer[]{30, 60}), new Task(Tasks.NECHRYAELS, 12), - new Task(Tasks.PYREFIENDS,6), + new Task(Tasks.PYREFIENDS, 6), new Task(Tasks.ROCK_SLUGS, 5), - //new Task(Tasks.SHADOW_WARRIORS,8), - new Task(Tasks.SKELETAL_WYVERN,7), - new Task(Tasks.SPIRTUAL_WARRIORS,4), - new Task(Tasks.SPIRTUAL_RANGERS,4), - new Task(Tasks.SPIRTUAL_MAGES,4), - new Task(Tasks.STEEL_DRAGONS,9), - new Task(Tasks.TROLLS,11), + // new Task(Tasks.SHADOW_WARRIORS, 8), + new Task(Tasks.SPIRTUAL_WARRIORS, 4), + new Task(Tasks.SPIRTUAL_RANGERS, 4), + new Task(Tasks.SPIRTUAL_MAGES, 4), + new Task(Tasks.TROLLS, 11), new Task(Tasks.TUROTHS, 10)), - //new Task(Tasks.WALL_BEASTS,6), + // new Task(Tasks.WALL_BEASTS, 6, new Integer[]{10, 20}), + // new Task(Tasks.WARPED_TERROR_BIRD, 5), + // new Task(Tasks.WARPED_TORTOISE, 5) - SUMONA(7780, 90, 35, new int[]{50, 185}, new int[]{12, 60, 180}, + SUMONA(7780, 85, 35, new int[]{120, 185}, new int[]{12, 60, 180}, new Task(Tasks.ABERRANT_SPECTRES, 15), new Task(Tasks.ABYSSAL_DEMONS, 10), - new Task(Tasks.AVIANSIES, 7), new Task(Tasks.BANSHEE, 15), new Task(Tasks.BASILISKS, 15), new Task(Tasks.BLACK_DEMONS, 10), - new Task(Tasks.BLOODVELDS, 10), new Task(Tasks.BLUE_DRAGONS, 5), + new Task(Tasks.BLOODVELDS, 10), new Task(Tasks.CAVE_CRAWLERS, 15), new Task(Tasks.CAVE_HORRORS, 15), - new Task(Tasks.CROCODILES, 4), new Task(Tasks.DAGANNOTHS, 10), - new Task(Tasks.DESERT_LIZARDS, 4), new Task(Tasks.DUST_DEVILS, 15), - new Task(Tasks.ELVES, 10), + new Task(Tasks.ELVES, 10, new Integer[]{60, 90}), new Task(Tasks.FIRE_GIANTS, 10), new Task(Tasks.GARGOYLES, 10), new Task(Tasks.GREATER_DEMONS, 10), new Task(Tasks.HELLHOUNDS, 10), - new Task(Tasks.IRON_DRAGONS, 7), + new Task(Tasks.IRON_DRAGONS, 7, new Integer[]{30, 60}), new Task(Tasks.KALPHITES, 10), new Task(Tasks.KURASKS, 15), new Task(Tasks.NECHRYAELS, 10), - // newTask(Tasks.RED_DRAGONS, 5), - new Task(Tasks.SCORPIONS, 4), + new Task(Tasks.RED_DRAGONS, 5, new Integer[]{30, 60}), + // new Task(Tasks.SCABARITES, 9, new Integer[]{30, 60}), new Task(Tasks.SPIRTUAL_MAGES, 10), new Task(Tasks.SPIRTUAL_WARRIORS, 10), - // new Task(Tasks.TERROR_DOGS, 10), + // new Task(Tasks.TERROR_DOGS, 10, new Integer[]{30, 60}), new Task(Tasks.TROLLS, 10), - new Task(Tasks.TUROTHS, 15), - new Task(Tasks.VAMPIRES, 10)), - //new Task(Tasks.WARPED_TORTOISE, 15)), + new Task(Tasks.TUROTHS, 15)), + // new Task(Tasks.WARPED_TORTOISE, 15)), - DURADEL(8275, 100, 50, new int[]{50, 199}, new int[]{15, 75, 225}, - new Task(Tasks.ABERRANT_SPECTRES,7), - new Task(Tasks.ABYSSAL_DEMONS,12), - new Task(Tasks.ANKOU,5), - new Task(Tasks.AVIANSIES,8), - new Task(Tasks.BLACK_DEMONS,8), - new Task(Tasks.BLACK_DRAGONS,9), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.BLUE_DRAGONS,4), - new Task(Tasks.CAVE_HORRORS,4), - new Task(Tasks.DAGANNOTHS,9), - new Task(Tasks.DARK_BEASTS,11), - new Task(Tasks.DUST_DEVILS,5), - new Task(Tasks.ELVES,4), - new Task(Tasks.FIRE_GIANTS,7), - new Task(Tasks.GARGOYLES,8), - new Task(Tasks.GREATER_DEMONS,9), + DURADEL(8275, 100, 50, new int[]{130, 200}, new int[]{15, 75, 225}, + new Task(Tasks.ABERRANT_SPECTRES, 7), + new Task(Tasks.ABYSSAL_DEMONS, 12), + new Task(Tasks.BLACK_DEMONS, 8), + new Task(Tasks.BLACK_DRAGONS, 9, new Integer[]{40, 80}), + new Task(Tasks.BLOODVELDS, 8), + new Task(Tasks.DAGANNOTHS, 9), + new Task(Tasks.DARK_BEASTS, 11), + new Task(Tasks.DUST_DEVILS, 5), + new Task(Tasks.FIRE_GIANTS, 7), + new Task(Tasks.GARGOYLES, 8), + new Task(Tasks.GORAKS, 9, new Integer[]{40, 80}), + new Task(Tasks.GREATER_DEMONS, 9), new Task(Tasks.HELLHOUNDS, 10), - new Task(Tasks.IRON_DRAGONS,5), - new Task(Tasks.KALPHITES,9), - new Task(Tasks.KURASKS,4), - new Task(Tasks.MITHRIL_DRAGONS,9), - new Task(Tasks.NECHRYAELS,9), - //new Task(Tasks.RED_DRAGONS,8), - new Task(Tasks.SKELETAL_WYVERN,7), - new Task(Tasks.SPIRTUAL_MAGES,2), - new Task(Tasks.SPIRTUAL_RANGERS,2), - new Task(Tasks.SPIRTUAL_WARRIORS,2), - new Task(Tasks.STEEL_DRAGONS,7), - new Task(Tasks.SUQAHS,8), - new Task(Tasks.TROLLS,6), - new Task(Tasks.TZHAAR, 10), - new Task(Tasks.VAMPIRES,8), - // new Task(Tasks.WARPED_TERRORBIRD,8), - new Task(Tasks.WATERFIENDS,2)); - //new Task(Tasks.MUTATED_ZYGOMITES,2), - //Boss Tasks below this point - Crash - //new Task(Tasks.JAD, 1), - //new Task(Tasks.COMMANDER_ZILYANA,1), - //new Task(Tasks.CHAOS_ELEMENTAL, 1), - //new Task(Tasks.GENERAL_GRARDOOR,1), - //new Task(Tasks.GIANT_MOLE,1), - //new Task(Tasks.KING_BLACK_DRAGON,1), - //new Task(Tasks.KRIL_TSUTSAROTH,1), - //new Task(Tasks.KREE_ARRA,1)); + new Task(Tasks.IRON_DRAGONS, 5, new Integer[]{40, 80}), + new Task(Tasks.KALPHITES, 9), + new Task(Tasks.MITHRIL_DRAGONS, 9, new Integer[]{4, 8}), + new Task(Tasks.NECHRYAELS, 9), + // new Task(Tasks.SCABARITES, 9, new Integer[]{40, 80}), + new Task(Tasks.SKELETAL_WYVERN, 7, new Integer[]{40, 80}), + new Task(Tasks.SPIRTUAL_MAGES, 2), + new Task(Tasks.STEEL_DRAGONS, 7, new Integer[]{40, 80}), + new Task(Tasks.SUQAHS, 8, new Integer[]{40, 80}), + // new Task(Tasks.WARPED_TERROR_BIRD, 8), + new Task(Tasks.WATERFIENDS, 2)); - private static HashMap idMap = new HashMap<>(); + private static final HashMap idMap = new HashMap<>(); static{ Arrays.stream(Master.values()).forEach(m -> idMap.putIfAbsent(m.npc_id, m)); } - int npc_id,required_combat,required_slayer; - public int[] assignment_range; - int[] streakPoints; - public List tasks; - Master(int npc_id, int required_combat, int required_slayer, int[] assignment_range, int[] streakPoints, Task... tasks) { + final int npc_id; + final int required_combat; + final int required_slayer; + public final int[] default_assignment_range; + final int[] streakPoints; + public final List tasks; + Master(int npc_id, int required_combat, int required_slayer, int[] default_assignment_range, int[] streakPoints, Task... tasks) { this.npc_id = npc_id; this.required_combat = required_combat; this.required_slayer = required_slayer; - this.assignment_range = assignment_range; + this.default_assignment_range = default_assignment_range; this.streakPoints = streakPoints; this.tasks = new ArrayList<>(Arrays.asList(tasks)); } @@ -301,9 +271,17 @@ public enum Master { public static class Task{ public Tasks task; public Integer weight; - Task(Tasks task, Integer weight){ + public Integer[] task_range; + public Task(Tasks task, Integer weight){ this.task = task; this.weight = weight; + this.task_range = new Integer[]{null, null}; + } + + Task(Tasks task, Integer weight, Integer[] task_range){ + this.task = task; + this.weight = weight; + this.task_range = task_range; } } } diff --git a/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java b/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java index aa9e96601..0f95eb9cb 100644 --- a/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java +++ b/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java @@ -1,8 +1,7 @@ package content.global.skill.slayer; 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.*; import core.game.node.entity.combat.equipment.SwitchAttack; import content.global.handlers.item.equipment.special.DragonfireSwingHandler; import core.game.node.entity.impl.Animator.Priority; @@ -13,8 +12,6 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.tools.RandomFunction; -import core.game.node.entity.combat.CombatSwingHandler; -import core.game.node.entity.combat.MultiSwingHandler; /** * Handles a mithril dragon npc. @@ -23,15 +20,23 @@ import core.game.node.entity.combat.MultiSwingHandler; @Initializable public final class MithrilDragonNPC extends AbstractNPC { - /** - * The dragonfire attack. - */ - private static final SwitchAttack DRAGONFIRE = DragonfireSwingHandler.get(false, 52, new Animation(81, Priority.HIGH), Graphics.create(1), null, null); - /** * Handles the combat. */ - private final CombatSwingHandler combatAction = new MultiSwingHandler(true, new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), new Animation(80, Priority.HIGH)), new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), new Animation(80, Priority.HIGH)), new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 500, 20, 20, 41, 40, 18, 255)), DRAGONFIRE, new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 16, 20, 20, 41, 40, 18, 255))); + private static final SwitchAttack DRAGONFIRE = DragonfireSwingHandler.get(false, 52, new Animation(81, Priority.HIGH), Graphics.create(1), null, null); + private static final SwitchAttack MELEE = new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), new Animation(80, Priority.HIGH)); + private static final SwitchAttack MAGIC = new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 500, 20, 20, 41, 40, 18, 255)); + private static class InRangeSwitchAttack extends SwitchAttack { + public InRangeSwitchAttack(CombatSwingHandler swingHandler, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile) { + super(swingHandler, animation, startGraphic, endGraphic, projectile); + } + @Override + public boolean canSelect(Entity entity, Entity victim, BattleState state) { + return CombatStyle.MELEE.getSwingHandler().canSwing(entity, victim) == InteractionType.NO_INTERACT; //only select range if not in melee distance + } + }; + private static final SwitchAttack RANGE = new InRangeSwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 16, 20, 20, 41, 40, 18, 255)); + private final CombatSwingHandler combatAction = new MultiSwingHandler(true, MELEE, MAGIC, DRAGONFIRE, RANGE); /** * Constructs a new {@code MithrilDragonNPC} {@code Object}. diff --git a/Server/src/main/content/global/skill/slayer/MogreNPC.kt b/Server/src/main/content/global/skill/slayer/MogreNPC.kt new file mode 100644 index 000000000..c1f5fce49 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/MogreNPC.kt @@ -0,0 +1,33 @@ +package content.global.skill.slayer + +import core.game.node.entity.Entity +import core.game.node.entity.npc.AbstractNPC +import core.game.world.map.Location +import org.rs09.consts.NPCs + +/** + * Represents a mogre npc. + * @author 'Vexia + * @author gregf36665 + * @version 2.0 + */ +class MogreNPC : AbstractNPC(NPCs.MOGRE_114, null) { + + override fun tick() { + super.tick() + val victim = properties.combatPulse.getVictim() + if (victim != null) { + if (victim.location.getDistance(getLocation()) > 15) { + clear() + } + } + } + + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return MogreNPC() + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MOGRE_114) + } +} diff --git a/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt b/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt index c953bf9de..e8c5e01e6 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt +++ b/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt @@ -72,7 +72,7 @@ object SlayerEquipmentFlags { val isCape = SlayerManager.getInstance(player).flags.equipmentFlags == 0x3F val hasMask = hasBlackMask(player) - return if(hasMask) 1.15 + return if(hasMask) 1.1667 else if(isCape) 1.075 else 1.0 } diff --git a/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java b/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java index 25f059d22..c8a57c44a 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java +++ b/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java @@ -15,7 +15,9 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.Initializable; + import static core.tools.DialogueConstKt.END_DIALOGUE; +import content.data.Quests; /** * Represents the dialogue plugin used for a slayer master. @@ -40,11 +42,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { */ private static final Item HOLY_SYMBOL = new Item(1718); - /** - * Represents the items to use. - */ - private static final Item[] ITEMS = new Item[]{new Item(9813), new Item(9814)}; - /** * Represents the coins item. */ @@ -67,8 +64,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { private final int level = 2; - private int rerolls = 0; - /** * Constructs a new {@code SlayerMasterDialogue} {@code Object}. */ @@ -98,7 +93,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { npc = (NPC) args[0]; } master = Master.forId(args[0] instanceof NPC ? ((NPC) args[0]).getId() : (int) args[0]); - quest = player.getQuestRepository().getQuest("Animal Magnetism"); + quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); if (master == Master.DURADEL) { if (Skillcape.isMaster(player, Skills.SLAYER)) { @@ -118,7 +113,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - rerolls = ServerStore.getInt(getStoreFile(), player.getUsername().toLowerCase(), 0); if (isDiary) { switch (stage) { case 999: @@ -349,7 +343,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { if (player.getInventory().freeSlots() != 0) { player.getInventory().add(GEM); SlayerManager.getInstance(player).generate(master); - interpreter.sendDialogues(master.getNpc(), getExpression(master), "We'll start you off hunting " + SlayerManager.getInstance(player).getTaskName() + "'s, you'll need to", "kill " + SlayerManager.getInstance(player).getAmount() + " of them."); + interpreter.sendDialogues(master.getNpc(), getExpression(master), "We'll start you off hunting " + SlayerUtils.pluralise(SlayerManager.getInstance(player).getTaskName()) + ", you'll need to", "kill " + SlayerManager.getInstance(player).getAmount() + " of them."); stage = 510; } else if (player.getInventory().freeSlots() == 0) { player("Sorry, I don't have enough inventory space."); @@ -480,36 +474,19 @@ public final class SlayerMasterDialogue extends DialoguePlugin { stage = 999; break; } - if (!SlayerManager.getInstance(player).hasTask()) { + boolean hasTask = SlayerManager.getInstance(player).hasTask(); + boolean reroll = hasTask && master == Master.TURAEL && !Master.hasSameTask(master, player); + if (!hasTask || reroll) { SlayerManager.getInstance(player).generate(master); - if (SlayerManager.getInstance(player).getTask() == Tasks.JAD) { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to", "defeat the almighty TzTok-Jad."); - } else { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to kill", "" + SlayerManager.getInstance(player).getAmount() + " " + SlayerManager.getInstance(player).getTaskName() + "s."); - } + interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to kill", "" + SlayerManager.getInstance(player).getAmount() + " " + SlayerUtils.pluralise(SlayerManager.getInstance(player).getTaskName()) + "."); stage = 844; break; } - if (Master.hasSameTask(master, player)) { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "You're still hunting something. But let me check something..."); - stage = 847; - } else { - SlayerManager.getInstance(player).flags.setTaskStreak(0); - SlayerManager.getInstance(player).generate(master); - if (SlayerManager.getInstance(player).getTask() == Tasks.JAD) { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to", "defeat the almighty TzTok-Jad."); - } else { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to kill", "" + SlayerManager.getInstance(player).getAmount() + " " + SlayerManager.getInstance(player).getTaskName() + "'s."); - } - stage = 844; - } + interpreter.sendDialogues(master.getNpc(), getExpression(master), "You're still hunting something. Come back when you've","finished your task."); + stage = END_DIALOGUE; break; case 844: - if (GameWorld.getSettings().getAllow_slayer_reroll()) { - options("Got any tips for me?", "Okay, great!", "I'd like to re-roll that task."); - } else { - options("Got any tips for me?", "Okay, great!"); - } + options("Got any tips for me?", "Okay, great!"); stage++; break; case 845: @@ -522,47 +499,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { player("Okay, great!"); stage = 999; break; - case 3: - player("I'd like to re-roll this task."); - if(rerolls == 10){ - stage++; - } else { - SlayerManager.getInstance(player).clear(); - getStoreFile().put(player.getUsername().toLowerCase(), rerolls + 1); - stage = 701; - } - } - break; - case 846: - npcl(FacialExpression.NEUTRAL, "Actually, you're out of free rerolls. You can buy a reroll from my reward store, though."); - stage = END_DIALOGUE; - break; - case 847: - if(rerolls < 10){ - npcl(FacialExpression.NEUTRAL, "You do have " + (10 - rerolls) + " rerolls left today, would you like to use one?"); - stage++; - } - else { - npcl(FacialExpression.NEUTRAL, "And it also seems you're out of rerolls for today. That's unfortunate."); - stage = END_DIALOGUE; - } - break; - case 848: - options("Yes, please.", "No, thanks."); - stage++; - break; - case 849: - switch(buttonId){ - case 1: - playerl(FacialExpression.FRIENDLY, "Yes, please."); - SlayerManager.getInstance(player).clear(); - getStoreFile().put(player.getUsername().toLowerCase(), rerolls + 1); - stage = 701; - break; - case 2: - playerl(FacialExpression.NEUTRAL, "No, thanks."); - stage = END_DIALOGUE; - break; } break; case 860: @@ -606,56 +542,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { } stage = 999; break; - case 906: - switch (buttonId) { - case 1: - player("May I buy a Quest Point cape?"); - stage = 907; - break; - case 2: - interpreter.sendDialogues(master.getNpc(), FacialExpression.HALF_GUILTY, "'Ello, and what are you after, then?"); - stage = 0; - break; - } - break; - case 907: - npc("You bet, " + player.getUsername() + "! Right when you give me 99000 coins."); - stage = 908; - break; - case 908: - options("Okay, here you go.", "No, thanks."); - stage = 909; - break; - case 909: - switch (buttonId) { - case 1: - player("Okay, here you go."); - stage = 910; - break; - case 2: - end(); - break; - } - break; - case 910: - if (player.getInventory().freeSlots() < 2) { - player("I don't seem to have enough inventory space."); - stage = 999; - return true; - } - if (!player.getInventory().containsItem(COINS)) { - player("I don't seem to have enough coins with", "me at this time."); - stage = 999; - return true; - } - if (player.getInventory().remove(COINS) && player.getInventory().add(ITEMS)) { - npc("Have fun with it."); - stage = 999; - } else { - player("I don't seem to have enough coins with", "me at this time."); - stage = 999; - } - break; } return true; } @@ -696,9 +582,4 @@ public final class SlayerMasterDialogue extends DialoguePlugin { public int[] getIds() { return new int[]{70, 1598, 1596, 1597, 1599, 7780, 8275, 8273, 8274, 8649}; } - - private JSONObject getStoreFile() { - return ServerStore.getArchive("daily-slayer-rerolls"); - } - } diff --git a/Server/src/main/content/global/skill/slayer/SlayerPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerPlugin.java index 35d6a4544..eb02797d6 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerPlugin.java +++ b/Server/src/main/content/global/skill/slayer/SlayerPlugin.java @@ -1,7 +1,6 @@ package content.global.skill.slayer; import core.cache.def.impl.SceneryDefinition; -import core.game.global.action.ClimbActionHandler; import core.game.global.action.DigAction; import core.game.global.action.DigSpadeHandler; import core.game.interaction.OptionHandler; @@ -12,6 +11,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles related slayer nodes. @@ -27,15 +27,12 @@ public class SlayerPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(8783).getHandlers().put("option:open", this); SceneryDefinition.forId(8785).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(23158).getHandlers().put("option:exit", this); SceneryDefinition.forId(23157).getHandlers().put("option:exit", this); SceneryDefinition.forId(15767).getHandlers().put("option:enter", this); SceneryDefinition.forId(15811).getHandlers().put("option:exit", this); SceneryDefinition.forId(15812).getHandlers().put("option:exit", this); - SceneryDefinition.forId(96).getHandlers().put("option:climb-up", this); - SceneryDefinition.forId(35121).getHandlers().put("option:climb-down", this); for (Location loc : BRYNE_DIGS) { DigSpadeHandler.register(loc, new DigAction() { @Override @@ -52,31 +49,26 @@ public class SlayerPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { switch (node.getId()) { - case 8783: - player.teleport(new Location(2044, 4649, 0)); - break; case 8785: + // Outside of trap door in East Ardy player.teleport(new Location(2543, 3327, 0)); break; case 23158: case 23157: + // Outside brine rat cave player.teleport(new Location(2729, 3733, 0)); break; case 15767: - if (!hasRequirement(player, "Cabin Fever")) + if (!hasRequirement(player, Quests.CABIN_FEVER)) return true; player.teleport(new Location(3748, 9373, 0)); + // Cave horrors - inside of cave break; case 15811: case 15812: + // Cave horrors - outside of cave player.teleport(new Location(3749, 2973, 0)); break; - case 96: - ClimbActionHandler.climb(player, null, new Location(2649, 9804, 0)); - break; - case 35121: - ClimbActionHandler.climb(player, null, new Location(2641, 9763, 0)); - break; } return true; } @@ -84,11 +76,9 @@ public class SlayerPlugin extends OptionHandler { @Override public Location getDestination(Node node, Node n) { if (n.getId() == 23158 || n.getId() == 23157) { + // Inside brine rate cave return new Location(2690, 10124, 0); } - if (n.getId() == 96) { - return new Location(2641, 9763, 0); - } return null; } diff --git a/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java index 84d54b18d..247e87b82 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java +++ b/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java @@ -17,6 +17,7 @@ import core.plugin.Plugin; import core.plugin.ClassScanner; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles the slayer reward interface plugin. @@ -309,7 +310,7 @@ public class SlayerRewardPlugin extends ComponentPlugin { @Override public boolean handle(Player player, Node node, String option) { - if (!hasRequirement(player, "Smoking Kills")) + if (!hasRequirement(player, Quests.SMOKING_KILLS)) return true; openTab(player, BUY); return true; diff --git a/Server/src/main/content/global/skill/slayer/SlayerUtils.kt b/Server/src/main/content/global/skill/slayer/SlayerUtils.kt index 34fc944c1..c034fc557 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerUtils.kt +++ b/Server/src/main/content/global/skill/slayer/SlayerUtils.kt @@ -1,19 +1,16 @@ package content.global.skill.slayer +import core.api.setVarp import core.game.node.entity.combat.BattleState import core.game.node.entity.player.Player import core.game.node.entity.player.link.SpellBookManager.SpellBook import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills -import content.global.skill.slayer.Master -import content.global.skill.slayer.Tasks import core.tools.RandomFunction import org.rs09.consts.Items -import java.util.ArrayList -import core.api.* object SlayerUtils { - fun generate(player: Player, master: Master): Tasks? + fun generate(player: Player, master: Master): Master.Task? { val tasks: MutableList = ArrayList(10) val taskWeightSum = intArrayOf(0) @@ -24,7 +21,7 @@ object SlayerUtils { tasks.shuffle(RandomFunction.RANDOM) var rnd = RandomFunction.random(taskWeightSum[0]) for (task in tasks) { - if (rnd < task!!.weight) return task.task + if (rnd < task!!.weight) return task rnd -= task.weight } return null @@ -35,11 +32,14 @@ object SlayerUtils { return player.getSkills().getLevel(Skills.SLAYER) >= task.levelReq && !SlayerManager.getInstance(player).flags.removed.contains(task) && task.hasQuestRequirements(player) } - fun assign(player: Player, task: Tasks, master: Master) + fun assign(player: Player, task: Master.Task, master: Master) { SlayerManager.getInstance(player).master = master - SlayerManager.getInstance(player).task = task - SlayerManager.getInstance(player).amount = RandomFunction.random(master.assignment_range[0], master.assignment_range[1]) + SlayerManager.getInstance(player).task = task.task + if (task.task_range[0] == null) + SlayerManager.getInstance(player).amount = RandomFunction.random(master.default_assignment_range[0], master.default_assignment_range[1]) + else + SlayerManager.getInstance(player).amount = RandomFunction.random(task.task_range[0], task.task_range[1]) if (master == Master.DURADEL) { player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 2, 8) } else if (master == Master.VANNAKA) { @@ -57,4 +57,24 @@ object SlayerUtils { .spellBook == SpellBook.MODERN.interfaceId ) } + + @JvmStatic + fun pluralise(str: String): String { + return when (str) { + "black bear" -> "bears" + "cyclops" -> "cyclopes" + "guard dog" -> "dogs" + "dwarf" -> "dwarves" + "elf warrior" -> "elves" + "jelly" -> "jellies" + "nechryael" -> str // the plural and singular is the same + "turoth" -> str // the plural and singular is the same + "tzhaar-mej" -> "tzHaar" + "werewolf" -> "werewolves" + "wolf" -> "wolves" + "kalphite worker" -> "kalphites" + "scarab swarm" -> "scabarites" + else -> str + "s" + } + } } diff --git a/Server/src/main/content/global/skill/slayer/Tasks.java b/Server/src/main/content/global/skill/slayer/Tasks.java index d4b77c66f..81a195f5a 100644 --- a/Server/src/main/content/global/skill/slayer/Tasks.java +++ b/Server/src/main/content/global/skill/slayer/Tasks.java @@ -8,104 +8,132 @@ import java.util.HashMap; import core.game.node.entity.player.Player; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * A non-garbage way of representing tasks + * Slayer level source: ... + * Combat level source: None * @author ceik + * @author gregf */ public enum Tasks { - ABERRANT_SPECTRES(65, new int[] { 1604, 1605, 1606, 1607 }, new String[] { "Aberrant spectres have an extremely potent stench that drains", "stats and life points. A nose peg, protects against the stench." }, 60, true, false), - ABYSSAL_DEMONS(85, new int[] { 1615, 4230 }, new String[] { "Abyssal Demons are nasty creatures to deal with, they aren't really part, ", "of this realm, and are able to move very quickly to trap their prey"}, 85, false, false), - ANKOU(40, new int[] { 4381, 4382, 4383 }, new String[] { "Neither skeleton nor ghost, but a combination of both." }, 1, true, false), - AVIANSIES(60, new int[] { 6245, 6243, 6235, 6232, 6244, 6246, 6233, 6241, 6238, 6237, 6240, 6242, 6239, 6234 }, new String[] { "Graceful, bird-like creature." }, 1, false, false), - BANSHEE(20, new int[] { 1612 }, new String[] { "Banshees use a piercing scream to shock their enemies,", "you'll need some Earmuffs to protect yourself from them." }, 15, true, false), - BASILISKS(40, new int[] { 1616, 1617, 4228 }, new String[] { "A mirror shield is much neccesary when hunting", "these mad creatures." }, 40, false, false), - BATS(5, new int[] { 412, 78, 1005, 2482, 3711, }, new String[] { "These little creatures are incredibly quick.", "make sure you keep your eye on them at all times." }, 1, false, false), - BEARS(13, new int[] { 106, 105, 1195, 3645, 3664, 1326, 1327 }, new String[] { "A large animal with a crunching punch." }, 1, false, false), - BIRDS(1, new int[] { 1475, 5120, 5121, 5122, 5123, 5133, 1475, 1476, 41, 951, 1017, 1401, 1402, 2313, 2314, 2315, 1016, 1550, 147, 1180, 1754, 1755, 1756, 2252, 4570, 4571, 1911, 6114, 46, 2693, 6113, 6112, 146, 149, 150, 450, 451, 1179, 1322, 1323, 1324, 1325, 1400, 2726, 2727, 3197, 138, 48, 4373, 4374, 4535, 139, 1751, 148, 1181, 6382, 2459, 2460, 2461, 2462, 2707, 2708, 6115, 6116, 3296, 6378, 1996, 3675, 3676, 6792, 6946, 7320, 7322, 7324, 7326, 7328, 1692, 6285, 6286, 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, 6322, 6323, 6324, 6325, 6326, 6327, 6328, 6329, 6330, 6331, 6332, }, new String[] { "irds aren't the most intelligent of creatures, but watch out for their", "sharp, stabbing beaks." }, 1, false, false), - BLACK_DEMONS(80, new int[] { 84, 677, 4702, 4703, 4704, 4705, 6208, }, new String[] { "Black Demons are magic creatures that are weak to magic attacks.", "They're the strongest demon and very dangerous." }, 1, false, false), - BLACK_DRAGONS(80, new int[] {54, 4673, 4674, 4675, 4676, 3376, 50 }, new String[] { "Black dragons are the strongest dragons", "watch out for their firey breath" }, 1, false, true, 40 | 80 << 16), - BLOODVELDS(50, new int[] { 1618, 1619, 6215, 7643, 7642 }, new String[] { "Bloodvelds are strange demonic creatures, they use their long rasping tongue", "to feed on just about anything they can find." }, 50, false, false), - BLUE_DRAGONS(65, new int[] { 55, 4681, 4682, 4683, 4684, 5178, 52, 4665, 4666, }, new String[] { "Blue dragons aren't as strong as other dragons but they're still", "very powerful, watch out for their firey breath." }, 1, false, true), - BRINE_RATS(45, new int[] { 3707 }, new String[] { "Small little creatures they are, yet so very", "powerfull." }, 47, false, false), - BRONZE_DRAGONS(75, new int[] { 1590 }, new String[] { "Bronze dragons aren't as strong as other dragons but they're still", "very powerful, watch out for their firey breath." }, 1, false, true, 30 | 60 << 16), - CATABLEPONS(35, new int[] { 4397, 4398, 4399, }, new String[] { "They use the magic spell Weaken to drain up to 15% of their", "opponent's maximum Strength level." }, 1, false, false), - CAVE_BUG(1, new int[] { 1832, 5750, }, new String[] { "It regenerates life points quickly and seems to be a good", "herblore monster." }, 7, false, false), - CAVE_CRAWLERS(10, new int[] { 1600, 1601, 1602, 1603, }, new String[] { "The poisonous parts of them are presumably removed." }, 10, false, false), - CAVE_HORRORS(85, new int[] { 4353, 4354, 4355, 4356, 4357, }, new String[] { "A Cave horror wears a creepy mask, it is", "prefered to wear a witchwood icon." }, 58, "Cabin Fever"), - CAVE_SLIMES(15, new int[] { 1831 }, new String[] { "These are lesser versions of jellies, watch out they can poison you." }, 17, false, false), - COCKATRICES(25, new int[] { 1620, 1621, 4227, }, new String[] { "A Mirror shield is necessary when", "fighting these monsters." }, 25, false, false), - COWS(5, new int[] { 1766, 1768, 2310, 81, 397, 955, 1767, 3309 }, new String[] { "Cow's may seem stupid, however they know more then", "you think. Don't under estimate them." }, 1, false, false), + ABERRANT_SPECTRES(65, new int[] { 1604, 1605, 1606, 1607, 7801, 7802, 7803, 7804 }, new String[] { "Aberrant Spectres are fetid, vile ghosts. The very", "smell of them will paralyse and harm you. A nose peg", "will help ignore their stink." }, 60, true, false), + ABYSSAL_DEMONS(85, new int[] { 1615 }, new String[] { "Abyssal Demons are nasty creatures to fight. They", "aren't really part of this realm, and are able to", "move very quickly to trap their prey."}, 85, false, false), + ANKOU(40, new int[] { 4381, 4382, 4383 }, new String[] { "Ankou are undead skeletal ghosts. They'll fight you", "up close but make sure to take advantage out of their", "limited defence." }, 1, true, false), + AVIANSIES(60, new int[] { 6245, 6243, 6235, 6232, 6244, 6246, 6233, 6241, 6238, 6237, 6240, 6242, 6239, 6234 }, new String[] { "Aviansies are bird-like creatures found in the icy", "dungeons of the north. Melee weapons can't reach them,", "so use Magic or Ranged attacks." }, 1, false, false), + BANSHEE(20, new int[] { 1612 }, new String[] { "Banshees use a piercing scream to shock their enemies.", "You'll need some earmuffs to protect yourself from them." }, 15, true, false), + BASILISKS(40, new int[] { 1616, 1617 }, new String[] { "Basilisks, like Cockatrice, have a gaze which will", "paralyse and harm their prey. You'll need a Mirror", "Shield to protect you." }, 40, false, false), + BATS(5, new int[] { 412, 78, 3711 }, new String[] { "Bats are rarely found on the ground, so you'll have", "to fight them while they're airborne, which won't be", "easy for melee." }, 1, false, false), + BEARS(13, new int[] { 106, 105, 1195, 3645, 3664, 1326, 1327 }, new String[] { "Bears are tough creatures and fierce fighters, watch", "out for their powerful claws." }, 1, false, false), + BIRDS(1, new int[] { 1475, 5120, 5121, 5122, 5123, 5133, 1475, 1476, 41, 951, 1017, 1401, 1402, 2313, 2314, 2315, 1016, 1550, 147, 1180, 1754, 1755, 1756, 2252, 4570, 4571, 1911, 6114, 46, 2693, 6113, 6112, 146, 149, 150, 450, 451, 1179, 1322, 1323, 1324, 1325, 1400, 2726, 2727, 3197, 138, 48, 4373, 4374, 4535, 139, 1751, 148, 1181, 6382, 2459, 2460, 2461, 2462, 2707, 2708, 6115, 6116, 3296, 6378, 1996, 3675, 3676, 6792, 6946, 7320, 7322, 7324, 7326, 7328, 1692, 6322, 3476, 1018, 1403, }, new String[] { "Birds aren't the most intelligent of creatures, but", "watch out for their sharp stabbing beaks." }, 1, false, false), + BLACK_DEMONS(80, new int[] { 84, 677, 4702, 4703, 4704, 4705, 6208, }, new String[] { "Black Demons are magic creatures that are weak", "to magic attacks. They're a very strong", "demon and very dangerous." }, 1, false, false), + BLACK_DRAGONS(80, new int[] {54, 4673, 4674, 4675, 4676, 3376, 50 }, new String[] { "Black dragons are the strongest dragons;", "watch out for their fiery breath." }, 1, false, true), + BLOODVELDS(50, new int[] { 1618, 1619, 6215, 7643, 7642 }, new String[] { "Bloodvelds are strange demonic creatures, they use their", "long rasping tongue to feed on just about", "anything they can find." }, 50, false, false), + BLUE_DRAGONS(65, new int[] { 55, 4681, 4682, 4683, 4684, 5178, 52, 4665, 4666, }, new String[] { "Blue dragons aren't as strong as other dragons but they're", "still very powerful, watch out for their fiery breath." }, 1, false, true), + BRINE_RATS(45, new int[] { 3707 }, new String[] { "Brine rats can be found in caves that are near the", "sea. They are hairless, bad-tempered and generally", "unfriendly." }, 47, Quests.OLAFS_QUEST), + BRONZE_DRAGONS(75, new int[] { 1590 }, new String[] { "Bronze Dragons are the weakest of the metallic", "dragons, their bronze scales are far thicker than", "normal bronze armour." }, 1, false, true), + CATABLEPONS(35, new int[] { 4397, 4398, 4399, }, new String[] { "Catablepon are mythical, cow like, magical creatures", "Beware their weakening glare." }, 1, false, false), + CAVE_BUG(1, new int[] { 1832, 5750, }, new String[] { "Cave Bugs are like Cave Crawlers, except smaller and", "easier to squish, though they still have a fondness", "for plants." }, 7, false, false), + CAVE_CRAWLERS(10, new int[] { 1600, 1601, 1602, 1603, }, new String[] { "Cave Crawlers are small and fast, often hiding in", "ambush. Avoid their barbed tongue or you'll", "get poisoned." }, 10, false, false), + CAVE_HORRORS(85, new int[] { 4353, 4354, 4355, 4356, 4357, }, new String[] { "Cave Horrors can be found under Mos Le'Harmless. You", "will need a Witchwood Icon to fight them effectively." }, 58, Quests.CABIN_FEVER), + CAVE_SLIMES(15, new int[] { 1831 }, new String[] { "Cave Slimes are the lesser cousins of Jellies, though", "don't be fooled they can still be dangerous as", "they're often poisonous." }, 17, false, false), + COCKATRICES(25, new int[] { 1620, 1621, 4227, }, new String[] { "Cockatrice, like Basilisks, have a gaze which will", "paralyse and harm their prey. You'll need a Mirror", "Shield to protect you." }, 25, false, false), + COWS(5, new int[] { 81, 1766, 1768, 2310, 397, 955, 1767, 3309 }, new String[] { "Cows are bigger than you, so they'll often hit fairly", "hard but are usually fairly slow to react." }, 1, false, false), CRAWLING_HAND(1,new int[] { 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657, 4226, 7640, 7641 }, new String[] { "Crawling Hands are undead severed hands, fast and", "dexterous they claw their victims." }, 5, true, false), - CROCODILES(50, new int[] { 1993, 6779 }, new String[] { "Crocodiles can be found near water and marshes in and near the Kharidian Desert." }, 1, false, false), - CYCLOPES(25,new int[] { 116, 4291, 4292, 6078, 6079, 6080, 6081, 6269, 6270 }, new String[] { "Large one eyed creatures who normally wield a", "large mallet." }, 1, false, false), - DAGANNOTHS(75, new int[] { 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 2454, 2455, 2456, 2881, 2882, 2883, 2887, 2888, 3591, }, new String[] { "There are many types of Dagannoth, the most powerful being the three Dagannoth Kings." }, 1, false, false), - DARK_BEASTS(90, new int[] { 2783 }, new String[] { "A dark beast can attack using magic or melee." }, 90, false, false), - DESERT_LIZARDS(15, new int[] { 2803, 2804, 2805, 2806, 2807 }, new String[] { "Desert lizards are big Slayer monsters found in the Kharidian Desert." }, 22, false, false), - DOG(15, new int[] { 99, 3582, 6374, 1994, 1593, 1594, 3582 }, new String[] { "Dogs are much like Wolves, they are", "pack creatures which will hunt in groups." }, 1, false, false), + CROCODILES(50, new int[] { 1993, 6779 }, new String[] { "Crocodiles are large reptiles which live near water.", "You'll want to have a stabbing weapon handy for", "puncturing their thick scaly hides." }, 1, false, false), + DAGANNOTHS(75, new int[] { 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 2454, 2455, 2456, 2881, 2882, 2883, 2887, 2888, 3591, }, new String[] { "Dagannoth are large sea dwelling creatures which are", "very aggressive. You'll often find them in caves", "near sea water." }, 1, Quests.HORROR_FROM_THE_DEEP), + DARK_BEASTS(90, new int[] { 2783 }, new String[] { "Dark Beasts are large, dog-like predators.", "Their massively muscled bodies protect", "them from crushing weapons." }, 90, Quests.MOURNINGS_END_PART_II), + DESERT_LIZARDS(15, new int[] { 2803, 2804, 2805, 2806, 2807, 2808 }, new String[] { "Lizards are large reptiles with tough skin. Those", "found in the desert will need you to douse them with", "freezing water to finish them off after a tough battle." }, 22, false, false), + DOG(15, new int[] { 99, 3582, 1994, 1593, 1594, 3582 }, new String[] { "Dogs are much like Wolves, they are", "pack creatures which will hunt in groups." }, 1, false, false), DUST_DEVILS(70, new int[] { 1624 }, new String[] { "Dust Devils use clouds of dust, sand, ash and whatever", "else they can inhale to blind and disorientate", "their victims." }, 65, false, false), - DWARF(6, new int[] { 118, 120, 121, 382, 3219, 3220, 3221, 3268, 3269, 3270, 3271, 3272, 3273, 3274, 3275, 3294, 3295, 4316, 5880, 5881, 5882, 5883, 5884, 5885, 2130, 2131, 2132, 2133, 3276, 3277, 3278, 3279, 119, 2423 }, new String[] { "They are slightly resistant to Magic attacks", "and are not recommended for low levels." }, 1, false, false), - EARTH_WARRIORS(35, new int[] { 124 }, new String[] { "An Earth warrior is a monster made of earth"," which fights using melee." }, 1, false, false), - ELVES(70, new int[] { 1183, 1184, 2359, 2360, 2361, 2362, 7438, 7439, 7440, 7441 }, new String[]{"Elves are agile creatures."}, 1, false, false), - FIRE_GIANTS(65, new int[] { 110, 1582, 1583, 1584, 1585, 1586, 7003, 7004 }, new String[] { "Like other giants, Fire Giants often wield large weapons", "learn to recognise what kind of weapon it is, and act accordingly." }, 1, false, false), - FLESH_CRAWLERS(15, new int[] { 4389, 4390, 4391 }, new String[] { "Flesh crawlers are medium level monsters found on", "level 2 of the Stronghold of Security." }, 1, false, false), - GARGOYLES(80, new int[] { 1610, 1611, 6389 }, new String[] { "Gargoyles are winged creatures of stone. You'll need to fight them to", "near death before breaking them apart with a rock hammer." }, 75, false, false), - GHOSTS(13, new int[] { 103, 104, 491, 1541, 1549, 2716, 2931, 4387, 388, 5342, 5343, 5344, 5345, 5346, 5347, 5348, 1698, 5349, 5350, 5351, 5352, 5369, 5370, 5371, 5372, 5373, 5374, 5572, 6094, 6095, 6096, 6097, 6098, 6504, 13645, 13466, 13467, 13468, 13469, 13470, 13471, 13472, 13473, 13474, 13475, 13476, 13477, 13478, 13479, 13480, 13481 }, new String[] { "A Ghost is an undead monster that is found", "in various places and dungeons. " }, 1, false, false), - GHOULS(25, new int[] { 1218, 3059 }, new String[] { "Ghouls are a humanoid race and the descendants of a long-dead society", "that degraded to the point that its people ate their dead." }, 1, false, false), + DWARF(6, new int[] { 118, 120, 121, 382, 3219, 3220, 3221, 3268, 3269, 3270, 3271, 3272, 3273, 3274, 3275, 3294, 3295, 4316, 5880, 5881, 5882, 5883, 5884, 5885, 2130, 2131, 2132, 2133, 3276, 3277, 3278, 3279, 119, 2423 }, new String[] { "Dwarves are a small but tough race of miners, often", "using pickaxes to pierce their opponents armour." }, 1, false, false), + EARTH_WARRIORS(35, new int[] { 124 }, new String[] { "Earth Warriors are a kind of earth elemental,", "grind them to dust with blunt weapons." }, 1, false, false), + ELVES(70, new int[] { 1183, 1184, 2359, 2360, 2361, 2362, 2373, 7438, 7439, 7440, 7441 }, new String[]{ "Elves are quick, agile, and vicious fighters which", "often favour bows and polearms."}, 1, Quests.REGICIDE), + // Waiting for either Rum Deal or Pirate Pete and Fever Spiders before adding this assignment + FEVER_SPIDER(1, new int[] { 2850 }, new String[] { "Fever Spiders are giant spiders that carry the deadly", "Spider Fever. If you don't want to catch it I suggest", "you wear Slayer Gloves to fight them." }, 42, Quests.RUM_DEAL), + FIRE_GIANTS(65, new int[] { 110, 1582, 1583, 1584, 1585, 1586, 7003, 7004 }, new String[] { "Like other giants, Fire Giants often wield large weapons", "learn to recognise what kind of weapon it is,", "and act accordingly." }, 1, false, false), + FLESH_CRAWLERS(15, new int[] { 4389, 4390, 4391 }, new String[] { "Flesh Crawlers are scavengers and will eat you - and", "anyone else, given the chance." }, 1, false, false), + GARGOYLES(80, new int[] { 1610, 6389 }, new String[] { "Gargoyles are winged creatures of stone. You'll need", "to fight them to near death before breaking them apart", "with a rock hammer." }, 75, false, false), + GHOSTS(13, new int[] { 103, 104, 491, 1541, 1549, 2716, 2931, 4387, 388, 5342, 5343, 5344, 5345, 5346, 5347, 5348, 1698, 5349, 5350, 5351, 5352, 5369, 5370, 5371, 5372, 5373, 5374, 5572, 6094, 6095, 6096, 6097, 6098, 6504, 13645, 13466, 13467, 13468, 13469, 13470, 13471, 13472, 13473, 13474, 13475, 13476, 13477, 13478, 13479, 13480, 13481 }, new String[] { "Ghosts are undead so magic is your best bet against", "them, there is even a spell specially for fighting", "the undead." }, 1, true, false), + GHOULS(25, new int[] { 1218, 3059 }, new String[] { "Ghouls aren't undead but they are stronger and", "tougher than they look. However they're also very", "cowardly and will run if they're losing a fight." }, 1, false, false), GOBLINS(1, new int[] { 100, 101, 102, 444, 445, 489, 1769, 1770, 1771, 1772, 1773, 1774, 1775, 1776, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, 2678, 2679, 2680, 2681, 3060, 3264, 3265, 3266, 3267, 3413, 3414, 3415, 3726, 4261, 4262, 4263, 4264, 4265, 4266, 4267, 4268, 4269, 4270, 4271, 4272, 4273, 4274, 4275, 4276, 4407, 4408, 4409, 4410, 4411, 4412, 4479, 4480, 4481, 4482, 4483, 4484, 4485, 4486, 4487, 4488, 4489, 4490, 4491, 4492, 4499, 4633, 4634, 4635, 4636, 4637, 5786, 5824, 5855, 5856, 6125, 6126, 6132, 6133, 6279, 6280, 6281, 6282, 6283, 6402, 6403, 6404, 6405, 6406, 6407, 6408, 6409, 6410, 6411, 6412, 6413, 6414, 6415, 6416, 6417, 6418, 6419, 6420, 6421, 6422, 6423, 6424, 6425, 6426, 6427, 6428, 6429, 6430, 6431, 6432, 6433, 6434, 6435, 6436, 6437, 6438, 6439, 6440, 6441, 6442, 6443, 6444, 6445, 6446, 6447, 6448, 6449, 6450, 6451, 6452, 6453, 6454, 6455, 6456, 6457, 6458, 6459, 6460, 6461, 6462, 6463, 6464, 6465, 6466, 6467, 6490, 6491, 6492, 6493, 6494, 6495, 6496, 6497 }, new String[] { "Goblins are mostly just annoying, but they can be vicious.", "Watch out for the spears they sometimes carry." }, 1, false, false), - GORAKS(70, new int[] { 4418, 6218 }, new String[] { "Goraks can be tough monsters to fight. Be prepared." }, 1, false, false), - GREATER_DEMONS(75, new int[] { 83, 4698, 4699, 4700, 4701, 6204 }, new String[] { "Greater Demons are magic creatures so they are weak to magical attacks.", "They're the strongest demon and very dangerous." }, 1, false, false), - GREEN_DRAGONS(52, new int[] { 941, 4677, 4678, 4679, 4680, 5362 }, new String[] { "Green dragons are very powerfull, they have", "fierce fiery breath." }, 1, false, true), - HARPIE_BUG_SWARMS(45, new int[] { 3153 }, new String[] { "Harpie Bug Swarms are insectoid Slayer monsters." }, 33, false, false), - HELLHOUNDS(75, new int[] { 49, 3586, 6210, }, new String[] { "Hellhounds are mid to high level demons." }, 1, false, false), - HILL_GIANTS(25, new int[] { 117, 4689, 4690, 3058, 4691, 4692, 4693 }, new String[] { "Hill giants can hit up to 19 damage, and they only attack with Melee." }, 1, false, false), - HOBGOBLINS(20, new int[] { 122, 123, 2685, 2686, 3061, 6608, 6642, 6661, 6684, 6710, 6722, 6727, 2687, 2688, 3583, 4898, 6275 }, new String[] { "Mysterious goblin like creatures." }, 1, false, false), - ICE_FIENDS(20, new int[] { 3406, 6217, 7714, 7715, 7716 }, new String[] { "An Icefiend is a monster found on top of Ice Mountain." }, 1, false, false), - ICE_GIANTS(50, new int[] { 111, 3072, 4685, 4686, 4687 }, new String[] { "Ice Giants often wield large weapons, learn to recognise", "what kind of weapon it is, and act accordingly" }, 1, false, false), - ICE_WARRIOR(45, new int[] { 125, 145, 3073 }, new String[] { "Ice warriors, are cold magestic creatures." }, 1, false, false), - INFERNAL_MAGES(40, new int[] { 1643, 1644, 1645, 1646, 1647 }, new String[] { "Infernal Mages are dangerous spell users, beware of their magic", "spells an go properly prepared" }, 45, false, false), - IRON_DRAGONS(80, new int[] { 1591 }, new String[] { "Iron dragons aren't as strong as other dragons but they're still", "very powerful, watch out for their firey breath." }, 1, false, true, 40 | 59 << 16), + GORAKS(70, new int[] { 4418, 6218 }, new String[] { "Goraks are extremely aggressive creatures. They have", "been imprisoned on an alternative plane, which is", "only accessible by using the fairyrings. Be extremely", "careful, their touch drains health as well as skills!" }, 1, Quests.FAIRYTALE_I_GROWING_PAINS), + GREATER_DEMONS(75, new int[] { 83, 4698, 4699, 4700, 4701, 6204 }, new String[] { "Greater Demons are magic creatures so they are weak", "to magical attacks. Though not the strongest demon,", "they are still dangerous." }, 1, false, false), + GREEN_DRAGONS(52, new int[] { 941, 4677, 4678, 4679, 4680, 5362, 742 }, new String[] { "Green Dragons are the weakest dragon but still very", "powerful, watch out for their fiery breath." }, 1, false, true), + HARPIE_BUG_SWARMS(45, new int[] { 3153 }, new String[] { "Harpie Bug Swarms are pesky critters that are hard to", "hit. You need a lit bug lantern to distract them with", "its hypnotic light." }, 33, false, false), + HELLHOUNDS(75, new int[] { 49, 3586, 6210, }, new String[] { "Hellhounds are a cross between Dogs and Demons, they", "are dangerous with a fierce bite." }, 1, false, false), + HILL_GIANTS(25, new int[] { 117, 4689, 4690, 3058, 4691, 4692, 4693 }, new String[] { "Hill Giants often wield large weapons, learn to", "recognise what kind of weapon it is and", "act accordingly." }, 1, false, false), + HOBGOBLINS(20, new int[] { 122, 123, 2685, 2686, 3061, 6608, 6642, 6661, 6684, 6710, 6722, 6727, 2687, 2688, 3583, 4898, 6275 }, new String[] { "Hobgoblins are sneaky underhanded creatures, they", "often wield spears and some times carry javelins too." }, 1, false, false), + ICE_FIENDS(20, new int[] { 3406, 6217, 7714, 7715, 7716 }, new String[] { "Icefiends are beings of ice and freezing rock, they're", "quick and agile so you'll want to be careful when", "getting close to them." }, 1, false, false), + ICE_GIANTS(50, new int[] { 111, 3072, 4685, 4686, 4687 }, new String[] { "Like other giants, Ice Giants often wield large", "weapons, learn to recognise what kind of weapon it is", "and act accordingly." }, 1, false, false), + ICE_WARRIOR(45, new int[] { 125, 145, 3073 }, new String[] { "Ice Warriors are a kind of ice elemental, shatter them", "with blunt weapons or melt them with fire." }, 1, false, false), + INFERNAL_MAGES(40, new int[] { 1643, 1644, 1645, 1646, 1647 }, new String[] { "Infernal Mages are dangerous spell users, beware of", "their magic spells and go properly prepared" }, 45, false, false), + IRON_DRAGONS(80, new int[] { 1591 }, new String[] { "Iron Dragons are some of the weaker metallic dragons,", "their iron scales are far thicker than normal", "iron armour." }, 1, false, true), JELLIES(57, new int[] { 1637, 1638, 1639, 1640, 1641, 1642 }, new String[] { "Jellies are nasty cube-like gelatinous creatures which", "absorb everything they come across into themselves." }, 52, false, false), - JUNGLE_HORRORS(65, new int[] { 4348, 4349, 4350, 4351, 4352 }, new String[] { "Jungle Horrors can be found all over Mos Le'Harmless.", "They are strong and aggressive, so watch out!" }, 1, false, false), - KALPHITES(15, new int[] { 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161 }, new String[] { "Kalaphite are large insects which live in great hives under the desert sands." }, 1, false, false), - KURASKS(65, new int[] { 1608, 1609, 4229, 7805, 7797 }, new String[] { "A kurask is a very quick creature." }, 70, false, false), - LESSER_DEMONS(60, new int[] { 82, 6203, 3064, 4694, 4695, 6206, 3064, 4696, 4697, 6101 }, new String[] { "Lesser Demons are magic creatures so they are weak to magical attacks." }, 1, false, false), - MITHRIL_DRAGONS(60, new int[] { 5363 }, new String[] { "Mithril dragons aren't as strong as other dragons but they're still", "very powerful, watch out for their firey breath." }, 1, false, true, 5 | 9 << 16), + JUNGLE_HORRORS(65, new int[] { 4348, 4349, 4350, 4351, 4352 }, new String[] { "Jungle Horrors can be found all over Mos Le'Harmless.", "They are strong and aggressive, so watch out!" }, 1, Quests.CABIN_FEVER), + KALPHITES(15, new int[] { 1153, 1154, 1155, 1156, 1157, 1159, 1160, 1161 }, new String[] { "Kalphites are large insects which live in great hives", "under the desert sands." }, 1, false, false), + // Waiting for the killer watt plane to be implemented before adding to assignments + KILLERWATTS(1, new int[] { 3201 }, new String[] { "Killerwatts store huge amounts of energy in their", "bodies, which is released if they are touched. You'll", "need to wear heavily insulated boots to counter this", "shocking effect." }, 37, Quests.ERNEST_THE_CHICKEN), + KURASKS(65, new int[] { 1608, 1609, 4229, 7805, 7797 }, new String[] { "Kurasks are large brutal creatures with very thick", "hides. You'll need a Leaf-Tipped Spear Sword or", "Battle-axe, Broad Arrows, or a Magic Dart to harm them." }, 70, false, false), + LESSER_DEMONS(60, new int[] { 82, 6203, 3064, 4694, 4695, 6206, 3064, 4696, 4697, 6101 }, new String[] { "Lesser Demons are magic creatures so they are weak to", "magical attacks. Though they're relatively weak they", "are still dangerous." }, 1, false, false), MINOTAURS(7, new int[] { 4404, 4405, 4406 }, new String[] { "Minotaurs are large manlike creatures but you'll", "want to be careful of their horns." }, 1, false, false), - MONKEYS(1, new int[] { 132, 1463, 1464, 2301, 4344, 4363, 6943, 7211, 7213, 7215, 7217, 7219, 7221, 7223, 7225, 7227, 1455, 1459, 1460, 1456, 1457, 1458 }, new String[] { "Small agile creatures, watch out they pinch!" }, 1, false, false), - MOSS_GIANTS(40, new int[] { 112, 1587, 1588, 1681, 4534, 4688, 4706 }, new String[] { "They are known to carry large sticks." }, 1, false, false), - NECHRYAELS(85, new int[] { 1613 }, new String[] { "Nechryael are demons of decay which summon small winged beings which", "help them fight their victems." }, 80, false, false), - OGRES(40, new int[] { 115, 374, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 3587, 6267, 7078, 7079, 7080, 7081, 7082 }, new String[] { "Ogres are brutal creatures, favouring large blunt maces and clubs", "they often attack without warning." }, 1, false, false), - OTHERWORDLY_BEING(40, new int[] { 126 }, new String[] { "A creature filled with everlasting power." }, 1, false, false), - PYREFIENDS(25, new int[] { 1633, 1634, 1635, 1636, 6216, 6631, 6641, 6660, 6668, 6683, 6709, 6721, }, new String[] { "A scorching hot creature, watch out!" }, 30, false, false), - RATS(1, new int[] { 2682, 2980, 2981, 3007, 88, 224, 4928, 4929, 4936, 4937, 3008, 3009, 3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 4396, 4415, 7202, 7204, 7417, 7461, 87, 446, 950, 4395, 4922, 4923, 4924, 4925, 4926, 4927, 4942, 4943, 4944, 4945, 86, 87, 446, 950, 4395, 4922, 4923, 4924, 4925, 4926, 4927, 4942, 4943, 4944, 4945 }, new String[] { "Quick little rodents!" }, 1, false, false), - ROCK_SLUGS(20, new int[] { 1631, 1632 }, new String[] { "A rock slug can leave behind a trail of his presence.." }, 20, false, false), - SCORPIONS(7, new int[] { 107, 1477, 4402, 4403, 144 }, new String[] { "A scorpion makes a piercing sound, watch out for", "its long sharp tail." }, 1, false, false), - SHADE(30, new int[] { 3617, 1250, 1241, 1246, 1248, 1250, 428, 1240 }, new String[] { "Shades are dark and mysterious", "they hide in the shadows so be wary of ambushes." }, 1, true, false), - SKELETONS(15, new int[] { 90, 91, 92, 93, 94, 459, 1471, 1575, 1973, 2036, 2037, 2715, 2717, 3065, 3151, 3291, 3581, 3697, 3698, 3699, 3700, 3701, 3702, 3703, 3704, 3705, 3844, 3850, 3851, 4384, 4385, 4386, 5332, 5333, 5334, 5335, 5336, 5337, 5338, 5339, 5340, 5341, 5359, 5365, 5366, 5367, 5368, 5381, 5384, 5385, 5386, 5387, 5388, 5389, 5390, 5391, 5392, 5411, 5412, 5422, 6091, 6092, 6093, 6103, 6104, 6105, 6106, 6107, 6764, 6765, 6766, 6767, 6768 }, new String[] { "Skeletons are undead monsters found in various locations." }, 1, true, false), - SPIDERS(1, new int[] { 61, 1004, 1221, 1473, 1474, 63, 4401, 2034, 977, 7207, 134, 1009, 59, 60, 4400, 58, 62, 1478, 2491, 2492, 6376, 6377, }, new String[] { "Level 24 spiders are aggressive and can hit up to 60 life points." }, 1, false, false), - SPIRTUAL_MAGES(60, new int[] { 6221, 6231, 6257, 6278 }, new String[] { "They are dangerous, they hit with mage." }, 83, false, false), - SPIRTUAL_RANGERS(60, new int[] { 6220, 6230, 6256, 6276 }, new String[] { "They are dangerous, they hit with range." }, 63, false, false), - SPIRTUAL_WARRIORS(60, new int[] { 6219, 6229, 6255, 6277, }, new String[] { "They are dangerous, they hit with melee." }, 68, false, false), - STEEL_DRAGONS( 85,new int[] { 1592, 3590 }, new String[] { "Steel dragons aren't as strong as other dragons but they're still", "very powerful, watch out for their firey breath." }, 1, false, true, 10 | 20 << 16), - TROLLS(60, new int[] { 72, 3584, 1098, 1096, 1097, 1095, 1101, 1105, 1102, 1103, 1104, 1130, 1131, 1132, 1133, 1134, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1138, 1560, 1561, 1562, 1563, 1564, 1565, 1566, 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1942, 3840, 3841, 3842, 3843, 3845, 1933, 1934, }, new String[] { "Trolls have a crushing attack, it's bets to wear a high crushing defence." }, 1, false, false), - TUROTHS(60, new int[] { 1626, 1627, 1628, 1629, 1630 }, new String[] { "Turoths are Slayer monsters that require a Slayer level of 55 to kill" }, 55, false, false), - TZHAAR(45, new int[] { 2591, 2592, 2593, 2745, 2594, 2595, 2596, 2597, 2604, 2605, 2606, 2607, 2608, 2609, 7755, 7753, 2598, 2599, 2600, 2601, 2610, 2611, 2612, 2613, 2614, 2615, 2616, 2624, 2617, 2618, 2625, 2602, 2603, 7754, 7767, 2610, 2611, 2612, 2613, 2614, 2615, 2616, 2624, 2625, 2627, 2628, 2629, 2630, 2631, 2632, 7746, 7747, 7748, 7749, 7750, 7751, 7752, 7753, 7754, 7755, 7756, 7757, 7758, 7759, 7760, 7761, 7762, 7763, 7764, 7765, 7766, 7767, 7768, 7769, 7770, 7771, 7747, 7747, 7748, 7749, 7750, 7751, 7752, 7753, 7757, 7765, 7769, 7768 }, new String[] { "Young Tzhaar's of the century are furious with your kind." }, 1, false, false), - SUQAHS (65, new int[] { 4527, 4528, 4529, 4530, 4531, 4532, 4533 }, new String[] { "Suquah are big, angry folk that inhabit Lunar Isle." }, 1, "Lunar Diplomacy"), - VAMPIRES(35, new int[] { 1023, 1220, 1223, 1225, 6214 }, new String[] { "Vampires are equipped with large fangs,", "they can do serious damage." }, 1, false, false), - WATERFIENDS(75, new int[] { 5361 }, new String[] { "A waterfiend takes no damage from fire!" }, 1, false, false), - WEREWOLFS(60, new int[] { 1665, 6006, 6007, 6008, 6009, 6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, 6023, 6024, 6025, 6212, 6213, 6607, 6609, 6614, 6617, 6625, 6632, 6644, 6663, 6675, 6686, 6701, 6712, 6724, 6728, }, new String[] { "There temper is alot more nasty then a regular wolf!" }, 1, false, false), - WOLVES(20, new int[] { 95, 96, 97, 141, 142, 143, 839, 1198, 1330, 1558, 1559, 1951, 1952, 1953, 1954, 1955, 1956, 4413, 4414, 6046, 6047, 6048, 6049, 6050, 6051, 6052, 6829, 6830, 7005 }, new String[] { "Wolves are more agressive then dog's." }, 1, false, false), - ZOMBIES(10, new int[] { 73, 74, 75, 76, 2058, 2714, 2863, 2866, 2869, 2878, 3622, 4392, 4393, 4394, 5293, 5294, 5295, 5296, 5297, 5298, 5299, 5300, 5301, 5302, 5303, 5304, 5305, 5306, 5307, 5308, 5309, 5310, 5311, 5312, 5313, 5314, 5315, 5316, 5317, 5318, 5319, 5320, 5321, 5322, 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, 5331, 5375, 5376, 5377, 5378, 5379, 5380, 5393, 5394, 5395, 5396, 5397, 5398, 5399, 5400, 5401, 5402, 5403, 5404, 5405, 5406, 5407, 5408, 5409, 5410, 6099, 6100, 6131, }, new String[] { "Zombies are creatures with no brain, they do hit farley", "high though." }, 1, true, false), - JAD(90, new int[] { }, new String[] { "TzTok-Jad is the king of the Fight Caves." }, 1, false, false, 1 | 1 << 16), - CHAOS_ELEMENTAL(90, new int[] { 3200 }, new String[] { "The Chaos Elemental is located in the deep Wilderness." }, 1, false, false, 5 | 25 << 16), - GIANT_MOLE(75, new int[] { 3340 }, new String[] { "Fighting the Giant Mole will require a light source." }, 1, false, false, 5 | 25 << 16), - KING_BLACK_DRAGON(75, new int[] { 50 }, new String[] { "The King Black Dragon is located in the deep wilderness." }, 1, false, true, 5 | 25 << 16), - COMMANDER_ZILYANA(90, new int[] { 6247 }, new String[] { "Commander Zilyana is one of the four Godwars bosses." }, 1, false, false, 5 | 25 << 16), - GENERAL_GRARDOOR(90, new int[] { 6260 }, new String[] { "General Grardoor is one of the four Godwars bosses." }, 1, false, false, 5 | 25 << 16), - KRIL_TSUTSAROTH(90, new int[] { 6203 }, new String[] { "Kril Tsutsaroth is one of the four Godwars bosses." }, 1, false, false, 5 | 25 << 16), - KREE_ARRA(90, new int[] { 6222 }, new String[] { "Kree'arra is one of the four Godwars bosses." }, 1, false, false, 5 | 25 << 16), - SKELETAL_WYVERN(70, new int[] { 3068, 3069, 3070, 3071 }, new String[] { "A skeletal wyvern requires an elemental, mirror", "or dragonfire shield." }, 72, false, false, 24 | 39 << 16); + MITHRIL_DRAGONS(60, new int[] { 5363 }, new String[] { "Mithril dragons are more vulnerable to magic and to", "stab-based melee attacks than to anything else." }, 1, false, true), + MOGRES(1, new int[] { 114 }, new String[] { "Mogres are a type of aquatic Ogre that is", "often mistaken for a giant mudskipper. You have to force", "them out of the water with a fishing explosive." }, 32, false, false), + // Waiting for molanisks transform to be implemented before adding this to assignments + MOLANISKS(1, new int[] { 5751 }, new String[] { "Molanisks are subterranean creatures. You can find", "them in caves deep below the ground. I heard that the", "goblins have recently had trouble with some, but they", "use a bell of some sort to deal with them."}, 39, Quests.DEATH_TO_THE_DORGESHUUN), + MONKEYS(1, new int[] { 132, 1463, 1464, 2301, 4344, 4363, 6943, 7211, 7213, 7215, 7217, 7219, 7221, 7223, 7225, 7227, 1455, 1459, 1460, 1456, 1457, 1458 }, new String[] { "Monkeys are tricky creatures, they are agile and", "fairly fast. Learn to anticipate their movements." }, 1, false, false), + MOSS_GIANTS(40, new int[] { 112, 1587, 1588, 1681, 4534, 4688, 4706 }, new String[] { "Like other giants, Moss Giants often wield large", "weapons, learn to recognise what kind of weapon it is", "and act accordingly." }, 1, false, false), + // Waiting for zygomite transform to be implemented before adding this to assignments + MUTATED_ZYGOMITES(1, new int[] { 3346, 3347 }, new String[] { "Mutated Zygomites are hard to destroy. They regenerate", "quickly so you will need to finish them with fungicide." }, 57, Quests.LOST_CITY), + NECHRYAELS(85, new int[] { 1613 }, new String[] { "Nechryael are demons of decay which summon small", "winged beings to help them fight their victims." }, 80, false, false), + OGRES(40, new int[] { 115, 374, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2060, 2801, 3419, 7078, 7079, 7080, 7081, 7082 }, new String[] { "Ogres are brutal creatures, favouring large blunt", "maces and clubs they often attack without warning." }, 1, false, false), + OTHERWORDLY_BEING(40, new int[] { 126 }, new String[] { "Otherworldly Beings are ethereal beings making them", "weak to magical attack." }, 1, false, false), + PYREFIENDS(25, new int[] { 1633, 1634, 1635, 1636, 6216, 6631, 6641, 6660, 6668, 6683, 6709, 6721, }, new String[] { "Pyrefiends are beings of fire and molten rock,", "they're quick and agile so you'll want to be careful", "when getting close to them." }, 30, false, false), + RED_DRAGONS(1, new int[] { 53, 1589, 3588, 4667, 4668, 4669, 4670, 4671, 4672 }, new String[] { "Red Dragons are very powerful, stronger than most", "dragons, watch out for their fiery breath." }, 1, false, false), + ROCK_SLUGS(20, new int[] { 1631, 1632 }, new String[] { "Rockslugs are strange stoney slugs. You'll need to", "fight them to near death before finishing them off", "with Salt." }, 20, false, false), + // Seems to need Contact or the NPCs added to dungeons before adding this assignment as a task + SCABARITES(1, new int[] { 2001, 4500, 5251, 5252, 5255, 5256, 5250, 5254, 6777, 6778, 6774, 6780, 6781, 6773 }, new String[] {"Scabarites are insectoid creatures, found beyond the", "Kharidian deserts. They can be extremely dangerous."}, 1, Quests.CONTACT), + SCORPIONS(7, new int[] { 107, 1477, 4402, 4403, 144 }, new String[] { "Scorpions are almost always poisonous, their hard", "carapace makes them resistant to crushing and", "stabbing attacks." }, 1, false, false), + SEA_SNAKES(1, new int[] { 3939, 3940 }, new String[] { "Sea Snakes are long and slithery with a venomous bite.", "The larger ones are more poisonous, so keep an eye on", "your health." }, 1, Quests.ROYAL_TROUBLE), + SHADE(30, new int[] { 3617, 1250, 1241, 1246, 1248, 1250, 428, 1240 }, new String[] { "Shades are undead so magic is your best best against", "them, you can find Shades at Mort'ton." }, 1, true, false), + // Dungeon needs to be connected to the Legend's Guild before adding this assignment as a task + SHADOW_WARRIORS(1, new int[] { 158 }, new String[] { "Shadow Warriors are dark and mysterious, they hide in", "the shadows so be wary of ambushes." }, 1, Quests.LEGENDS_QUEST), + SKELETAL_WYVERN(70, new int[] { 3068, 3069, 3070, 3071 }, new String[] { "Skeletal Wyverns are extremely dangerous and they are", "hard to hit with arrows as they slip right through.", "To stand a good chance of surviving you'll need some", "elemental shielding from its icy breath." }, 72, false, false), + SKELETONS(15, new int[] { 90, 91, 92, 93, 94, 459, 1471, 1575, 1973, 2036, 2037, 2715, 2717, 3065, 3151, 3291, 3581, 3697, 3698, 3699, 3700, 3701, 3702, 3703, 3704, 3705, 3844, 3850, 3851, 4384, 4385, 4386, 5332, 5333, 5334, 5335, 5336, 5337, 5338, 5339, 5340, 5341, 5359, 5365, 5366, 5367, 5368, 5381, 5384, 5385, 5386, 5387, 5388, 5389, 5390, 5391, 5392, 5411, 5412, 5422, 6091, 6092, 6093, 6103, 6104, 6105, 6106, 6107, 6764, 6765, 6766, 6767, 6768, 2050, 2056, 2057, 1539, 7640 }, new String[] { "Skeletons are undead so magic is your best bet against", "them, there is even a spell specially for fighting the undead." }, 1, true, false), + SPIDERS(1, new int[] { 61, 1004, 1221, 1473, 1474, 63, 4401, 2034, 977, 7207, 134, 1009, 59, 60, 4400, 58, 62, 1478, 2491, 2492, 6376, 6377, }, new String[] { "Spiders are often poisonous, and many varieties are", "camouflaged too." }, 1, false, false), + SPIRTUAL_MAGES(60, new int[] { 6221, 6231, 6257, 6278 }, new String[] { "Spiritual mages can be found in the icy caverns near", "Trollheim, supporting the cause of their chosen god." }, 83, Quests.DEATH_PLATEAU), + SPIRTUAL_RANGERS(60, new int[] { 6220, 6230, 6256, 6276 }, new String[] { "Spiritual rangers can be found in the icy caverns near", "Trollheim, supporting the cause of their chosen god." }, 63, Quests.DEATH_PLATEAU), + SPIRTUAL_WARRIORS(60, new int[] { 6219, 6229, 6255, 6277, }, new String[] { "Spiritual warriors can be found in the icy caverns near", "Trollheim, supporting the cause of their chosen god." }, 68, Quests.DEATH_PLATEAU), + STEEL_DRAGONS( 85,new int[] { 1592, 3590 }, new String[] { "Steel dragons are dangerous and metallic, with steel", "scales that are far thicker than normal steel armour. As", "you are an accomplished slayer, I am sure you'll be", "able to deal with them easily."}, 1, false, true), + SUQAHS (65, new int[] { 4527, 4528, 4529, 4530, 4531, 4532, 4533 }, new String[] { "Suqahs can only be found on the mystical Lunar Isle.", "They are capable of melee and magic attacks and often", "drop hide, teeth and herbs!" }, 1, Quests.LUNAR_DIPLOMACY), + // No access to Lair of Tarn Razorlor but this should be added as a task when there is access + TERROR_DOGS(1, new int[] { 5417, 5418 }, new String[] { "Terror dogs are the personal pets of Tarn Razorlor.", "Wherever you find him, you will find them. They are", "bad-tempered and generally unfriendly." }, 40, Quests.HAUNTED_MINE), + TROLLS(60, new int[] { 72, 3584, 1098, 1096, 1097, 1095, 1101, 1105, 1102, 1103, 1104, 1130, 1131, 1132, 1133, 1134, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1138, 1560, 1561, 1562, 1563, 1564, 1565, 1566, 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1942, 3840, 3841, 3842, 3843, 3845, 1933, 1934, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 391, 392, 393, 394, 395, 396}, new String[] { "Trolls regenerate damage quickly but are still", "vulnerable to poisons, they usually use crushing", "weapons." }, 1, false, false), + TUROTHS(60, new int[] { 1622, 1611, 1623, 1626, 1627, 1628, 1629, 1630, 7800}, new String[] { "Turoth are large vicious creatures with thick hides.", "You'll need a Leaf-Tipped Spear Sword or Battle-axe,", "Broad Arrows, or a Magic Dart to harm them." }, 55, false, false), + VAMPIRES(35, new int[] { 1220, 1223, 1225, 6214 }, new String[] { "Vampires are extremely powerful beings. They feed on", "the blood of the living so watch out you don't", "get bitten." }, 1, false, false), + // Waiting for the wall beast movement bug to be fixed before adding this as a task + WALL_BEASTS(1, new int[] { 7823 }, new String[] { "Wall Beasts are really much larger creatures but", "you'll only see their arms. You'll want something", "sharp on your head to stop them grabbing you." }, 35, false, false ), + // No way to get to Poison Swamp Cave + // If a grapple is implemented from W Castle Wars to E Posion Swamps + // The dungeon connected and populated + // then these could be added as a task + WARPED_TERROR_BIRD(1, new int[] { 6285, 6286, 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, 6323, 6324, 6325, 6326, 6327, 6328, 6329, 6330, 6331, 6332, 6608 }, new String[] { "Warped Creatures can supposedly be found within a", "mysterious dungeon on the eastern edge of the Poison", "Waste. Be aware that to defeat them, you'll need to", "purify them in some way." },56, Quests.THE_PATH_OF_GLOUPHRIE), + WARPED_TORTOISE(1, new int[] { 6296, 6297 }, new String[] { "Warped Creatures can supposedly be found within a", "mysterious dungeon on the eastern edge of the Poison", "Waste. Be aware that to defeat them, you'll need to", "purify them in some way." },56, Quests.THE_PATH_OF_GLOUPHRIE), + WATERFIENDS(75, new int[] { 5361 }, new String[] { "Waterfiends are creatures of water, which live under", "the Baxtorian Lake. Their watery form is well defended", "against slashing and piercing weapons, so use", "something blunt." }, 1, false, false), + WEREWOLVES(60, new int[] { 6006, 6007, 6008, 6009, 6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, 6023, 6024, 6025, 6212, 6213, 6607, 6609, 6614, 6617, 6625, 6632, 6644, 6663, 6675, 6686, 6701, 6712, 6724, 6728, }, new String[] { "Werewolves are feral creatures, they are strong and", "tough with sharp claws and teeth." }, 1, false, false), + WOLVES(20, new int[] { 95, 96, 97, 141, 142, 143, 839, 1198, 1330, 1558, 1559, 1951, 1952, 1953, 1954, 1955, 1956, 4413, 4414, 6046, 6047, 6048, 6049, 6050, 6051, 6052, 6829, 6830, 7005 }, new String[] { "Wolves are pack animals, so you'll always find them", "in groups. Watch out for their bite, it can be nasty." }, 1, false, false), + ZOMBIES(10, new int[] { 73, 74, 75, 76, 2060, 2714, 2863, 2866, 2869, 2878, 3622, 4392, 4393, 4394, 5293, 5294, 5295, 5296, 5297, 5298, 5299, 5300, 5301, 5302, 5303, 5304, 5305, 5306, 5307, 5308, 5309, 5310, 5311, 5312, 5313, 5314, 5315, 5316, 5317, 5318, 5319, 5320, 5321, 5322, 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, 5331, 5375, 5376, 5377, 5378, 5379, 5380, 5393, 5394, 5395, 5396, 5397, 5398, 5399, 5400, 5401, 5402, 5403, 5404, 5405, 5406, 5407, 5408, 5409, 5410, 6099, 6100, 6131, 8149, 8150, 8151, 8152, 8153, 8159, 8160, 8161, 8162, 8163, 8164, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 7641, 1465, 1466, 1467, 2837, 2838, 2839, 2840, 2841, 2842, 5629, 5630, 5631, 5632, 5633, 5634, 5635, 5636, 5637, 5638, 5639, 5640, 5641, 5642, 5643, 5644, 5645, 5646, 5647, 5648, 5649, 5650, 5651, 5652, 5653, 5654, 5655, 5656, 5657, 5658, 5659, 5660, 5661, 5662, 5663, 5664, 5665, 2843, 2844, 2845, 2846, 2847, 2848}, new String[] { "Zombies are undead so magic is your best bet against", "them, there is even a spell specially for", "fighting the undead." }, 1, true, false), + // Bosses need to be handled differently/have a source. They should probably be removed from here + JAD(90, new int[] { }, new String[] { "You must complete the entire fight cave, including", "TzTok-Jad. Beware, only those skilled in combat should", "attempt this and death will reset your task." }, 1, false, false), + CHAOS_ELEMENTAL(90, new int[] { 3200 }, new String[] { "The Chaos Elemental roams the deepest Wilderness. It", "can teleport you and make you unequip items randomly." }, 1, false, false), + GIANT_MOLE(75, new int[] { 3340 }, new String[] { "Dig on the mole-hills in Falador Park to enter the", "mole cave, and bring a lantern he can't extinguish." }, 1, false, false), + KING_BLACK_DRAGON(75, new int[] { 50 }, new String[] { "The King Black Dragon's cave is reached by pulling a", "lever in the Wilderness. An anti-fire shield is", "strongly recommended." }, 1, false, true), + COMMANDER_ZILYANA(90, new int[] { 6247 }, new String[] { "Commander Zilyana is in the God Wars Dungeon.", "She frequently uses magic attacks." }, 1, false, false), + GENERAL_GRARDOOR(90, new int[] { 6260 }, new String[] { "General Graardor is in the God Wars Dungeon.", "He uses melee and ranged attacks." }, 1, false, false), + KRIL_TSUTSAROTH(90, new int[] { 6203 }, new String[] { "K'ril Tsutsaroth is in the God Wars Dungeon.", "He's poisonous and he can hit through prayers." }, 1, false, false), + KREE_ARRA(90, new int[] { 6222 }, new String[] { "Kree'arra roosts in the God Wars Dungeon.", "Melee attacks don't work on flying creatures,", "and you'll need a grappling hook to get in." }, 1, false, false), + ; static final HashMap taskMap = new HashMap<>(); static{ @@ -117,8 +145,7 @@ public enum Tasks { public final int[] ids; public boolean undead = false; public boolean dragon = false; - public int amtHash; - public String questReq = ""; + public Quests questReq = null; Tasks(int combatCheck, int[] ids, String[] info, int levelReq, boolean undead, boolean dragon){ this.levelReq = levelReq; this.ids = ids; @@ -128,17 +155,7 @@ public enum Tasks { this.combatCheck = combatCheck; } - Tasks(int combatCheck, int[] ids, String[] info, int levelReq, boolean undead, boolean dragon, int amtHash){ - this.levelReq = levelReq; - this.ids = ids; - this.info = info; - this.undead = undead; - this.dragon = dragon; - this.amtHash = amtHash; - this.combatCheck = combatCheck; - } - - Tasks (int combatCheck, int[] ids, String[] info, int levelReq, String questReq) { + Tasks (int combatCheck, int[] ids, String[] info, int levelReq, Quests questReq) { this.combatCheck = combatCheck; this.ids = ids; this.info = info; @@ -155,7 +172,7 @@ public enum Tasks { } public boolean hasQuestRequirements (Player player) { - return questReq.equals("") || hasRequirement(player, questReq, false); + return questReq == null || hasRequirement(player, questReq, false); } public static Tasks forId(int id){ diff --git a/Server/src/main/content/global/skill/slayer/WaterfiendBehavior.kt b/Server/src/main/content/global/skill/slayer/WaterfiendBehavior.kt index e814eafcf..a1d216746 100644 --- a/Server/src/main/content/global/skill/slayer/WaterfiendBehavior.kt +++ b/Server/src/main/content/global/skill/slayer/WaterfiendBehavior.kt @@ -16,7 +16,7 @@ class WaterfiendBehavior : NPCBehavior(*Tasks.WATERFIENDS.ids) { true, SwitchAttack( CombatStyle.MAGIC.swingHandler, - Animation(1581, Priority.HIGH), + Animation(302, Priority.HIGH), // 299 = attack animation, 302 = idle animation. (authentic bug) null, null, Projectile.create( @@ -33,7 +33,7 @@ class WaterfiendBehavior : NPCBehavior(*Tasks.WATERFIENDS.ids) { ), SwitchAttack( CombatStyle.RANGE.swingHandler, - Animation(1581, Priority.HIGH), + Animation(302, Priority.HIGH), // 299 = attack animation, 302 = idle animation. (authentic bug) null, null, Projectile.create( diff --git a/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java b/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java index 7e8e55cd4..f86de623f 100644 --- a/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java @@ -152,7 +152,7 @@ public final class AncientCavern extends MapZone implements Plugin { } }); - player.getInterfaceManager().open(c); + player.getInterfaceManager().openOverlay(c); break; case 3: PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); diff --git a/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java b/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java index c47cfff3c..af34faff2 100644 --- a/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java @@ -28,7 +28,6 @@ import java.util.Map; * Handles the lumbridge dungeon. * @author Vexia */ -@Initializable public final class LumbridgeDungeon extends MapZone implements Plugin { /** diff --git a/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java b/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java index edce0a909..3dec3dd92 100644 --- a/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java @@ -132,7 +132,7 @@ public final class SmokeDungeon extends MapZone implements Plugin { * @param player the player. */ private static void effect(Player player) { - int hit = 2; + int hit = 10; setDelay(player); if (RandomFunction.random(2) == 1) { player.sendChat(CHATS[RandomFunction.random(CHATS.length)]); diff --git a/Server/src/main/content/global/skill/slayer/unused-tasks b/Server/src/main/content/global/skill/slayer/unused-tasks deleted file mode 100644 index 03501f20e..000000000 --- a/Server/src/main/content/global/skill/slayer/unused-tasks +++ /dev/null @@ -1,92 +0,0 @@ - /*WALL_BEASTS(new Task(new int[] { 7823 }, new String[] { "A spiny helmet or equivalent is required to start fighting one " }, 35, new Master[] { Master.MAZCHNA }, false, Equipment.SPINY_HELMET) { - @Override - public boolean isDisabled() { - return true; - } - }), - WARPED_TERRORBIRDS(new Task(new int[] { 6285, 6286, 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, 6322, 6323, 6324, 6325, 6326, 6327, 6328, 6329, 6330, 6331, 6332, }, new String[] { "They require 56 Slayer and a Crystal chime to be killed. " }, 56, new Master[] { Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }), - WARPED_TORTOISES(new Task(new int[] { 6296, 6297 }, new String[] { "A warped tortoise is not as slow as they look." }, 56, new Master[] { Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -/*CERBERUS(new Task(new int[] {8632, 8634}, new String[] {"Cerberus is about the best guard dog there is!"}, 91, new Master[] {Master.VANNAKA, Master.CHAELDAR, Master.NIEVE, Master.DURADEL}, false, 20 | 25 << 16) { - @Override - public boolean isDisabled() { - return true; - } - })*/ -/*ZYGOMITES(new Task(new int[] { 3346, 3347 }, new String[] { "Mutated zygomites are monsters that appear as Fungi until provoked." }, 57, new Master[] { Master.CHAELDAR }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -/*SUQAHS(new Task(new int[] { 4527, 4528, 4529, 4530, 4531, 4532, 4533 }, new String[] {}, 1, new Master[] { Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }), - TERROR_DOG(new Task(new int[] { 5417, 5418 }, new String[] { "Terror Dogs are much like Wolves, they are pack creatures which will hunt in groups." }, 1, new Master[] { Master.VANNAKA, Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -/*SHADOW_WARRIORS(new Task(new int[] { 158 }, new String[] { "Shadow warriors are dark and mysterious, they hide in the shadows so be wary of ambushes." }, 1, new Master[] { Master.VANNAKA, Master.CHAELDAR }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -//MOGRES(new Task(new int[] { 114 }, new String[] { "Mogres are Slayer monsters and require level 32 Slayer to kill. " }, 32, new Master[] { Master.MAZCHNA, Master.VANNAKA }, false)), - /*MOLANISKS(new Task(new int[] { 5751 }, new String[] { "Molanisks are found attached to cave walls and must be lured", "off with a Slayer bell" }, 39, new Master[] { Master.VANNAKA }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ - - /*RED_DRAGONS(new Task(new int[] { 53, 4669, 4670, 4671, 4672, 1589, 3588, 4667, 4668, }, new String[] { "Red dragons aren't as strong as other dragons but they're still", "very powerful, watch out for their firey breath." }, 1, new Master[] { Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ - /*SCABARITES(new Task(new int[] { 2009 }, new String[] { "The Scabarites are fairly strong monsters, and can hit pretty hard." }, 1, new Master[] { Master.CHAELDAR }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ - /*SEA_SNAKES(new Task(new int[] { 3943, 3939, }, new String[] { "They are quick, make sure you don't let your", "eye off of them for 1 second." }, 1, new Master[] { Master.VANNAKA }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -/*ELVES(new Task(new int[] { 1184, 1183, 1185 }, new String[] { "Elves are quick, agile and vicious fighters which", "often favour bows and polearms." }, 1, new Master[] { Master.VANNAKA, Master.CHAELDAR, Master.NIEVE }, false) { - @Override - public boolean isDisabled() { - return true; - } - }), - FEVER_SPIDERS(new Task(new int[] { 2850 }, new String[] { "Fever Spiders are giant spiders that carry the deadly Spider Fever.", "If you don't want to catch it I suggest you wear Slayer Gloves to fight them." }, 1, new Master[] { Master.CHAELDAR, Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ - /*KILLERWATTS(new Task(new int[] { 3201, 3202 }, new String[] { "Vexia halfassed this... (this was shadow knight text)" }, 37, new Master[] { Master.VANNAKA }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ \ No newline at end of file diff --git a/Server/src/main/content/global/skill/smithing/Bars.java b/Server/src/main/content/global/skill/smithing/Bars.java index 2a8b9f493..7514693fe 100644 --- a/Server/src/main/content/global/skill/smithing/Bars.java +++ b/Server/src/main/content/global/skill/smithing/Bars.java @@ -360,8 +360,6 @@ public enum Bars { /** * Steel Lantern */ - STEEL_LANTERN(BarType.STEEL, SmithingType.TYPE_LANTERN, 4527, 49), - STEEL_BULLSEYE(BarType.STEEL, SmithingType.TYPE_BULLSEYE, 4544, 49), /** diff --git a/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java b/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java index 84d4e7edf..64833097c 100644 --- a/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java +++ b/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java @@ -1,55 +1,23 @@ package content.global.skill.smithing; +import content.global.skill.skillcapeperks.SkillcapePerks; +import core.api.Container; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; -import core.game.node.item.Item; import core.plugin.Initializable; -import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; /** * Represents the dialogue plugin used for making a dragon shield. * @author 'Vexia - * @version 1.0 + * @author Player Name + * @version 1.1 */ @Initializable public final class DragonShieldDialogue extends DialoguePlugin { - - /** - * Represents the item shield parts. - */ - private static final Item[] SHIELD_PARTS = new Item[] { new Item(2366), new Item(2368) }; - - /** - * Represents th edraconic visage item. - */ - private static final Item DRACONIC_VISAGE = new Item(11286); - - /** - * Represents the anti dragon fire shield. - */ - private static final Item ANTI_DRAGONSHIELD = new Item(1540); - - /** - * Represents the dragon fire shield item. - */ - private static final Item DRAGON_FIRESHIELD = new Item(11284); - - /** - * Represents the shield item. - */ - private static final Item SQ_SHIELD = new Item(1187); - - /** - * Represents the hammering animation. - */ - private static Animation ANIMATION = new Animation(898); - - /** - * Represents the shield type. - */ - private int type; - /** * Constructs a new {@code DragonShieldDialogue} {@code Object}. */ @@ -74,11 +42,26 @@ public final class DragonShieldDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - type = (int) args[0]; + if (!inInventory(player, Items.HAMMER_2347, 1) && !SkillcapePerks.isActive(SkillcapePerks.BAREFISTED_SMITHING, player)) { + interpreter.sendDialogue("You need a hammer to work the metal with."); + } + int type = (int) args[0]; if (type == 1) { - interpreter.sendDialogue("You set to work trying to fix the ancient shield. It's seen some", "heavy reward and needs some serious work doing to it."); + if (!(inInventory(player, Items.SHIELD_RIGHT_HALF_2368, 1) && inInventory(player, Items.SHIELD_LEFT_HALF_2366, 1))) { + interpreter.sendDialogue("You need the other half of the shield."); //todo authentic message + return false; + } + interpreter.sendDialogue("You set to work trying to fix the ancient shield. It's seen some", "heavy action and needs some serious work doing to it."); stage = 0; } else { + if (!inInventory(player, Items.ANTI_DRAGON_SHIELD_1540, 1)) { + interpreter.sendDialogue("You need an anti-dragon shield to attach the visage to."); //todo authentic message + return false; + } + if (!inInventory(player, Items.DRACONIC_VISAGE_11286, 1)) { + interpreter.sendDialogue("You don't have anything you could attach to the shield."); //todo authentic message + return false; + } interpreter.sendDialogue("You set to work, trying to attach the ancient draconic", "visage to your anti-dragonbreath shield. It's not easy to", "work with the ancient artifact and it takes all of your", "skills as a master smith."); stage = 10; } @@ -89,26 +72,26 @@ public final class DragonShieldDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - player.lock(5); - player.animate(ANIMATION); - interpreter.sendDialogue("Even for an experienced armourer it is not an easy task, but", "eventually it is ready. You have restored the dragon square shield to", "its former glory."); - if (player.getInventory().remove(SHIELD_PARTS)) { - player.getInventory().add(SQ_SHIELD); + lock(player, 5); + animate(player, 898, false); + if (removeItem(player, Items.SHIELD_RIGHT_HALF_2368, Container.INVENTORY) && removeItem(player, Items.SHIELD_LEFT_HALF_2366, Container.INVENTORY)) { + interpreter.sendDialogue("Even for an experienced armourer it is not an easy task, but", "eventually it is ready. You have restored the dragon square shield to", "its former glory."); + addItem(player, Items.DRAGON_SQ_SHIELD_1187, 1, Container.INVENTORY); + rewardXP(player, Skills.SMITHING, 75); } - player.getSkills().addExperience(Skills.SMITHING, 75, true); stage = 1; break; case 1: end(); break; case 10: - player.lock(5); - player.animate(ANIMATION); - interpreter.sendDialogue("Even for an experienced armourer it is not an easy task, but", "eventually it is ready. You have crafted the", "draconic visage and anti-dragonbreath shield into a", "dragonfire shield."); - if (player.getInventory().remove(DRACONIC_VISAGE, ANTI_DRAGONSHIELD)) { - player.getInventory().add(DRAGON_FIRESHIELD); + lock(player, 5); + animate(player, 898, false); + if (removeItem(player, Items.ANTI_DRAGON_SHIELD_1540, Container.INVENTORY) && removeItem(player, Items.DRACONIC_VISAGE_11286, Container.INVENTORY)) { + interpreter.sendDialogue("Even for an experienced armourer it is not an easy task, but", "eventually it is ready. You have crafted the", "draconic visage and anti-dragonbreath shield into a", "dragonfire shield."); + addItem(player, Items.DRAGONFIRE_SHIELD_11284, 1, Container.INVENTORY); + rewardXP(player, Skills.SMITHING, 2000); } - player.getSkills().addExperience(Skills.SMITHING, 2000); stage = 1; break; } diff --git a/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java b/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java index 1468549d2..be0190327 100644 --- a/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java +++ b/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java @@ -19,6 +19,7 @@ import core.plugin.Plugin; import java.util.ArrayList; import java.util.List; +import content.data.Quests; /** * Represents the plugin used for the furnace. @@ -64,7 +65,7 @@ public final class FurnaceOptionPlugin extends OptionHandler { private static void show(final Player player) { player.getInterfaceManager().openChatbox(311); player.getPacketDispatch().sendItemZoomOnInterface(2349, 150, 311, 4); - if (player.getQuestRepository().isComplete("The Knight's Sword")) { + if (player.getQuestRepository().isComplete(Quests.THE_KNIGHTS_SWORD)) { player.getPacketDispatch().sendString("



Blurite", 311, 20); } player.getPacketDispatch().sendItemZoomOnInterface(Bar.BLURITE.getProduct().getId(), 150, 311, 5); @@ -77,26 +78,6 @@ public final class FurnaceOptionPlugin extends OptionHandler { player.getPacketDispatch().sendItemZoomOnInterface(2363, 150, 311, 12); } - /** - * Method used to handle the tutorial island interaction. - * @param player the player. - */ - private final void handleTutorialIsland(final Player player) { - if (player.getInventory().containItems(438, 436)) { - player.animate(ANIMATION); - GameWorld.getPulser().submit(new Pulse(2, player) { - @Override - public boolean pulse() { - player.getInventory().remove(ITEMS); - player.getInventory().add(Bar.BRONZE.getProduct()); - player.getSkills().addExperience(Skills.SMITHING, Bar.BRONZE.getExperience()); - return true; - } - - }); - } - } - /** * Represents the plugin used to handle the ore on the furance. * @author 'Vexia diff --git a/Server/src/main/content/global/skill/smithing/SmithingBuilder.java b/Server/src/main/content/global/skill/smithing/SmithingBuilder.java index 3638be844..6159f161b 100644 --- a/Server/src/main/content/global/skill/smithing/SmithingBuilder.java +++ b/Server/src/main/content/global/skill/smithing/SmithingBuilder.java @@ -41,7 +41,41 @@ public final class SmithingBuilder { public void build(Player player) { player.getGameAttributes().removeAttribute("smith-type"); player.getGameAttributes().setAttribute("smith-type", type); - player.getPacketDispatch().sendInterfaceConfig(300, 267, false);// pickaxe + if (type.name().equals("BLURITE")) { + // interface 300 spawns with most things already there. Hide everything except what we want + int[] values = { + 17, // dagger + 25, // axe + 33, // mace + 41, // med helm + // show this 49, // bolts + 57, // sword + 65, // dart tip + 73, // nail + 105, // arrow tip + 113, // scimmy + // show this 121, // limbs + 129, // long sword + 137, // throwing knife + 145, // full helm + 153, // square shield + 177, // warhammer + 185, // battleaxe + 193, // chain body + 201, // kiteshield + 217, // 2h + 225, // plate sk + 233, // plate l + 241, // platebody + }; + for (int childId : values) { + player.getPacketDispatch().sendInterfaceConfig(300, childId, true); + + } + } + else { + player.getPacketDispatch().sendInterfaceConfig(300, 267, false);// pickaxe + } final Bars bars[] = Bars.getBars(type); for (int i = 0; i < bars.length; i++) { if (bars[i].getSmithingType() == SmithingType.TYPE_GRAPPLE_TIP) { @@ -50,17 +84,23 @@ public final class SmithingBuilder { if (bars[i].getSmithingType() == SmithingType.TYPE_DART_TIP) { player.getPacketDispatch().sendInterfaceConfig(300, 65, false); } - if (bars[i].getSmithingType() == SmithingType.TYPE_SPIT_IRON || bars[i].getSmithingType() == SmithingType.TYPE_WIRE || bars[i].getSmithingType() == SmithingType.TYPE_BULLSEYE) { + if (bars[i].getSmithingType() == SmithingType.TYPE_WIRE){ + player.getPacketDispatch().sendInterfaceConfig(300, 81, false); + } + if (bars[i].getSmithingType() == SmithingType.TYPE_SPIT_IRON){ player.getPacketDispatch().sendInterfaceConfig(300, 89, false); } + if ( bars[i].getSmithingType() == SmithingType.TYPE_BULLSEYE) { + player.getPacketDispatch().sendInterfaceConfig(300, 161, false); + } if (bars[i].getSmithingType() == SmithingType.TYPE_CLAWS) { player.getPacketDispatch().sendInterfaceConfig(300, 209, false); } - if (bars[i].getSmithingType() == SmithingType.TYPE_LANTERN || bars[i].getSmithingType() == SmithingType.TYPE_OIL_LANTERN) { + if (bars[i].getSmithingType() == SmithingType.TYPE_OIL_LANTERN) { player.getPacketDispatch().sendInterfaceConfig(300, 161, false); } if (bars[i].getSmithingType() == SmithingType.TYPE_STUDS) { - player.getPacketDispatch().sendInterfaceConfig(300, 169, false); + player.getPacketDispatch().sendInterfaceConfig(300, 97, false); } String color = ""; if (player.getSkills().getLevel(Skills.SMITHING) < bars[i].getLevel()) { @@ -79,7 +119,7 @@ public final class SmithingBuilder { } InterfaceContainer.generateItems(player, new Item[] { new Item(bars[i].getProduct(), bars[i].getSmithingType().getProductAmount()) }, new String[] { "" }, 300, bars[i].getSmithingType().getChild() - 1); } - player.getPacketDispatch().sendString(type.getBarName(), 300, 15); + player.getPacketDispatch().sendString(type.getBarName(), 300, 14); player.getInterfaceManager().open(new Component(300)); } diff --git a/Server/src/main/content/global/skill/smithing/SmithingPulse.java b/Server/src/main/content/global/skill/smithing/SmithingPulse.java index f9b7eec98..97a844d51 100644 --- a/Server/src/main/content/global/skill/smithing/SmithingPulse.java +++ b/Server/src/main/content/global/skill/smithing/SmithingPulse.java @@ -14,6 +14,7 @@ import core.tools.StringUtils; import static core.api.ContentAPIKt.hasRequirement; import static core.api.ContentAPIKt.sendDialogue; +import content.data.Quests; /** * Represents the pulse used to smith a bar. @@ -67,11 +68,11 @@ public class SmithingPulse extends SkillPulse { player.getDialogueInterpreter().sendDialogue("You need a hammer to work the metal with."); return false; } - if (!player.getQuestRepository().isComplete("The Tourist Trap") && bar.getSmithingType() == SmithingType.TYPE_DART_TIP) { + if (!player.getQuestRepository().isComplete(Quests.THE_TOURIST_TRAP) && bar.getSmithingType() == SmithingType.TYPE_DART_TIP) { player.getDialogueInterpreter().sendDialogue("You need to complete Tourist Trap to smith dart tips."); return false; } - if (!hasRequirement(player, "Death Plateau", false) && bar.getSmithingType() == SmithingType.TYPE_CLAWS) { + if (!hasRequirement(player, Quests.DEATH_PLATEAU, false) && bar.getSmithingType() == SmithingType.TYPE_CLAWS) { sendDialogue(player, "You need to complete Death Plateau to smith claws."); return false; } diff --git a/Server/src/main/content/global/skill/smithing/SmithingType.java b/Server/src/main/content/global/skill/smithing/SmithingType.java index 99054f7ec..fd38a6209 100644 --- a/Server/src/main/content/global/skill/smithing/SmithingType.java +++ b/Server/src/main/content/global/skill/smithing/SmithingType.java @@ -52,7 +52,7 @@ public enum SmithingType { /** * Bullseye */ - TYPE_BULLSEYE(1, 90, 91, new int[] { 96, 95, 94, 93 }, 1), + TYPE_BULLSEYE(1, 162, 163, new int[] { 168, 167, 166, 165 }, 1), /** * Spit Iron @@ -61,7 +61,7 @@ public enum SmithingType { /** * */ - TYPE_WIRE(1, 90, 91, new int[] { 96, 95, 94, 93 }, 1), + TYPE_WIRE(1, 82, 83, new int[] { 88, 87, 86, 85 }, 1), /** * Arrow Tips @@ -98,11 +98,6 @@ public enum SmithingType { */ TYPE_SQUARE_SHIELD(2, 154, 155, new int[] { 160, 159, 158, 157 }, 1), - /** - * Latern - */ - TYPE_LANTERN(1, 162, 163, new int[] { 168, 167, 166, 165 }, 1), - /** * Oil Lantern */ @@ -116,7 +111,7 @@ public enum SmithingType { /** * The studs type. */ - TYPE_STUDS(1, 170, 171, new int[] { 175, 176, 174, 173, 172 }, 1), + TYPE_STUDS(1, 98, 99, new int[] { 104, 103, 102, 101, 100 }, 1), /** * Warhammer diff --git a/Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java b/Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java index 06c55f1e1..5f7876db9 100644 --- a/Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java +++ b/Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java @@ -1,9 +1,11 @@ package content.global.skill.smithing.smelting; import static core.api.ContentAPIKt.*; -import core.api.EquipmentSlot; + +import core.api.Container; import core.game.event.ResourceProducedEvent; import core.game.container.impl.EquipmentContainer; +import core.tools.Log; import org.rs09.consts.Items; import core.game.world.map.Location; import core.game.node.entity.skill.SkillPulse; @@ -16,6 +18,7 @@ import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; import core.tools.StringUtils; import org.rs09.consts.Sounds; +import content.data.Quests; /** * Represents the pulse used to smelt. @@ -87,7 +90,7 @@ public class SmeltingPulse extends SkillPulse { if (bar == null || player == null) { return false; } - if (bar == Bar.BLURITE && !player.getQuestRepository().isComplete("The Knight's Sword")) { + if (bar == Bar.BLURITE && !player.getQuestRepository().isComplete(Quests.THE_KNIGHTS_SWORD)) { return false; } if (player.getSkills().getLevel(Skills.SMITHING) < bar.getLevel()) { @@ -174,16 +177,6 @@ public class SmeltingPulse extends SkillPulse { return amount < 1; } - /** - * Checks if the player has a ring of forging. - * - * @param player the player. - * @return {@code True} if so. - */ - public boolean hasForgingRing(Player player) { - return player.getEquipment().containsItem(RING_OF_FORGING); - } - /** * Checks if the forging is a succes. * @@ -192,19 +185,21 @@ public class SmeltingPulse extends SkillPulse { */ public boolean success(Player player) { if (bar == Bar.IRON && !superHeat) { - if (hasForgingRing(player)) { - Item ring = getItemFromEquipment(player, EquipmentSlot.RING); - if(ring != null){ - if(getCharge(ring) == 1000) setCharge(ring, 140); - adjustCharge(ring, -1); - if(getCharge(ring) == 0){ - player.getEquipment().remove(ring); + if (inEquipment(player, Items.RING_OF_FORGING_2568, 1)) { + int charges = getAttribute(player, "ringOfForgingCharges", 140) - 1; + if (charges <= 0) { + if (removeItem(player, Items.RING_OF_FORGING_2568, Container.EQUIPMENT)) { + charges = 140; sendMessage(player, "Your ring of forging uses up its last charge and disintegrates."); + } else { + log(this.getClass(), Log.ERR, "Failed to delete empty ring of forging for player " + player.getName()); + return false; //unfair but prevents exploit if the impossible happens } } + setAttribute(player, "/save:ringOfForgingCharges", charges); return true; } else { - return RandomFunction.getRandom(100) <= (player.getSkills().getLevel(Skills.SMITHING) >= 45 ? 80 : 50); + return RandomFunction.nextBool(); } } return true; diff --git a/Server/src/main/content/global/skill/summoning/SummoningPouch.java b/Server/src/main/content/global/skill/summoning/SummoningPouch.java index d55af8fa0..b91634318 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningPouch.java +++ b/Server/src/main/content/global/skill/summoning/SummoningPouch.java @@ -34,7 +34,7 @@ public enum SummoningPouch { /** * Represents a granite crab pouch. */ - GRANITE_CRAB_POUCH(4, 12009, 16, 31.6, 6796, 0.2, 2, false, new Item(12158), new Item(12155), new Item(440), new Item(12183, 7)), + GRANITE_CRAB_POUCH(4, 12009, 16, 21.6, 6796, 0.2, 2, false, new Item(12158), new Item(12155), new Item(440), new Item(12183, 7)), /** * Represents a spirit mosquito pouch. diff --git a/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt b/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt index 2a8debf26..c47862b62 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt +++ b/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt @@ -44,7 +44,7 @@ class SummoningTabListener : InterfaceListener { // Dismiss now if (player.getFamiliarManager().getFamiliar() is Pet) { val pet = player.familiarManager.familiar as Pet - player.familiarManager.removeDetails(pet.getItemIdHash()) + player.familiarManager.removeDetails(pet.getItemId()) } player.familiarManager.dismiss() } diff --git a/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java b/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java index 68d038c0d..6296daef4 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java +++ b/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java @@ -34,6 +34,7 @@ import core.plugin.Plugin; import core.plugin.ClassScanner; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Handles the summoning training room. @@ -81,7 +82,7 @@ public final class SummoningTrainingRoom extends OptionHandler { public boolean handle(final Player player, Node node, String option) { Scenery object = (Scenery) node; Location loc = null; - Quest quest = player.getQuestRepository().getQuest("Wolf Whistle"); + Quest quest = player.getQuestRepository().getQuest(Quests.WOLF_WHISTLE); int questVal = quest.getStage(player) == 0 ? 0 : quest.getStage(player) > 0 && quest.getStage(player) < 100 ? 5 : 28893; switch (option) { case "close": @@ -312,7 +313,7 @@ public final class SummoningTrainingRoom extends OptionHandler { @Override public boolean open(Object... args) { cutscene = (CutscenePlugin) args[0]; - quest = player.getQuestRepository().getQuest("Wolf Whistle"); + quest = player.getQuestRepository().getQuest(Quests.WOLF_WHISTLE); fluffy = NPC.create(6990, cutscene.getBase().transform(41, 52, 1)); fluffy.init(); fluffy.faceTemporary(player, 1); diff --git a/Server/src/main/content/global/skill/summoning/familiar/BabyChinchompaNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BabyChinchompaNPC.java deleted file mode 100644 index 66c03042b..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/BabyChinchompaNPC.java +++ /dev/null @@ -1,103 +0,0 @@ -package content.global.skill.summoning.familiar; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.Metamorphosis; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Handles the baby chinchompa pet. - * @author Empathy - * - */ -@Initializable -public class BabyChinchompaNPC extends Metamorphosis { - - /** - * The chinchompa ids. - */ - private static final int[] CHINCHOMPA_IDS = new int[] { 8643, 8644, 8657, 8658 }; - - /** - * Constructs a new {@code BabyChinchompaNPC} object. - */ - public BabyChinchompaNPC() { - super(CHINCHOMPA_IDS); - } - - @Override - public DialoguePlugin getDialoguePlugin() { - return new BabyChinchompaDialogue(); - } - - @Override - public int getRandomNpcId() { - int i = RandomFunction.getRandom(getIds().length - 1); - if (getIds()[i] == 8658) { - int x = RandomFunction.getRandom(30); - if (x == 1) { - return getIds()[i]; - } else { - return getIds()[i-1]; - } - } - return getIds()[i]; - } - - /** - * Handles the BabyChinchompa Dialogue. - * @author Empathy - * - */ - public final class BabyChinchompaDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BabyChinchompaDialogue} {@code Object}. - */ - public BabyChinchompaDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BabyChinchompaDialogue} {@code Object}. - * - * @param player the player. - */ - public BabyChinchompaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BabyChinchompaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, npc.getId() != 8658 ? "Squeak! Squeak!" : "Squeaka! Squeaka!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8643, 8644, 8657, 8658 }; - } - } -} diff --git a/Server/src/main/content/global/skill/summoning/familiar/CockatriceFamiliarNPC.java b/Server/src/main/content/global/skill/summoning/familiar/CockatriceFamiliarNPC.java index d1716b3d5..02564d7e1 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/CockatriceFamiliarNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/CockatriceFamiliarNPC.java @@ -34,6 +34,7 @@ public final class CockatriceFamiliarNPC implements Plugin { ClassScanner.definePlugin(new SpiritPengatrice()); ClassScanner.definePlugin(new SpiritCoraxatrice()); ClassScanner.definePlugin(new SpiritVulatrice()); + ClassScanner.definePlugin(new SpiritSaratrice()); return this; } @@ -58,7 +59,11 @@ public final class CockatriceFamiliarNPC implements Plugin { GameWorld.getPulser().submit(new Pulse(1, familiar.getOwner(), familiar, target) { @Override public boolean pulse() { - target.getSkills().updateLevel(skill, -3, 0); + if(skill == 5) { + target.skills.decrementPrayerPoints(3); + }else { + target.getSkills().updateLevel(skill, -3, 0); + } Projectile.magic(familiar, target, 1468, 40, 36, 71, 10).send(); familiar.sendFamiliarHit(target, 10, Graphics.create(1469)); return true; @@ -297,4 +302,43 @@ public final class CockatriceFamiliarNPC implements Plugin { } } + + /** + * Represents the Spirit Saratrice familiar. + * @author Aero - DeadlyGenga + */ + public class SpiritSaratrice extends Forager { + + /** + * Constructs a new {@code SpiritSaratriceNPC} {@code Object}. + */ + public SpiritSaratrice() { + this(null, 6879); + } + + /** + * Constructs a new {@code SpiritSaratriceNPC} {@code Object}. + * @param owner The owner. + * @param id The id. + */ + public SpiritSaratrice(Player owner, int id) { + super(owner, id, 3600, 12099, 3, WeaponInterface.STYLE_CAST, COCKATRICE_EGG); + } + + @Override + public Familiar construct(Player owner, int id) { + return new SpiritSaratrice(owner, id); + } + + @Override + protected boolean specialMove(FamiliarSpecial special) { + return petrifyingGaze(this, special, Skills.PRAYER); + } + + @Override + public int[] getIds() { + return new int[] { 6879, 6880 }; + } + + } } diff --git a/Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java b/Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java index ff053a5da..b369bf4d6 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java @@ -55,7 +55,7 @@ public final class DismissDialoguePlugin extends DialoguePlugin { if (player.getFamiliarManager().getFamiliar() instanceof Pet) { interpreter.sendDialogues(player, null, "Run along; I'm setting you free."); Pet pet = (Pet) player.getFamiliarManager().getFamiliar(); - player.getFamiliarManager().removeDetails(pet.getItemIdHash()); + player.getFamiliarManager().removeDetails(pet.getItemId()); } else { end(); } diff --git a/Server/src/main/content/global/skill/summoning/familiar/Familiar.java b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java index b74f15e6b..b85c1ec16 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/Familiar.java +++ b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java @@ -27,7 +27,6 @@ import core.tools.Log; import core.tools.RandomFunction; import core.game.node.entity.combat.CombatPulse; import core.game.node.entity.combat.CombatSwingHandler; -import core.tools.SystemLogger; import core.game.world.GameWorld; import content.global.skill.summoning.SummoningPouch; import org.rs09.consts.Sounds; @@ -120,7 +119,30 @@ public abstract class Familiar extends NPC implements Plugin { */ private final int attackStyle; - private boolean firstCall = true; + /** + * The amount of points to drain every tick. + * This is a constant depending on the familiar's level req and time remaining (GL #1903). + * https://runescape.wiki/w/Summoning_points?oldid=2171795: "Over the life of the familiar, the number of summoning + * points drained will be equal to the level required to summon the familiar (unless you run out of summoning + * points). This means that if a player summons a bunyip with 75 Summoning points remaining, 7 points will be + * drained immediately, and 61 more will be drained over the life the bunyip, for a total of 68 points (the level of + * the bunyip)." + */ + private final double pointsPerTick; + + /** + * Keeps track of the fractional pointsPerTick that have been drained already. If >1.0, drain a point and subtract + * 1.0. Note that this means that we will never drain a point on the final tick (unless pointsPerTick turned out to + * be integer, but this case is handled by the 'ticks > 0' check in handleTickActions()). This is intentional; it + * allows us to, correctly, artificially extend the interval by one so that the drain events are evenly spaced + * throughout the lifetime of the summon (refer to the dreadfowl example below). + */ + private double fracDrain = 0.0; + + /** + * Whether this is the first call (i.e. not a renew summon). + */ + private boolean firstCall = true; /** * Constructs a new {@code Familiar} {@code Object}. @@ -141,6 +163,23 @@ public abstract class Familiar extends NPC implements Plugin { this.specialCost = specialCost; this.combatFamiliar = NPCDefinition.forId(getOriginalId() + 1).getName().equals(getName()); this.attackStyle = attackStyle; + /* The initial points are drained on summon. Then, the remaining points are drained over an interval. + * To prevent the last point from being drained only very late, we artificially extend the interval by one. + * Example: a dreadfowl drains 1 point on summon, and then needs to drain 3 points over 400 ticks. Naively + * draining a point on ticks 133, 266, and 399 allows players to save a point at the expense of just one tick. + * Instead, we drain on ticks 100, 200, and 300. + * Example 2: a spirit tz-kih drains 3 points on summon, then needs to drain 19 more points over 1800 ticks. + * This means it needs to drain a point every 90 ticks, since the 0th tick remaining will not drain. + * Example 3: a vampire bat needs to drain 27 points over 3300 ticks. It hence drains a point every ~118 ticks. + * Example 4: an abyssal titan drains 10 points on summon, then needs to drain 83 more points over 3200 ticks. + * This means it needs to drain a point every ~34 ticks. + */ + if (pouchId == -1) { + this.pointsPerTick = 0.0; + } else { + int drain = pouch.getLevelRequired() - pouch.getSummonCost() + 1; + this.pointsPerTick = (double) drain / maximumTicks; + } } /** @@ -176,17 +215,22 @@ public abstract class Familiar extends NPC implements Plugin { transform(); } } - - @Override + + @Override public void init() { init(getSpawnLocation(), true); } @Override public void handleTickActions() { - if (ticks-- % 50 == 0) { - updateSpecialPoints(-15); + ticks--; + fracDrain += pointsPerTick; + if (fracDrain > 1.0 && ticks > 0) { + fracDrain -= 1.0; owner.getSkills().updateLevel(Skills.SUMMONING, -1, 0); + } + if (ticks % 50 == 0) { + updateSpecialPoints(-15); if (!getText().isEmpty()) { super.sendChat(getText()); } @@ -231,7 +275,7 @@ public abstract class Familiar extends NPC implements Plugin { @Override public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (entity == owner) { - if(message) { + if (message) { owner.getPacketDispatch().sendMessage("You can't just betray your own familiar like that!"); } return false; @@ -243,13 +287,13 @@ public abstract class Familiar extends NPC implements Plugin { } if (!getProperties().isMultiZone()) { if (entity instanceof Player && !((Player) entity).getProperties().isMultiZone()) { - if(message) { + if (message) { ((Player) entity).getPacketDispatch().sendMessage("You have to be in multicombat to attack a player's familiar."); } return false; } if (entity instanceof Player) { - if(message) { + if (message) { ((Player) entity).getPacketDispatch().sendMessage("This familiar is not in the a multicombat zone."); } } @@ -257,13 +301,13 @@ public abstract class Familiar extends NPC implements Plugin { } if (entity instanceof Player) { if (!((Player) entity).getSkullManager().isWilderness()) { - if(message) { + if (message) { ((Player) entity).getPacketDispatch().sendMessage("You have to be in the wilderness to attack a player's familiar."); } return false; } if (!owner.getSkullManager().isWilderness()) { - if(message) { + if (message) { ((Player) entity).getPacketDispatch().sendMessage("This familiar's owner is not in the wilderness."); } return false; @@ -340,8 +384,8 @@ public abstract class Familiar extends NPC implements Plugin { private void sendTimeRemaining() { int minutes = ticks / 100; int centiminutes = ticks % 100; - setVarbit(owner, 4534, minutes); - setVarbit(owner, 4290, centiminutes > 49 ? 1 : 0); + setVarbit(owner, 4534, minutes); + setVarbit(owner, 4290, centiminutes > 49 ? 1 : 0); } /** @@ -542,9 +586,9 @@ public abstract class Familiar extends NPC implements Plugin { * Sends the familiar packets. */ public void sendConfiguration() { - setVarp(owner, 448, getPouchId()); - setVarp(owner, 1174, getOriginalId()); - setVarp(owner, 1175, specialCost << 23); + setVarp(owner, 448, getPouchId()); + setVarp(owner, 1174, getOriginalId()); + setVarp(owner, 1175, specialCost << 23); sendTimeRemaining(); updateSpecialPoints(0); } @@ -565,13 +609,13 @@ public abstract class Familiar extends NPC implements Plugin { if (isInvisible()) return true; getProperties().setTeleportLocation(destination); if (!(this instanceof Pet)) { - if(firstCall) { + if (firstCall) { // TODO: Each familiar has its own initial summon sound that needs to be implemented at some point - playAudio(owner, Sounds.SUMMON_NPC_188); - firstCall = false; - } else { - playAudio(owner, Sounds.SUMMON_NPC_188); - } + playAudio(owner, Sounds.SUMMON_NPC_188); + firstCall = false; + } else { + playAudio(owner, Sounds.SUMMON_NPC_188); + } if (size() > 1) { graphics(LARGE_SUMMON_GRAPHIC); } else { @@ -607,10 +651,10 @@ public abstract class Familiar extends NPC implements Plugin { getPulseManager().clear(); owner.getInterfaceManager().removeTabs(7); owner.getFamiliarManager().setFamiliar(null); - setVarp(owner, 448, -1); - setVarp(owner, 1176, 0); - setVarp(owner, 1175, 182986); - setVarp(owner, 1174, -1); + setVarp(owner, 448, -1); + setVarp(owner, 1176, 0); + setVarp(owner, 1175, 182986); + setVarp(owner, 1174, -1); owner.getAppearance().sync(); owner.getInterfaceManager().setViewedTab(3); } @@ -624,14 +668,14 @@ public abstract class Familiar extends NPC implements Plugin { if (specialPoints > 60) { specialPoints = 60; } - setVarp(owner, 1177, specialPoints); + setVarp(owner, 1177, specialPoints); } @Override public Plugin newInstance(Object arg) throws Throwable { for (int id : getIds()) { if (FamiliarManager.getFamiliars().containsKey(id)) { - log(this.getClass(), Log.ERR, "Familiar " + id + " was already registered!"); + log(this.getClass(), Log.ERR, "Familiar " + id + " was already registered!"); return null; } FamiliarManager.getFamiliars().put(id, this); diff --git a/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java index f6a4b8f90..3e8e3b7e8 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java @@ -4,7 +4,6 @@ import content.global.skill.summoning.pet.Pet; import content.global.skill.summoning.pet.Pets; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; -import core.game.container.Container; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import core.game.node.entity.skill.Skills; @@ -17,10 +16,10 @@ import core.game.node.item.Item; import core.game.world.map.Location; import core.game.world.map.zone.ZoneRestriction; import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Items; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import static core.api.ContentAPIKt.*; @@ -38,9 +37,9 @@ public final class FamiliarManager { private static final Map FAMILIARS = new HashMap<>(); /** - * The pet details mapping, sorted by item id. + * The pet details mapping. */ - private final Map petDetails = new HashMap(); + private final Map> petDetails = new HashMap<>(); /** * The player. @@ -70,50 +69,74 @@ public final class FamiliarManager { this.player = player; } - public final void parse(JSONObject familiarData) { + public void parse(JSONObject familiarData) { int currentPet = -1; if (familiarData.containsKey("currentPet")) { currentPet = Integer.parseInt(familiarData.get("currentPet").toString()); } - JSONArray petDetails = (JSONArray) familiarData.get("petDetails"); - for (int i = 0; i < petDetails.size(); i++) { - JSONObject detail = (JSONObject) petDetails.get(i); - PetDetails details = new PetDetails(0); - details.updateHunger(Double.parseDouble(detail.get("hunger").toString())); - details.updateGrowth(Double.parseDouble(detail.get("growth").toString())); - int itemIdHash = Integer.parseInt(detail.get("petId").toString()); - // The below is for migrating legacy saves, which stored baby item IDs + growth stages - if (detail.containsKey("stage")) { - // The "itemIdHash" is actually the baby item ID. The "stage" gives the actual pet stage we want. - int babyItemId = itemIdHash; - int itemId = babyItemId; - int stage = Integer.parseInt(detail.get("stage").toString()); - if (stage > 0) { - Pets pets = Pets.forId(babyItemId); - itemId = pets.getNextStageItemId(itemId); - if (stage > 1) { - itemId = pets.getNextStageItemId(itemId); + if (player.version < 2) { //migrate the v1 format + for (Pets pet : Pets.values()) { + for (int id : new int[]{pet.getBabyItemId(), pet.getGrownItemId(), pet.getOvergrownItemId()}) { + if (id != -1) { + petDetails.put(id, new ArrayList()); } } - Item item = new Item(itemId); - item.setCharge(1000); //this is the default value that will correspond to the player's item - itemIdHash = item.getIdHash(); - if (currentPet != -1 && currentPet == babyItemId) { - currentPet = itemIdHash; + } + + JSONArray petDetails = (JSONArray) familiarData.get("petDetails"); + for (Object petDetail : petDetails) { + JSONObject detail = (JSONObject) petDetail; + PetDetails details = new PetDetails(0); + details.updateHunger(Double.parseDouble(detail.get("hunger").toString())); + details.updateGrowth(Double.parseDouble(detail.get("growth").toString())); + int itemId; + int itemIdHash = Integer.parseInt(detail.get("petId").toString()); + // The below is for migrating the v0 format, which stored baby item IDs + growth stages + if (detail.containsKey("stage")) { + // The itemIdHash is actually the baby item ID. The "stage" gives the actual pet stage we want. + int babyItemId = itemIdHash; + itemId = babyItemId; + int stage = Integer.parseInt(detail.get("stage").toString()); + if (stage > 0) { + Pets pets = Pets.forId(babyItemId); + itemId = pets.getNextStageItemId(itemId); + if (stage > 1) { + itemId = pets.getNextStageItemId(itemId); + } + } + } else { + itemId = itemIdHash >> 16 & 0xFFFF; //in the legacy v1 format, was hash rather than item id + } + this.petDetails.get(itemId).add(details); + } + if (currentPet > 65536) { + currentPet = currentPet >> 16 & 0xFFFF; //in the legacy v1 format, was hash rather than item id + } + } else { + JSONObject petDetails = (JSONObject) familiarData.get("petDetails"); + for (Object key : petDetails.keySet()) { + int itemId = Integer.parseInt(key.toString()); + this.petDetails.put(itemId, new ArrayList<>()); + JSONArray values = (JSONArray) petDetails.get(key.toString()); + for (Object petDetail : values) { + JSONObject detail = (JSONObject) petDetail; + PetDetails details = new PetDetails(0); + details.updateHunger(Double.parseDouble(detail.get("hunger").toString())); + details.updateGrowth(Double.parseDouble(detail.get("growth").toString())); + this.petDetails.get(itemId).add(details); } } - this.petDetails.put(itemIdHash, details); } - if (currentPet != -1) { - PetDetails details = this.petDetails.get(currentPet); - int itemId = currentPet >> 16 & 0xFFFF; - Pets pets = Pets.forId(itemId); - if (details == null) { - details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); - this.petDetails.put(currentPet, details); + int last = this.petDetails.get(currentPet).size() - 1; + PetDetails details; + if (last < 0) { //missing data in save due to historical bug (see GL !2077) + details = new PetDetails(0); + } else { + details = this.petDetails.get(currentPet).get(last); } - familiar = new Pet(player, details, itemId, pets.getNpcId(itemId)); + Pets pets = Pets.forId(currentPet); + familiar = new Pet(player, details, currentPet, pets.getNpcId(currentPet)); } else if (familiarData.containsKey("familiar")) { JSONObject currentFamiliar = (JSONObject) familiarData.get("familiar"); int familiarId = Integer.parseInt( currentFamiliar.get("originalId").toString()); @@ -147,7 +170,7 @@ public final class FamiliarManager { public void summon(Item item, boolean pet, boolean deleteItem) { boolean renew = false; if (hasFamiliar()) { - if(familiar.getPouchId() == item.getId()) { + if (familiar.getPouchId() == item.getId()) { renew = true; } else { player.getPacketDispatch().sendMessage("You already have a follower."); @@ -180,7 +203,7 @@ public final class FamiliarManager { player.getPacketDispatch().sendMessage("Invalid familiar " + npcId + " - report on 2009Scape GitLab"); return; } - if(!renew) { + if (!renew) { fam = fam.construct(player, npcId); if (fam.getSpawnLocation() == null) { player.getPacketDispatch().sendMessage("The spirit in this pouch is too big to summon here. You will need to move to a larger"); @@ -193,7 +216,7 @@ public final class FamiliarManager { } player.getSkills().updateLevel(Skills.SUMMONING, -pouch.getSummonCost(), 0); player.getSkills().addExperience(Skills.SUMMONING, pouch.getSummonExperience()); - if(!renew) { + if (!renew) { familiar = fam; spawnFamiliar(); } else { @@ -217,11 +240,10 @@ public final class FamiliarManager { * @param deleteItem the item. * @param location the location. */ - public void morphPet(final Item item, boolean deleteItem, Location location) { - if (hasFamiliar()) { - familiar.dismiss(); - } - summonPet(item, deleteItem, true, location); + public void morphPet(final Item item, boolean deleteItem, Location location, double hunger, double growth) { + int hasWarned = ((Pet) familiar).getHasWarned(); + familiar.dismiss(); + summonPet(item, deleteItem, true, location, hasWarned, hunger, growth); } /** @@ -230,7 +252,7 @@ public final class FamiliarManager { * @param deleteItem the item. */ private boolean summonPet(final Item item, boolean deleteItem) { - return summonPet(item, deleteItem, false, null); + return summonPet(item, deleteItem, false, null, 0, -1, -1); } /** @@ -238,9 +260,8 @@ public final class FamiliarManager { * @param item the item. * @param morph the pet. */ - private boolean summonPet(final Item item, boolean deleteItem, boolean morph, Location location) { + private boolean summonPet(final Item item, boolean deleteItem, boolean morph, Location location, int hasWarned, double hunger, double growth) { final int itemId = item.getId(); - int itemIdHash = item.getIdHash(); if (itemId > 8850 && itemId < 8900) { return false; } @@ -252,52 +273,27 @@ public final class FamiliarManager { player.getDialogueInterpreter().sendDialogue("You need a summoning level of " + pets.getSummoningLevel() + " to summon this."); return false; } - - // If this pet does not have an individual ID yet, we need to find it an available one. - // If it does, we need to verify that this ID is not already used for a different pet. This is needed to correct a historical bug that allowed multiple pets to be assigned the same individual ID (the historical code only checked the *current* stage item ID, failing to realize that we also need to account for *future* stage item IDs, in case the current pet grows up, resulting in a clash when it did). Saves affected by that bug will have multiple copies of the same item pointing to the same pet, which we have an opportunity to rectify now. - ArrayList taken = new ArrayList(); - Container[] searchSpace = {player.getInventory(), player.getBankPrimary(), player.getBankSecondary()}; - for (int checkId = pets.getBabyItemId(); checkId != -1; checkId = pets.getNextStageItemId(checkId)) { - Item check = new Item(checkId, 1); - for (Container container : searchSpace) { - for (Item i : container.getAll(check)) { - taken.add(i.getCharge()); - } - } + if (!this.petDetails.containsKey(itemId)) { + petDetails.put(itemId, new ArrayList()); } - PetDetails details = petDetails.get(itemIdHash); - int individual = item.getCharge(); - if (details != null) { //we have this pet on file, but we need to check that it wasn't affected by the historical bug mentioned above - details.setIndividual(individual); - int count = 0; - for (int i : taken) { - if (i == individual) { - count++; - } - } - if (count > 1) { //this pet is sadly conjoined with another individual of its kind; untangle it by initializing it anew (which is what should have happened in the first place, save the minor detail of hunger propagation from the previous stage, which we no longer have any record of) - details = null; - } - } - if (details == null) { //init new pet - details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); - for (individual = 0; taken.contains(individual) && individual < 0xFFFF; individual++) {} - details.setIndividual(individual); - // Make a copy of the item to extract what the item's idHash will be when including the individual ID as a "charge" value. - // The copy is necessary since the player's inventory still contains the default-charged item, which we will be removing only later. - Item newItem = item.copy(); - newItem.setCharge(individual); - petDetails.put(newItem.getIdHash(), details); + int last = this.petDetails.get(itemId).size() - 1; + if (last < 0) { //new pet + last = 0; + PetDetails details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); + this.petDetails.get(itemId).add(details); } + PetDetails details = this.petDetails.get(itemId).get(last); int npcId = pets.getNpcId(itemId); if (npcId > 0) { familiar = new Pet(player, details, itemId, npcId); + ((Pet) familiar).setHasWarned(hasWarned); + if (hunger != -1) ((Pet) familiar).getDetails().setHunger(hunger); + if (growth != -1) ((Pet) familiar).getDetails().setGrowth(growth); if (deleteItem) { player.animate(new Animation(827)); - // We cannot use player().getInventory().remove(item), because that will remove the first pet item it sees, rather than the specific one (with the specific charge value) the player clicked. - // Instead, find the specific item the player dropped by slot, and remove that specific one. - int slot = player.getInventory().getSlotHash(item); - player.getInventory().remove(item, slot, true); + if (!player.getInventory().remove(item, true)) { + return false; + } } if (morph) { morphFamiliar(location); @@ -346,6 +342,9 @@ public final class FamiliarManager { for (int food : pets.getFood()) { if (food == foodId) { player.getInventory().remove(new Item(foodId)); + if (foodId == Items.BUCKET_OF_MILK_1927) { + player.getInventory().add(new Item(Items.BUCKET_1925)); + } player.getPacketDispatch().sendMessage("Your pet happily eats the " + ItemDefinition.forId(food).getName() + "."); player.animate(new Animation(827)); npc.getDetails().updateHunger(-15.0); @@ -364,11 +363,7 @@ public final class FamiliarManager { return; } Pet pet = ((Pet) familiar); - PetDetails details = pet.getDetails(); - Item petItem = new Item(pet.getItemId()); - petItem.setCharge(details.getIndividual()); - if (player.getInventory().add(petItem)) { - petDetails.put(pet.getItemIdHash(),details); + if (player.getInventory().add(new Item(pet.getItemId()))) { player.animate(Animation.create(827)); player.getFamiliarManager().dismiss(); } @@ -423,11 +418,26 @@ public final class FamiliarManager { } /** - * Removes the details for this pet. - * @param itemIdHash The item id hash of the pet. + * Adds pet details for a new pet to that pet's stack. + * @param itemId The item id of the pet. + * @param details The new pet details. */ - public void removeDetails(int itemIdHash) { - petDetails.remove(itemIdHash); + public void addDetails(int itemId, PetDetails details) { + if (petDetails.get(itemId) == null) { + petDetails.put(itemId, new ArrayList<>()); + } + petDetails.get(itemId).add(details); + } + + /** + * Removes the details for this pet. + * @param itemId The item id of the pet. + */ + public void removeDetails(int itemId) { + int last = petDetails.get(itemId).size() - 1; + if (last >= 0) { + petDetails.get(itemId).remove(last); + } } /** @@ -521,7 +531,7 @@ public final class FamiliarManager { } - public Map getPetDetails() { + public Map> getPetDetails() { return petDetails; } } diff --git a/Server/src/main/content/global/skill/summoning/familiar/GraniteLobsterNPC.java b/Server/src/main/content/global/skill/summoning/familiar/GraniteLobsterNPC.java index b66b19993..d2db30c53 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/GraniteLobsterNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/GraniteLobsterNPC.java @@ -22,7 +22,7 @@ public class GraniteLobsterNPC extends Forager { /** * The fish. */ - private static final Item[] FISH = new Item[] { new Item(383), new Item(377) }; + private static final Item[] FISH = new Item[] { new Item(383), new Item(371) }; /** * Constructs a new {@code GraniteLobsterNPC} {@code Object}. @@ -61,7 +61,7 @@ public class GraniteLobsterNPC extends Forager { @Override public void handlePassiveAction() { - if (RandomFunction.random(4) == 1) { + if (RandomFunction.random(40) == 1) { final Item item = FISH[RandomFunction.random(FISH.length)]; animate(Animation.create(8107)); Fish fish = Fish.forItem(item); diff --git a/Server/src/main/content/global/skill/summoning/familiar/HydraNPC.java b/Server/src/main/content/global/skill/summoning/familiar/HydraNPC.java index 1cc985a30..04ba4c6bc 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/HydraNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/HydraNPC.java @@ -43,7 +43,7 @@ public class HydraNPC extends Familiar { Scenery scenery = (Scenery)node; FarmingPatch farmingPatch = FarmingPatch.forObject(scenery); if(farmingPatch != null) { - Patch patch = farmingPatch.getPatchFor(owner); + Patch patch = farmingPatch.getPatchFor(owner, true); patch.regrowIfTreeStump(); return true; } diff --git a/Server/src/main/content/global/skill/summoning/familiar/KalphiteNPC.java b/Server/src/main/content/global/skill/summoning/familiar/KalphiteNPC.java deleted file mode 100644 index 62b5020a0..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/KalphiteNPC.java +++ /dev/null @@ -1,117 +0,0 @@ -package content.global.skill.summoning.familiar; - -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.Metamorphosis; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; - -/** - * Handles the kalphite princess pet. - * @author Empathy - * - */ -@Initializable -public class KalphiteNPC extends Metamorphosis { - - /** - * The kalphite ids. - */ - private static final int[] KALPHITE_IDS = new int[] { 8602, 8603 }; - - /** - * Constructs a new {@code KalphiteNPC} object. - */ - public KalphiteNPC() { - super(KALPHITE_IDS); - } - - - @Override - public DialoguePlugin getDialoguePlugin() { - return new KalphitePrincessDialogue(); - } - - /** - * Handles the KalphitePrincess dialogue. - * @author Empathy - * - */ - public final class KalphitePrincessDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code KalphitePrincessDialogue} {@code Object}. - */ - public KalphitePrincessDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KalphitePrincessDialogue} {@code Object}. - * - * @param player the player. - */ - public KalphitePrincessDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KalphitePrincessDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What is it with your kind and potato cactus?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Truthfully?"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yeah, please."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Soup. We make a fine soup with it."); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Kalphites can cook?"); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Nah, we just collect it and put it there because we", "know fools like yourself will come down looking for it", "then inevitably be killed by my mother."); - stage = 5; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Evidently not, that's how I got you!"); - stage = 6; - break; - case 6: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Touch�."); - stage = 7; - break; - case 7: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8602, 8603 }; - } - } -} diff --git a/Server/src/main/content/global/skill/summoning/familiar/SnakelingNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SnakelingNPC.java deleted file mode 100644 index cd6372f62..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/SnakelingNPC.java +++ /dev/null @@ -1,114 +0,0 @@ -package content.global.skill.summoning.familiar; - -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.Metamorphosis; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; - -/** - * Handles the metamorphosis of the zulrah pet. - * @author Empathy - * - */ -@Initializable -public class SnakelingNPC extends Metamorphosis { - - /** - * The snakeling ids. - */ - public static final int[] SNAKELING_IDS = new int[] { 8626, 8627, 8628 }; - - /** - * - * Constructs a new {@code SnakelingNPC} object. - */ - public SnakelingNPC() { - super(SNAKELING_IDS); - } - - @Override - public DialoguePlugin getDialoguePlugin() { - return new PetSnakelingDialogue(); - } - - /** - * Handles the pet snakeling dialogue. - * @author Empathy - * - */ - public final class PetSnakelingDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code PetSnakelingDialogue} {@code Object}. - */ - public PetSnakelingDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code PetSnakelingDialogue} {@code Object}. - * - * @param player the player. - */ - public PetSnakelingDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PetSnakelingDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - player("Hey little snake!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc(FacialExpression.OLD_NORMAL, "Soon, Zulrah shall establish dominion over this plane."); - stage = 1; - break; - case 1: - player("Wanna play fetch?"); - stage = 2; - break; - case 2: - npc(FacialExpression.OLD_NORMAL, "Submit to the almighty Zulrah."); - stage = 3; - break; - case 3: - player("Walkies? Or slidies...?"); - stage = 4; - break; - case 4: - npc(FacialExpression.OLD_NORMAL, "Zulrah's wilderness as a God will soon be demonstrated."); - stage = 5; - break; - case 5: - player("I give up..."); - stage = 6; - break; - case 6: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return SNAKELING_IDS; - } - } - -} diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java index 83f1d21ae..18d4946b2 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java @@ -56,7 +56,7 @@ public class SpiritCobraNPC extends Familiar { * @author Vexia */ public enum Egg { - COCKATRICE(new Item(1944), new Item(12109)), SARATRICE(new Item(10533), new Item(12113)), ZAMATRICE(new Item(10532), new Item(12115)), GUTHATRICE(new Item(10531), new Item(12111)), CORACATRICE(new Item(11964), new Item(12119)), PENGATRICE(new Item(12483), new Item(12117)), VULATRICE(new Item(11695), new Item(12121)); + COCKATRICE(new Item(1944), new Item(12109)), SARATRICE(new Item(5077), new Item(12113)), ZAMATRICE(new Item(5076), new Item(12115)), GUTHATRICE(new Item(5078), new Item(12111)), CORACATRICE(new Item(11964), new Item(12119)), PENGATRICE(new Item(12483), new Item(12117)), VULATRICE(new Item(11965), new Item(12121)); /** * The egg item. diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritKalphiteNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritKalphiteNPC.java index 3768df594..09a83505c 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritKalphiteNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritKalphiteNPC.java @@ -44,30 +44,31 @@ public class SpiritKalphiteNPC extends BurdenBeast { @Override protected boolean specialMove(FamiliarSpecial special) { - if (!isOwnerAttackable()) { - return false; - } - final List entitys = RegionManager.getLocalEntitys(owner, 6); - visualize(Animation.create(8517), Graphics.create(1350)); - GameWorld.getPulser().submit(new Pulse(1, owner) { - @Override - public boolean pulse() { - int count = 0; - for (Entity entity : entitys) { - if (count > 5) { - return true; - } - if (!canCombatSpecial(entity)) { - continue; - } - Projectile.magic(SpiritKalphiteNPC.this, entity, 1349, 40, 36, 50, 5).send(); - sendFamiliarHit(entity, 20); - count++; - } - return true; - } - }); - return false; + return false; ///bodge this for now, until someone fixes this abomination. +// if (!isOwnerAttackable()) { +// return false; +// } +// final List entitys = RegionManager.getLocalEntitys(owner, 6); +// visualize(Animation.create(8517), Graphics.create(1350)); +// GameWorld.getPulser().submit(new Pulse(1, owner) { +// @Override +// public boolean pulse() { +// int count = 0; +// for (Entity entity : entitys) { +// if (count > 5) { +// return true; +// } +// if (!canCombatSpecial(entity)) { +// continue; +// } +// Projectile.magic(SpiritKalphiteNPC.this, entity, 1349, 40, 36, 50, 5).send(); +// sendFamiliarHit(entity, 20); +// count++; +// } +// return true; +// } +// }); +// return false; } @Override diff --git a/Server/src/main/content/global/skill/summoning/familiar/SummonFamiliarPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/SummonFamiliarPlugin.java index 8f6a407d8..ee216033e 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SummonFamiliarPlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SummonFamiliarPlugin.java @@ -9,6 +9,7 @@ import core.game.node.item.Item; import core.game.world.map.zone.ZoneBorders; import core.plugin.Initializable; import core.plugin.Plugin; +import content.data.Quests; /** * Handles summoning a familiar. @@ -26,7 +27,7 @@ public final class SummonFamiliarPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { Item item = (Item) node; - if (!player.getQuestRepository().isComplete("Wolf Whistle") && player.getAttribute("in-cutscene", null) == null) { + if (!player.getQuestRepository().isComplete(Quests.WOLF_WHISTLE) && player.getAttribute("in-cutscene", null) == null) { player.getPacketDispatch().sendMessage("You have to complete Wolf Whistle before you can summon a familiar."); return true; } diff --git a/Server/src/main/content/global/skill/summoning/familiar/VetionNPC.java b/Server/src/main/content/global/skill/summoning/familiar/VetionNPC.java deleted file mode 100644 index f939260f2..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/VetionNPC.java +++ /dev/null @@ -1,33 +0,0 @@ -package content.global.skill.summoning.familiar; - -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.npc.Metamorphosis; - -/** - * Handles the metamorphosis of Vet'ion Jr. - * @author Empathy - * - */ -@Initializable -public class VetionNPC extends Metamorphosis { - - /** - * The Vet'ion Ids. - */ - public static final int[] VETION_IDS = new int[] { 8600, 8654 }; - - /** - * - * Constructs a new{@code VetionNPC} object. - */ - public VetionNPC() { - super(VETION_IDS); - } - - @Override - public DialoguePlugin getDialoguePlugin() { - return null; - } - -} diff --git a/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt b/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt index 79ef7c12d..4b9969574 100644 --- a/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt +++ b/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt @@ -1,11 +1,8 @@ package content.global.skill.summoning.pet import core.api.* -import core.cache.def.impl.SceneryDefinition import core.game.node.Node import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import core.game.node.item.GroundItemManager import core.game.interaction.* import core.tools.StringUtils @@ -69,8 +66,9 @@ class IncubatorHandler : InteractionListener { val product = egg.product val name = product.name.lowercase() - sendMessage(player, "You take your $name out of the incubator.") - addItem(player, product.id) + if (addItem(player, product.id)) { + sendMessage(player, "You take your $name out of the incubator.") + } return true } } diff --git a/Server/src/main/content/global/skill/summoning/pet/IncubatorTimer.kt b/Server/src/main/content/global/skill/summoning/pet/IncubatorTimer.kt index 6395a79eb..899deee2a 100644 --- a/Server/src/main/content/global/skill/summoning/pet/IncubatorTimer.kt +++ b/Server/src/main/content/global/skill/summoning/pet/IncubatorTimer.kt @@ -42,6 +42,13 @@ class IncubatorTimer : PersistTimer (500, "incubation") { root["eggs"] = arr } + override fun onRegister(entity: Entity) { + if (entity !is Player) return + for ((region, _) in incubatingEggs) { + setVarbit(entity.asPlayer(), varbitForRegion(region), 1, true) + } + } + override fun run (entity: Entity) : Boolean { if (entity !is Player) return false for ((_, egg) in incubatingEggs) { @@ -60,14 +67,14 @@ class IncubatorTimer : PersistTimer (500, "incubation") { } companion object { - val TAVERLY_REGION = 11573 - val TAVERLY_VARBIT = 4277 + val TAVERLEY_REGION = 11573 + val TAVERLEY_VARBIT = 4277 val YANILLE_REGION = 10288 val YANILLE_VARBIT = 4221 fun varbitForRegion (region: Int) : Int { return when (region) { - TAVERLY_REGION -> TAVERLY_VARBIT + TAVERLEY_REGION -> TAVERLEY_VARBIT YANILLE_REGION -> YANILLE_VARBIT else -> -1 } diff --git a/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java b/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java index ebeb5b477..24a387d18 100644 --- a/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java +++ b/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java @@ -67,7 +67,7 @@ public final class KittenInteractDialogue extends DialoguePlugin { interpreter.sendDialogues(player, null, "That cat sure loves to be stroked."); stage = 99; break; - case 2:// chase-vermine + case 2:// chase-vermin end(); player.sendChat("Go on puss...kill that rat!"); boolean cant = true; @@ -117,8 +117,8 @@ public final class KittenInteractDialogue extends DialoguePlugin { player.sendChat("Shoo cat!"); Pet currentPet = (Pet) player.getFamiliarManager().getFamiliar(); player.getFamiliarManager().getFamiliar().sendChat("Miaow!"); - player.getFamiliarManager().removeDetails(currentPet.getItemIdHash()); player.getFamiliarManager().getFamiliar().dismiss(); + player.getFamiliarManager().removeDetails(currentPet.getItemId()); player.getPacketDispatch().sendMessage("The cat has run away."); } end(); diff --git a/Server/src/main/content/global/skill/summoning/pet/Pet.java b/Server/src/main/content/global/skill/summoning/pet/Pet.java index e4812dac1..a115040d1 100644 --- a/Server/src/main/content/global/skill/summoning/pet/Pet.java +++ b/Server/src/main/content/global/skill/summoning/pet/Pet.java @@ -5,7 +5,6 @@ import content.global.skill.summoning.familiar.Familiar; import content.global.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.player.Player; import core.game.node.item.Item; -import core.game.world.GameWorld; import static core.api.ContentAPIKt.*; @@ -30,7 +29,7 @@ public final class Pet extends Familiar { /** * The growth rate of the pet. */ - private double growthRate; + private final double growthRate; /** * The pets type. @@ -67,10 +66,13 @@ public final class Pet extends Familiar { @Override public void handleTickActions() { final PetDetails petDetails = details; - if (getPet().getFood().length > 0) { + if (getPet().getFood().length > 0 && !pet.isGrownCat(itemId)) { if(!SkillcapePerks.isActive(SkillcapePerks.PET_MASTERY, owner)) { double amount = itemId == pet.getBabyItemId() ? 0.025 : 0.018; - if (GameWorld.getSettings().isDevMode()) { + if (owner.getAttribute("petrate",1) == 0) { + amount = 0 ; + } + else if (owner.getAttribute("petrate",1) == 2) { amount *= 100; } petDetails.updateHunger(amount); @@ -86,8 +88,8 @@ public final class Pet extends Familiar { hasWarned = 2; } if (hunger >= 100.0 && growthRate != 0 && pet.getFood().length != 0) { - owner.getFamiliarManager().removeDetails(this.getItemIdHash()); owner.getFamiliarManager().dismiss(); + owner.getFamiliarManager().removeDetails(getItemId()); owner.getFamiliarManager().setFamiliar(null); setVarp(owner, 1175, 0); owner.sendMessage("Your pet has run away."); @@ -96,7 +98,10 @@ public final class Pet extends Familiar { double growth = petDetails.getGrowth(); double growthrate = pet.getGrowthRate(); if (growthrate > 0.000) { - if (GameWorld.getSettings().isDevMode()) { + if (owner.getAttribute("petrate",1) == 0) { + growthrate = 0; + } + else if (owner.getAttribute("petrate",1) == 2) { growthrate *= 100; } petDetails.updateGrowth(growthrate); @@ -126,16 +131,13 @@ public final class Pet extends Familiar { // then this pet is already overgrown return; } - owner.getFamiliarManager().removeDetails(this.getItemIdHash()); - owner.getFamiliarManager().dismiss(); + if (pet.isKitten(itemId)) { + owner.incrementAttribute("/save:stats_manager:cats_raised"); + } + owner.getFamiliarManager().addDetails(newItemId, details); + owner.getFamiliarManager().removeDetails(getItemId()); + owner.getFamiliarManager().morphPet(new Item(newItemId), false, location, details.getHunger(), 0); owner.getPacketDispatch().sendMessage("Your pet has grown larger."); - int npcId = pet.getNpcId(newItemId); - details.updateGrowth(-100.0); - Pet newPet = new Pet(owner, details, newItemId, npcId); - newPet.growthRate = growthRate; - newPet.hasWarned = hasWarned; - owner.getFamiliarManager().setFamiliar(newPet); - owner.getFamiliarManager().spawnFamiliar(); } @Override @@ -161,16 +163,6 @@ public final class Pet extends Familiar { return itemId; } - /** - * Gets the itemId with the individual hashed in. - * @return The itemIdHash. - */ - public int getItemIdHash() { - Item item = new Item(itemId); - item.setCharge(details.getIndividual()); - return item.getIdHash(); - } - /** * Gets the details. * @return The details. @@ -187,9 +179,36 @@ public final class Pet extends Familiar { return pet; } + /** + * Gets the hunger level. + */ + public double getHunger() { + return details.getHunger(); + } + + /** + * Gets the growth level. + */ + public double getGrowth() { + return details.getGrowth(); + } + + /** + * Gets the hunger warning level. + */ + public int getHasWarned() { + return hasWarned; + } + + /** + * Sets the hunger warning level. + */ + public void setHasWarned(int value) { + this.hasWarned = value; + } + @Override public int[] getIds() { return new int[] { 761, 762, 763, 764, 765, 766, 3505, 3598, 6969, 7259, 7260, 6964, 7249, 7251, 6960, 7241, 7243, 6962, 7245, 7247, 6966, 7253, 7255, 6958, 7237, 7239, 6915, 7277, 7278, 7279, 7280, 7018, 7019, 7020, 6908, 7313, 7316, 6947, 7293, 7295, 7297, 7299, 6911, 7261, 7263, 7265, 7267, 7269, 6919, 7301, 7303, 7305, 7307, 6949, 6952, 6955, 6913, 7271, 7273, 6945, 7319, 7321, 7323, 7325, 7327, 6922, 6942, 7210, 7212, 7214, 7216, 7218, 7220, 7222, 7224, 7226, 6900, 6902, 6904, 6906, 768, 769, 770, 771, 772, 773, 3504, 6968, 7257, 7258, 6965, 7250, 7252, 6961, 7242, 7244, 6963, 7246, 7248, 6967, 7254, 7256, 6859, 7238, 7240, 6916, 7281, 7282, 7283, 7284, 7015, 7016, 7017, 6909, 7314, 7317, 6948, 7294, 7296, 7298, 7300, 6912, 7262, 7264, 7266, 7268, 7270, 6920, 7302, 7304, 7306, 7308, 6950, 6953, 6956, 6914, 7272, 7274, 6946, 7320, 7322, 7324, 7326, 7328, 6923, 6943, 7211, 7213, 7215, 7217, 7219, 7221, 7223, 7225, 7227, 6901, 6903, 6905, 6907, 774, 775, 776, 777, 778, 779, 3503, 6951, 6954, 6957 }; } - } diff --git a/Server/src/main/content/global/skill/summoning/pet/PetDetails.java b/Server/src/main/content/global/skill/summoning/pet/PetDetails.java index b3e01c869..b3b0b8f05 100644 --- a/Server/src/main/content/global/skill/summoning/pet/PetDetails.java +++ b/Server/src/main/content/global/skill/summoning/pet/PetDetails.java @@ -19,11 +19,6 @@ public final class PetDetails { */ private double growth = 0.0; - /** - * The individual, an in principle arbitrary integer read off of the item's charge slot. - */ - private int individual; - /** * Constructs a new {@code PetDetails} {@code Object}. * @param growth The growth value. @@ -64,6 +59,13 @@ public final class PetDetails { return hunger; } + /** + * Sets the hunger. (You probably want to use updateHunger() instead.) + */ + public void setHunger(double value) { + this.hunger = value; + } + /** * Gets the growth. * @return The growth. @@ -73,18 +75,9 @@ public final class PetDetails { } /** - * Sets the individual. - * @param individual The individual to set. + * Sets the growth. (You probably want to use updateGrowth() instead.) */ - public void setIndividual(int individual) { - this.individual = individual; - } - - /** - * Gets the individual. - * @return The individual. - */ - public int getIndividual() { - return individual; + public void setGrowth(double value) { + this.growth = value; } } diff --git a/Server/src/main/content/global/skill/summoning/pet/Pets.java b/Server/src/main/content/global/skill/summoning/pet/Pets.java index 843075d28..3258099fc 100644 --- a/Server/src/main/content/global/skill/summoning/pet/Pets.java +++ b/Server/src/main/content/global/skill/summoning/pet/Pets.java @@ -18,23 +18,13 @@ public enum Pets { /** * A cat/kitten pet. */ - CAT(1555, 1561, 1567, 761, 768, 774, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_1(1556, 1562, 1568, 762, 769, 775, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_2(1557, 1563, 1569, 763, 770, 776, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_3(1558, 1564, 1570, 764, 771, 777, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_4(1559, 1565, 1571, 765, 772, 778, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_5(1560, 1566, 1572, 766, 773, 779, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), HELLCAT(7583, 7582, 7581, 3505, 3504, 3503, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_7(14089, 14090, 15092, 8217, 8214, 8216, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), + CAT(1555, 1561, 1567, 761, 768, 774, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_1(1556, 1562, 1568, 762, 769, 775, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_2(1557, 1563, 1569, 763, 770, 776, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_3(1558, 1564, 1570, 764, 771, 777, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_4(1559, 1565, 1571, 765, 772, 778, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_5(1560, 1566, 1572, 766, 773, 779, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), HELLCAT(7583, 7582, 7581, 3505, 3504, 3503, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), CAT_6(14089, 14090, 15092, 8217, 8214, 8216, 0.0154320987654321, 0, 321, 319, 363, 365, 341, 339, 15264, 345, 347, 377, 379, 353, 355, 389, 391, 7944, 7946, 349, 351, 331, 329, 327, 325, 395, 397, 383, 385, 317, 315, 371, 373, 335, 333, 359, 361, 15264, 15270, 1927), /** * A clockwork cat. */ CLOCKWORK_CAT(7771, 7772, -1, 3598, -1, -1, 0.0, 0), - /** - * The firemaker's curse pets. - */ - SEARING_FLAME(22994, -1, -1, 14769, -1, -1, 0.0, 0), GLOWING_EMBER(22993, -1, -1, 14768, -1, -1, 0.0, 0), TWISTED_FIRESTARTER(22995, -1, -1, 14770, -1, -1, 0.0, 0), WARMING_FLAME(22992, -1, -1, 14767, -1, -1, 0.0, 0), - - /** - * Troll baby pet. - */ - TROLL_BABY(23030, 23030, -1, 14846, -1, -1, 0.0, 0), - /** * A bulldog pet. */ @@ -63,37 +53,7 @@ public enum Pets { /** * A terrier pet. */ - TERRIER(12512, 12513, -1, 6958, 6859, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), TERRIER_1(12700, 12701, -1, 7237, 7238, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), TERRIER_2(12702, 12703, -1, 7239, 7240, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), - - /** - * A creeping hand pet. - */ - //CREEPING_HAND(14652, -1, -1, 8619, -1, -1, 0.0033333333333333, 4, 1059), - - /** - * Minitrice pet. - */ - //MINITRICE(14653, -1, -1, 8620, -1, -1, 0.0033333333333333, 4, 225), - - /** - * Baby basilisk pet. - */ - //BABY_BASILISK(14654, -1, -1, 8621, -1, -1, 0.0033333333333333, 4, 221), - - /** - * Baby kurask pet. - */ - //BABY_KURASK(14655, -1, -1, 8622, -1, -1, 0.0033333333333333, 4, 526), - - /** - * Abyssal minion pet. - */ - //ABYSSAL_MINION(14651, -1, -1, 8624, -1, -1, 0.0033333333333333, 4, 592), - - /** - * Rune guardian pets. - */ - RUNE_GUARDIAN(15626, -1, -1, 9656, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_1(15627, -1, -1, 9657, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_2(15628, -1, -1, 9658, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_3(15629, -1, -1, 9659, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_4(15630, -1, -1, 9660, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_5(15631, -1, -1, 9661, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_6(15632, -1, -1, 9662, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_7(15633, -1, -1, 9663, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_8(15634, -1, -1, 9664, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_9(15635, -1, -1, 9665, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_10(15636, -1, -1, 9666, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_11(15637, -1, -1, 9667, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_12(15638, -1, -1, 9668, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_13(15639, -1, -1, 9669, -1, -1, 0.0033333333333333, 4), + TERRIER(12512, 12513, -1, 6958, 6959, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), TERRIER_1(12700, 12701, -1, 7237, 7238, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), TERRIER_2(12702, 12703, -1, 7239, 7240, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), /** * Gecko pet. @@ -115,11 +75,6 @@ public enum Pets { */ PENGUIN(12481, 12482, -1, 6908, 6909, -1, 0.0046296296296296, 30, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), PENGUIN_1(12763, 12762, -1, 7313, 7314, -1, 0.0046296296296296, 30, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), PENGUIN_2(12765, 12764, -1, 7316, 7317, -1, 0.0046296296296296, 30, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), - /** - * A tooth creature pet. - */ - TOOTH_CREATURE(18671, 18669, -1, 11411, 11413, -1, 0.075757575757576, 37, 1927, 1977), - /** * A giant crab pet. */ @@ -155,11 +110,6 @@ public enum Pets { */ RACCOON(12486, 12487, -1, 6913, 6914, -1, 0.0029444444444444, 80, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 2132, 2134, 2136, 2138, 10816, 9986, 9978), RACCOON_1(12734, 12735, -1, 7271, 7272, -1, 0.0029444444444444, 80, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 2132, 2134, 2136, 2138, 10816, 9986, 9978), RACCOON_2(12736, 12737, -1, 7273, 7274, -1, 0.0029444444444444, 80, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 2132, 2134, 2136, 2138, 10816, 9986, 9978), - /** - * A sneaker peeper pet. - */ - SNEAKER_PEEPER(19894, 19895, -1, 13089, 13090, -1, 0.05, 80, 221), - /** * A vulture pet. */ @@ -180,61 +130,6 @@ public enum Pets { */ BABY_DRAGON(12469, 12470, -1, 6900, 6901, -1, 0.0052, 99, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), BABY_DRAGON_1(12471, 12472, -1, 6902, 6903, -1, 0.0052, 99, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), BABY_DRAGON_2(12473, 12474, -1, 6904, 6905, -1, 0.0052, 99, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), BABY_DRAGON_3(12475, 12476, -1, 6906, 6907, -1, 0.0052, 99, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270); - -// /** -// * Emperor's pets (that's right, MY pet"S"). -// */ -// GIANT_WOLPERTINGER(8888, -1, -1, 6990, -1, -1, 0.0, 99), DRAKAN(8889, -1, -1, 4794, -1, -1, 0.0, 99), DILL(8890, -1, -1, 7770, -1, -1, 0.0, 99), - -// /** -// * Vexias pet. -// */ -// IMP(9952, -1, -1, 1531, -1, -1, 0.0, 99), BIG_GUY(9951, -1, -1, 3101, -1, -1, 0.0, 99), LITTLE_GUY(8887, -1, -1, 5805, -1, -1, 0.0, 99), -// -// /** -// * Godwars boss pets. -// */ -// KRIL_JR(14648, -1, -1, 8591, -1, -1, 0.0, 1), KREE_JR(14645, -1, -1, 8592, -1, -1, 0.0, 1), ZILYANA_JR(14647, -1, -1, 8593, -1, -1, 0.0, 1), GRAARDOOR_JR(14646, -1, -1, 8594, -1, -1, 0.0, 1), -// -// /** -// * Classic boss pets. -// */ -// CHAOS_ELE_JR(14638, -1, -1, 8595, -1, -1, 0.0, 1), PRINCE_BLACK_DRAGON(14649, -1, -1, 8596, -1, -1, 0.0, 1), BABY_MOLE(14642, -1, -1, 8601, -1, -1, 0.0, 1), KQ_FORM_1(14643, -1, -1, 8602, -1, -1, 0.0, 1), KQ_FORM_2(14650, -1, -1, 8603, -1, -1, 0.0, 1), DARK_CORE(14653, -1, -1, 8630, -1, -1, 0.0, 1), -// -// /** -// * The boss pets for the Dagannoths -// */ -// DAGANNOTH_SUPREME(14639, -1, -1, 8605, -1, -1, 0.0, 1), DAGANNOTH_PRIME(14640, -1, -1, 8606, -1, -1, 0.0, 1), DAGANNOTH_REX(14641, -1, -1, 8607, -1, -1, 0.0, 1), -// /** -// * The new OSRS bosses. -// */ -// CALLISTO_CUB(14658, -1, -1, 8597, -1, -1, 0.0, 1), -// SCORPIA_JR(14661, -1, -1, 8598, -1, -1, 0.0, 1), -// VENENATIS_JR(14657, -1, -1, 8654, -1, -1, 0.0, 1), -// VETION_JR(14659, -1, -1, 8600, -1, -1, 0.0, 1), -// VETION_JR_2(14660, -1, -1, 8654, -1, -1, 0.0, 1), -// RELEASE_THE_KRAKEN(14651, -1, -1, 8608, -1, -1, 0.0, 1), -// SMOKE_DEVIL(14644, -1, -1, 8609, -1, -1, 0.0, 1), -// SNAKELING_YELLOW(14654, -1, -1, 8626, -1, -1, 0.0, 1), -// SNAKELING_ORANGE(14655, -1, -1, 8627, -1, -1, 0.0, 1), -// SNAKELING_PURPLE(14656, -1, -1, 8628, -1, -1, 0.0, 1), -// /** -// * The boss pet representing the likeness of the Penance Queen -// */ -// DRAMA_QUEEN(14652, -1, -1, 8604, -1, -1, 0.0, 1), -// /** -// * The skilling pets -// */ -// BEAVER(14821, -1, -1, 8635, -1, -1, 0.0, 1), -// ROCK_GOLEM(14822, -1, -1, 8637, -1, -1, 0.0, 1), -// BABY_RED_CHINCHOMPA(14823, -1, -1, 8643, -1, -1, 0.0, 1), -// BABY_GREY_CHINCHOMPA(14824, -1, -1, 8644, -1, -1, 0.0, 1), -// BABY_BLACK_CHINCHOMPA(14825, -1, -1, 8657, -1, -1, 0.0, 1), -// BABY_GOLD_CHINCHOMPA(14826, -1, -1, 8658, -1, -1, 0.0, 1), -// HERON(14827, -1, -1, 8647, -1, -1, 0.0, 1), -// TZREK_JAD(14828, -1, -1, 8650, -1, -1, 0.0, 1); - - /** * The baby pets mapping. */ @@ -481,4 +376,44 @@ public enum Pets { } return -1; } -} \ No newline at end of file + + /** + * Checks if this pet is a kitten + * @return a boolean, true if the pet is a kitten + */ + public boolean isKitten(int id) { + switch (this) { + case CAT: + case CAT_1: + case CAT_2: + case CAT_3: + case CAT_4: + case CAT_5: + case CAT_6: + case HELLCAT: + return id == babyItemId; + default: + return false; + } + } + + /** + * Checks if this pet is a grown cat + * @return a boolean, true if the pet is a grown cat + */ + public boolean isGrownCat(int id) { + switch (this) { + case CAT: + case CAT_1: + case CAT_2: + case CAT_3: + case CAT_4: + case CAT_5: + case CAT_6: + case HELLCAT: + return id == grownItemId || id == overgrownItemId; + default: + return false; + } + } +} diff --git a/Server/src/main/content/global/skill/thieving/Pickpockets.kt b/Server/src/main/content/global/skill/thieving/Pickpockets.kt index d5e30efd1..4580f2518 100644 --- a/Server/src/main/content/global/skill/thieving/Pickpockets.kt +++ b/Server/src/main/content/global/skill/thieving/Pickpockets.kt @@ -10,7 +10,7 @@ import core.api.utils.WeightedItem import java.util.stream.IntStream enum class Pickpockets(val ids: IntArray, val requiredLevel: Int, val low: Double, val high: Double, val experience: Double, val stunDamageMin: Int, val stunDamageMax: Int, val stunTime: Int, val table: WeightBasedTable) { - MAN(intArrayOf(1, 2, 3, 4, 5, 6, 16, 24, 25, 170, 1086, 3224, 3915, 3226, 3227, 5924, 5923), 1, 180.0, 240.0, 8.0, 1, 1,5, WeightBasedTable.create( + MAN(intArrayOf(1, 2, 3, 4, 5, 6, 16, 24, 25, 170, 1086, 2683, 2684, 3224, 3915, 3226, 3227, 5924, 5923), 1, 180.0, 240.0, 8.0, 1, 1,5, WeightBasedTable.create( WeightedItem(Items.COINS_995,3,3,1.0,true) )), FARMER(intArrayOf(7, 1757, 1758), 10, 180.0, 240.0, 14.5, 1,1,5, WeightBasedTable.create( diff --git a/Server/src/main/content/global/skill/thieving/Stall.java b/Server/src/main/content/global/skill/thieving/Stall.java index bd1dcefee..59498da4a 100644 --- a/Server/src/main/content/global/skill/thieving/Stall.java +++ b/Server/src/main/content/global/skill/thieving/Stall.java @@ -6,6 +6,9 @@ import core.tools.RandomFunction; import java.util.*; +import static org.rs09.consts.Items.CANDLE_36; +import static org.rs09.consts.Scenery.CANDLES_19127; + /** * Represents a thieving stall. * @author Ceikry, Woahscam @@ -16,7 +19,7 @@ public enum Stall { CRAFTING_STALL(new Integer[]{4874, 6166}, new Integer[] { 4797, 6984 }, 5, new Item[]{new Item(1592, 1), new Item(1597, 1), new Item(1755, 1)}, 16, 12,"crafting supplies"), TEA_STALL(new Integer[]{635, 6574}, new Integer[] { 634, 6573 }, 5, new Item[]{new Item(712, 1)}, 16, 12,"tea"), SILK_STALL(new Integer[]{34383, 2560}, new Integer[] { 34381, 634 }, 20, new Item[]{new Item(950, 1)}, 24, 13,"silk"), - WINE_STALL(new Integer[]{2046}, new Integer[] { 634 }, 22, new Item[]{new Item(1935, 1), new Item(1937, 1), new Item(1993, 1), new Item(7919, 1)}, 27, 27,"wine"), //OBJECT MISSING IN CACHE + WINE_STALL(new Integer[]{14011}, new Integer[] { 634 }, 22, new Item[]{new Item(1935, 1), new Item(1937, 1), new Item(1993, 1), new Item(7919, 1)}, 27, 27,"wine"), MARKET_SEED_STALL(new Integer[]{7053}, new Integer[] { 634 }, 27, new Item[]{new Item(5096, 1), new Item(5097, 1), new Item(5101, 1), new Item(5318, 1), new Item(5319, 1), new Item(5324, 1)}, 10, 19,"seeds"), FUR_STALL(new Integer[]{ 34387, 2563, 4278}, new Integer[] { 34381, 634, 634 }, 35, new Item[]{new Item(6814, 1), new Item(958, 1)}, 36, 25,"fur"), FISH_STALL(new Integer[]{ 4277, 4705, 4707 }, new Integer[] { 634, 634, 634 }, 42, new Item[]{new Item(331, 1), new Item(359, 1), new Item(377, 1)}, 42, 27,"fish"), @@ -28,9 +31,9 @@ public enum Stall { SCIMITAR_STALL(new Integer[]{4878}, new Integer[] { 4797 }, 65, new Item[]{new Item(1323, 1)}, 100, 134,"equipment"), MAGIC_STALL(new Integer[]{4877}, new Integer[] { 4797 }, 65, new Item[]{new Item(556, 1), new Item(557, 1), new Item(554, 1), new Item(555, 1), new Item(563, 1)}, 100, 134,"equipment"), GENERAL_STALL(new Integer[]{4876}, new Integer[] { 4797 }, 5, new Item[]{new Item(1931, 1), new Item(2347, 1), new Item(590, 1)}, 16, 12,"goods"), - FOOD_STALL(new Integer[]{4875}, new Integer[] { 4797 }, 5, new Item[]{new Item(1963, 1)}, 16, 12,"food"); + FOOD_STALL(new Integer[]{4875}, new Integer[] { 4797 }, 5, new Item[]{new Item(1963, 1)}, 16, 12,"food"), //CRAFTING_STALL (Ape Atoll) shares same drops/exp as regular crafting stall - + CANDLES(new Integer[]{CANDLES_19127}, new Integer[]{CANDLES_19127}, 20, new Item[]{new Item(CANDLE_36, 1)}, 20, 0, "candles"); //Quest Stalls Rocking Out //CUSTOMS_EVIDENCE_FILES(new Integer[]{FIND OBJ ID}, FIND OBJ EMPTY ID, 63, new Item[]{new Item(1333, 1), new Item(1617, 1), new Item(1619, 1), new Item(1623, 1), new Item(385, 1), new Item(2359, 1), new Item(2357, 1), new Item(2351, 1), new Item(7114, 1), new Item(7134, 1), new Item(1025, 1), new Item(1281, 1), new Item(1325, 1), new Item(1323, 1), new Item(1321, 1), new Item(995, 300)}, 75, 100); diff --git a/Server/src/main/content/global/skill/thieving/StallThiefPulse.java b/Server/src/main/content/global/skill/thieving/StallThiefPulse.java index 7cb9b2dd8..b08185335 100644 --- a/Server/src/main/content/global/skill/thieving/StallThiefPulse.java +++ b/Server/src/main/content/global/skill/thieving/StallThiefPulse.java @@ -1,6 +1,7 @@ package content.global.skill.thieving; import core.game.event.ResourceProducedEvent; +import core.game.node.entity.combat.ImpactHandler; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; @@ -15,6 +16,7 @@ import core.tools.RandomFunction; import core.tools.StringUtils; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents the pulse used to thieve a stall. @@ -71,12 +73,12 @@ public final class StallThiefPulse extends SkillPulse { player.getPacketDispatch().sendMessage("You don't have enough inventory space."); return false; } - if (player.getLocation().isInRegion(10553) && !isQuestComplete(player, "Fremennik Trials") && stall.full_ids.contains(4278)) { + if (player.getLocation().isInRegion(10553) && !isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) && stall.full_ids.contains(4278)) { sendDialogue(player, "The fur trader is staring at you suspiciously. You cannot steal from his stall while he distrusts you."); return false; } - if (player.getLocation().isInRegion(10553) && !isQuestComplete(player, "Fremennik Trials") && stall.full_ids.contains(4277)) { + if (player.getLocation().isInRegion(10553) && !isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) && stall.full_ids.contains(4277)) { sendDialogue(player, "The fishmonger is staring at you suspiciously. You cannot steal from his stall while he distrusts you."); return false; } @@ -119,6 +121,9 @@ public final class StallThiefPulse extends SkillPulse { player.getPacketDispatch().sendMessage("You steal grapes from the grape stall."); return true; } + if(stall == Stall.CANDLES) { + return true; + } player.getPacketDispatch().sendMessage("You steal " + (StringUtils.isPlusN(item.getName()) ? "an" : "a") + " " + item.getName().toLowerCase() + " from the " + stall.name().toLowerCase().replace('_',' ') + "."); player.dispatch(new ResourceProducedEvent(item.getId(), item.getAmount(), node, 0)); } @@ -127,11 +132,12 @@ public final class StallThiefPulse extends SkillPulse { @Override public void message(int type) { - switch (type) { - case 0: - player.getPacketDispatch().sendMessage("You attempt to steal some " + stall.msgItem + " from the " + stall.name().toLowerCase().replace('_',' ')); - break; + if(stall == Stall.CANDLES) { + return; } + if (type == 0) { + player.getPacketDispatch().sendMessage("You attempt to steal some " + stall.msgItem + " from the " + stall.name().toLowerCase().replace('_', ' ')); + } } /** @@ -141,6 +147,15 @@ public final class StallThiefPulse extends SkillPulse { private boolean success() { int mod = 0; if (RandomFunction.random(15 + mod) < 4) { + if(stall == Stall.CANDLES) { + stun(player, 15, false); + impact(player, 1, ImpactHandler.HitsplatType.NORMAL); + // Location playerLoc = player.getLocation(); + // forceMove(player, playerLoc, new Location(playerLoc.getX() - 1, playerLoc.getY() - 1), + // 0, 4, Direction.SOUTH_WEST, 819, null); + player.sendMessage("A higher power smites you"); + return false; + } for (NPC npc : RegionManager.getLocalNpcs(player.getLocation(), 8)) { if (!npc.getProperties().getCombatPulse().isAttacking() && (npc.getId() == 32 || npc.getId() == 2236)) { npc.sendChat("Hey! Get your hands off there!"); diff --git a/Server/src/main/content/global/skill/thieving/ThievingOptionPlugin.java b/Server/src/main/content/global/skill/thieving/ThievingOptionPlugin.java index f5d0c8a26..2d5103734 100644 --- a/Server/src/main/content/global/skill/thieving/ThievingOptionPlugin.java +++ b/Server/src/main/content/global/skill/thieving/ThievingOptionPlugin.java @@ -20,6 +20,7 @@ public class ThievingOptionPlugin extends OptionHandler { public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.setOptionHandler("steal-from", this); SceneryDefinition.setOptionHandler("steal from", this); + SceneryDefinition.setOptionHandler("steal", this); return this; } @@ -28,6 +29,7 @@ public class ThievingOptionPlugin extends OptionHandler { switch (option) { case "steal-from": case "steal from": + case "steal": player.getPulseManager().run(new StallThiefPulse(player, (Scenery) node, Stall.forObject((Scenery) node))); player.getLocks().lockInteractions(6); break; diff --git a/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java b/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java index 3af68e890..67f0cbaa0 100644 --- a/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java +++ b/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java @@ -1,6 +1,6 @@ package content.global.travel.glider; -import content.region.kandarin.quest.grandtree.TheGrandTree; +import content.data.Quests; import core.game.component.Component; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -58,7 +58,7 @@ public final class CaptainDalburDialogue extends DialoguePlugin { stage = 1; break; case 1: - if(!isQuestComplete(player, TheGrandTree.questName)){ + if(!isQuestComplete(player, Quests.THE_GRAND_TREE)){ interpreter.sendDialogues(npc, FacialExpression.ANNOYED, "I only fly friends of the gnomes!"); stage = END_DIALOGUE; } diff --git a/Server/src/main/content/global/travel/glider/GliderPlugin.java b/Server/src/main/content/global/travel/glider/GliderPlugin.java index c62e404d9..b3cbe634c 100644 --- a/Server/src/main/content/global/travel/glider/GliderPlugin.java +++ b/Server/src/main/content/global/travel/glider/GliderPlugin.java @@ -1,6 +1,6 @@ package content.global.travel.glider; -import content.region.kandarin.quest.grandtree.TheGrandTree; +import content.data.Quests; import core.api.ContentAPIKt; import core.cache.def.impl.NPCDefinition; import core.game.component.Component; @@ -31,7 +31,7 @@ public final class GliderPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - if(isQuestComplete(player, TheGrandTree.questName)){ + if(isQuestComplete(player, Quests.THE_GRAND_TREE)){ player.getInterfaceManager().open(new Component(138)); Gliders.sendConfig(node.asNpc(), player); } else { diff --git a/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java b/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java index f7782c200..41b226d36 100644 --- a/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java +++ b/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java @@ -11,6 +11,7 @@ import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.item.Item; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents the dialogue used to handle the sailing from and to karamja. @@ -46,7 +47,7 @@ public class SeamanDialoguePlugin extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - if (args.length > 1 && player.getQuestRepository().isComplete("Pirate's Treasure")) { + if (args.length > 1 && player.getQuestRepository().isComplete(Quests.PIRATES_TREASURE)) { if (player.getEquipment().get(EquipmentContainer.SLOT_RING) != null && player.getEquipment().get(EquipmentContainer.SLOT_RING).getId() == Items.RING_OF_CHAROSA_6465) { travel(); } else if (player.getAchievementDiaryManager().getDiary(DiaryType.KARAMJA).isComplete(0)) { diff --git a/Server/src/main/content/global/travel/ship/ShipCharter.java b/Server/src/main/content/global/travel/ship/ShipCharter.java index 99121e375..4058fc375 100644 --- a/Server/src/main/content/global/travel/ship/ShipCharter.java +++ b/Server/src/main/content/global/travel/ship/ShipCharter.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.List; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents a class used to charter ships. @@ -84,7 +85,10 @@ public final class ShipCharter { */ public static int getCost(final Player player, Destination destination) { int cost = destination.getCost(player, destination); - if (player.getEquipment().containsItem(RING_OF_CHAROS)) {// TODO: cabin fever quest + if (hasRequirement(player, Quests.CABIN_FEVER)) { + cost -= Math.round((cost / 2.)); + } + if (player.getEquipment().containsItem(RING_OF_CHAROS)) { cost -= Math.round((cost / 2.)); } return cost; @@ -96,12 +100,10 @@ public final class ShipCharter { * @return the hidden childs. */ public static int[] getHiddenComponents(final Player player, Destination base) { - final Destination[] restrictions = new Destination[] { /** - * - * Destination.MOS_LE_HARMLESS, - */ - Destination.OO_GLOG, Destination.SHIPYARD, /* Destination.PORT_TYRAS, */ - Destination.CRANDOR }; + final Destination[] restrictions = new Destination[] { /* Destination.MOS_LE_HARMLESS, */ + Destination.OO_GLOG, Destination.SHIPYARD, /* Destination.PORT_TYRAS, */ + Destination.CRANDOR + }; List childs = new ArrayList<>(20); for (Destination destination : restrictions) { childs.add(destination.getXChild()); @@ -137,15 +139,20 @@ public final class ShipCharter { * @author 'Vexia */ public enum Destination { - CATHERBY(Location.create(2792, 3417, 1), 25, new int[] { 480, 0, 480, 625, 1600, 3250, 1000, 1600, 3200, 3400 }, Location.create(2797, 3414, 0), 3, 14), + CATHERBY(Location.create(2792, 3417, 1), 25, new int[] { 480, 0, 480, 1250, 1600, 3250, 1000, 1600, 3200, 3400 }, Location.create(2797, 3414, 0), 3, 14), PORT_PHASMATYS(Location.create(3705, 3503, 1), 24, new int[] { 3650, 3250, 1850, 0, 0, 0, 2050, 1850, 3200, 1100 }, Location.create(3702, 3502, 0), 2, 13) { @Override public boolean checkTravel(Player player) { - return requireQuest(player, "Priest in Peril", "to go there"); + return requireQuest(player, Quests.PRIEST_IN_PERIL, "to go there."); } }, - CRANDOR(new Location(2792, 3417, 1), 32, new int[] { 0, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, null, 10, 21), - BRIMHAVEN(Location.create(2763, 3238, 1), 28, new int[] { 0, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, Location.create(2760, 3238, 0), 6, 17){ + CRANDOR(Location.create(2792, 3417, 1), 32, new int[] { 0, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, null, 10, 21) { + @Override + public boolean checkTravel(Player player) { + return requireQuest(player, Quests.DRAGON_SLAYER, "to go there."); + } + }, + BRIMHAVEN(Location.create(2763, 3238, 1), 28, new int[] { 0, 480, 480, 1950, 400, 3650, 1600, 400, 3200, 3800 }, Location.create(2760, 3238, 0), 6, 17){ @Override public int getCost(Player player, Destination destination) { boolean hasGloves = DiaryType.KARAMJA.hasRewardEquipment(player); @@ -153,7 +160,7 @@ public final class ShipCharter { return super.getCost(player, destination); } }, - PORT_SARIM(Location.create(3038, 3189, 1), 30, new int[] { 1600, 1000, 0, 325, 1280, 650, 1280, 400, 3200, 1400 }, Location.create(3039, 3193, 0), 8, 19){ + PORT_SARIM(Location.create(3038, 3189, 1), 30, new int[] { 1600, 1000, 0, 650, 1280, 650, 1280, 400, 3200, 1400 }, Location.create(3039, 3193, 0), 8, 19){ @Override public int getCost(Player player, Destination destination) { boolean hasGloves = DiaryType.KARAMJA.hasRewardEquipment(player); @@ -161,8 +168,14 @@ public final class ShipCharter { return super.getCost(player, destination); } }, - PORT_TYRAS(Location.create(2142, 3122, 0), 23, new int[] { 3200, 3200, 3200, 1600, 3200, 3200, 3200, 3200, 0, 3200 }, Location.create(2143, 3122, 0), 1, 12), - KARAMJA(Location.create(2957, 3158, 1), 27, new int[] { 200, 480, 0, 225, 400, 1850, 0, 200, 3200, 2000 }, Location.create(2954, 3156, 0), 5, 16) { + PORT_TYRAS(Location.create(2142, 3122, 0), 23, new int[] { 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 0, 3200 }, Location.create(2143, 3122, 0), 1, 12) { + @Override + public boolean checkTravel(Player player) { + return hasRequirement(player, Quests.REGICIDE); + } + + }, + KARAMJA(Location.create(2957, 3158, 1), 27, new int[] { 200, 480, 0, 450, 400, 1850, 0, 200, 3200, 2000 }, Location.create(2954, 3156, 0), 5, 16) { @Override public int getCost(Player player, Destination destination) { boolean hasGloves = DiaryType.KARAMJA.hasRewardEquipment(player); @@ -170,7 +183,7 @@ public final class ShipCharter { return super.getCost(player, destination); } }, - PORT_KHAZARD(Location.create(2674, 3141, 1), 29, new int[] { 1600, 1000, 0, 325, 180, 650, 1280, 400, 3200, 1400 }, Location.create(2674, 3144, 0), 7, 18){ + PORT_KHAZARD(Location.create(2674, 3141, 1), 29, new int[] { 1600, 1000, 0, 2050, 180, 650, 1280, 400, 3200, 1400 }, Location.create(2674, 3144, 0), 7, 18){ @Override public int getCost(Player player, Destination destination) { boolean hasGloves = DiaryType.KARAMJA.hasRewardEquipment(player); @@ -178,9 +191,19 @@ public final class ShipCharter { return super.getCost(player, destination); } }, - SHIPYARD(Location.create(3001, 3032, 0), 26, new int[] { 400, 1600, 200, 225, 720, 1850, 400, 0, 3200, 900 }, Location.create(3001, 3032, 0), 4, 15), - OO_GLOG(Location.create(2623, 2857, 0), 33, new int[] { 300, 3400, 2000, 550, 5000, 2800, 1400, 900, 3200, 0}, Location.create(2622, 2857, 0), 11, 22), - MOS_LE_HARMLESS(Location.create(3671, 2931, 0), 31, new int[] { 725, 625, 1025, 0, 1025, 0, 325, 275, 1600, 500 }, Location.create(3671, 2933, 0), 9, 20); + SHIPYARD(Location.create(3001, 3032, 0), 26, new int[] { 400, 1600, 200, 450, 720, 1850, 400, 0, 3200, 900 }, Location.create(3001, 3032, 0), 4, 15) { + @Override + public boolean checkTravel(Player player) { + return requireQuest(player, Quests.THE_GRAND_TREE, "to go there."); + } + }, + OO_GLOG(Location.create(2623, 2857, 0), 33, new int[] { 300, 3400, 2000, 1100, 5000, 2800, 1400, 900, 3200, 0}, Location.create(2622, 2857, 0), 11, 22), + MOS_LE_HARMLESS(Location.create(3671, 2931, 0), 31, new int[] { 1550, 1250, 2050, 0, 2050, 0, 650, 550, 3200, 1000 }, Location.create(3671, 2933, 0), 9, 20) { + @Override + public boolean checkTravel(Player player) { + return hasRequirement(player, Quests.CABIN_FEVER); + } + }; /** * Constructs a new {@code ShipCharter} {@code Object}. diff --git a/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt index 494a79e12..2f6e9a7ed 100644 --- a/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt +++ b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt @@ -1,9 +1,5 @@ package content.global.travel.trees -import core.api.isQuestComplete -import core.api.openDialogue -import core.api.sendDialogue -import core.api.teleport import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType @@ -12,15 +8,16 @@ import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import org.rs09.consts.NPCs -import content.region.kandarin.quest.tree.TreeGnomeVillage import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.world.GameWorld.Pulser import core.tools.END_DIALOGUE +import content.data.Quests +import core.api.* class GnomeSpiritTreeListener: InteractionListener { - val spiritTrees = intArrayOf(1317,1293,1294) + val spiritTrees = intArrayOf(1317,1293,1294,8355) override fun defineListeners() { on(spiritTrees, IntType.SCENERY, "talk-to"){ player, _ -> @@ -39,14 +36,18 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { Location(2542, 3170, 0), Location(2461, 3444, 0), Location(2556, 3259, 0), - Location(3184, 3508, 0) + Location(3184, 3508, 0), + Location(3060, 3256, 0), //Port Sarim + Location(2613, 3856, 0), //Etceteria + Location(2800, 3203, 0) //Brimhaven + ) private val ANIMATIONS = arrayOf(Animation(7082), Animation(7084)) private val GRAPHICS = arrayOf(Graphics(1228), Graphics(1229)) fun hasQuestCompleted(player: Player): Boolean { - if (!isQuestComplete(player, TreeGnomeVillage.questName)) { + if (!isQuestComplete(player, Quests.TREE_GNOME_VILLAGE)) { sendDialogue(player, "The tree doesn't feel like talking.") stage = END_DIALOGUE return false @@ -86,6 +87,15 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { stage = END_DIALOGUE return } + + var plantedSpiritTreeLocation = when { + getVarbit(player!!, 720) == 20 -> "Port Sarim" + getVarbit(player!!, 722) == 20 -> "Etceteria" + getVarbit(player!!, 724) == 20 -> "Brimhaven" + else -> null + //There should never be a case where more than one spirit tree is planted. + } + if(plantedSpiritTreeLocation == null) { when (stage) { 0 -> interpreter!!.sendOptions( "Where would you like to go?", @@ -100,6 +110,29 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { 3 -> sendTeleport(player!!, LOCATIONS[2]) 4 -> sendTeleport(player!!, LOCATIONS[3]) } + } + } + else when (stage) { + 0 -> interpreter!!.sendOptions( + "Where would you like to go?", + "Tree Gnome Village", + "Tree Gnome Stronghold", + "Battlefield of Khazard", + "Grand Exchange", + plantedSpiritTreeLocation + ).also { stage++ } + 1 -> when (buttonID) { + 1 -> sendTeleport(player!!, LOCATIONS[0]) + 2 -> sendTeleport(player!!, LOCATIONS[1]) + 3 -> sendTeleport(player!!, LOCATIONS[2]) + 4 -> sendTeleport(player!!, LOCATIONS[3]) + 5 -> when (plantedSpiritTreeLocation) { + "Port Sarim" -> sendTeleport(player!!,LOCATIONS[4]) + "Etceteria" -> sendTeleport(player!!,LOCATIONS[5]) + "Brimhaven" -> sendTeleport(player!!,LOCATIONS[6]) + else -> stage = END_DIALOGUE + } + } } } } diff --git a/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt b/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt index 08d059bcf..cd50cb9bf 100644 --- a/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt +++ b/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt @@ -10,6 +10,7 @@ import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.world.GameWorld +import content.data.Quests private val VALID_LOGS = intArrayOf(Items.LOGS_1511, Items.OAK_LOGS_1521,Items.WILLOW_LOGS_1519,Items.MAPLE_LOGS_1517,Items.YEW_LOGS_1515,Items.MAGIC_LOGS_1513) private val FILL_ANIM = Animation(9136) @@ -24,8 +25,8 @@ class AFUBeaconListeners : InteractionListener { override fun defineListeners() { on(IntType.SCENERY,"add-logs","light"){ player, node -> val beacon = AFUBeacon.forLocation(node.location) - val questComplete = player.questRepository.isComplete("All Fired Up") - val questStage = player.questRepository.getStage("All Fired Up") + val questComplete = player.questRepository.isComplete(Quests.ALL_FIRED_UP) + val questStage = player.questRepository.getStage(Quests.ALL_FIRED_UP) if ((beacon != AFUBeacon.RIVER_SALVE && beacon != AFUBeacon.RAG_AND_BONE && !questComplete) || (beacon == AFUBeacon.RIVER_SALVE && questStage < 20 && !questComplete) @@ -68,7 +69,7 @@ class AFUBeaconListeners : InteractionListener { } AFUBeacon.GOBLIN_VILLAGE -> { - if(!player.questRepository.isComplete("Lost Tribe")){ + if(!player.questRepository.isComplete(Quests.THE_LOST_TRIBE)){ player.dialogueInterpreter.sendDialogues(NPC(beacon.keeper).getShownNPC(player), core.game.dialogue.FacialExpression.THINKING,"We no trust you outsider. You no light our beacon.","(Complete Lost Tribe to use this beacon.)") return } @@ -151,7 +152,7 @@ class AFUBeaconListeners : InteractionListener { experience += session?.getBonusExperience() ?: 0.0 player.skills.addExperience(Skills.FIREMAKING,experience) } else { - player.questRepository.getQuest("All Fired Up").setStage(player, player.questRepository.getStage("All Fired Up") + 10) + player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player, player.questRepository.getStage(Quests.ALL_FIRED_UP) + 10) } } 2 -> player.unlock().also { return true } @@ -183,7 +184,7 @@ class AFUBeaconListeners : InteractionListener { if(questComplete){ session?.refreshTimer(beacon,logs.id) } else { - player.questRepository.getQuest("All Fired Up").setStage(player, 80) + player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player, 80) } } 2 -> player.unlock().also { return true } diff --git a/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt b/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt index ac6017994..22e87dfdc 100644 --- a/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt +++ b/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt @@ -1,5 +1,6 @@ package content.minigame.allfiredup +import content.data.Quests import core.game.node.entity.impl.ForceMovement import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills @@ -12,7 +13,6 @@ import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.api.* -import java.util.* /** * Handles repairing and climbing of the 3 beacon shortcuts needed to access them @@ -26,26 +26,74 @@ class AFURepairClimbHandler : InteractionListener { override fun defineListeners() { on(repairIDs, IntType.SCENERY, "repair"){ player, _ -> - var rco: RepairClimbObject = RepairClimbObject.GWD - for(ent in RepairClimbObject.values()) if(ent.destinationDown?.withinDistance(player.location,2) == true || ent.destinationUp?.withinDistance(player.location,2) == true) rco = ent - repair(player,rco) - return@on true + if (hasRequirement(player, Quests.ALL_FIRED_UP)){ + val rco: RepairClimbObject? = getClimbingObject(player) + repair(player,rco!!) + return@on true + } + return@on false } on(climbIDs, IntType.SCENERY, "climb"){ player, node -> - var rco: RepairClimbObject = RepairClimbObject.GWD - for(ent in RepairClimbObject.values()) if(ent.destinationDown?.withinDistance(player.location,2) == true || ent.destinationUp?.withinDistance(player.location,2) == true) rco = ent - climb(player,rco,node.location) + val rco: RepairClimbObject? = getClimbingObject(player) + climb(player,rco!!,node.location) return@on true } } + private fun getClimbingObject(player: Player): RepairClimbObject?{ + for(ent in RepairClimbObject.values()) + if(ent.destinationDown?.withinDistance(player.location,2) == true || + ent.destinationUp?.withinDistance(player.location,2) == true){ + return ent + } + return RepairClimbObject.GWD //the only one that does not have down/up destinations + } + private fun repair(player: Player,rco: RepairClimbObject){ + if (rco == RepairClimbObject.TEMPLE){ + // You can do this 2 different ways + val hasSmithingLevel = getStatLevel(player, Skills.SMITHING) >= 70 + val hasConstructionLevel = getStatLevel(player, Skills.CONSTRUCTION) >= 59 + + if (!hasConstructionLevel && !hasSmithingLevel){ + sendDialogue(player, "You need level 70 smithing or 59 construction for this.") + return + } + + val hasHammer = inInventory(player, Items.HAMMER_2347) + val hasSmithingItems = hasHammer && inInventory(player, Items.IRON_BAR_2351, 2) + val hasConstructionItems = hasHammer && inInventory(player, Items.PLANK_960, 2) + + if (hasSmithingLevel && hasSmithingItems){ + if (removeItem(player,Item(Items.IRON_BAR_2351, 2))) { + setVarbit(player, rco.varbit, 1, true) + return + } + } + // Only check this if the smithing repair didn't work + if (hasConstructionLevel && hasConstructionItems){ + val nails = NailType.get(player, 4) + if (nails != null){ + if (removeItem(player, Item(Items.PLANK_960, 2)) && removeItem(player, Item(nails.itemId, 4))) { + setVarbit(player, rco.varbit, 1, true) + return + } + } + } + + var msg = "You need " + msg += if (hasSmithingLevel) "a hammer and 2 iron bars" else "" + msg += if (hasSmithingLevel && hasConstructionLevel) " or " else "" + msg += if (hasConstructionLevel) "a hammer, 2 planks and 4 nails for this." else " for this." + sendDialogue(player, msg) + return + } val skill = rco.levelRequirement?.first ?: 0 val level = rco.levelRequirement?.second ?: 0 - if(player.skills.getLevel(skill) < level){ - player.dialogueInterpreter.sendDialogue("You need level $level ${Skills.SKILL_NAME[skill]} for this.") + if(getStatLevel(player, skill) < level){ + sendDialogue(player, "You need level $level ${Skills.SKILL_NAME[skill]} for this.") return } @@ -53,47 +101,40 @@ class AFURepairClimbHandler : InteractionListener { val requiredItems = when(rco){ RepairClimbObject.DEATH_PLATEAU -> { - arrayOf(Item(Items.PLANK_960,2)) + Item(Items.PLANK_960,2) } RepairClimbObject.BURTHORPE -> { - arrayOf(Item(Items.IRON_BAR_2351,2)) + Item(Items.IRON_BAR_2351,2) } RepairClimbObject.GWD -> { requiresNeedle = true - arrayOf(Item(Items.JUTE_FIBRE_5931,3)) - } - - RepairClimbObject.TEMPLE -> { - arrayOf(Item(Items.IRON_BAR_2351,2)) + Item(Items.JUTE_FIBRE_5931,3) } + else -> return } if(requiresNeedle){ - if(player.inventory.containsItem(Item(Items.NEEDLE_1733)) && player.inventory.containItems(*requiredItems.map { it.id }.toIntArray())) { - player.inventory.remove(*requiredItems) - if (Random().nextBoolean()) player.inventory.remove(Item(Items.NEEDLE_1733)) - } else { - player.dialogueInterpreter.sendDialogue("You need a needle and ${requiredItems.map { "${it.amount} ${it.name.toLowerCase()}s" }.toString().replace("[","").replace("]","")} for this.") + if (!inInventory(player, Items.NEEDLE_1733) || !removeItem(player, requiredItems)) { + sendDialogue(player, "You need a needle and ${requiredItems.amount} ${requiredItems.name.lowercase()}s for this.") return } } else { - if(player.inventory.containsItem(Item(Items.HAMMER_2347)) && player.inventory.containItems(*requiredItems.map { it.id }.toIntArray())) { + if(inInventory(player, Items.HAMMER_2347) && inInventory(player, requiredItems.id, requiredItems.amount)) { val nails = NailType.get(player,4) if(nails == null && rco == RepairClimbObject.DEATH_PLATEAU){ - player.dialogueInterpreter.sendDialogue("You need 4 nails for this.") + sendDialogue(player, "You need 4 nails for this.") return } else if (rco == RepairClimbObject.DEATH_PLATEAU){ - player.inventory.remove(Item(nails.itemId,4)) + removeItem(player, Item(nails.itemId,4)) } - player.inventory.remove(*requiredItems) + removeItem(player, requiredItems) } else { - player.dialogueInterpreter.sendDialogue("You need a hammer and ${requiredItems.map { "${it.amount} ${it.name.toLowerCase()}s" }.toString().replace("[","").replace("]","")} for this.") + sendDialogue(player, "You need a hammer and ${requiredItems.amount} ${requiredItems.name.lowercase()}s for this.") return } } - setVarbit(player, rco.varbit, 1, true) } @@ -105,7 +146,7 @@ class AFURepairClimbHandler : InteractionListener { DEATH_PLATEAU(5161,Location.create(2949, 3623, 0),Location.create(2954, 3623, 0), Pair(Skills.CONSTRUCTION,42)), BURTHORPE(5160,Location.create(2941, 3563, 0),Location.create(2934, 3563, 0),Pair(Skills.SMITHING,56)), GWD(5163,null,null,Pair(Skills.CRAFTING,60)), - TEMPLE(5164,Location.create(2949, 3835, 0),Location.create(2956, 3835, 0),Pair(Skills.SMITHING,64)); + TEMPLE(5164,Location.create(2949, 3835, 0),Location.create(2956, 3835, 0),Pair(0,0)); // This needs to be handled specially so don't have levels here fun getOtherLocation(player: Player): Location?{ if(player.location == destinationDown) return destinationUp diff --git a/Server/src/main/content/minigame/barbassault/BarbAssaultArea.kt b/Server/src/main/content/minigame/barbassault/BarbAssaultArea.kt new file mode 100644 index 000000000..7c95758ba --- /dev/null +++ b/Server/src/main/content/minigame/barbassault/BarbAssaultArea.kt @@ -0,0 +1,18 @@ +package content.minigame.barbassault + +import core.api.MapArea +import core.api.getRegionBorders +import core.game.node.entity.Entity +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneType + +class BarbAssaultArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(7509)) + } + + override fun areaEnter(entity: Entity) { + zone.zoneType = ZoneType.BARBARIAN_ASSAULT.id + super.areaEnter(entity) + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt b/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt index c0e50104d..d4b31bc8e 100644 --- a/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt +++ b/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt @@ -6,15 +6,19 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable +import core.tools.END_DIALOGUE import org.rs09.consts.Items import org.rs09.consts.NPCs -import core.tools.END_DIALOGUE import java.text.SimpleDateFormat import java.time.temporal.ChronoUnit import java.util.* @Initializable class CaptainCainDialogue(player: Player? = null) : DialoguePlugin(player) { + companion object { + const val TORSO_PRICE = 4_500_000 + } + val sdf = SimpleDateFormat("ddMMyyyy") override fun newInstance(player: Player?): DialoguePlugin { return CaptainCainDialogue(player) @@ -38,21 +42,25 @@ class CaptainCainDialogue(player: Player? = null) : DialoguePlugin(player) { 2 -> playerl(FacialExpression.HALF_THINKING, "No, thanks.").also { stage = END_DIALOGUE } } - 10 -> npcl(FacialExpression.FRIENDLY, "Alright, then, that'll be 7,500,000 gold please.").also { stage++ } - 11 -> options("Here you go!","Nevermind.").also { stage++ } + 10 -> npcl(FacialExpression.FRIENDLY, "Alright, then, that'll be %,d gold please.".format(TORSO_PRICE)).also { stage++ } + 11 -> options("Here you go!","Never mind.").also { stage++ } 12 -> when(buttonId){ - 1 -> if(inInventory(player, 995, 7500000)) + 1 -> if(inInventory(player, 995, TORSO_PRICE)) playerl(FacialExpression.FRIENDLY, "Here you go!").also { stage = 20 } else playerl(FacialExpression.HALF_GUILTY, "Actually, I don't have that much.").also { stage = END_DIALOGUE } - 2 -> playerl(FacialExpression.FRIENDLY, "On second thought, nevermind.").also { stage = END_DIALOGUE } + 2 -> playerl(FacialExpression.FRIENDLY, "On second thought, never mind.").also { stage = END_DIALOGUE } } 20 -> { - npcl(FacialExpression.FRIENDLY, "Thank you much, kind sir. And here's your torso.") - if(removeItem(player, Item(995,7500000), Container.INVENTORY)) { - addItem(player, Items.FIGHTER_TORSO_10551, 1) + if (hasSpaceFor(player, Item(Items.FIGHTER_TORSO_10551)) || amountInInventory(player, Items.COINS_995) == TORSO_PRICE) { + npcl(FacialExpression.FRIENDLY, "Thank you much, kind sir. And here's your torso.") + if (removeItem(player, Item(Items.COINS_995, TORSO_PRICE), Container.INVENTORY)) { + addItem(player, Items.FIGHTER_TORSO_10551, 1) + } + } else { + npcl(FacialExpression.FRIENDLY, "Sorry, you don't have space for it! Give my regards to Player Name - he made me check this before I take your cash.") } stage = END_DIALOGUE } diff --git a/Server/src/main/content/minigame/bountyhunter/MaximillianSackvilleDialogue.kt b/Server/src/main/content/minigame/bountyhunter/MaximillianSackvilleDialogue.kt deleted file mode 100644 index a5b047041..000000000 --- a/Server/src/main/content/minigame/bountyhunter/MaximillianSackvilleDialogue.kt +++ /dev/null @@ -1,120 +0,0 @@ -package content.minigame.bountyhunter - -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable -import org.rs09.consts.NPCs -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE - -/** - * Provides dialogue tree for Maximillian Sackville, - * the Bounty Hounter roving banker. - * - * @author vddCore - */ -@Initializable -class MaximillianSackvilleDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - START_DIALOGUE -> when { - hasIronmanRestriction(player, IronmanMode.ULTIMATE) -> { - npcl( - FacialExpression.NEUTRAL, - "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( - FacialExpression.NEUTRAL, - "Good day, how may I help you?" - ).also { - if (hasAwaitingGrandExchangeCollections(player)) { - stage++ - } else { - stage += 2 - } - } - } - } - - 1 -> npcl( - FacialExpression.NEUTRAL, - "Before we go any further, I should inform you that you " + - "have items ready for collection from the Grand Exchange." - ).also { stage++ } - - 2 -> playerl( - FacialExpression.ASKING, - "Who are you?" - ).also { stage++ } - - 3 -> npcl( - FacialExpression.NEUTRAL, - "How inconsiderate of me, dear ${if (player.isMale) "sir" else "madam"}. " + - "My name is Maximillian Sackville and I conduct operations here on behalf " + - "of The Bank of Gielinor." - ).also { stage++ } - - 4 -> showTopics( - Topic(FacialExpression.NEUTRAL, "I'd like to access my bank account.", 10), - IfTopic( - FacialExpression.NEUTRAL, - "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", - 11, - hasActivatedSecondaryBankAccount(player) - ), - Topic(FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 12), - Topic(FacialExpression.NEUTRAL, "I'd like to collect items.", 13), - Topic(FacialExpression.ASKING, "Aren't you afraid of working in the Wilderness?", 5) - ) - - 5 -> npcl( - FacialExpression.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." - ).also { stage++ } - - 6 -> npcl( - FacialExpression.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." - ).also { stage = END_DIALOGUE } - - 10 -> { - openBankAccount(player) - end() - } - - 11 -> { - toggleBankAccount(player) - - npcl( - FacialExpression.NEUTRAL, - "Naturally. You can now access your ${getBankAccountName(player)} bank account." - ).also { stage = END_DIALOGUE } - } - - 12 -> { - openBankPinSettings(player) - end() - } - - 13 -> { - openGrandExchangeCollectionBox(player) - end() - } - } - - return true - } - - override fun getIds() = intArrayOf(NPCs.BANKER_6538) -} \ No newline at end of file diff --git a/Server/src/main/content/minigame/bountyhunter/UnimplementedCraterArea.kt b/Server/src/main/content/minigame/bountyhunter/UnimplementedCraterArea.kt new file mode 100644 index 000000000..60050f9c7 --- /dev/null +++ b/Server/src/main/content/minigame/bountyhunter/UnimplementedCraterArea.kt @@ -0,0 +1,75 @@ +package content.minigame.bountyhunter.handlers + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.NPCs +import core.game.dialogue.DialogueFile +import core.game.world.GameWorld + +class UnimplementedCraterArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf( + ZoneBorders(3200, 5632, 3391, 5823) + ) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player && ( + defineAreaBorders()[0].insideBorder(entity) + )) { + kickThemOut(entity) + } + } + + private fun kickThemOut(entity: Player) { + val watchdog = NPC(NPCs.BANKER_6538) + watchdog.isNeverWalks = true + watchdog.isWalks = false + watchdog.location = entity.location + watchdog.init() + entity.lock() + + runTask(watchdog, 1) { + watchdog.moveStep() + watchdog.face(entity) + openDialogue(entity, UnimplementedCraterDialogue(), watchdog) + GameWorld.Pulser.submit(object : Pulse() { + override fun pulse(): Boolean { + if (getAttribute(entity, "teleporting-away", false)) + return true + if (!entity.isActive) + poofClear(watchdog) + if (entity.dialogueInterpreter.dialogue == null || entity.dialogueInterpreter.dialogue.file == null) + openDialogue(entity, UnimplementedCraterDialogue(), watchdog) + return !watchdog.isActive || !entity.isActive + } + }) + } + } + + class UnimplementedCraterDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage) { + 0 -> npcl(core.game.dialogue.FacialExpression.WORRIED, "This is unimplemented content, and you are now stuck. Don't worry, I'll get you out of here!").also { stage++ } + 1 -> { + end() + visualize(npc!!, 1818, 343) + sendGraphics(342, player!!.location) + setAttribute(player!!, "teleporting-away", true) + runTask(player!!, 3) { + poofClear(npc!!) + teleport(player!!, Location.create(3179, 3685, 0)) + unlock(player!!) + removeAttribute(player!!, "teleporting-away") + } + } + } + } + } +} + diff --git a/Server/src/main/content/minigame/castlewars/CastleWarsListeners.kt b/Server/src/main/content/minigame/castlewars/CastleWarsListeners.kt index eed986625..24fdb55cc 100644 --- a/Server/src/main/content/minigame/castlewars/CastleWarsListeners.kt +++ b/Server/src/main/content/minigame/castlewars/CastleWarsListeners.kt @@ -18,7 +18,6 @@ import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.Sounds import rs09.game.content.activity.castlewars.areas.CastleWarsWaitingArea -import java.util.* @Suppress("unused") class CastleWarsListeners : InteractionListener { diff --git a/Server/src/main/content/minigame/castlewars/CastlewarsBook.kt b/Server/src/main/content/minigame/castlewars/CastlewarsBook.kt new file mode 100644 index 000000000..8395ece6a --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/CastlewarsBook.kt @@ -0,0 +1,146 @@ +package content.minigame.castlewars + +import content.data.Quests +import content.global.handlers.iface.BookInterface +import content.global.handlers.iface.BookLine +import content.global.handlers.iface.Page +import content.global.handlers.iface.PageSet +import core.api.getAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.system.command.sets.ModelViewerCommandSet.Companion.ATTRIBUTE_MODEL_NUMBER +import core.game.system.command.sets.ModelViewerCommandSet.Companion.ATTRIBUTE_PITCH +import core.game.system.command.sets.ModelViewerCommandSet.Companion.ATTRIBUTE_YAW +import core.game.system.command.sets.ModelViewerCommandSet.Companion.ATTRIBUTE_ZOOM +import core.game.system.command.sets.ModelViewerCommandSet.Companion.DEF_BOOK +import core.game.world.GameWorld +import org.rs09.consts.Items + +class CastlewarsBook : InteractionListener { + + companion object { + private val TITLE = "Castle Wars Manual" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Objective:", 38), + BookLine("The aim is to get into your", 39), + BookLine("opponents castle and take", 40), + BookLine("their team standard. Then", 41), + BookLine("bring that back and capture", 42), + BookLine("it on your teams standard. ", 43), + ), + Page( + BookLine("Toolkit:", 58), + BookLine("This useful item allows you", 59), + BookLine("to repair broken doors and", 60), + BookLine("catapults. Simply use it on", 61), + BookLine("the item to be repaired, or", 62), + BookLine("have one in your inventory", 63), + BookLine("when you select the option,", 64), + BookLine("and you'll rebuild it!", 65) + ) + ), + PageSet( + Page( + BookLine("Bandages:", 43), + BookLine("These can be used to heal", 44), + BookLine("some health and restore some", 45), + BookLine("of your running energy.", 46), + BookLine("You can also use them to", 47), + BookLine("heal fellow players.", 48), + ), + Page( + BookLine("Explosive Potion:", 58), + BookLine("A simple but effective item,", 59), + BookLine("use it to blow up your", 60), + BookLine("opponents catapult and", 61), + BookLine("barricades! It can also be", 62), + BookLine("used to clear the tunnels", 63), + BookLine("under the arena for some,", 64), + BookLine("sneak attacks into your", 65), + BookLine("opponents castle!", 66) + ) + ), + PageSet( + Page( + BookLine("Barricade:", 43), + BookLine("Use these constructs to block", 44), + BookLine("your opponents movement", 45), + BookLine("and prevent them accessing", 46), + BookLine("your castle. Each team can", 47), + BookLine("only have 10 built at any one", 48), + BookLine("time.", 49), + ), + Page( + BookLine("Bucket:", 58), + BookLine("Fill a bucket with water and", 59), + BookLine("you can use it to put out a", 60), + BookLine("burning catapult or barricade,", 61), + BookLine("but be quick or it'll be", 62), + BookLine("destroyed.", 63), + ) + ), + PageSet( + Page( + BookLine("Tinderbox:", 43), + BookLine("Logs aren't all that's", 44), + BookLine("flammable, use a tinderbox to", 45), + BookLine("set light to your opponents", 46), + BookLine("You can also use them to", 47), + BookLine("catapult and barricades.", 48), + ), + Page( + BookLine("Pickaxe:", 58), + BookLine("Use a pickaxe to mine your", 59), + BookLine("way through the tunnels", 60), + BookLine("under the arena for a sneak", 61), + BookLine("attack into your opponents", 62), + BookLine("castle. Don't forget to", 63), + BookLine("collapse the tunnels into your", 64), + BookLine(" own castle though! ", 65), + ) + ), + PageSet( + Page( + BookLine("Catapult:", 43), + BookLine("Use this war machine to", 44), + BookLine("launch rocks at your", 45), + BookLine("opponents. Just give it rough", 46), + BookLine("coordinates and let the rock", 47), + BookLine("fly, just be careful not to hit", 48), + BookLine("your team with it!", 49), + ), + Page( + BookLine("Rock:", 58), + BookLine("Used as ammo for the", 59), + BookLine("catapult, and not much else.", 60), + BookLine("Brings new meaning to the", 61), + BookLine("phrase 'flies like a rock'.", 62), + ) + ), + ) + private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_2_27, TITLE, CONTENTS) + BookInterface.clearModelsOnPage(player, BookInterface.FANCY_BOOK_2_27); + BookInterface.setItemOnPage(player, 0, Items.TOOLKIT_4051, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + BookInterface.setItemOnPage(player, 1, Items.BANDAGES_4049, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[2], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[2], 768, 192, 1792) + BookInterface.setItemOnPage(player, 1, Items.EXPLOSIVE_POTION_4045, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + BookInterface.setItemOnPage(player, 2, Items.BARRICADE_4053, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[2], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[2], 768, 192, 1792) + BookInterface.setItemOnPage(player, 2, Items.BUCKET_OF_WATER_1929, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + BookInterface.setItemOnPage(player, 3, Items.TINDERBOX_590, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[2], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[2], 768, 192, 1792) + BookInterface.setItemOnPage(player, 3, Items.BRONZE_PICKAXE_1265, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + BookInterface.setModelOnPage(player, 4, 38202, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[4], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[4], 4048, 192, 768) + BookInterface.setItemOnPage(player, 4, Items.ROCK_4043, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + return true + } + } + + override fun defineListeners() { + on(Items.CASTLEWARS_MANUAL_4055, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_2_27, ::display) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/castlewars/areas/CastleWarsArea.kt b/Server/src/main/content/minigame/castlewars/areas/CastleWarsArea.kt index ab1089bdc..c6b336c55 100644 --- a/Server/src/main/content/minigame/castlewars/areas/CastleWarsArea.kt +++ b/Server/src/main/content/minigame/castlewars/areas/CastleWarsArea.kt @@ -65,7 +65,6 @@ abstract class CastleWarsArea : MapArea, LogoutListener, InteractionListener { defineAreaBorders().forEach { border -> if (border.insideBorder(player)) { sendMessage(player, "You can't remove your team's colours") - // TODO: Equipping a cape or helmet causes issues return@onUnequip false } } diff --git a/Server/src/main/content/minigame/castlewars/areas/CastleWarsGameArea.kt b/Server/src/main/content/minigame/castlewars/areas/CastleWarsGameArea.kt index 2b32486aa..fb44ea7d9 100644 --- a/Server/src/main/content/minigame/castlewars/areas/CastleWarsGameArea.kt +++ b/Server/src/main/content/minigame/castlewars/areas/CastleWarsGameArea.kt @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneType import core.tools.Log import core.tools.ticksPerMinute import org.rs09.consts.Components @@ -70,6 +71,7 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener { override fun areaEnter(entity: Entity) { val player = entity as? Player ?: return super.areaEnter(player) + zone.zoneType = ZoneType.CASTLE_WARS.id registerTimer (player, spawnTimer("teleblock", (CastleWars.gameTimeMinutes)*60*2)) if (saradominPlayers.contains(player)) { diff --git a/Server/src/main/content/minigame/duel/DuelSession.java b/Server/src/main/content/minigame/duel/DuelSession.java index e28c9bfab..51b4a6d9b 100644 --- a/Server/src/main/content/minigame/duel/DuelSession.java +++ b/Server/src/main/content/minigame/duel/DuelSession.java @@ -263,7 +263,7 @@ public final class DuelSession extends ComponentPlugin { Player o = getOpposite(p); o.getImpactHandler().setDisabledTicks(6); o.teleport(RandomFunction.getRandomElement(DuelArea.RESPAWN_LOCATIONS)); - boolean victory = type == 0 || type == 2 || type == 1 && p.getImpactHandler().getImpactLog().containsKey(o); + boolean victory = type == 0 || type == 2 || type == 1 && p.getImpactHandler().getPlayerImpactLog().containsKey(o.getDetails().getUid()); fightState = 2; p.removeExtension(DuelSession.class); end(); diff --git a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt index 8a74870f1..d619d6539 100644 --- a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt @@ -74,8 +74,8 @@ class FishingTrawlerInteractionHandler : InteractionListener { } on(REWARD_NET, IntType.SCENERY, "inspect"){ player, _ -> - val session: FishingTrawlerSession? = player.getAttribute("ft-session",null) - if(session == null || session.boatSank){ + val rolls = player.getAttribute("/save:ft-rolls", 0) + if (rolls == 0) { player.dialogueInterpreter.sendDialogues(player, core.game.dialogue.FacialExpression.GUILTY,"I'd better not go stealing other people's fish.") return@on true } @@ -162,9 +162,8 @@ class NetLootDialogue(player: Player? = null): core.game.dialogue.DialoguePlugin } override fun open(vararg args: Any?): Boolean { - session = player.getAttribute("ft-session",null) - if(session == null) return false - rolls = ceil(session!!.fishAmount / session!!.players.size.toDouble()).toInt() + rolls = player.getAttribute("/save:ft-rolls", 0) + if (rolls == 0) return false player.dialogueInterpreter.sendOptions("Skip Junk Items?","Yes","No") stage = 0 return true @@ -177,7 +176,7 @@ class NetLootDialogue(player: Player? = null): core.game.dialogue.DialoguePlugin 2 -> TrawlerLoot.addLootAndMessage(player, level, rolls, false) } player.skills.addExperience(Skills.FISHING,(((0.015 * player.skills.getLevel(Skills.FISHING))) * player.skills.getLevel(Skills.FISHING)) * rolls) - player.removeAttribute("ft-session") + player.removeAttribute("ft-rolls") end() return true } diff --git a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt index 6e78ee35d..3cabb8d72 100644 --- a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt @@ -26,6 +26,7 @@ import core.game.system.command.sets.STATS_BASE import core.tools.secondsToTicks import core.tools.ticksToSeconds import java.util.concurrent.TimeUnit +import kotlin.math.ceil import kotlin.random.Random @@ -153,6 +154,9 @@ class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : MapA player.interfaceManager.closeOverlay() player.properties.teleportLocation = Location.create(2666, 3162, 0) player.incrementAttribute("/save:$STATS_BASE:$FISHING_TRAWLER_GAMES_WON") + val rolls = ceil(session.fishAmount / session.players.size.toDouble()).toInt() + player.removeAttribute("ft-session") + player.setAttribute("/save:ft-rolls", rolls) clearLogoutListener(player, "ft-logout") } session.zone.unregister(getRegionBorders(session.region.id)) @@ -160,6 +164,9 @@ class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : MapA for(player in session.players){ session.updateOverlay(player) + if(session.timeLeft <= 1) { + lockInteractions(player, 2) + } } session.tickMurphy() return !session.isActive diff --git a/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt b/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt index 70ff89220..87ed06dbe 100644 --- a/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt +++ b/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt @@ -1,8 +1,12 @@ package content.minigame.fishingtrawler import content.global.skill.fishing.Fish +import core.api.Container +import core.api.addItem +import core.api.addItemOrDrop import core.api.splitLines import core.game.node.entity.player.Player +import core.game.node.entity.player.link.IronmanMode import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.game.node.item.WeightedChanceItem @@ -56,25 +60,35 @@ object TrawlerLoot { */ @JvmStatic fun addLootAndMessage(player: Player, fishLevel: Int, rolls: Int, skipJunk: Boolean) { - val frequencyList = listOf>(HashMap(), HashMap(), HashMap()) + if (rolls < 1) return + val frequencyList = listOf>(HashMap(), HashMap(), HashMap()) getLoot(fishLevel, rolls, skipJunk).forEach { - if (!player.bank.add(it)) GroundItemManager.create(it, player) when (it.id) { - in trawlerFishIds -> frequencyList[0].merge(it.name, 1, Int::plus) - in trawlerMisc -> frequencyList[1].merge(it.name, 1, Int::plus) - in junkItems -> frequencyList[2].merge(it.name, 1, Int::plus) + in trawlerFishIds -> frequencyList[0].merge(it.id, 1, Int::plus) + in trawlerMisc -> frequencyList[1].merge(it.id, 1, Int::plus) + in junkItems -> frequencyList[2].merge(it.id, 1, Int::plus) } } - player.sendMessage(colorize("%RYour reward has been sent to your bank:")) - // Extract and join each frequency maps entries as string. Split based on length, then send each line as message. + // Extract and join each frequency map's entries as items frequencyList.forEachIndexed { idx, fMap -> if (fMap.isNotEmpty()) { + // Give reward + fMap.forEach { + if (player.ironmanManager.mode == IronmanMode.ULTIMATE || !addItem(player, it.key, it.value, Container.BANK)) { + val notedIdIfFish = if (idx == 0) it.key + 1 else it.key + addItemOrDrop(player, notedIdIfFish, it.value) + } + } + // Split based on length, then send each line as message splitLines( - fMap.entries.joinToString(prefix = if (idx == 0) "Fish: " else if (idx == 1) "Misc: " else "Junk: ", postfix = ".") { "${it.key}: ${it.value}" }, + fMap.entries.joinToString(prefix = if (idx == 0) "Fish: " else if (idx == 1) "Misc: " else "Junk: ", postfix = ".") { "${Item(it.key).name}: ${it.value}" }, 85 ).forEach { player.sendMessage(it) } } } + if (player.ironmanManager.mode != IronmanMode.ULTIMATE) { + player.sendMessage(colorize("%RYour reward has been sent to your bank:")) + } } private val lootTable = arrayOf( diff --git a/Server/src/main/content/minigame/mta/EnchantSpell.kt b/Server/src/main/content/minigame/mta/EnchantSpell.kt index d6df04fe8..53f05bc13 100644 --- a/Server/src/main/content/minigame/mta/EnchantSpell.kt +++ b/Server/src/main/content/minigame/mta/EnchantSpell.kt @@ -1,6 +1,8 @@ package content.minigame.mta import content.minigame.mta.impl.EnchantingZone.Shapes +import core.ServerConstants +import core.api.replaceSlot import core.game.node.Node import core.game.node.entity.Entity import core.game.node.entity.combat.spell.SpellType @@ -9,6 +11,8 @@ import core.game.node.entity.player.link.SpellBookManager.SpellBook import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.combat.spell.MagicSpell import core.game.node.entity.combat.spell.Runes +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 core.game.world.update.flag.context.Graphics @@ -47,7 +51,7 @@ class EnchantSpell : MagicSpell { return false } entity.interfaceManager.setViewedTab(6) - val enchanted = jewellery?.getOrDefault(target.id,null) + var enchanted = jewellery?.getOrDefault(target.id,null) if (enchanted == null) { entity.packetDispatch.sendMessage("You can't use this spell on this item.") @@ -56,12 +60,15 @@ class EnchantSpell : MagicSpell { if (!meetsRequirements(entity, true, true)) { return false } - - if (entity.inventory.remove(target)) { - visualize(entity, target) - entity.inventory.add(enchanted) + if (enchanted.id == Items.RING_OF_WEALTH_2572 && ServerConstants.RING_OF_WEALTH_TELEPORT) { + enchanted = Item(Items.RING_OF_WEALTH_14638) } + visualize(entity, target) + val ret = replaceSlot(entity, target.slot, enchanted) + if (ret != target) { + PlayerMonitor.log(entity, LogType.DUPE_ALERT, "Unknown slot-replacement problem when enchanting jewellery (adding $enchanted replaced $ret rather than $target)") + } //MTA-Specific Code if (entity.zoneMonitor.isInZone("Enchantment Chamber")) { @@ -184,10 +191,10 @@ class EnchantSpell : MagicSpell { SpellBook.MODERN.register(51, EnchantSpell(68, 78.0, mapOf( //Begin Jewelry Enchantment - Items.DRAGONSTONE_RING_1645 to Item(14646), - Items.DRAGON_NECKLACE_1664 to Item(Items.SKILLS_NECKLACE4_11105), - Items.DRAGONSTONE_AMMY_1702 to Item(Items.AMULET_OF_GLORY4_1712), - Items.DRAGON_BRACELET_11115 to Item(Items.COMBAT_BRACELET4_11118), + Items.DRAGONSTONE_RING_1645 to Item(Items.RING_OF_WEALTH_2572), + Items.DRAGON_NECKLACE_1664 to Item(Items.SKILLS_NECKLACE_11113), + Items.DRAGONSTONE_AMMY_1702 to Item(Items.AMULET_OF_GLORY_1704), + Items.DRAGON_BRACELET_11115 to Item(Items.COMBAT_BRACELET_11126), //Begin MTA-Specific Enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), diff --git a/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java index e2d496492..72728d1e6 100644 --- a/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java +++ b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java @@ -4,6 +4,7 @@ import content.minigame.mta.impl.TelekineticZone; import core.game.interaction.SpecialGroundItems; import core.game.node.Node; import core.game.node.entity.Entity; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.spell.SpellType; import core.game.node.entity.combat.spell.SpellBlocks; import core.game.node.entity.impl.Projectile; @@ -24,7 +25,7 @@ import core.game.global.action.PickupHandler; import core.game.world.GameWorld; import org.rs09.consts.Sounds; -import static core.api.ContentAPIKt.playAudio; +import static core.api.ContentAPIKt.*; /** * Represents the telekenitic grab spell. @@ -176,8 +177,12 @@ public final class TelekineticGrabSpell extends MagicSpell { } if (entity instanceof Player) { final Player player = (Player) entity; - if (!player.getInventory().hasSpaceFor(((Item) item))) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); + if (!CombatSwingHandler.isProjectileClipped(player, item, false)) { + sendMessage(player, "I can't reach that."); //TODO authentic message? + return false; + } + if (!hasSpaceFor(player, item)) { + sendMessage(player, "You don't have enough inventory space."); return false; } if (!PickupHandler.canTake(player, item, 1)) { diff --git a/Server/src/main/content/minigame/mta/impl/TelekineticZone.java b/Server/src/main/content/minigame/mta/impl/TelekineticZone.java index 1ff0f9b7e..a810548d6 100644 --- a/Server/src/main/content/minigame/mta/impl/TelekineticZone.java +++ b/Server/src/main/content/minigame/mta/impl/TelekineticZone.java @@ -32,7 +32,7 @@ import content.minigame.mta.MTAZone; /** * Handles the telekinetic zone. - * @author Vexia + * @author Vexia, Player Name */ public class TelekineticZone extends MTAZone { @@ -302,7 +302,7 @@ public class TelekineticZone extends MTAZone { } Location next = statue.getLocation().transform(dir, 1); Path path = Pathfinder.find(statue.getLocation(), next); - boolean end = !path.isSuccessful() || path.isMoveNear() || path.getPoints().size() != 1; + boolean end = !path.isSuccessful() || path.isMoveNear() || path.getPoints().size() > 2; if (end) { return true; } diff --git a/Server/src/main/content/minigame/pestcontrol/PCLanderZone.java b/Server/src/main/content/minigame/pestcontrol/PCLanderZone.java index 21cc7fce5..5ace493bb 100644 --- a/Server/src/main/content/minigame/pestcontrol/PCLanderZone.java +++ b/Server/src/main/content/minigame/pestcontrol/PCLanderZone.java @@ -80,7 +80,7 @@ public final class PCLanderZone extends MapZone { for (PestControlActivityPlugin a : activities) { if (a.getWaitingPlayers().remove(e)) { if (logout) { - e.getProperties().setTeleportLocation(a.getLeaveLocation()); + e.setLocation(a.getLeaveLocation()); } break; } diff --git a/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java b/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java index ffab29af7..9140bfda2 100644 --- a/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java +++ b/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java @@ -15,6 +15,8 @@ import core.game.node.item.Item; import core.plugin.Plugin; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.getStatLevel; + /** * Represents the pest control reward interface. * @author 'Vexia @@ -185,20 +187,36 @@ public final class PCRewardInterface extends ComponentPlugin { } /** - * Method used to calculate the experience the player can recieve in this - * skill. + * Method used to calculate the experience the player can receive in this skill. * @param player the player. * @return the experience as an integer. */ - public static double calculateExperience(final Player player, final int skillId) { - int level = player.getSkills().getStaticLevel(skillId); - double divideBy = 30;//17.5-33 ideal range - if (skillId == Skills.PRAYER) { - divideBy = 67;// 34-75 ideal range - } else if (skillId == Skills.MAGIC || skillId == Skills.RANGE) { - divideBy = 29;//19.1-31 ideal range + public static int calculateExperience(final Player player, final int skillId, final int points) { + int level = getStatLevel(player, skillId); + int N = 0; + switch (skillId) { + case Skills.PRAYER: + N = 18; + break; + case Skills.MAGIC: + case Skills.RANGE: + N = 32; + break; + case Skills.ATTACK: + case Skills.STRENGTH: + case Skills.DEFENCE: + case Skills.HITPOINTS: + N = 35; + break; } - return (int) ((level * level) / divideBy) * (player.getSkills().experienceMutiplier / 2); + int xpPerPoint = (int) ((double) (level * level) / 600) * N; + double bonus = 1.0; + if (points >= 100) { + bonus = 1.1; + } else if (points >= 10) { + bonus = 1.01; + } + return (int) (points * xpPerPoint * bonus); } /** @@ -207,7 +225,7 @@ public final class PCRewardInterface extends ComponentPlugin { * @param skillId the skillId. * @return the string to send. */ - public static final String getSkillCondition(final Player player, final int skillId) { + public static String getSkillCondition(final Player player, final int skillId) { if (player.getSkills().getStaticLevel(skillId) < 25) { return RED + "Must reach level 25 first."; } @@ -221,7 +239,7 @@ public final class PCRewardInterface extends ComponentPlugin { * @return the string. */ public static String getSkillXp(final Player player, int skillId) { - return Skills.SKILL_NAME[skillId] + " - " + (int) calculateExperience(player, skillId) + " xp"; + return Skills.SKILL_NAME[skillId] + " - " + calculateExperience(player, skillId, 1) + " xp"; } /** @@ -229,7 +247,7 @@ public final class PCRewardInterface extends ComponentPlugin { * @param skill the skill index. * @return the skill child id. */ - public static final int getSkillChild(final int skill) { + public static int getSkillChild(final int skill) { return SKILL_HEADER[skill]; } @@ -264,7 +282,7 @@ public final class PCRewardInterface extends ComponentPlugin { * Method used to confirm the reward. * @param player the player. */ - public final void confirm(final Player player) { + public void confirm(final Player player) { if (!hasReward(player)) { player.getPacketDispatch().sendMessage("Please choose a reward."); return; @@ -281,9 +299,9 @@ public final class PCRewardInterface extends ComponentPlugin { if (player.getSavedData().getActivityData().getPestPoints() >= points) { player.getSavedData().getActivityData().decreasePestPoints(points); if (reward.isSkillReward()) { - final double experience = ((int) calculateExperience(player, reward.getSkill()) * points); + int experience = calculateExperience(player, reward.getSkill(), points); player.getSkills().addExperience(reward.getSkill(), experience); - message = "The Void Knight has granted you " + (int) (experience * player.getSkills().experienceMutiplier) + " " + reward.getName() + "."; + message = "The Void Knight has granted you " + experience + " " + reward.getName() + "."; } else { if (!reward.checkItemRequirement(player, option)) { return; diff --git a/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt b/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt index b54f8dd36..a295a8795 100644 --- a/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt +++ b/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt @@ -13,6 +13,7 @@ import org.rs09.consts.Items import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType +import content.data.Quests /** * Adds functionality to the pharoah's scepter @@ -23,7 +24,7 @@ class PharaohSceptre : InteractionListener { val SCEPTRES = intArrayOf(Items.PHARAOHS_SCEPTRE_9044, Items.PHARAOHS_SCEPTRE_9046, Items.PHARAOHS_SCEPTRE_9048, Items.PHARAOHS_SCEPTRE_9050) on(SCEPTRES, IntType.ITEM, "teleport", "operate"){ player, node -> - if (!hasRequirement(player, "Icthlarin's Little Helper")) + if (!hasRequirement(player, Quests.ICTHLARINS_LITTLE_HELPER)) return@on true val sceptre = node.asItem() diff --git a/Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt b/Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt index 4739bf5c2..53eb47fa9 100644 --- a/Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt +++ b/Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt @@ -23,6 +23,7 @@ import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.world.GameWorld import core.plugin.ClassScanner +import content.data.Quests class GardenObjectsPlugin : InteractionListener { @@ -503,7 +504,7 @@ class GardenObjectsPlugin : InteractionListener { override fun open(vararg args: Any): Boolean { npc = args[0] as NPC - quest = player.questRepository.getQuest("Prince Ali Rescue") + quest = player.questRepository.getQuest(Quests.PRINCE_ALI_RESCUE) when (quest!!.getStage(player)) { 100 -> { interpreter.sendDialogues(player, null, "I'd like to talk about sq'irks.") diff --git a/Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java b/Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java index 4e6be91c4..bc3aa169b 100644 --- a/Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java +++ b/Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java @@ -11,6 +11,7 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Graphics; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Dialogue for Sorceress Apprentice @@ -238,7 +239,7 @@ public class SorceressApprenticeDialogue extends DialoguePlugin { } public static void teleport(final NPC npc, final Player player) { - if (!hasRequirement(player, "Prince Ali Rescue")) + if (!hasRequirement(player, Quests.PRINCE_ALI_RESCUE)) return; npc.faceTemporary(player, 4); npc.graphics(new Graphics(108)); diff --git a/Server/src/main/content/minigame/troublebrewing/TroubleBrewingArea.kt b/Server/src/main/content/minigame/troublebrewing/TroubleBrewingArea.kt new file mode 100644 index 000000000..55a299b4e --- /dev/null +++ b/Server/src/main/content/minigame/troublebrewing/TroubleBrewingArea.kt @@ -0,0 +1,18 @@ +package content.minigame.troublebrewing + +import core.api.MapArea +import core.api.getRegionBorders +import core.game.node.entity.Entity +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneType + +class TroubleBrewingArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(15150)) + } + + override fun areaEnter(entity: Entity) { + zone.zoneType = ZoneType.TROUBLE_BREWING.id + super.areaEnter(entity) + } +} diff --git a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt index 787e1fc5c..9fe119605 100644 --- a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt +++ b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt @@ -3,8 +3,21 @@ package content.minigame.vinesweeper import BlinkinDialogue import FarmerDialogue.Companion.FARMER_FLAG_LINES import WinkinDialogue +import content.minigame.vinesweeper.Vinesweeper.Companion.FARMERS +import content.minigame.vinesweeper.Vinesweeper.Companion.FARMER_CLEAR_RADIUS +import content.minigame.vinesweeper.Vinesweeper.Companion.HOLES +import content.minigame.vinesweeper.Vinesweeper.Companion.NUMBERS +import content.minigame.vinesweeper.Vinesweeper.Companion.RABBITS +import content.minigame.vinesweeper.Vinesweeper.Companion.SEED_LOCS +import content.minigame.vinesweeper.Vinesweeper.Companion.populateSeeds +import content.minigame.vinesweeper.Vinesweeper.Companion.scheduleNPCs +import content.minigame.vinesweeper.Vinesweeper.Companion.sendPoints import core.api.* +import core.cache.def.impl.ItemDefinition import core.game.component.Component +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener import core.game.interaction.MovementPulse import core.game.node.entity.Entity import core.game.node.entity.combat.DeathTask @@ -18,6 +31,8 @@ import core.game.node.item.Item import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.GameWorld.ticks import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.map.zone.ZoneBorders @@ -25,21 +40,9 @@ import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import core.plugin.Initializable import core.tools.RandomFunction -import content.minigame.vinesweeper.Vinesweeper.Companion.FARMERS -import content.minigame.vinesweeper.Vinesweeper.Companion.FARMER_CLEAR_RADIUS -import content.minigame.vinesweeper.Vinesweeper.Companion.HOLES -import content.minigame.vinesweeper.Vinesweeper.Companion.NUMBERS -import content.minigame.vinesweeper.Vinesweeper.Companion.RABBITS -import content.minigame.vinesweeper.Vinesweeper.Companion.SEED_LOCS -import content.minigame.vinesweeper.Vinesweeper.Companion.populateSeeds -import content.minigame.vinesweeper.Vinesweeper.Companion.scheduleNPCs -import content.minigame.vinesweeper.Vinesweeper.Companion.sendPoints -import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import core.game.interaction.InterfaceListener -import core.game.world.GameWorld -import core.game.world.GameWorld.ticks import org.rs09.consts.* +import kotlin.math.max +import kotlin.math.min import org.rs09.consts.Graphics as Gfx import org.rs09.consts.Scenery as Sceneries @@ -60,9 +63,9 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { if(entity is Player) { entity.interfaceManager.closeOverlay() if(!logout) { - entity.sendMessage("Winkin's Farm thanks you for your visit.") - entity.sendMessage("Leftover ogleroots and flags have been returned to the establishment.") - entity.sendMessage("You have been reimbursed at a rate of 10gp per ogleroot and the flags have been collected.") + sendMessage(entity, "Winkin's Farm thanks you for your visit.") + sendMessage(entity, "Leftover ogleroots and flags have been returned to the establishment.") + sendMessage(entity, "You have been reimbursed at a rate of 10gp per ogleroot and the flags have been collected.") val flags = entity.inventory.getAmount(Item(Items.FLAG_12625)) if(flags > 0) { entity.setAttribute("/save:vinesweeper:stored-flags", flags) @@ -82,7 +85,12 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { on(Sceneries.PORTAL_29534, IntType.SCENERY, "enter") { player, _ -> val x = player.getAttribute("vinesweeper:return-tele:x", 3052) val y = player.getAttribute("vinesweeper:return-tele:y", 3304) - teleport(player, Location(x, y)) + val loc = Location(x, y) + if (ZoneBorders.forRegion(11060).insideBorder(loc) && !ItemDefinition.canEnterEntrana(player)) { + sendMessage(player, "The power of Saradomin prevents you from taking armour or weaponry to Entrana."); + return@on true + } + teleport(player, loc) return@on true } on(SIGNS, IntType.SCENERY, "read") { player, node -> @@ -210,7 +218,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { player.packetDispatch.sendInterfaceConfig(686, 60, true) val level = player.skills.getStaticLevel(Skills.FARMING) // TODO: more precise formula - val points_per_xp = if (level < 40) { 2.0*(40.0 - level.toDouble())/10.0 } else { 1.0 } + val points_per_xp = if (level < 40) { max(1.0, 2.0*(40.0 - level.toDouble())/10.0) } else { 1.0 } val points = player.getAttribute("vinesweeper:points", 0) val xp = points / points_per_xp player.skills.addExperience(Skills.FARMING, xp) @@ -369,7 +377,10 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { } fun populateSeeds() { - while(SEED_LOCS.size < MAX_SEEDS) { + val holes = countHoles() + val seeds = min(1.0 * MAX_SEEDS, holes * 0.13).toInt() + var tries = 0 // Prevent the while loop from crashing the server + while(SEED_LOCS.size < seeds && tries++ < 1000) { val loc = VINESWEEPER_BORDERS.getRandomLoc() val scenery = getScenery(loc) if(scenery != null && HOLES.contains(scenery.id)) { @@ -378,9 +389,23 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { } } - fun faceHole(player: Player, hole: Scenery) { - + private fun countHoles(): Int { + val northEastX = VINESWEEPER_BORDERS.northEastX + val northEastY = VINESWEEPER_BORDERS.northEastY + val southWestX = VINESWEEPER_BORDERS.southWestX + val southWestY = VINESWEEPER_BORDERS.southWestY + var holeCount = 0 + for (x in southWestX .. northEastX){ + for (y in southWestY .. northEastY){ + val scenery = getScenery(x, y, 0) + if(scenery != null && HOLES.contains(scenery.id)) { + holeCount++ + } + } + } + return holeCount } + fun plantFlag(player: Player, hole: Scenery) { if(player.inventory.remove(Item(Items.FLAG_12625, 1))) { player.lock() diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AchiettiesDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AchiettiesDialogue.kt deleted file mode 100644 index a4585b87e..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AchiettiesDialogue.kt +++ /dev/null @@ -1,39 +0,0 @@ -import core.api.openDialogue -import core.game.dialogue.DialogueBuilder -import core.game.dialogue.DialogueBuilderFile -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player -import core.plugin.Initializable -import org.rs09.consts.NPCs - -/** - * @author qmqz - * @author Trident101 - */ - -@Initializable -class AchiettiesDialogue(player: Player? = null) : DialoguePlugin(player){ - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - openDialogue(player, AchiettiesDialogueFile(), npc) - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return AchiettiesDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.ACHIETTIES_796) - } -} - -class AchiettiesDialogueFile : DialogueBuilderFile() { - - override fun create(b: DialogueBuilder) { - b.defaultDialogue().npcl(FacialExpression.FRIENDLY, - "Greetings. Welcome to the Heroes' Guild." - ) - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BernaldDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BernaldDialogue.kt index 7984ceed8..2e1ca6b44 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BernaldDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BernaldDialogue.kt @@ -20,8 +20,8 @@ class BernaldDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { // // Garden of Tranquility has not been implemented. -// if(hasRequirement(player!!, "Garden of Tranquility")) { -// if (isQuestComplete(player!!, DeathPlateau.questName)) { +// if(hasRequirement(player!!, Quests.GARDEN_OF_TRANQUILITY)) { +// if (isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { // when (stage) { // 0 -> playerl(FacialExpression.FRIENDLY, "How are your grapes coming along?").also { stage++ } // 1 -> npc(FacialExpression.FRIENDLY, "Marvellous, thanks to your help, " + player.username + "!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.kt index ca982ff6c..8a885cd94 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests /** * Breoca Dialogue @@ -18,7 +19,7 @@ import org.rs09.consts.NPCs @Initializable class BreocaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int) : Boolean { - if(isQuestComplete(player!!, "Death Plateau")) { + if(isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } 1 -> npcl(FacialExpression.HAPPY, "I heard about what you did, thank you!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.kt index 6eae6fa18..006641ebd 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests /** * Ceolburg Dialogue @@ -18,7 +19,7 @@ import org.rs09.consts.NPCs @Initializable class CeolburgDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int) : Boolean { - if(isQuestComplete(player!!, "Death Plateau")) { + if(isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } 1 -> npcl(FacialExpression.HAPPY, "I heard about what you did, thank you!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.kt index 430da5566..2724f9c19 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.kt @@ -1,8 +1,7 @@ package content.region.asgarnia.burthorpe.dialogue -import content.region.asgarnia.burthorpe.quest.deathplateau.DeathPlateau +import content.data.Quests import content.region.asgarnia.burthorpe.quest.deathplateau.DenulthDialogueFile -import content.region.asgarnia.burthorpe.quest.trollstronghold.TrollStronghold import core.api.isQuestComplete import core.api.isQuestInProgress import core.api.openDialogue @@ -25,7 +24,7 @@ class DenulthDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { // When Troll Stronghold is complete - if (isQuestComplete(player!!, TrollStronghold.questName)) { + if (isQuestComplete(player!!, Quests.TROLL_STRONGHOLD)) { when(stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } 1 -> npcl(FacialExpression.HAPPY, "Welcome back friend!").also { stage++ } @@ -49,13 +48,13 @@ class DenulthDialogue(player: Player? = null) : DialoguePlugin(player) { } // Troll Stronghold in progress - if (isQuestInProgress(player!!, TrollStronghold.questName, 1, 99)) { + if (isQuestInProgress(player!!, Quests.TROLL_STRONGHOLD, 1, 99)) { openDialogue(player!!, content.region.asgarnia.burthorpe.quest.trollstronghold.DenulthDialogueFile(), npc) return true } // When Death Plateau is completed, start Troll Stronghold - if (isQuestComplete(player!!, DeathPlateau.questName)) { + if (isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { when(stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } 1 -> npcl(FacialExpression.HAPPY, "Welcome back friend!").also { stage++ } @@ -78,7 +77,7 @@ class DenulthDialogue(player: Player? = null) : DialoguePlugin(player) { ) 16 -> npcl(FacialExpression.HAPPY, "God speed friend! I would send some of my men with you, but none of them are brave enough to follow.").also { stage = END_DIALOGUE - setQuestStage(player!!, TrollStronghold.questName, 1) + setQuestStage(player!!, Quests.TROLL_STRONGHOLD, 1) } 20 -> npcl(FacialExpression.ANGRY, "You are right citizen. The White Knights have taken advantage of the old and weak king, they control most of Asgarnia, including Falador. However they do not control Burthorpe!").also { stage++ } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.kt index 6346fc9f6..73759083a 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.kt @@ -1,15 +1,16 @@ package content.region.asgarnia.burthorpe.dialogue -import content.region.asgarnia.burthorpe.quest.deathplateau.DeathPlateau -import content.region.asgarnia.burthorpe.quest.trollstronghold.TrollStronghold +import content.data.Quests import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.dialogue.Topic import core.game.node.entity.player.Player +import core.game.node.item.Item import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import org.rs09.consts.Items import org.rs09.consts.NPCs /** @@ -24,11 +25,12 @@ class DunstanDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { // When Troll Stronghold is complete - if (isQuestComplete(player!!, TrollStronghold.questName)) { + if (isQuestComplete(player!!, Quests.TROLL_STRONGHOLD)) { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } 1 -> npcl(FacialExpression.FRIENDLY, "Hi! What can I do for you?").also { stage++ } 2 -> showTopics( + Topic(FacialExpression.THINKING, "Can you put some spikes on my Climbing boots?", 30), Topic(FacialExpression.THINKING, "Is it OK if I use your anvil?", 10), Topic(FacialExpression.FRIENDLY, "Nothing, thanks.", END_DIALOGUE), Topic(FacialExpression.FRIENDLY, "How is your son getting on?", 15), @@ -42,22 +44,54 @@ class DunstanDialogue(player: Player? = null) : DialoguePlugin(player) { 15 -> npcl(FacialExpression.FRIENDLY, "He is getting on fine! He has just been promoted to Sergeant! I'm really proud of him!").also { stage++ } 16 -> playerl(FacialExpression.FRIENDLY, "I'm happy for you!").also { stage++ } 17 -> npcl(FacialExpression.FRIENDLY, "Anything else before I get on with my work?").also { stage = 2 } + 30 -> playerl(FacialExpression.FRIENDLY, "Can you put some spikes on my Climbing boots?").also { stage++ } + 31 -> npcl(FacialExpression.NEUTRAL,"For you, no problem.").also { stage++ } + 32 -> npc(FacialExpression.THINKING, "Do you realise that you can only use the Climbing", "boots right now? The Spiked boots can only be used in", "the Icelands but no ones been able to get there for", "years!").also { stage++ } + 33 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, but I still want them.", 40, true), + Topic(FacialExpression.NEUTRAL, "Oh OK, I'll leave them thanks.", 43), + ) + 40 -> { + if (inInventory(player!!, Items.CLIMBING_BOOTS_3105) && inInventory(player!!, Items.IRON_BAR_2351)) { + sendDoubleItemDialogue(player!!, Items.IRON_BAR_2351, Items.CLIMBING_BOOTS_3105, "You give Dunstan an Iron bar and the climbing boots.") + sendMessage(player!!, "You give Dunstan an Iron bar and the climbing boots.") + if (removeItem(player!!, Item(Items.CLIMBING_BOOTS_3105)) && removeItem(player!!, Item(Items.IRON_BAR_2351))) { + addItemOrDrop(player!!, Items.SPIKED_BOOTS_3107) + stage++ + } else { + stage = END_DIALOGUE + } + } else if (inInventory(player!!, Items.CLIMBING_BOOTS_3105)){ + npcl(FacialExpression.NEUTRAL,"Sorry, I'll need an iron bar to make the spikes.") + stage = 2 + } else { + playerl(FacialExpression.NEUTRAL,"I don't have them on me.") + stage = 2 + } + } + 41 -> sendItemDialogue(player!!, Items.SPIKED_BOOTS_3107, "Dunstan has given you the spiked boots.").also { stage++ + sendMessage(player!!, "Dunstan has given you the spiked boots.") + } + 43 -> npcl(FacialExpression.FRIENDLY, "Anything else before I get on with my work?").also { + stage = 2 + } } return true } // Troll Stronghold in progress - if (isQuestInProgress(player!!, TrollStronghold.questName, 1, 99)) { + if (isQuestInProgress(player!!, Quests.TROLL_STRONGHOLD, 1, 99)) { openDialogue(player!!, content.region.asgarnia.burthorpe.quest.trollstronghold.DunstanDialogueFile(), npc) return true } // When Death Plateau is complete - if (isQuestComplete(player!!, DeathPlateau.questName)) { + if (isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } 1 -> npcl(FacialExpression.FRIENDLY, "Hi! What can I do for you?").also { stage++ } 2 -> showTopics( + Topic(FacialExpression.THINKING, "Can you put some spikes on my Climbing boots?", 30), Topic(FacialExpression.THINKING, "Is it OK if I use your anvil?", 10), Topic(FacialExpression.FRIENDLY, "Nothing, thanks.", END_DIALOGUE), Topic(FacialExpression.FRIENDLY, "How is your son getting on?", 15), @@ -70,12 +104,43 @@ class DunstanDialogue(player: Player? = null) : DialoguePlugin(player) { 15 -> npcl(FacialExpression.SAD, "He was captured by those cursed trolls! I don't know what to do. Even the imperial guard are too afraid to go rescue him.").also { stage++ } 16 -> playerl(FacialExpression.ASKING, "What happened?").also { stage++ } 17 -> npcl(FacialExpression.SAD, "Talk to Denulth, he can tell you all about it. Anything else before I get on with my work?").also { stage = 2 } + 30 -> npcl(FacialExpression.NEUTRAL,"For you, no problem.").also { stage++ } + 31 -> npc(FacialExpression.THINKING, "Do you realise that you can only use the Climbing", "boots right now? The Spiked boots can only be used in", "the Icelands but no ones been able to get there for", "years!").also { stage++ } + 32 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, but I still want them.", 40, true), + Topic(FacialExpression.NEUTRAL, "Oh OK, I'll leave them thanks.", 43), + ) + 40 -> { + if (inInventory(player!!, Items.CLIMBING_BOOTS_3105) && inInventory(player!!, Items.IRON_BAR_2351)) { + sendDoubleItemDialogue(player!!, Items.IRON_BAR_2351, Items.CLIMBING_BOOTS_3105, "You give Dunstan an Iron bar and the climbing boots.") + sendMessage(player!!, "You give Dunstan an Iron bar and the climbing boots.") + if (removeItem(player!!, Item(Items.CLIMBING_BOOTS_3105)) && removeItem(player!!, Item(Items.IRON_BAR_2351))) { + addItemOrDrop(player!!, Items.SPIKED_BOOTS_3107) + stage++ + } else { + stage = END_DIALOGUE + } + } else if (inInventory(player!!, Items.CLIMBING_BOOTS_3105)){ + npcl(FacialExpression.NEUTRAL,"Sorry, I'll need an iron bar to make the spikes.") + stage = 2 + } else { + playerl(FacialExpression.NEUTRAL,"I don't have them on me.") + stage = 2 + } + } + 41 -> sendItemDialogue(player!!, Items.SPIKED_BOOTS_3107, "Dunstan has given you the spiked boots.").also { + stage = 43 + sendMessage(player!!, "Dunstan has given you the spiked boots.") + } + 43 -> npcl(FacialExpression.FRIENDLY, "Anything else before I get on with my work?").also { + stage = 2 + } } return true } // Death Plateau in progress - if (isQuestInProgress(player!!, DeathPlateau.questName, 21, 24)) { + if (isQuestInProgress(player!!, Quests.DEATH_PLATEAU, 21, 24)) { // Call the dialogue file for Dunstan from the deathplateau quest folder. openDialogue(player!!, content.region.asgarnia.burthorpe.quest.deathplateau.DunstanDialogueFile(), npc) return true diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt index 9a200f9e5..e59b3d907 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.dialogue +import core.ServerConstants import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -21,7 +22,7 @@ import core.tools.START_DIALOGUE class EmeraldBenedictDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { - START_DIALOGUE -> if(hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { + START_DIALOGUE -> if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { npcl( FacialExpression.ANNOYED, "Get lost, tin can." @@ -53,6 +54,12 @@ class EmeraldBenedictDialogue(player: Player? = null) : DialoguePlugin(player) { 4, hasActivatedSecondaryBankAccount(player) ), + IfTopic( + FacialExpression.ASKING, + "Yes, but can you open a secondary bank account for me?", + 7, + ServerConstants.SECOND_BANK && !hasActivatedSecondaryBankAccount(player) + ), Topic(FacialExpression.ASKING, "Yes, but can you show me my PIN settings?", 5), Topic(FacialExpression.ASKING, "Yes, but can you show me my collection box?", 6), Topic(FacialExpression.ANNOYED, "Yes, thanks. And I'll keep hold of it too.", END_DIALOGUE) @@ -68,7 +75,7 @@ class EmeraldBenedictDialogue(player: Player? = null) : DialoguePlugin(player) { npcl( FacialExpression.SUSPICIOUS, "Sure thing. Feel free to rummage through whatever's in your ${getBankAccountName(player)} now." - ).also { stage = END_DIALOGUE } + ).also { stage = 2 } } 5 -> { @@ -80,10 +87,24 @@ class EmeraldBenedictDialogue(player: Player? = null) : DialoguePlugin(player) { openGrandExchangeCollectionBox(player) end() } + + 7 -> { + npcl( + FacialExpression.SUSPICIOUS, + "Sure, just give me five million in gold and I'll take care of it." + ).also { stage++ } + } + + 8 -> { + playerl( + FacialExpression.SUSPICIOUS, + "On second thought, I think I'll ask somebody more reputable..." + ).also { stage = END_DIALOGUE } + } } return true } override fun getIds(): IntArray = intArrayOf(NPCs.EMERALD_BENEDICT_2271) -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt index e9ccfc492..4fb041be6 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt @@ -1,6 +1,6 @@ package content.region.asgarnia.burthorpe.dialogue -import content.region.asgarnia.burthorpe.quest.deathplateau.DeathPlateau +import content.data.Quests import content.region.asgarnia.burthorpe.quest.deathplateau.EohricDialogueFile import core.api.getQuestStage import core.api.openDialogue @@ -19,7 +19,7 @@ import org.rs09.consts.NPCs @Initializable class EohricDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - if (getQuestStage(player!!, DeathPlateau.questName) >= 5) { + if (getQuestStage(player!!, Quests.DEATH_PLATEAU) >= 5) { // Call the dialogue file for Eohric from the deathplateau quest folder. openDialogue(player!!, EohricDialogueFile(), npc) return true diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HaroldDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HaroldDialogue.kt index b98a66184..62d510ac1 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HaroldDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HaroldDialogue.kt @@ -1,14 +1,12 @@ package content.region.asgarnia.burthorpe.dialogue -import content.region.asgarnia.burthorpe.quest.deathplateau.DeathPlateau +import content.data.Quests import content.region.asgarnia.burthorpe.quest.deathplateau.HaroldDialogueFile import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE @@ -23,7 +21,7 @@ import org.rs09.consts.NPCs @Initializable class HaroldDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - if (isQuestInProgress(player!!, DeathPlateau.questName, 10, 29)) { + if (isQuestInProgress(player!!, Quests.DEATH_PLATEAU, 10, 29)) { // Call the dialogue file for Harold from the deathplateau quest folder. openDialogue(player!!, HaroldDialogueFile(), npc) } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HelemosDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HelemosDialogue.kt new file mode 100644 index 000000000..aede4cd5f --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HelemosDialogue.kt @@ -0,0 +1,47 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class HelemosDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, HelemosDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return HelemosDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HELEMOS_797) + } +} +class HelemosDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + + b.onPredicate { _ -> true } + .npc("Welcome to the Heroes' Guild!") + .options() + .let { optionBuilder -> + optionBuilder.option("So do you sell anything here?") + .playerl("So do you sell anything good here?") + .npcl("Why yes! We DO run an exclusive shop for our members!") + .endWith { _, player -> + openNpcShop(player, NPCs.HELEMOS_797) + end() + } + optionBuilder.option_playerl("So what can I do here?") + .npcl("Look around... there are all sorts of things to keep our guild members entertained!") + .end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.kt index b8361c40a..6b0116cd3 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests /** * Hild Dialogue @@ -18,7 +19,7 @@ import org.rs09.consts.NPCs @Initializable class HildDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int) : Boolean { - if(isQuestComplete(player!!, "Death Plateau")) { + if(isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } 1 -> npcl(FacialExpression.HAPPY, "I heard about what you did, thank you!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.kt index 2a7f3f280..3e3b73b90 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests /** * Hygd Dialogue @@ -18,7 +19,7 @@ import org.rs09.consts.NPCs @Initializable class HygdDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int) : Boolean { - if(isQuestComplete(player!!, "Death Plateau")) { + if(isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } 1 -> npcl(FacialExpression.HAPPY, "I heard about what you did, thank you!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/JadeDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/JadeDialogue.kt deleted file mode 100644 index 85ebda973..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/JadeDialogue.kt +++ /dev/null @@ -1,106 +0,0 @@ -package content.region.asgarnia.burthorpe.dialogue - -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable -import org.rs09.consts.NPCs -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE - -/** - * Provides a dialogue tree for Jade inside Warriors' Guild. - * - * @author vddCore - */ -@Initializable -class JadeDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - START_DIALOGUE -> if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { - npcl( - FacialExpression.NEUTRAL, - "Greetings, warrior. I wish I could help you, but " + - "our services are not available for Ultimate ${if (player.isMale) "Ironmen" else "Ironwomen"}." - ).also { stage = END_DIALOGUE } - } - else { - npcl( - FacialExpression.NEUTRAL, - "Greetings warrior, how may I help you?" - ).also { - if (hasAwaitingGrandExchangeCollections(player)) { - stage++ - } else { - stage += 2 - } - } - } - - 1 -> npcl( - FacialExpression.NEUTRAL, - "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(FacialExpression.NEUTRAL, "I'd like to access my bank account, please.", 10), - IfTopic( - FacialExpression.NEUTRAL, - "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", - 13, - hasActivatedSecondaryBankAccount(player) - ), - Topic(FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 11), - Topic(FacialExpression.NEUTRAL, "I'd like to see my collection box.", 12), - Topic(FacialExpression.ASKING, "How long have you worked here?", 3) - ) - - 3 -> npcl( - FacialExpression.FRIENDLY, - "Oh, ever since the Guild opened. I like it here." - ).also { stage++ } - - 4 -> playerl( - FacialExpression.ASKING, - "Why's that?" - ).also { stage++ } - - 5 -> npcl( - FacialExpression.FRIENDLY, - "Well... What with all these warriors around, there's not much chance of my bank being robbed, is there?" - ).also { stage = 2 } - - 10 -> { - openBankAccount(player) - end() - } - - 11 -> { - openBankPinSettings(player) - end() - } - - 12 -> { - openGrandExchangeCollectionBox(player) - end() - } - - 13 -> { - toggleBankAccount(player) - npcl( - FacialExpression.FRIENDLY, - "Of course! Your ${getBankAccountName(player)} account is now active!" - ).also { stage = END_DIALOGUE } - } - } - - return true - } - - override fun getIds(): IntArray = intArrayOf(NPCs.JADE_4296) -} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcgaDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcgaDialogue.kt index 514035edc..0606c4542 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcgaDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcgaDialogue.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests /** @@ -19,7 +20,7 @@ import org.rs09.consts.NPCs @Initializable class OcgaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int) : Boolean { - if(isQuestComplete(player!!, "Death Plateau")) { + if(isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } 1 -> npcl(FacialExpression.HAPPY, "I heard about what you did, thank you!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.kt index 4cd42579c..0570a0d24 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests /** * Penda Dialogue @@ -18,7 +19,7 @@ import org.rs09.consts.NPCs @Initializable class PendaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int) : Boolean { - if(isQuestComplete(player!!, "Death Plateau")) { + if(isQuestComplete(player!!, Quests.DEATH_PLATEAU)) { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..5).toIntArray().random() } 1 -> npcl(FacialExpression.HAPPY, "I heard about what you did, thank you!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/UnferthDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/UnferthDialogue.kt index 84b26124e..5c0a23214 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/UnferthDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/UnferthDialogue.kt @@ -8,9 +8,8 @@ import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests /** * Unferth Dialogue @@ -37,7 +36,7 @@ class UnferthDialogue(player: Player? = null) : DialoguePlugin(player) { class UnferthDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onPredicate { player -> isQuestComplete(player, "A Tail of Two Cats") }.playerl( + b.onPredicate { player -> isQuestComplete(player, Quests.A_TAIL_OF_TWO_CATS) }.playerl( FacialExpression.FRIENDLY, "Hi Unferth. How are you doing?" ).npcl( FacialExpression.GUILTY, "It's just not the same without Bob around." diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java index 5c3900341..60e6c86fd 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.handlers; +import core.ServerConstants; import core.cache.def.impl.SceneryDefinition; import content.data.EnchantedJewellery; import core.game.global.action.DoorActionHandler; @@ -18,10 +19,15 @@ import core.plugin.Initializable; import core.plugin.ClassScanner; import static core.api.ContentAPIKt.hasRequirement; +import static core.api.ContentAPIKt.sendMessage; + +import content.data.Quests; +import org.rs09.consts.Items; /** * Represents the hero guild. * @author Vexia + * @author Player Name */ @Initializable public final class HeroGuildPlugin extends OptionHandler { @@ -42,8 +48,8 @@ public final class HeroGuildPlugin extends OptionHandler { switch (id) { case 2624: case 2625: - if (!hasRequirement(player, "Heroes' Quest")) - return true; + if (!hasRequirement(player, Quests.HEROES_QUEST)) + return true; DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); break; } @@ -55,6 +61,7 @@ public final class HeroGuildPlugin extends OptionHandler { /** * Handles the recharging of dragonstone jewellery. * @author Vexia + * @author Player Name */ public static final class JewelleryRechargePlugin extends UseWithHandler { @@ -64,8 +71,7 @@ public final class HeroGuildPlugin extends OptionHandler { private static final int[] IDS = new int[] { 1710, 1708, 1706, 1704, 11107, 11109, 11111, 11113, 11120, 11122, 11124, 11126, 10354, 10356, 10358, 10360, 10362, 14644,14642,14640,14638, 2572 }; /** - * Constructs a new {@Code JewelleryRechargePlugin} {@Code - * Object} + * Constructs a new JewelleryRechargePlugin object */ public JewelleryRechargePlugin() { super(IDS); @@ -82,18 +88,24 @@ public final class HeroGuildPlugin extends OptionHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - if (!hasRequirement(player, "Heroes' Quest")) - return true; + if (!hasRequirement(player, Quests.HEROES_QUEST)) { + return true; //hasRequirement shows the message + } final EnchantedJewellery jewellery; assert event.getUsedItem() != null; jewellery = EnchantedJewellery.Companion.getIdMap().get(event.getUsedItem().getId()); - if (!hasRequirement(player, "Heroes' Quest")) - return true; - if (jewellery == EnchantedJewellery.COMBAT_BRACELET || jewellery == EnchantedJewellery.SKILLS_NECKLACE) - if (!hasRequirement(player, "Legend's Quest")) - return true; - if (jewellery == null && event.getUsedItem().getId() != 2572) { - return true; + if (jewellery == null) { + return false; //nothing interesting happens + } + if (jewellery == EnchantedJewellery.RING_OF_WEALTH) { + if (!ServerConstants.RING_OF_WEALTH_TELEPORT) { + return false; + } + } + if (jewellery == EnchantedJewellery.COMBAT_BRACELET || jewellery == EnchantedJewellery.SKILLS_NECKLACE) { + if (!hasRequirement(player, Quests.LEGENDS_QUEST)) { + return true; + } } boolean fam = event.getUsedWith() instanceof NPC; if (fam && jewellery != EnchantedJewellery.AMULET_OF_GLORY & jewellery != EnchantedJewellery.AMULET_OF_GLORY_T) { @@ -112,9 +124,9 @@ public final class HeroGuildPlugin extends OptionHandler { player.getInventory().replace(rechargedItem, event.getUsedItem().getSlot()); String name = jewellery.getJewelleryName(rechargedItem); if (!fam) { - player.sendMessage("You dip the " + name + " in the fountain..."); + sendMessage(player, "You dip the " + name.toLowerCase() + " in the fountain..."); } else { - player.sendMessage("Your titan recharges the glory."); + sendMessage(player, "Your titan recharges the " + name.toLowerCase() + "."); } return true; } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateau.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateau.kt index b6b31939f..24e3f6cbc 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateau.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateau.kt @@ -5,9 +5,9 @@ import core.api.getAttribute import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills -import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * Death Plateau Quest @@ -15,16 +15,13 @@ import org.rs09.consts.Items * @author ovenbread */ @Initializable -class DeathPlateau : Quest("Death Plateau",44, 43, 1, 314, 0, 1, 80) { - companion object { - const val questName = "Death Plateau" - } +class DeathPlateau : Quest(Quests.DEATH_PLATEAU,44, 43, 1, 314, 0, 1, 80) { override fun drawJournal(player: Player?, stage: Int) { super.drawJournal(player, stage) var line = 12 var stage = getStage(player) - var started = player?.questRepository?.getStage(questName)!! > 0 + var started = player?.questRepository?.getStage(Quests.DEATH_PLATEAU)!! > 0 if(!started){ line(player, "I can start this quest by speaking to !!Denulth?? who is in his", line++) diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauDoorDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauDoorDialogueFile.kt index e0db1a73d..64c29cec7 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauDoorDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauDoorDialogueFile.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.deathplateau +import content.data.Quests import core.api.getQuestStage import core.api.getScenery import core.api.sendDialogue @@ -26,7 +27,7 @@ class DeathPlateauDoorDialogueFile(val door: Int) : DialogueFile() { if(door == 2) { npc = NPC(NPCs.TENZING_1071) - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { in 0 .. 19 -> { when (stage) { 0 -> sendDialogue(player!!, "You knock on the door.").also { stage++ } @@ -53,7 +54,7 @@ class DeathPlateauDoorDialogueFile(val door: Int) : DialogueFile() { } if(door == 3) { npc = NPC(NPCs.TENZING_1071) - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { in 0..24 -> { when (stage) { 0 -> npcl(FacialExpression.FRIENDLY, "Where do you think you're going? This is private property!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauInteractionListener.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauInteractionListener.kt index ed6850780..01a253ad4 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauInteractionListener.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauInteractionListener.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.deathplateau +import content.data.Quests import core.api.* import core.game.global.action.DoorActionHandler import core.game.interaction.IntType @@ -24,22 +25,19 @@ class DeathPlateauInteractionListener : InteractionListener { ) } override fun defineListeners() { - on(Scenery.DOOR_3747, SCENERY, "open") { player, _ -> - // Harold's Door - if (player.location == location(2906, 3543, 1)) { - openDialogue(player, DeathPlateauDoorDialogueFile(1)) - } else { - DoorActionHandler.handleAutowalkDoor(player, getScenery(2906, 3543, 1)) + on(Scenery.DOOR_3747, SCENERY, "open") { player, node -> + // Harold's door + when (player.location) { + location(2906, 3543, 1), location(2905, 3543, 1), location(2907, 3543, 1) -> openDialogue(player, DeathPlateauDoorDialogueFile(1)) + else -> DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } return@on true } on(Scenery.DOOR_3745, SCENERY, "open") { player, node -> - if (node.location == location(2823, 3555, 0)) { - // 1st Door to Tenzing - openDialogue(player, DeathPlateauDoorDialogueFile(2)) - } else if (node.location == location(2820, 3558, 0)) { - // 2nd Door to chicken pen - openDialogue(player, DeathPlateauDoorDialogueFile(3)) + when (node.location) { + location(2823, 3555, 0) -> openDialogue(player, DeathPlateauDoorDialogueFile(2)) //1st door to Tenzing + location(2820, 3558, 0) -> openDialogue(player, DeathPlateauDoorDialogueFile(3)) //2nd door to chicken pen + else -> DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } return@on true } @@ -77,16 +75,16 @@ class DeathPlateauInteractionListener : InteractionListener { GroundItemManager.get(Items.STONE_BALL_3111, location(2895, 3562, 0), player) != null && GroundItemManager.get(Items.STONE_BALL_3112, location(2895, 3563, 0), player) != null && GroundItemManager.get(Items.STONE_BALL_3113, location(2895, 3564, 0), player) != null) { - if (getQuestStage(player, DeathPlateau.questName) == 16) { + if (getQuestStage(player, Quests.DEATH_PLATEAU) == 16) { sendMessage(player, "The equipment room door has unlocked.") - setQuestStage(player, DeathPlateau.questName, 19) + setQuestStage(player, Quests.DEATH_PLATEAU, 19) } } return@onUseWith true } on(Scenery.LARGE_DOOR_3743, SCENERY, "open") { player, node -> - if (getQuestStage(player, DeathPlateau.questName) > 16) { + if (getQuestStage(player, Quests.DEATH_PLATEAU) > 16) { DoorActionHandler.handleAutowalkDoor(player, node as core.game.node.scenery.Scenery) } else { sendMessage(player, "The door is locked.") @@ -94,4 +92,4 @@ class DeathPlateauInteractionListener : InteractionListener { return@on true } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DenulthDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DenulthDialogueFile.kt index 7838a6c5c..2046cf51a 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DenulthDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DenulthDialogueFile.kt @@ -1,14 +1,13 @@ package content.region.asgarnia.burthorpe.quest.deathplateau +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression import core.game.dialogue.Topic -import core.game.node.entity.npc.NPC import core.game.node.item.Item import core.tools.END_DIALOGUE import org.rs09.consts.Items -import org.rs09.consts.NPCs /** @@ -20,7 +19,7 @@ import org.rs09.consts.NPCs class DenulthDialogueFile : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { in 0..4 -> { when (stage) { 0 -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } @@ -54,7 +53,7 @@ class DenulthDialogueFile : DialogueFile() { 309 -> playerl(FacialExpression.FRIENDLY, "A stone what...?!").also { stage++ } 310 -> npcl(FacialExpression.FRIENDLY, "Well citizen, the Prince is fond of puzzles. Why we couldn't just have a key is beyond me!").also { stage++ } 311 -> playerl(FacialExpression.SUSPICIOUS, "I'll get on it right away!").also { - setQuestStage(player!!, DeathPlateau.questName, 5) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 5) stage = END_DIALOGUE } } @@ -104,7 +103,7 @@ class DenulthDialogueFile : DialogueFile() { } 11 -> npcl(FacialExpression.FRIENDLY, "This certificate proves that we have accepted Dunstan's son for training in the Imperial Guard!").also { stage++ } 12 -> playerl(FacialExpression.FRIENDLY, "Thank you Denulth, I shall be back shortly!").also { - setQuestStage(player!!, DeathPlateau.questName, 23) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 23) stage = END_DIALOGUE } } @@ -155,7 +154,7 @@ class DenulthDialogueFile : DialogueFile() { stage = 8 } } - 7 -> playerl(FacialExpression.FRIENDLY, "I have opened the door but I don't have the combination on me.").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "I have opened the door but I don't have the combination on me.").also { stage = END_DIALOGUE } 8 -> playerl(FacialExpression.FRIENDLY, "Yes! The door is open and here is the combination.").also { stage++ } 9 -> sendItemDialogue(player!!, Items.COMBINATION_3102, "You give Denulth the combination to the equipment room.").also { if (removeItem(player!!, Item(Items.COMBINATION_3102))) { @@ -169,7 +168,7 @@ class DenulthDialogueFile : DialogueFile() { 12 -> npcl(FacialExpression.FRIENDLY, "You are now an honourary member of the Imperial Guard!").also { stage++ } 13 -> { stage = END_DIALOGUE - finishQuest(player!!, DeathPlateau.questName) + finishQuest(player!!, Quests.DEATH_PLATEAU) } } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DunstanDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DunstanDialogueFile.kt index 2567c54c8..6cf2eabf4 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DunstanDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DunstanDialogueFile.kt @@ -3,17 +3,16 @@ package content.region.asgarnia.burthorpe.quest.deathplateau import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC import core.game.node.item.Item import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.Items -import org.rs09.consts.NPCs +import content.data.Quests class DunstanDialogueFile : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { 21 -> { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } @@ -29,7 +28,7 @@ class DunstanDialogueFile : DialogueFile() { 10 -> npcl(FacialExpression.FRIENDLY, "My son has just turned 16 and I'd very much like him to join the Imperial Guard. The Prince's elite forces are invite only so it's very unlikely he'll get in. If you can arrange that you have a deal!").also { stage++ } 11 -> playerl(FacialExpression.FRIENDLY, "That won't be a problem as I'm helping out the Imperial Guard!").also { stage++ } 12 -> npcl(FacialExpression.FRIENDLY, "Excellent! You'll need to bring an Iron bar for the spikes!").also { - setQuestStage(player!!, "Death Plateau", 22) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 22) stage = END_DIALOGUE } } @@ -60,7 +59,7 @@ class DunstanDialogueFile : DialogueFile() { 5 -> npcl(FacialExpression.FRIENDLY, "Thank you!").also { // Jumps to the next stage immediately in one continuous dialogue (questStage 24, stage 2). - setQuestStage(player!!, "Death Plateau", 24) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 24) stage = 2 } } @@ -84,15 +83,17 @@ class DunstanDialogueFile : DialogueFile() { 4 -> playerl(FacialExpression.FRIENDLY, "I don't have the climbing boots.").also { stage = END_DIALOGUE } 5 -> playerl(FacialExpression.FRIENDLY, "I don't have the iron bar or the climbing boots.").also { stage = END_DIALOGUE } - 7 -> sendDoubleItemDialogue(player!!, Items.IRON_BAR_2351, Items.CLIMBING_BOOTS_3105, "You give Dunstan an iron bar and the climbing boots.").also { + 7 -> sendDoubleItemDialogue(player!!, Items.IRON_BAR_2351, Items.CLIMBING_BOOTS_3105, "You give Dunstan an Iron bar and the climbing boots.").also { + sendMessage(player!!, "You give Dunstan an Iron bar and the climbing boots.") if (removeItem(player!!, Item(Items.CLIMBING_BOOTS_3105)) && removeItem(player!!, Item(Items.IRON_BAR_2351))) { + addItemOrDrop(player!!, Items.SPIKED_BOOTS_3107) stage++ } else { stage = END_DIALOGUE } } 8 -> sendItemDialogue(player!!, Items.SPIKED_BOOTS_3107, "Dunstan has given you the spiked boots.").also { stage++ - addItemOrDrop(player!!, Items.SPIKED_BOOTS_3107) + sendMessage(player!!, "Dunstan has given you the spiked boots.") } 9 -> playerl(FacialExpression.FRIENDLY, "Thank you!").also { stage++ } 10 -> npcl(FacialExpression.FRIENDLY, "No problem.").also { diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/EohricDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/EohricDialogueFile.kt index 426f4846a..8e0d5f0a6 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/EohricDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/EohricDialogueFile.kt @@ -4,10 +4,9 @@ import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression import core.game.dialogue.Topic -import core.game.node.entity.npc.NPC import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE -import org.rs09.consts.NPCs +import content.data.Quests /** * Eohric sub dialogue file for death plateau. @@ -17,7 +16,7 @@ import org.rs09.consts.NPCs */ class EohricDialogueFile : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { in 5..9 -> { when (stage) { START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hi!").also { stage++ } @@ -31,7 +30,7 @@ class EohricDialogueFile : DialogueFile() { 11 -> player(FacialExpression.FRIENDLY, "Do you know where he is staying?").also { stage++ } 12 -> npc(FacialExpression.FRIENDLY, "Harold is staying at the Toad and Chicken.").also { stage++ } 13 -> player(FacialExpression.FRIENDLY, "Thanks!").also { - setQuestStage(player!!, "Death Plateau", 10) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 10) HaroldDialogueFile.resetNpc(player!!) stage = END_DIALOGUE } @@ -58,7 +57,7 @@ class EohricDialogueFile : DialogueFile() { 2 -> playerl(FacialExpression.HALF_GUILTY, "I found Harold but he won't talk to me!.").also { stage++ } 3 -> npcl(FacialExpression.THINKING, "Hmm. Harold has got in trouble a few over his drinking and gambling. Perhaps he'd open up after a drink?").also { stage++ } 4 -> playerl(FacialExpression.FRIENDLY, "Thanks, I'll try that!").also { - setQuestStage(player!!, "Death Plateau", 12) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 12) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/HaroldDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/HaroldDialogueFile.kt index 810b427b1..e4242212b 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/HaroldDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/HaroldDialogueFile.kt @@ -11,6 +11,7 @@ import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.Animations import org.rs09.consts.Items +import content.data.Quests /** * Harold sub dialogue file for death plateau. @@ -38,7 +39,7 @@ class HaroldDialogueFile : DialogueFile() { setAttribute(player!!, ATTRIBUTE_JUMPSTAGE, 0) } println(getAttribute(player!!, ATTRIBUTE_HAROLD_MONEY, -1)) - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { 10 -> { // First time meeting. when (stage) { START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } @@ -47,7 +48,7 @@ class HaroldDialogueFile : DialogueFile() { 3 -> npcl(FacialExpression.FRIENDLY, "Yeah.").also { stage++ } 4 -> playerl(FacialExpression.HAPPY, "Denulth said that you lost the combination to the equipment room ?").also { stage++ } 5 -> npcl(FacialExpression.FRIENDLY, "I don't want to talk about it!").also { - setQuestStage(player!!, "Death Plateau", 11) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 11) stage = END_DIALOGUE } } @@ -71,7 +72,7 @@ class HaroldDialogueFile : DialogueFile() { 4 -> { if (inInventory(player!!, Items.ASGARNIAN_ALE_1905, 1)) { removeItem(player!!, Items.ASGARNIAN_ALE_1905) - setQuestStage(player!!, "Death Plateau", 12) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 12) sendMessage(player!!, "You give Harold an Asgarnian Ale.") setAttribute(player!!, ATTRIBUTE_HAROLD_MONEY, 200) sendItemDialogue(player!!, Items.ASGARNIAN_ALE_1905, "You give Harold an Asgarnian Ale.").also { stage++ } @@ -81,7 +82,7 @@ class HaroldDialogueFile : DialogueFile() { } 5 -> { end() - setQuestStage(player!!, "Death Plateau", 13) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 13) animate(npc!!, Animations.HUMAN_EATTING_829) runTask(npc!!, 5) { npcl(FacialExpression.FRIENDLY, "Arrh. That hit the spot!").also { stage = END_DIALOGUE } @@ -108,19 +109,16 @@ class HaroldDialogueFile : DialogueFile() { 21 -> playerl(FacialExpression.ASKING, "What?").also { stage++ } 22 -> npcl(FacialExpression.FRIENDLY, "I really fancy one of those Blurberry Specials. I never get over to the Gnome Stronghold so I haven't had one for ages!").also { stage++ } 23 -> { - if (removeItem(player!!, Items.BLURBERRY_SPECIAL_2064)) { + if (removeItem(player!!, Items.BLURBERRY_SPECIAL_2064) || removeItem(player!!, Items.PREMADE_BLURB_SP_2028)) { sendMessage(player!!, "You give Harold a Blurberry Special.") sendItemDialogue(player!!, Items.BLURBERRY_SPECIAL_2064, "You give Harold a Blurberry Special.").also { stage++ } - } else if (removeItem(player!!, Items.PREMADE_BLURB_SP_2028)) { - sendMessage(player!!, "You give Harold a Blurberry Special.") - sendItemDialogue(player!!, Items.PREMADE_BLURB_SP_2028, "You give Harold a Blurberry Special.").also { stage++ } } else { player(FacialExpression.FRIENDLY, "I'll go and get you one.").also { stage = END_DIALOGUE } } } 24 -> { end() - setQuestStage(player!!, DeathPlateau.questName, 14) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 14) npc!!.isWalks = false animate(npc!!, Animations.HUMAN_EATTING_829) runTask(npc!!, 4) { @@ -204,7 +202,7 @@ class HaroldDialogueFile : DialogueFile() { 37 -> npcl(FacialExpression.FRIENDLY, "I'll write you out an IOU for the rest.").also { stage++ } 38 -> { addItemOrDrop(player!!, Items.IOU_3103) - setQuestStage(player!!, DeathPlateau.questName, 15) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 15) sendMessage(player!!, "Harold has given you an IOU scribbled on some paper.") sendItemDialogue(player!!, Items.IOU_3103, "Harold has given you an IOU scribbled on some paper.").also {stage = END_DIALOGUE} } @@ -292,7 +290,7 @@ class HaroldDialogueFile : DialogueFile() { 34 -> npcl(FacialExpression.DRUNK, "I owe you the resht!").also { stage++ } 35 -> { addItemOrDrop(player!!, Items.IOU_3103) - setQuestStage(player!!, DeathPlateau.questName, 15) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 15) sendMessage(player!!, "Harold has given you an IOU scribbled on some paper.") sendItemDialogue(player!!, Items.IOU_3103, "Harold has given you an IOU scribbled on some paper.").also {stage = END_DIALOGUE} } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/IOUNoteDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/IOUNoteDialogueFile.kt index 0d44bc767..4a4531422 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/IOUNoteDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/IOUNoteDialogueFile.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.deathplateau +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -9,7 +10,7 @@ import org.rs09.consts.Items class IOUNoteDialogueFile : DialogueFile() { var a = 0 override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { in 15..16 -> { when (stage) { 0 -> player(FacialExpression.NEUTRAL, "The IOU says that Harold owes me some money.").also { stage++ } @@ -18,7 +19,7 @@ class IOUNoteDialogueFile : DialogueFile() { 3 -> { if (removeItem(player!!, Items.IOU_3103)) { addItemOrDrop(player!!, Items.COMBINATION_3102) - setQuestStage(player!!, DeathPlateau.questName, 16) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 16) sendItemDialogue(player!!, Items.COMBINATION_3102, "You have found the combination!").also { stage++ } } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SabaDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SabaDialogueFile.kt index 3c1c90632..820fdf26e 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SabaDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SabaDialogueFile.kt @@ -5,11 +5,12 @@ import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression import core.game.dialogue.Topic import core.tools.END_DIALOGUE +import content.data.Quests class SabaDialogueFile : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { 19 -> { when (stage) { 0 -> player(FacialExpression.FRIENDLY, "Hello!").also { stage++ } @@ -34,7 +35,7 @@ class SabaDialogueFile : DialogueFile() { 29 -> npcl(FacialExpression.HALF_GUILTY,"Before the trolls came there used to be a nettlesome Sherpa that took humans exploring or something equally stupid. Perhaps he'd know another way.").also { stage++ } 30 -> playerl(FacialExpression.FRIENDLY, "Where does this Sherpa live?").also { stage++ } 31 -> npcl(FacialExpression.ANNOYED,"I don't know but it can't be far as he used to be around all the time!").also { - setQuestStage(player!!, "Death Plateau", 20) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 20) stage = END_DIALOGUE } } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SecretWayLocation.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SecretWayLocation.kt index 3a078a77d..96d821a9f 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SecretWayLocation.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SecretWayLocation.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.deathplateau +import content.data.Quests import core.api.* import core.game.node.entity.Entity import core.game.node.entity.player.Player @@ -11,9 +12,9 @@ class SecretWayLocation : MapArea { } override fun areaEnter(entity: Entity) { - if (entity is Player && getQuestStage(entity, DeathPlateau.questName) == 25) { + if (entity is Player && getQuestStage(entity, Quests.DEATH_PLATEAU) == 25) { sendPlayerDialogue(entity, "I think this is far enough, I can see Death Plateau and it looks like the trolls haven't found the path. I'd better go and tell Denulth.") - setQuestStage(entity, DeathPlateau.questName, 26) + setQuestStage(entity, Quests.DEATH_PLATEAU, 26) } } } \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/TenzingDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/TenzingDialogueFile.kt index aacba1673..c69cb4a94 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/TenzingDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/TenzingDialogueFile.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.deathplateau +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -12,7 +13,7 @@ import org.rs09.consts.Items class TenzingDialogueFile : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, DeathPlateau.questName)) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { 20 -> { when (stage) { START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } @@ -34,7 +35,7 @@ class TenzingDialogueFile : DialogueFile() { 20 -> npcl(FacialExpression.FRIENDLY, "Thank you traveller!").also { stage++ } 21 -> sendItemDialogue(player!!, Items.CLIMBING_BOOTS_3105, "Tenzing has given you his Climbing boots.").also { addItemOrDrop(player!!, Items.CLIMBING_BOOTS_3105, 1) - setQuestStage(player!!, DeathPlateau.questName, 21) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 21) stage = END_DIALOGUE } 30 -> npcl(FacialExpression.ANNOYED, "Hmph.").also { stage = END_DIALOGUE } @@ -104,7 +105,7 @@ class TenzingDialogueFile : DialogueFile() { 9 -> npcl(FacialExpression.FRIENDLY, "I don't think the Trolls have found the secret way yet, if they had I would've been attacked by now.").also { stage++ } 10 -> playerl(FacialExpression.FRIENDLY, "OK thanks but I think I'd better check the path. I don't want to send the Imperial Guards to their death!").also { stage++ } 11 -> npcl(FacialExpression.FRIENDLY, "You are wise for one so young.").also { - setQuestStage(player!!, DeathPlateau.questName, 25) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 25) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/AchiettiesDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/AchiettiesDialogue.kt new file mode 100644 index 000000000..be02fd712 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/AchiettiesDialogue.kt @@ -0,0 +1,180 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class AchiettiesDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, AchiettiesDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return AchiettiesDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ACHIETTIES_796) + } +} + +class AchiettiesDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + + b.onQuestStages(Quests.HEROES_QUEST, 0,1) + .branch { player -> + return@branch getQuestStage(player, Quests.HEROES_QUEST) + }.let{ branch -> + branch.onValue(0) + .npcl(FacialExpression.FRIENDLY, "Greetings. Welcome to the Heroes' Guild.") + .npcl("Only the greatest heroes of this land may gain entrance to this guild.") + // - If the player's skill levels are lower than the quest requirements. (I think this is after 2009) + // linel("Before starting this quest, be aware that one or more of your skill levels are lower than what is required to fully complete it.") + .options() + .let { optionBuilder -> + optionBuilder.option("I'm a hero, may I apply to join?") + .playerl("I'm a hero. May I apply to join?") + .branch { player -> + return@branch if (HeroesQuest.hasRequirements(player)) { + 1 + } else { + 0 + } + }.let { branch -> + branch.onValue(0) + .npcl("You're a hero? I've never heard of YOU. You are required to possess at least 55 quest points to file an application.") + .npcl("Additionally you must have completed the Shield of Arrav, Lost City, Merlin's Crystal and Dragon Slayer quests.") + .end() + return@let branch + }.onValue(1) + .betweenStage { df, player, _, _ -> + if(getQuestStage(player, Quests.HEROES_QUEST) == 0) { + setQuestStage(player, Quests.HEROES_QUEST, 1) + } + } + .npcl("Well you seem to meet our initial requirements, so you may now begin the tasks to earn membership in the Heroes' Guild.") + .npcl("The three items required for entrance are: An Entranan Firebird feather, a Master Thieves' armband, and a cooked Lava Eel.") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Any hints on getting the thieves armband?") + .npcl("I'm sure you have the relevant contacts to find out about that.") + .end() + optionBuilder2.option_playerl("Any hints on getting the feather?") + .npcl("Not really - other than Entranan firebirds tend to live on Entrana.") + .end() + optionBuilder2.option_playerl("Any hints on getting the eel?") + .npcl("Maybe go and find someone who knows a lot about fishing?") + .end() + optionBuilder2.option_playerl("I'll start looking for all those things then.") + .npcl("Good luck with that.") + .end() + } + + optionBuilder.option_playerl("Good for the foremost heroes of the land.") + .npcl("Yes. Yes it is.") + .end() + } + branch.onValue(1) + .npcl("Greetings. Welcome to the Heroes' Guild.") + .npcl("How goes thy quest adventurer?") + .playerl("It's tough. I've not done it yet.") + .npcl("Remember, the items you need to enter are:") + .npcl("An Entranan Firebirds' feather, A Master Thieves armband, and a cooked Lava Eel.") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Any hints on getting the thieves armband?") + .npcl("I'm sure you have the relevant contacts to find out about that.") + .end() + optionBuilder2.option_playerl("Any hints on getting the feather?") + .npcl("Not really - other than Entranan firebirds tend to live on Entrana.") + .end() + optionBuilder2.option_playerl("Any hints on getting the eel?") + .npcl("Maybe go and find someone who knows a lot about fishing?") + .end() + optionBuilder2.option_playerl("I'll start looking for all those things then.") + .npcl("Good luck with that.") + .end() + } + } + + b.onQuestStages(Quests.HEROES_QUEST, 2, 3, 4) + .npcl("Greetings. Welcome to the Heroes' Guild.") + .npcl("How goes thy quest adventurer?") + .playerl("It's tough. I've not done it yet.") + .npcl("Remember, the items you need to enter are:") + .npcl("An Entranan Firebirds' feather, A Master Thieves armband, and a cooked Lava Eel.") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Any hints on getting the thieves armband?") + .npcl("I'm sure you have the relevant contacts to find out about that.") + .end() + optionBuilder2.option_playerl("Any hints on getting the feather?") + .npcl("Not really - other than Entranan firebirds tend to live on Entrana.") + .end() + optionBuilder2.option_playerl("Any hints on getting the eel?") + .npcl("Maybe go and find someone who knows a lot about fishing?") + .end() + optionBuilder2.option_playerl("I'll start looking for all those things then.") + .npcl("Good luck with that.") + .end() + } + + b.onQuestStages(Quests.HEROES_QUEST, 6) + .npcl("Greetings. Welcome to the Heroes' Guild.") + .npcl("How goes thy quest adventurer?") + .branch { player -> + return@branch if (HeroesQuest.allItemsInInventory(player)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .playerl("It's tough. I've not done it yet.") + .npcl("Remember, the items you need to enter are:") + .npcl("An Entranan Firebirds' feather, A Master Thieves armband, and a cooked Lava Eel.") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Any hints on getting the thieves armband?") + .npcl("I'm sure you have the relevant contacts to find out about that.") + .end() + optionBuilder2.option_playerl("Any hints on getting the feather?") + .npcl("Not really - other than Entranan firebirds tend to live on Entrana.") + .end() + optionBuilder2.option_playerl("Any hints on getting the eel?") + .npcl("Maybe go and find someone who knows a lot about fishing?") + .end() + optionBuilder2.option_playerl("I'll start looking for all those things then.") + .npcl("Good luck with that.") + .end() + } + + branch.onValue(1) + .playerl("I have all the required items.") + .npcl("I see that you have. Well done. Now, to complete the quest, and gain entry to the Heroes' Guild in your final task all that you have to do is...") + .playerl("W-what? What do you mean? There's MORE?") + .npcl("I'm sorry, I was just having a little fun with you. Just a little Heroes' Guild humour there. What I really meant was") + .npcl("Congratulations! You have completed the Heroes' Guild entry requirements! You will find the door now open for you! Enter, Hero! And take this reward!") + .endWith { _, player -> + if (HeroesQuest.allItemsInInventory(player)) { + removeItem(player, Items.FIRE_FEATHER_1583) + removeItem(player, Items.LAVA_EEL_2149) + removeItem(player, Items.THIEVES_ARMBAND_1579) + if (getQuestStage(player, Quests.HEROES_QUEST) == 6) { + finishQuest(player, Quests.HEROES_QUEST) + } + } + } + } + + b.onQuestStages(Quests.HEROES_QUEST, 100) + .npcl("Greetings. Welcome to the Heroes' Guild.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/AlfonseTheWaiterDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/AlfonseTheWaiterDialogue.kt new file mode 100644 index 000000000..efb262b09 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/AlfonseTheWaiterDialogue.kt @@ -0,0 +1,62 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.getQuestStage +import core.api.openDialogue +import core.api.openNpcShop +import core.api.setQuestStage +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class AlfonseTheWaiterDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return AlfonseTheWaiterDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, AlfonseTheWaiterDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ALFONSE_THE_WAITER_793) + } +} +class AlfonseTheWaiterDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onPredicate { _ -> true } + .npc("Welcome to the Shrimp and Parrot.", "Would you like to order, sir?") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Yes please.") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + + optionBuilder.option_playerl("No thank you.") + .end() + + optionBuilder.optionIf("Do you sell Gherkins?"){ player -> return@optionIf getQuestStage( + player, + Quests.HEROES_QUEST + ) >= 2 && HeroesQuest.isPhoenix(player) } + .playerl("Do you sell Gherkins?") + .npc("Hmmmm Gherkins eh? Ask Charlie the cook, round the", "back. He may have some 'gherkins' for you!") + .linel("Alfonse winks at you.") + .endWith { _, player -> + if(getQuestStage(player, Quests.HEROES_QUEST) == 2) { + setQuestStage(player, Quests.HEROES_QUEST, 3) + } + } + + optionBuilder.option("Where do you get your Karambwan from?") + .npc("We buy directly off Lubufu, a local fisherman. He", "seems to have a monopoly over Karambwan sales.") + .end() + + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/CharlieTheCookDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/CharlieTheCookDialogue.kt new file mode 100644 index 000000000..2bd65f817 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/CharlieTheCookDialogue.kt @@ -0,0 +1,84 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.getQuestStage +import core.api.openDialogue +import core.api.setQuestStage +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class CharlieTheCookDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return CharlieTheCookDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, CharlieTheCookDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.CHARLIE_THE_COOK_794) + } +} +class CharlieTheCookDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .npc(FacialExpression.ANGRY, "Hey! What are you doing back here?") + .options() + .let { optionBuilder -> + val continuePath = b.placeholder() + + optionBuilder.optionIf("I'm looking for a gherkin...") { player -> return@optionIf getQuestStage(player, Quests.HEROES_QUEST) >= 3 && HeroesQuest.isPhoenix(player) } + .playerl("I'm looking for a gherkin...") + .goto(continuePath) + + optionBuilder.optionIf("I'm a fellow member of the Phoenix Gang.") { player -> return@optionIf getQuestStage(player, Quests.HEROES_QUEST) >= 3 && HeroesQuest.isPhoenix(player) } + .playerl("I'm a fellow member of the Phoenix Gang.") + .goto(continuePath) + + optionBuilder.option_playerl("Just exploring.") + .npcl(FacialExpression.ANGRY, "Well, get out! This kitchen isn't for exploring. It's a private establishment! It's out of bounds to customers!") + .end() + + return@let continuePath.builder() + } + .npcl("Ah, a fellow Phoenix! So, tell me compadre... What brings you to sunny Brimhaven?") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Sun, sand, and the fresh sea air!") + .playerl("Sun, sand, and the fresh sea air!") + .npcl("Well, can't say I blame you, compadre. I used to be a city boy myself, but have to admit it's a lot nicer living here nowadays. Brimhaven's certainly good for it.") + .playerl("I also want to steal Scarface Pete's candlesticks.") + .npcl("Ah yes, of course. The candlesticks. Well, I have to be honest with you, compadre, we haven't made much progress in that task ourselves so far.") + .npcl("We can however offer a little assistance. Setting up this restaurant was the start of things; we have a secret door out the back of here that leads through the back of Cap'n Arnav's garden.") + .npcl("Now, at the other side of Cap'n Arnav's garden, is an old side entrance to Scarface Pete's mansion. It seems to have been blocked off from the rest of the mansion some years ago and we can't seem to find a way through.") + .npcl("We're positive this is the key to entering the house undetected, however, and I promise to let you know if we find anything there.") + .playerl("Mind if I check it out for myself?") + .npcl("Not at all! The more minds we have working on the problem, the quicker we get that loot!") + .endWith { _, player -> + if (getQuestStage(player, Quests.HEROES_QUEST) == 3) { + setQuestStage(player, Quests.HEROES_QUEST, 4) + } + } + + optionBuilder.option_playerl("I want to steal Scarface Pete's candlesticks.") + .npcl("Ah yes, of course. The candlesticks. Well, I have to be honest with you, compadre, we haven't made much progress in that task ourselves so far.") + .npcl("We can however offer a little assistance. Setting up this restaurant was the start of things; we have a secret door out the back of here that leads through the back of Cap'n Arnav's garden.") + .npcl("Now, at the other side of Cap'n Arnav's garden, is an old side entrance to Scarface Pete's mansion. It seems to have been blocked off from the rest of the mansion some years ago and we can't seem to find a way through.") + .npcl("We're positive this is the key to entering the house undetected, however, and I promise to let you know if we find anything there.") + .playerl("Mind if I check it out for myself?") + .npcl("Not at all! The more minds we have working on the problem, the quicker we get that loot!") + .endWith { _, player -> + if (getQuestStage(player, Quests.HEROES_QUEST) == 3) { + setQuestStage(player, Quests.HEROES_QUEST, 4) + } + } + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GarvDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GarvDialogue.kt new file mode 100644 index 000000000..dc9433443 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GarvDialogue.kt @@ -0,0 +1,72 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class GarvDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GarvDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return GarvDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GARV_788) + } +} + +class GarvDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + // Technically this won't happen since you have to get past Grubor. + b.onQuestStages(Quests.HEROES_QUEST, 0, 1, 2) + .npcl("Hello. What do you want?") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Can I go in there?") + .npcl("No. In there is private.") + .end() + optionBuilder.option_playerl("I want for nothing!") + .npcl("You're one of a very lucky few then.") + .end() + } + + b.onQuestStages(Quests.HEROES_QUEST, 3, 4, 5, 6, 100) + // .npcl("Oi! Where do you think you're going pal?") - When you click on the door instead of Garv. + .npcl("Hello. What do you want?") + .playerl("Hi. I'm Hartigen. I've come to work here.") + .branch { player -> + return@branch if (inEquipment(player, Items.BLACK_FULL_HELM_1165) && inEquipment(player, Items.BLACK_PLATEBODY_1125) && inEquipment(player, Items.BLACK_PLATELEGS_1077)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("I assume you have your I.D. papers then?") + .branch { player -> + return@branch if (inInventory(player, Items.ID_PAPERS_1584)) { 1 } else { 0 } + }.let { branch2 -> + branch2.onValue(1) + .npcl("You'd better come in then, Grip will want to talk to you.") + .endWith { _, player -> + if(getQuestStage(player, Quests.HEROES_QUEST) == 3) { + setQuestStage(player, Quests.HEROES_QUEST, 4) + } + } + branch2.onValue(0) + .playerl("Uh... Yeah. About that...I must have left them in my other suit of armour.") + .end() + } + branch.onValue(0) + .npcl("Hartigen the Black Knight? I don't think so. He doesn't dress like that.") + .end() + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GerrantDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GerrantDialogue.kt new file mode 100644 index 000000000..c106acdec --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GerrantDialogue.kt @@ -0,0 +1,66 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class GerrantDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return GerrantDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GerrantDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GERRANT_558) + } +} +class GerrantDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onPredicate { _ -> true } + .npc(FacialExpression.HAPPY, "Welcome! You can buy fishing equipment at my store.", "We'll also buy anything you catch off you.") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Let's see what you've got then.") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + + optionBuilder.option_playerl("Sorry, I'm not interested.") + .end() + + optionBuilder.optionIf("I want to find out how to catch a lava eel.") { player -> return@optionIf getQuestStage(player, Quests.HEROES_QUEST) >= 1 } + .playerl("I want to find out how to catch a lava eel.") + .npcl("Lava eels, eh? That's a tricky one, that is. You'll need a lava-proof fishing rod. The method for making this would be to take an ordinary fishing rod, and then cover it with fire-proof blamish oil.") + .branch { player -> + return@branch if (inInventory(player, Items.BLAMISH_SNAIL_SLIME_1581)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("Of course, you knew that already.") + .playerl("So where can I fish lava eels?") + .npcl("Taverley dungeon or the lava maze in the Wilderness.") + .end() + + branch.onValue(0) + .npcl("You know... thinking about it... I may have a jar of blamish slime around here somewhere. Now where did I put it?") + .linel("Gerrant searches around a bit.") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.BLAMISH_SNAIL_SLIME_1581) + } + .npcl("Aha! Here it is! Take this slime, mix it with some Harralander and water and you'll have the blamish oil you need to treat your fishing rod.") + .end() + } + + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GripBehavior.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GripBehavior.kt new file mode 100644 index 000000000..62d72dc0d --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GripBehavior.kt @@ -0,0 +1,59 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.node.entity.Entity +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.GroundItem +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class GripBehavior : NPCBehavior(NPCs.GRIP_792) { + // Attacking Grip + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + // You cannot attack if you are a black arm gang member. + if (attacker is Player && HeroesQuest.isBlackArm(attacker)) { + openDialogue(attacker, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + //"I can't attack the head guard here! There are too", "many witnesses around to see me do it! I'd have the", "whole of Brimhaven after me! Besides, if he dies I want", "the promotion!" + START_DIALOGUE -> sendPlayerDialogue(attacker, "I can't attack the head guard here! There are too many witnesses around to see me do it! I'd have the whole of Brimhaven after me! Besides, if he dies I want the promotion!") .also { stage++ } + 1 -> sendDialogueLines(attacker, "Perhaps you need another player's help...?").also { + stage = END_DIALOGUE + } + } + } + }) + return false + } + return true + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + if (getQuestStage(killer, Quests.HEROES_QUEST) == 4) { + setQuestStage(killer, Quests.HEROES_QUEST, 5) + } + + val gi = GroundItem( + Item(Items.GRIPS_KEY_RING_1588), + self.location, + 5000, + null, + ) + gi.forceVisible = true + gi.isRemainPrivate = false + + val gim = GroundItemManager.create(gi) + gim.isRemainPrivate = false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GripDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GripDialogue.kt new file mode 100644 index 000000000..4146b03e6 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GripDialogue.kt @@ -0,0 +1,118 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class GripDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GripDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return GripDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GRIP_792) + } +} + +class GripDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .branch { player -> + return@branch if (getAttribute(player, HeroesQuest.attributeGripTookPapers, false)) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .goto(continuePath) + branch.onValue(0) + .playerl("Hi there. I am Hartigen, reporting for duty as your new deputy sir!") + .npcl("Ah good, at last. You took your time getting here! Now let me see...") + .npcl("I'll get your hours and duty roster sorted out in a while. Oh, and do you have your I.D. papers with you? Internal security is almost as important as external security for a guard.") + .branch { player -> + return@branch if (inInventory(player, Items.ID_PAPERS_1584)) { 1 } else { 0 } + }.let { branch -> + val continuePath2 = b.placeholder() + branch.onValue(1) + .playerl("Right here sir!") + .linel("You hand the ID papers over to Grip.") + .betweenStage { df, player, _, _ -> + if (removeItem(player, Items.ID_PAPERS_1584)) { + setAttribute(player, HeroesQuest.attributeGripTookPapers, true) + } + } + .goto(continuePath2) + branch.onValue(0) + .playerl("Oh, dear. I don't have that with me any more.") + .npcl("Well, that's no good! Go get them immediately, then report back for duty.") + .end() + return@let continuePath2.builder() + } + .goto(continuePath) + return@let continuePath.builder() + } + .options() + .let { optionBuilder -> + val returnJoin = b.placeholder() + + optionBuilder.option_playerl("So can I please guard the treasure room please?") + .npcl("Well, I might post you outside it sometimes. I prefer to be the only one allowed inside however.") + .npcl("There's some pretty valuable artefacts in there! Those keys stay ONLY with the head guard and Scarface Pete.") + .goto(returnJoin) + + optionBuilder.optionIf("So what do my duties involve?") { player -> + return@optionIf !getAttribute(player, HeroesQuest.attributeGripSaidDuties, false) + } + .betweenStage { _, player, _, _ -> + setAttribute(player, HeroesQuest.attributeGripSaidDuties, true) + } + .playerl("So what do my duties involve?") + .npcl("You'll have various guard related duties on various shifts. I'll assign specific duties as they are required as and when they become necessary. Just so you know, if anything happens to me") + .npcl("you'll need to take over as head guard here. You'll find important keys to the treasure room and Pete's quarters inside my jacket - although I doubt anything bad's going to happen to") + .npcl("me anytime soon!") + .linel("Grip laughs to himself at the thought.") + .goto(returnJoin) + + optionBuilder.option_playerl("Well, I'd better sort my new room out.") + .npcl("Yeah, I'll give you time to settle in. Better get a good night's sleep, I expect you to report for duty at oh five hundred hours tomorrow on the dot!") + .end() + + + optionBuilder.optionIf("Anything I can do now?") { player -> + return@optionIf getAttribute(player, HeroesQuest.attributeGripSaidDuties, false) + } + .playerl("Anything I can do now?") + .branch { player -> + return@branch if (inInventory(player, Items.MISCELLANEOUS_KEY_1586)) { + 1 + } else { + 0 + } + }.let { branch -> + branch.onValue(1) + .npcl("Can't think of anything right now.") + .end() + + branch.onValue(0) + .npcl("Hmm. Well, you could find out what this key opens for me. Apparently it's for something in this building, but for the life of me I can't find what.") + .linel("Grip hands you a key.") + .endWith { _, player -> + addItemOrDrop(player, Items.MISCELLANEOUS_KEY_1586) + } + } + + returnJoin.builder() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GruborDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GruborDialogue.kt new file mode 100644 index 000000000..958d893b5 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/GruborDialogue.kt @@ -0,0 +1,87 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class GruborDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return GruborDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GruborDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GRUBOR_789) + } +} + +class GruborDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onPredicate { player -> + getQuestStage(player, Quests.HEROES_QUEST) >= 2 && + getAttribute(player, HeroesQuest.attributeGruborLetsYouIn, false) && + HeroesQuest.isBlackArm(player) + } + .playerl("Hi.") + .npcl("Hi, I'm a little busy right now.") + .end() + + b.onPredicate { player -> + getQuestStage(player, Quests.HEROES_QUEST) >= 2 && + !getAttribute(player, HeroesQuest.attributeGruborLetsYouIn, false) && + HeroesQuest.isBlackArm(player) + } + .npcl(FacialExpression.THINKING, "Yes? What do you want?") + .options() + .let { optionBuilder -> + + optionBuilder.option_playerl("Rabbit's foot.") + .npcl("Eh? What are you on about? Go away!") + .end() + + optionBuilder.option_playerl("Four leaved clover.") + .npcl("Oh you're one of the gang are you? Ok, hold up a second, I'll just let you in through here.") + .linel("You hear the door being unbarred from inside.") + .endWith { _, player -> + setAttribute(player, HeroesQuest.attributeGruborLetsYouIn, true) + } + + optionBuilder.option_playerl("Lucky horseshoe.") + .npcl("Eh? What are you on about? Go away!") + .end() + + optionBuilder.option_playerl("Black cat.") + .npcl("Eh? What are you on about? Go away!") + .end() + } + + + b.onPredicate { _ -> true } + .npcl(FacialExpression.THINKING, "Yes? What do you want?") + .options() + .let { optionBuilder -> + + optionBuilder.option_playerl("Would you like your hedges trimming?") + .npcl("Eh? Don't be daft! We don't even HAVE any hedges!") + .end() + + optionBuilder.option_playerl("I want to come in.") + .npcl("No, go away.") + .end() + + optionBuilder.option_playerl("Do you want to trade?") + .npcl("No, I'm busy.") + .end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/HeroesQuest.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/HeroesQuest.kt new file mode 100644 index 000000000..7eadbf5e5 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/HeroesQuest.kt @@ -0,0 +1,262 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import content.region.misthalin.varrock.quest.shieldofarrav.ShieldofArrav +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +/** + * Heroes' Quest + */ +@Initializable +class HeroesQuest : Quest(Quests.HEROES_QUEST,75, 74, 1, 188, 0, 1, 15) { + /** + * Do note: "other players can help you even if they have already finished Heroes' Quest" + * 1 - Talked to Achietties to start the quest + * + * PHOENIX + * 2 - Talked to Katrine + * 3 - Talked to Alfonse + * 4 - Talked to Charlie + * 5 - HIDDEN Killed Grip (You need key from Black Arm Friend) + * 6 - Talked to Katrine with Candlestick + * BLACK ARM + * 2 - Talked to Straven + * 3 - Talked to Trobert + * 4 - Talked to Garv + * 5 - HIDDEN Unlocked Chest (You need Grip killed from Phoenix Friend) + * 6 - Talked to Katrine with Candlestick + * + * 100 - Achiettes with all the items + */ + + companion object { + const val attributeGruborLetsYouIn = "/save:quest:heroesquest-gruborletsyouin" + const val attributeGripTookPapers = "/save:quest:heroesquest-griptookpapers" + const val attributeGripSaidDuties = "/save:quest:heroesquest-gripsaidduties" + const val attributeHasOpenedBackdoor = "/save:quest:heroesquest-hasopenedbackdoor" + const val attributeHasOpenedChestDoor = "/save:quest:heroesquest-hasopenedchestdoor" + + fun checkQuestsAreComplete(player: Player): Boolean { + return isQuestComplete(player, Quests.SHIELD_OF_ARRAV) && + isQuestComplete(player, Quests.LOST_CITY) && + isQuestComplete(player, Quests.MERLINS_CRYSTAL) && + isQuestComplete(player, Quests.DRAGON_SLAYER) && + getQuestPoints(player) >= 55 + } + + /** Abstraction of Shield of Arrav isPhoenix function */ + fun isPhoenix(player: Player): Boolean { + return ShieldofArrav.isPhoenix(player) + } + + /** Abstraction of Shield of Arrav isBlackArm function */ + fun isBlackArm(player: Player): Boolean { + return ShieldofArrav.isBlackArm(player) + } + + fun hasRequirements(player: Player): Boolean { + return arrayOf( + hasLevelStat(player, Skills.HERBLORE, 25), + hasLevelStat(player, Skills.MINING, 50), + hasLevelStat(player, Skills.FISHING, 53), + hasLevelStat(player, Skills.COOKING, 53), + isQuestComplete(player, Quests.SHIELD_OF_ARRAV), + isQuestComplete(player, Quests.LOST_CITY), + isQuestComplete(player, Quests.MERLINS_CRYSTAL), + isQuestComplete(player, Quests.DRAGON_SLAYER), + getQuestPoints(player) >= 55, + ).all { it } + } + + fun allItemsInInventory(player: Player): Boolean { + return inInventory(player, Items.FIRE_FEATHER_1583) && + inInventory(player, Items.LAVA_EEL_2149) && + inInventory(player, Items.THIEVES_ARMBAND_1579) + } + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player!!, Quests.HEROES_QUEST) > 0 + + if(!started){ + if (checkQuestsAreComplete(player)) { + line(player, "I can start this quest by speaking to !!Achietties?? at the", line++) + line(player, "!!Heroes' Guild?? located !!North?? of !!Taverly??", line++) + line(player, "as all required quests are complete, and I have enough QP.", line++) + } else { + line(player, "I can start this quest by speaking to !!Achietties?? at the", line++) + line(player, "!!Heroes' Guild?? located !!North?? of !!Taverly?? after completing", line++) + line(player, "!!The Shield of Arrav??", line++, isQuestComplete(player, Quests.SHIELD_OF_ARRAV)) + line(player, "!!The Lost City??", line++, isQuestComplete(player, Quests.LOST_CITY)) + line(player, "!!Merlin's Crystal??", line++, isQuestComplete(player, Quests.MERLINS_CRYSTAL)) + line(player, "!!The Dragon Slayer??", line++, isQuestComplete(player, Quests.DRAGON_SLAYER)) + line(player, "!!and gaining 55 Quest Points??", line++, getQuestPoints(player) >= 55) + } + line(player, "To complete this quest I need:", line++, false) + line(player, "!!Level 25 Herblore??", line++, hasLevelStat(player, Skills.HERBLORE, 25)) + line(player, "!!Level 50 Mining??", line++, hasLevelStat(player, Skills.MINING, 50)) + line(player, "!!Level 53 Fishing??", line++, hasLevelStat(player, Skills.FISHING, 53)) + line(player, "!!Level 53 Cooking??", line++, hasLevelStat(player, Skills.COOKING, 53)) + } else if (stage < 100) { + line(player, "!!Achietties?? will let me into the !!Heroes' Guild?? if I can get:", line++) + + // This is completely dependent on what you have in your inventory. + if (inInventory(player, Items.FIRE_FEATHER_1583)) { + line(player, "An Entranan Firebird Feather - I now have one on me!", line++, true) + } else { + line(player, "An !!Entranan Firebird Feather?? - I should check on !!Entrana??", line++) + } + + // This is completely dependent on what you have in your inventory. + if (inInventory(player, Items.LAVA_EEL_2149)) { + line(player, "A cooked lava eel - I now have one on me!", line++, true) + } else { + line(player, "A !!cooked lava eel?? - I should speak to a !!Fishing Expert??", line++) + } + + if (isPhoenix(player)) { + if (inInventory(player, Items.THIEVES_ARMBAND_1579)) { + line(player, "A Master Thieves Armband - I now have one on me!", line++, true) + } else { + line(player, "A !!Master Thieves Armband?? - the !!Phoenix Gang can help me??", line++) + } + + if (!inInventory(player, Items.THIEVES_ARMBAND_1579)) { + if (stage >= 2) { + line(player, "I spoke to Straven about the Master Thieves Armband.", line++, true) + } + + if (stage >= 3) { + line(player, "Then I told Alfonse the password 'Gherkin'.", line++, true) + } else if (stage >= 2) { + line(player, "He told me I can get one by stealing !!Pete's Candlestick??", line++) + line(player, "I should use the password he gave me at !!Brimhaven??", line++) + } + + if (stage >= 4) { + line(player, "Charlie told me about a secret door into Scarface Pete's", line++, true) + line(player, "hideout, but he couldn't find a way of getting through it.", line++, true) + } else if (stage >= 3) { + line(player, "He said, secretly speak to !!Charlie?? round the back.", line++) + } + + if (stage >= 6) { + line(player, "A rival gang member collected a candlestick for me after I", line++, true) + line(player, "killed Grip and got the Treasure Room key for them.", line++, true) + line(player, "I gave Straven Scarface Pete's candlestick, and in reward", line++, true) + line(player, "he gave me a Master Thieves Armband to prove my skills.", line++, true) + } else if (stage >= 4) { + line(player, "Maybe !!another player?? can help to get through this !!door???.", line++) + } + } + } + if (isBlackArm(player)) { + if (inInventory(player, Items.THIEVES_ARMBAND_1579)) { + line(player, "A Master Thieves Armband - I now have one on me!", line++, true) + } else { + line(player, "A !!Master Thieves Armband?? - the !!Black Arms can help me??", line++) + } + + if (!inInventory(player, Items.THIEVES_ARMBAND_1579)) { + if (stage >= 2) { + line(player, "I spoke to Katrine about the Master Thieves Armband.", line++, true) + } + + if (stage >= 3) { + line(player, "I used the Black Arm password to enter the Brimhaven HQ.", line++, true) + } else if (stage >= 2) { + line(player, "She told me I can get one by stealing !!Pete's Candlestick??", line++) + line(player, "I should use the password she gave me at !!Brimhaven??", line++) + } + + if (stage >= 4) { + line(player, "I managed to pass myself off as Hartigen and enter the", line++, true) + line(player, "HQ.", line++, true) + } else if (stage >= 3) { + line(player, "I need to disguise myself as !!Hartigen the Black Knight?? in", line++) + line(player, "order to get inside !!Scarface Pete's hideout??", line++) + } + + if (stage >= 6) { + line(player, "I collected the candlesticks with the Treasure Room key", line++, true) + line(player, "after a rival gang member killed Grip.", line++, true) + line(player, "I gave Katrine Scarface Pete's candlestick, and in reward", line++, true) + line(player, "she gave me a Master Thieves Armband to prove my skills.", line++, true) + } else if (stage >= 4) { + line(player, "I can move around the hideout, but now I need Grips keys", line++) + line(player, "to get into the treasure room and get the candlesticks.", line++) + line(player, "I need !!another player's help?? with this, as it's so risky.", line++) + } + } + } + + if (allItemsInInventory(player)) { + line(player, "Now that I have !!all the required items??, I should go and speak to", line++) + line(player, "!!Achietties?? and give them to her", line++) + } + } else { + // Everything above is replaced by this. + line(player, "I gave Achietties an Entranan Firebird Feather, A cooked", line++, true) + line(player, "lava eel from a dangerous fishing spot and after some", line++, true) + line(player, "difficulty, a Master Thief Armband.", line++, true) + line(player, "Once I had handed these over to Achietties I had proved", line++, true) + line(player, "myself worthy of entrance to the Heroes' Guild.", line++, true) + line++ + line++ + line(player,"QUEST COMPLETE!", line) + } + } + + override fun reset(player: Player) { + if (getQuestStage(player, Quests.HEROES_QUEST) == 0) { + removeAttribute(player, attributeGruborLetsYouIn) + removeAttribute(player, attributeGripTookPapers) + removeAttribute(player, attributeGripSaidDuties) + removeAttribute(player, attributeHasOpenedBackdoor) + removeAttribute(player, attributeHasOpenedChestDoor) + + // For testing: if you set quest stage to 0, it will switch your gang (blackarm <-> phoenix) + // Remember to set stage to 0 twice to keep your gang. + println("Swapping gang for Heroes Quest.") + ShieldofArrav.swapGang(player) + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Heroes Quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.DRAGON_BATTLEAXE_1377, 240, 277, 5) + + drawReward(player,"1 Quest Point", ln++) + drawReward(player,"Access to the Heroes' Guild", ln++) + drawReward(player,"A total of 29,232 XP spread", ln++) + drawReward(player,"over twelve skills", ln++) + + rewardXP(player, Skills.ATTACK, 3075.0) + rewardXP(player, Skills.DEFENCE, 3075.0) + rewardXP(player, Skills.STRENGTH, 3075.0) + rewardXP(player, Skills.HITPOINTS, 3075.0) + rewardXP(player, Skills.RANGE, 2075.0) + rewardXP(player, Skills.FISHING, 2725.0) + rewardXP(player, Skills.COOKING, 2825.0) + rewardXP(player, Skills.WOODCUTTING, 1575.0) + rewardXP(player, Skills.FIREMAKING, 1575.0) + rewardXP(player, Skills.SMITHING, 2725.0) + rewardXP(player, Skills.MINING, 2575.0) + rewardXP(player, Skills.HERBLORE, 1325.0) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/HeroesQuestListener.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/HeroesQuestListener.kt new file mode 100644 index 000000000..8764e7ba2 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/HeroesQuestListener.kt @@ -0,0 +1,155 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.npc.NPC +import core.game.node.item.GroundItem +import core.game.world.map.Location +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class HeroesQuestListener: InteractionListener { + + override fun defineListeners() { + // Black arm gang office door. + on(Scenery.DOOR_2626, IntType.SCENERY, "open") { player, node -> + if (getQuestStage(player, Quests.HEROES_QUEST) >= 2 && + getAttribute(player, HeroesQuest.attributeGruborLetsYouIn, false) && + HeroesQuest.isBlackArm(player)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + openDialogue(player, GruborDialogueFile(), NPC(NPCs.GRUBOR_789)) + } + return@on true + } + + // Kitchen entrance + on(Scenery.DOOR_2628, IntType.SCENERY, "open") { player, node -> + if (getQuestStage(player, Quests.HEROES_QUEST) >= 3 && HeroesQuest.isPhoenix(player)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendDialogue(player, "This door is locked.") + } + return@on true + } + + // Kitchen wall + on(Scenery.WALL_2629, IntType.SCENERY, "push") { player, node -> + if (getQuestStage(player, Quests.HEROES_QUEST) >= 4 && HeroesQuest.isPhoenix(player)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + openDialogue(player, CharlieTheCookDialogueFile(), NPC(NPCs.CHARLIE_THE_COOK_794)) + } + return@on true + } + + // Mansion frontdoor + on(Scenery.DOOR_2627, IntType.SCENERY, "open") { player, node -> + if (getQuestStage(player, Quests.HEROES_QUEST) >= 4 && HeroesQuest.isBlackArm(player)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + openDialogue(player, GarvDialogueFile(), NPC(NPCs.GARV_788)) + } + return@on true + } + + // Cupboard + + on(Scenery.CUPBOARD_2636, IntType.SCENERY, "search") { player, node -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> sendNPCDialogue(player, NPCs.PIRATE_GUARD_799, "I don't think Mr Grip will like you opening that. That's his private drinks cabinet.") .also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.NEUTRAL, "He won't notice me having a quick look.", 2), + Topic(FacialExpression.NEUTRAL, "Ok, I'll leave it.", END_DIALOGUE) + ) + 2 -> end().also { + val gripNpc = findNPC(NPCs.GRIP_792) + sendChat(gripNpc!!, "Stay out of my drinks cabinet!") + forceWalk(gripNpc, Location(2777, 3198, 0), "smart") + } + } + } + }) + return@on true + } + + // Mansion backdoor + on(Scenery.DOOR_2622, IntType.SCENERY, "open") { player, node -> + if (getAttribute(player, HeroesQuest.attributeHasOpenedBackdoor, false)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendDialogue(player, "This door is locked.") + } + return@on true + } + onUseWith(IntType.SCENERY, Items.MISCELLANEOUS_KEY_1586, Scenery.DOOR_2622) { player, used, with -> + setAttribute(player, HeroesQuest.attributeHasOpenedBackdoor, true) + DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) + return@onUseWith true + } + + // Chest door + on(Scenery.DOOR_2621, IntType.SCENERY, "open") { player, node -> + if (getAttribute(player, HeroesQuest.attributeHasOpenedChestDoor, false)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendDialogue(player, "This door is locked.") + } + return@on true + } + onUseWith(IntType.SCENERY, Items.GRIPS_KEY_RING_1588, Scenery.DOOR_2621) { player, used, with -> + setAttribute(player, HeroesQuest.attributeHasOpenedChestDoor, true) + DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) + return@onUseWith true + } + + // Chest + on(Scenery.CHEST_2632, IntType.SCENERY, "open"){ player, node -> + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.CHEST_2633, -1) + return@on true + } + on(Scenery.CHEST_2633, IntType.SCENERY, "close"){ player, node -> + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.CHEST_2632, -1) + return@on true + } + on(Scenery.CHEST_2633, IntType.SCENERY, "search"){ player, node -> + if (inInventory(player, Items.PETES_CANDLESTICK_1577)) { + sendMessage(player, "You search the chest but find nothing.") + } else { + if (getQuestStage(player, Quests.HEROES_QUEST) == 4) { + setQuestStage(player, Quests.HEROES_QUEST, 5) + } + sendDialogue(player, "You find two candlesticks in the chest. So that will be one for you, and one for the person who killed Grip for you.") + addItemOrDrop(player, Items.PETES_CANDLESTICK_1577, 2) + } + return@on true + } + + // + on(Items.FIRE_FEATHER_1583, IntType.GROUNDITEM, "take") { player, groundItem -> + if (inEquipment(player, Items.ICE_GLOVES_1580)) { + addItem(player, Items.FIRE_FEATHER_1583) + removeGroundItem(groundItem as GroundItem) + } else { + sendChat(player, "Ouch!") + player.impactHandler.manualHit(player, 9, ImpactHandler.HitsplatType.NORMAL) + sendMessage(player, "It is too hot to take. You need something cold to pick it up with.") + } + return@on true + } + + // OilFishingRodListener.kt + DrinkBlamishOilListener.kt + FinishedPotion.java + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/KatrineDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/KatrineDialogueFile.kt new file mode 100644 index 000000000..f5c3f1d56 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/KatrineDialogueFile.kt @@ -0,0 +1,127 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.* +import org.rs09.consts.Items + +class KatrineDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + // 0 is handled by default in the old KatrineDialogue. + b.onQuestStages(Quests.HEROES_QUEST, 1) + .playerl("Hey.") + .npcl("Hey.") + .options() + .let { optionBuilder -> + + optionBuilder.option_playerl("Who are all those people in there?") + .npcl("They're just various rogues and thieves.") + .playerl("They don't say a lot...") + .npcl("Nope.") + .end() + + optionBuilder.option_playerl("Is there any way I can get the rank of master thief?") + .npcl("Master thief? Ain't we the ambitious one!") + .npcl("Well, you're gonna have to do something pretty amazing.") + .playerl("Anything you can suggest?") + .npcl("Well, some of the MOST coveted prizes in thiefdom right now are in the pirate town of Brimhaven on Karamja.") + .npcl("The pirate leader Scarface Pete has a pair of extremely valuable candlesticks.") + .npcl("His security is VERY good.") + .npcl("We, of course, have gang members in a town like Brimhaven who may be able to help you.") + .npcl("Visit our hideout in the alleyway on palm street.") + .npcl("To get in you will need to tell them the secret password 'four leaved clover'.") + .endWith { _, player -> + if(getQuestStage(player, Quests.HEROES_QUEST) == 1) { + setQuestStage(player, Quests.HEROES_QUEST, 2) + } + } + + } + + // This is not authentic, she falls back to a boring default conversation, but I guess people might need help during the quest + b.onQuestStages(Quests.HEROES_QUEST, 2, 3, 4) + .playerl("What am I supposed to be doing again?") + .npcl("You told me you wanted to get the rank of master thief! Now pay attention.") + .npcl("Some of the MOST coveted prizes in thiefdom right now are in the pirate town of Brimhaven on Karamja.") + .npcl("The pirate leader Scarface Pete has a pair of extremely valuable candlesticks.") + .npcl("His security is VERY good.") + .npcl("We, of course, have gang members in a town like Brimhaven who may be able to help you.") + .npcl("Visit our hideout in the alleyway on palm street.") + .npcl("To get in you will need to tell them the secret password 'four leaved clover'.") + .end() + + // As FYI, the fallback for 2,3,4 is + /** + * .options() + * .let { optionBuilder -> + * optionBuilder.option_playerl("Who are all those people in there?") + * .npcl("They're just various rogues and thieves.") + * .playerl("They don't say a lot...") + * .npcl("Nope.") + * .end() + * optionBuilder.option("Teach me to be a top class criminal!") + * .playerl("Teach me to be a top class criminal.") + * .npcl("Teach yourself.") + * .end() + * + * + * + * Player: + * I have a candlestick now! + * Katrine: + * Good for you. I'll give a master thief's armband to the one who retrieved that. I know it wasn't you. + */ + + + b.onQuestStages(Quests.HEROES_QUEST, 5) + .branch { player -> + return@branch if (inInventory(player, Items.PETES_CANDLESTICK_1577)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Who are all those people in there?") + .npcl("They're just various rogues and thieves.") + .playerl("They don't say a lot...") + .npcl("Nope.") + .end() + optionBuilder.option("I have a candlestick now.") + .playerl("I have a candlestick now!") + .npcl("Wow... is... it REALLY it?") + .npcl("This really is a FINE bit of thievery.") + .npcl("Us thieves have been trying to get hold of this one for a while!") + .npc("You wanted to be ranked as a master thief didn't you?", "Well, I guess this just about ranks as good enough!") + .linel("Katrine gives you a master thief armband.") + .endWith { _, player -> + if (removeItem(player, Items.PETES_CANDLESTICK_1577)) { + addItemOrDrop(player, Items.THIEVES_ARMBAND_1579) + if (getQuestStage(player, Quests.HEROES_QUEST) == 5) { + setQuestStage(player, Quests.HEROES_QUEST, 6) + } + } + } + + branch.onValue(0) + .playerl("What am I supposed to be doing again?") + .npcl("You told me you wanted to get the rank of master thief! Now pay attention.") + .npcl("Some of the MOST coveted prizes in thiefdom right now are in the pirate town of Brimhaven on Karamja.") + .npcl("The pirate leader Scarface Pete keeps an extremely valuable candlesticks.") + .npcl("His security is VERY good.") + .npcl("We, of course, have gang members in a town like Brimhaven who may be able to help you.") + .npcl("Visit our hideout in the alleyway on palm street.") + .npcl("To get in you will need to tell them the secret password 'four leaved clover'.") + .end() + } + } + + // I lost the armband and some stupid default shit. + b.onQuestStages(Quests.HEROES_QUEST, 6) + .playerl("I have lost my master thief's armband...") + .npcl("Lucky I 'ave a spare ain't it? Don't lose it again.") + .endWith { _, player -> + addItemOrDrop(player, Items.THIEVES_ARMBAND_1579) + } + + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/StravenDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/StravenDialogueFile.kt new file mode 100644 index 000000000..68d236905 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/StravenDialogueFile.kt @@ -0,0 +1,73 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.* +import org.rs09.consts.Items + +class StravenDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + // 0 is handled by default in the old StravenDialogue. + b.onQuestStages(Quests.HEROES_QUEST, 1) + .playerl("How would I go about getting a Master Thief armband?") + .npcl("Ooh... tricky stuff. Took me YEARS to get that rank.") + .npcl("Well, what some of the more aspiring thieves in our gang are working on right now is to steal some very valuable candlesticks from Scarface Pete - the pirate leader on Karamja.") + .npcl("His security is excellent, and the target very valuable so that might be enough to get you the rank.") + .npcl("Go talk to our man Alfonse, the waiter in the Shrimp and Parrot.") + .npcl("Use the secret word 'gherkin' to show you're one of us.") + .endWith { _, player -> + if(getQuestStage(player, Quests.HEROES_QUEST) == 1) { + setQuestStage(player, Quests.HEROES_QUEST, 2) + } + } + + b.onQuestStages(Quests.HEROES_QUEST, 2, 3, 4) + .playerl("What am I supposed to be doing again?") + .npcl("You told me you wanted to get a Master thief's armband! Now pay attention.") + .npcl("Some of the more aspiring thieves in our gang are working on right now is to steal some very valuable candlesticks from Scarface Pete - the pirate leader on Karamja.") + .npcl("His security is excellent, and the target very valuable so that might be enough to get you the rank.") + .npcl("Go talk to our man Alfonse, the waiter in the Shrimp and Parrot.") + .npcl("Use the secret word 'gherkin' to show you're one of us.") + .end() + + + b.onQuestStages(Quests.HEROES_QUEST, 5) + .branch { player -> + return@branch if (inInventory(player, Items.PETES_CANDLESTICK_1577)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("I have retrieved a candlestick!") + .npcl("Hmmm. Not bad, not bad. Let's see it, make sure it's genuine.") + .linel("You hand Straven the candlestick.") + .playerl("So is this enough to get me a Master Thief armband?") + .npcl("Hmm... I dunno... Aww, go on then. I suppose I'm in a generous mood today.") + .linel("Straven hands you a Master Thief armband.") + .endWith { _, player -> + if (removeItem(player, Items.PETES_CANDLESTICK_1577)) { + addItemOrDrop(player, Items.THIEVES_ARMBAND_1579) + if (getQuestStage(player, Quests.HEROES_QUEST) == 5) { + setQuestStage(player, Quests.HEROES_QUEST, 6) + } + } + } + + branch.onValue(0) + .playerl("What am I supposed to be doing again?") + .npcl("You told me you wanted to get a Master thief's armband! Now pay attention.") + .npcl("Some of the more aspiring thieves in our gang are working on right now is to steal some very valuable candlesticks from Scarface Pete - the pirate leader on Karamja.") + .npcl("His security is excellent, and the target very valuable so that might be enough to get you the rank.") + .npcl("Go talk to our man Alfonse, the waiter in the Shrimp and Parrot.") + .npcl("Use the secret word 'gherkin' to show you're one of us.") + .end() + } + + // I lost the armband and some stupid default shit. + b.onQuestStages(Quests.HEROES_QUEST, 6) + .playerl("I'm afraid I've lost my master thief's armband.") + .npcl("Lucky for you I have a spare. Don't lose it again!") + .endWith { _, player -> + addItemOrDrop(player, Items.THIEVES_ARMBAND_1579) + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/TrobertDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/TrobertDialogue.kt new file mode 100644 index 000000000..12561ad44 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/heroesquest/TrobertDialogue.kt @@ -0,0 +1,92 @@ +package content.region.asgarnia.burthorpe.quest.heroesquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class TrobertDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, TrobertDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return TrobertDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TROBERT_1884) + } +} + +class TrobertDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + // Technically this won't happen since you have to get past Grubor. + b.onQuestStages(Quests.HEROES_QUEST, 0, 1) + .npcl("Welcome to our Brimhaven headquarters. I'm Trobert and I'm in charge here.") + .playerl("Pleased to meet you.") + .npcl("Likewise.") + .end() + + b.onQuestStages(Quests.HEROES_QUEST, 2) + .npcl("Welcome to our Brimhaven headquarters. I'm Trobert and I'm in charge here.") + .options() + .let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option("So can you help me get Scarface Pete's candlesticks?") + .goto(continuePath) + optionBuilder.option_playerl("Pleased to meet you.") + .npcl("Likewise.") + .goto(continuePath) + return@let continuePath.builder() + } + .playerl("So can you help me get Scarface Pete's candlesticks?") + .npcl("Well, we have made some progress there. We know that one of the only keys to Pete's treasure room is carried by Grip, the head guard, so we thought it might be good to get close to him somehow.") + .npcl("Grip was taking on a new deputy called Hartigen, an Asgarnian Black Knight who was deserting the Black Knight Fortress and seeking new employment here on Brimhaven.") + .npcl("We managed to waylay him on the journey here, and steal his I.D. papers. Now all we need is to find somebody willing to impersonate him and take the deputy role to get that key for us.") + .options() + .let { optionBuilder -> + + optionBuilder.option_playerl("I volunteer to undertake that mission!") + .npcl("Good good. Well, here's the ID papers, take them and introduce yourself to the guards at Scarface Pete's mansion, we'll have that treasure in no time.") + .endWith { _, player -> + addItemOrDrop(player, Items.ID_PAPERS_1584) + if(getQuestStage(player, Quests.HEROES_QUEST) == 2) { + setQuestStage(player, Quests.HEROES_QUEST, 3) + } + } + + optionBuilder.option_playerl("Well, good luck then.") + .npcl("Someone will show up eventually.") + .end() + } + + b.onQuestStages(Quests.HEROES_QUEST, 3, 4, 5) + .branch { player -> + return@branch if (inInventory(player, Items.ID_PAPERS_1584)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("How's it going?") + .playerl("Fine, thanks.") + .end() + branch.onValue(0) + .playerl("I have lost Hartigen's ID papers.") + .npcl("Well, that was careless of you, wasn't it? Fortunately for you, he had a spare. Take this one, but please try to be more careful with this one.") + .endWith { _, player -> + addItemOrDrop(player, Items.ID_PAPERS_1584) + } + } + + b.onQuestStages(Quests.HEROES_QUEST, 6, 100) + .npcl("How's it going?") + .playerl("Fine, thanks.") + .end() + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/BerryNpc.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/BerryNpc.kt index 116a95063..0b04d9ba1 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/BerryNpc.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/BerryNpc.kt @@ -1,8 +1,8 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold +import content.data.Quests import core.api.isQuestInProgress import core.api.produceGroundItem -import core.api.transformNpc import core.game.node.entity.Entity import core.game.node.entity.combat.CombatStyle import core.game.node.entity.npc.AbstractNPC @@ -44,7 +44,7 @@ class BerryNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id, locati } override fun finalizeDeath(killer: Entity?) { - if (isQuestInProgress(killer as Player, TrollStronghold.questName, 8, 10)) { + if (isQuestInProgress(killer as Player, Quests.TROLL_STRONGHOLD, 8, 10)) { produceGroundItem(killer, Items.CELL_KEY_2_3137, 1, this.location) } super.finalizeDeath(killer) diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogue.kt index 2a2b2df0a..a1d76a63e 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogue.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold +import content.data.Quests import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -13,7 +14,7 @@ import org.rs09.consts.NPCs @Initializable class DadDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (getQuestStage(player!!, TrollStronghold.questName)) { + when (getQuestStage(player!!, Quests.TROLL_STRONGHOLD)) { in 3..4 -> { when (stage) { START_DIALOGUE -> npcl(FacialExpression.OLD_HAPPY, "What tiny human do in troll arena? Dad challenge human to fight!").also { stage++ } @@ -27,7 +28,7 @@ class DadDialogue(player: Player? = null) : DialoguePlugin(player) { 3 -> npcl(FacialExpression.OLD_HAPPY, "Tiny human brave. Dad squish!").also { stage++ } 4 -> npc!!.attack(player).also { npc!!.skills.lifepoints = npc!!.skills.maximumLifepoints // Reset dad to max hitpoints. - setQuestStage(player!!, TrollStronghold.questName, 4) + setQuestStage(player!!, Quests.TROLL_STRONGHOLD, 4) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogueFile.kt index 69b9d5334..4428bf902 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogueFile.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold +import content.data.Quests import core.api.setQuestStage import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -13,14 +14,14 @@ class DadDialogueFile(private val dialogueNum: Int = 0) : DialogueFile() { 1 -> when (stage) { START_DIALOGUE -> npcl(FacialExpression.OLD_HAPPY, "No human pass through arena without defeating Dad!").also { stage = END_DIALOGUE - setQuestStage(player!!, TrollStronghold.questName, 3) + setQuestStage(player!!, Quests.TROLL_STRONGHOLD, 3) } } 2 -> when (stage) { START_DIALOGUE -> npcl(FacialExpression.OLD_NORMAL, "Tiny human brave. Dad squish!").also { stage++ } 1 -> npc!!.attack(player).also { npc!!.skills.lifepoints = npc!!.skills.maximumLifepoints // Reset dad to max hitpoints. - setQuestStage(player!!, TrollStronghold.questName, 4) + setQuestStage(player!!, Quests.TROLL_STRONGHOLD, 4) stage = END_DIALOGUE } } @@ -31,7 +32,7 @@ class DadDialogueFile(private val dialogueNum: Int = 0) : DialogueFile() { Topic(FacialExpression.ANGRY_WITH_SMILE, "I'm not done yet! Prepare to die!", 2) ) 2 -> player!!.attack(npc).also { - setQuestStage(player!!, TrollStronghold.questName, 5) + setQuestStage(player!!, Quests.TROLL_STRONGHOLD, 5) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadNpc.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadNpc.kt index d7c5e58cd..f489aa1ed 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadNpc.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadNpc.kt @@ -1,7 +1,7 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold +import content.data.Quests import core.api.* -import core.game.node.Node import core.game.node.entity.Entity import core.game.node.entity.combat.BattleState import core.game.node.entity.combat.CombatStyle @@ -27,7 +27,7 @@ class DadNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id, location val player = entity.asPlayer() // Attack Dad. If quest is done, you cannot attack Dad. - when (getQuestStage(player, TrollStronghold.questName)) { + when (getQuestStage(player, Quests.TROLL_STRONGHOLD)) { 3 -> openDialogue(player, DadDialogueFile(2), this.asNpc()).also { return false } 4 -> { return attackable } in 5 .. 100 -> sendMessage(player, "You don't need to fight him again.").also { return false } @@ -44,8 +44,8 @@ class DadNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id, location if (opponent.skills.lifepoints < 30) { player.properties.combatPulse.stop() opponent.properties.combatPulse.stop() - if (getQuestStage(player!!.asPlayer(), TrollStronghold.questName) == 4){ - setQuestStage(player!!.asPlayer(), TrollStronghold.questName, 5) + if (getQuestStage(player!!.asPlayer(), Quests.TROLL_STRONGHOLD) == 4){ + setQuestStage(player!!.asPlayer(), Quests.TROLL_STRONGHOLD, 5) } submitWorldPulse(object : Pulse(){ var counter = 0 @@ -64,8 +64,8 @@ class DadNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id, location override fun finalizeDeath(killer: Entity?) { // In case Dad gets one shotted to death. super.finalizeDeath(killer) - if (getQuestStage(killer!!.asPlayer(), TrollStronghold.questName) == 4){ - setQuestStage(killer!!.asPlayer(), TrollStronghold.questName, 5) + if (getQuestStage(killer!!.asPlayer(), Quests.TROLL_STRONGHOLD) == 4){ + setQuestStage(killer!!.asPlayer(), Quests.TROLL_STRONGHOLD, 5) } } override fun handleTickActions() { diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DenulthDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DenulthDialogueFile.kt index 45318ea53..2575fc4b6 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DenulthDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DenulthDialogueFile.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -14,7 +15,7 @@ import org.rs09.consts.Items */ class DenulthDialogueFile : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, TrollStronghold.questName)) { + when (getQuestStage(player!!, Quests.TROLL_STRONGHOLD)) { in 1..7 -> { when (stage) { START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "How are you getting on with rescuing Godric?").also { stage++ } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DunstanDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DunstanDialogueFile.kt index aa9615ea7..4cd3a3f1a 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DunstanDialogueFile.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DunstanDialogueFile.kt @@ -1,30 +1,65 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold -import core.api.finishQuest -import core.api.getQuestStage +import content.data.Quests +import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression import core.game.dialogue.Topic +import core.game.node.item.Item import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import org.rs09.consts.Items class DunstanDialogueFile : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, TrollStronghold.questName)) { + when (getQuestStage(player!!, Quests.TROLL_STRONGHOLD)) { in 1..10 -> { when (stage) { START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Have you managed to rescue Godric yet?").also { stage++ } 1 -> playerl(FacialExpression.FRIENDLY, "Not yet.").also { stage++ } 2 -> npcl(FacialExpression.FRIENDLY, "Please hurry! Who knows what they will do to him? Is there anything I can do in the meantime?").also { stage++ } 3 -> showTopics( + Topic(FacialExpression.THINKING, "Can you put some spikes on my Climbing boots?", 30), Topic(FacialExpression.THINKING, "Is it OK if I use your anvil?", 10), - Topic(FacialExpression.FRIENDLY, "Nothing, thanks.", END_DIALOGUE), + Topic(FacialExpression.NEUTRAL, "Nothing, thanks.", 20), ) 10 -> npcl(FacialExpression.FRIENDLY, "So you're a smithy are you?").also { stage++ } 11 -> playerl(FacialExpression.FRIENDLY, "I dabble.").also { stage++ } 12 -> npcl(FacialExpression.FRIENDLY, "A fellow smith is welcome to use my anvil!").also { stage++ } 13 -> playerl(FacialExpression.FRIENDLY, "Thanks!").also { stage++ } 14 -> npcl(FacialExpression.FRIENDLY, "Anything else before I get on with my work?").also { stage = 3 } + 20 -> npcl(FacialExpression.NEUTRAL, "All right. Speak to you later then.").also { stage = END_DIALOGUE } + 30 -> playerl("Can you put some spikes on my Climbing boots?").also { stage++ } + 31 -> npcl("For you, no problem.").also { stage++ } + 32 -> npc("Do you realise that you can only use the Climbing", "boots right now? The Spiked boots can only be used in", "the Icelands but no ones been able to get there for", "years!").also { stage++ } + 33 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, but I still want them.", 40, true), + Topic(FacialExpression.NEUTRAL, "Oh OK, I'll leave them thanks.", 43), + ) + 40 -> { + if (inInventory(player!!, Items.CLIMBING_BOOTS_3105) && inInventory(player!!, Items.IRON_BAR_2351)) { + sendDoubleItemDialogue(player!!, Items.IRON_BAR_2351, Items.CLIMBING_BOOTS_3105, "You give Dunstan an Iron bar and the climbing boots.") + sendMessage(player!!, "You give Dunstan an Iron bar and the climbing boots.") + if (removeItem(player!!, Item(Items.CLIMBING_BOOTS_3105)) && removeItem(player!!, Item(Items.IRON_BAR_2351))) { + addItemOrDrop(player!!, Items.SPIKED_BOOTS_3107) + stage++ + } else { + stage = END_DIALOGUE + } + } else if (inInventory(player!!, Items.CLIMBING_BOOTS_3105)){ + npcl("Sorry, I'll need an iron bar to make the spikes.") + stage = 3 + } else { + playerl("I don't have them on me.") + stage = 3 + } + } + 41 -> sendItemDialogue(player!!, Items.SPIKED_BOOTS_3107, "Dunstan has given you the spiked boots.").also { stage++ + sendMessage(player!!, "Dunstan has given you the spiked boots.") + } + 43 -> npcl(FacialExpression.FRIENDLY, "Anything else before I get on with my work?").also { + stage = 3 + } } } 11 -> { @@ -35,7 +70,7 @@ class DunstanDialogueFile : DialogueFile() { 3 -> npcl(FacialExpression.FRIENDLY, "I have very little to offer you by way of thanks, but perhaps you will accept these family heirlooms. They were found by my great-great-grandfather, but we still don't have any idea what they do.").also { stage++ } 4 -> { stage = END_DIALOGUE - finishQuest(player!!, TrollStronghold.questName) + finishQuest(player!!, Quests.TROLL_STRONGHOLD) } } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollGeneralsNpc.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollGeneralsNpc.kt index 1a0488e69..cee11ddfc 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollGeneralsNpc.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollGeneralsNpc.kt @@ -1,10 +1,7 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold -import content.region.kandarin.quest.grandtree.ForemanDialogue -import content.region.kandarin.quest.grandtree.TheGrandTree +import content.data.Quests import core.api.* -import core.game.interaction.IntType -import core.game.interaction.InteractionListener import core.game.node.entity.Entity import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.player.Player @@ -25,7 +22,7 @@ class TrollGeneralsNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id } override fun finalizeDeath(killer: Entity?) { - if(isQuestInProgress(killer as Player, TrollStronghold.questName, 1, 7)) { + if(isQuestInProgress(killer as Player, Quests.TROLL_STRONGHOLD, 1, 7)) { produceGroundItem(killer, Items.PRISON_KEY_3135, 1, this.location) } super.finalizeDeath(killer) diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStronghold.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStronghold.kt index 971e7f392..6630bfa14 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStronghold.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStronghold.kt @@ -1,51 +1,37 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold -import content.region.asgarnia.burthorpe.quest.deathplateau.DeathPlateau import core.api.* import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * Troll Stronghold Quest * @author ovenbread */ @Initializable -class TrollStronghold : Quest("Troll Stronghold",128, 127, 1, 317, 0, 1, 50) { +class TrollStronghold : Quest(Quests.TROLL_STRONGHOLD,128, 127, 1, 317, 0, 1, 50) { - /** - * 1 - Talked to Denulth to start the quest - * 3 - Enter the Arena with Dad - * 4 - Start fighting Dad - * 5 - Dad surrenders or gets killed, allowed to exit the Arena - * 8 - Unlocks Prison Gate - * 9 - Unlocked Mad Eadgar's cell - * 10 - Unlocked Godric's cell - * 11 - Unlocked both Mad Eadgar's and Godric's cell - * 100 - Finish at Dunstan - */ - companion object { - const val questName = "Troll Stronghold" - } override fun drawJournal(player: Player?, stage: Int) { super.drawJournal(player, stage) var line = 12 var stage = getStage(player) - var started = getQuestStage(player!!, questName) > 0 + var started = getQuestStage(player!!, Quests.TROLL_STRONGHOLD) > 0 if(!started){ line(player, "I can start this quest by speaking to !!Denulth?? in his tent at", line++) line(player, "the !!Imperial Guard camp?? in !!Burthorpe?? after completing the", line++) - line(player, "!!Death Plateau Quest??", line++, isQuestComplete(player, DeathPlateau.questName)) + line(player, "!!Death Plateau Quest??", line++, isQuestComplete(player, Quests.DEATH_PLATEAU)) line++ line(player, "To complete this quest I need:", line++) line(player, "Level 15 Agility.", line++, hasLevelStat(player, Skills.AGILITY, 15)) line(player, "I also need to be able to defeat a !!level 113 Troll??.", line++) line(player, "Level 30 Thieving might be useful.", line++, hasLevelStat(player, Skills.THIEVING, 30)) - if (isQuestComplete(player, DeathPlateau.questName) && hasLevelStat(player, Skills.AGILITY, 15) && hasLevelStat(player, Skills.THIEVING, 30)) { + if (isQuestComplete(player, Quests.DEATH_PLATEAU) && hasLevelStat(player, Skills.AGILITY, 15) && hasLevelStat(player, Skills.THIEVING, 30)) { line(player, "I have all the requirements to start this quest.", line++) } } else { @@ -61,10 +47,12 @@ class TrollStronghold : Quest("Troll Stronghold",128, 127, 1, 317, 0, 1, 50) { if (stage >= 5) { line(player, "I have defeated the !!Troll Champion??", line++, true) } else if (stage >= 3) { - line(player, "I have to defeat the !!Troll Champion??", line++) + line(player, "I have accepted the !!Troll Champion's?? challenge.", line++) } - if (stage in 5..7) { - line++ + line++ + if (stage >= 7) { + line(player, "I found my way into the Troll Stronghold", line++, true) + } else if (stage >= 5) { line(player, "I have to find a way to get into the !!Troll Stronghold??", line++) } line++ diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStrongholdListener.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStrongholdListener.kt index 9e03991ab..f276b3944 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStrongholdListener.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStrongholdListener.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold +import content.data.Quests import core.api.* import core.game.global.action.DoorActionHandler import core.game.interaction.IntType @@ -22,12 +23,12 @@ class TrollStrongholdListener: InteractionListener { // Entrance to arena with Dad in it. on(intArrayOf(Scenery.ARENA_ENTRANCE_3782, Scenery.ARENA_ENTRANCE_3783), IntType.SCENERY, "open"){ player, node -> // Only get the dialogue once. - if (getQuestStage(player, TrollStronghold.questName) == 1) { + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) == 1) { openDialogue(player, DadDialogueFile(1), findNPC(NPCs.DAD_1125)!!) } // Only allow players through when they start Troll Stronghold. // No one is allowed to go to GWD unless they start the Troll Stronghold quest. - if (getQuestStage(player, TrollStronghold.questName) > 0) { + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) > 0) { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } else { sendMessage(player, "You need to start the Troll Stronghold quest.") @@ -37,7 +38,7 @@ class TrollStrongholdListener: InteractionListener { // Not allowed to exit arena into the troll stronghold until Dad is defeated. on(intArrayOf(Scenery.ARENA_EXIT_3785, Scenery.ARENA_EXIT_3786), IntType.SCENERY, "open"){ player, node -> - if (getQuestStage(player, TrollStronghold.questName) < 5){ + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) < 5){ openDialogue(player, DadDialogueFile(1), findNPC(NPCs.DAD_1125)!!) } else { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) @@ -47,12 +48,12 @@ class TrollStrongholdListener: InteractionListener { // Key to unlock the prison door on(Scenery.PRISON_DOOR_3780, IntType.SCENERY, "unlock"){ player, node -> - if (getQuestStage(player, TrollStronghold.questName) >= 8){ + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) >= 8){ DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } else { if (inInventory(player, Items.PRISON_KEY_3135)) { - if (getQuestStage(player, TrollStronghold.questName) == 5) { - setQuestStage(player, TrollStronghold.questName, 8) + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) == 5) { + setQuestStage(player, Quests.TROLL_STRONGHOLD, 8) } if (removeItem(player, Items.PRISON_KEY_3135)) { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) @@ -84,7 +85,7 @@ class TrollStrongholdListener: InteractionListener { 3 -> { val success: Boolean = success(player, Skills.THIEVING) if(success){ - if(isQuestInProgress(player, TrollStronghold.questName, 8, 10)) { + if(isQuestInProgress(player, Quests.TROLL_STRONGHOLD, 8, 10)) { addItem(player, Items.CELL_KEY_1_3136) sendMessage(player, "You find a small key on Twig.") } else { @@ -124,7 +125,7 @@ class TrollStrongholdListener: InteractionListener { 3 -> { val success: Boolean = success(player, Skills.THIEVING) if(success){ - if(isQuestInProgress(player, TrollStronghold.questName, 8, 10)) { + if(isQuestInProgress(player, Quests.TROLL_STRONGHOLD, 8, 10)) { addItem(player, Items.CELL_KEY_2_3137) sendMessage(player, "You find a small key on Berry.") } else { @@ -149,10 +150,10 @@ class TrollStrongholdListener: InteractionListener { fun unlockMadEadgarCellDoor(player: Player, node: Node) { if (inInventory(player, Items.CELL_KEY_1_3136)){ sendMessage(player, "You unlock the cell door.") - if (getQuestStage(player, TrollStronghold.questName) == 8) { - setQuestStage(player, TrollStronghold.questName, 9) - } else if (getQuestStage(player, TrollStronghold.questName) == 10) { - setQuestStage(player, TrollStronghold.questName, 11) + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) == 8) { + setQuestStage(player, Quests.TROLL_STRONGHOLD, 9) + } else if (getQuestStage(player, Quests.TROLL_STRONGHOLD) == 10) { + setQuestStage(player, Quests.TROLL_STRONGHOLD, 11) } // Animate Mad Eadgar leaving cell. val npc = findNPC(NPCs.EADGAR_1113)!! @@ -219,10 +220,10 @@ class TrollStrongholdListener: InteractionListener { fun unlockGodricCellDoor(player: Player, node: Node) { if (inInventory(player, Items.CELL_KEY_2_3137)){ sendMessage(player, "You unlock the cell door.") - if (getQuestStage(player, TrollStronghold.questName) == 8) { - setQuestStage(player, TrollStronghold.questName, 10) - } else if (getQuestStage(player, TrollStronghold.questName) == 9) { - setQuestStage(player, TrollStronghold.questName, 11) + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) == 8) { + setQuestStage(player, Quests.TROLL_STRONGHOLD, 10) + } else if (getQuestStage(player, Quests.TROLL_STRONGHOLD) == 9) { + setQuestStage(player, Quests.TROLL_STRONGHOLD, 11) } // Animate Godric leaving cell. val npc = findNPC(NPCs.GODRIC_1114)!! @@ -292,7 +293,7 @@ class TrollStrongholdListener: InteractionListener { // Reentry Secret Door on(Scenery.SECRET_DOOR_3762, IntType.SCENERY, "open"){ player, _ -> - if (getQuestStage(player, TrollStronghold.questName) >= 8) { + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) >= 8) { player.properties.teleportLocation = Location.create(2824, 10050, 0) } else { sendMessage(player, "The door is locked.") diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TwigNpc.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TwigNpc.kt index 97e253d71..deb316aeb 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TwigNpc.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TwigNpc.kt @@ -1,16 +1,14 @@ package content.region.asgarnia.burthorpe.quest.trollstronghold -import core.api.getQuestStage +import content.data.Quests import core.api.isQuestInProgress import core.api.produceGroundItem -import core.api.transformNpc import core.game.node.entity.Entity import core.game.node.entity.combat.CombatStyle import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.player.Player import core.game.world.map.Location import core.plugin.Initializable -import core.tools.RandomFunction import org.rs09.consts.Items import org.rs09.consts.NPCs @@ -46,7 +44,7 @@ class TwigNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id, locatio } override fun finalizeDeath(killer: Entity?) { - if (isQuestInProgress(killer as Player, TrollStronghold.questName, 8, 10)) { + if (isQuestInProgress(killer as Player, Quests.TROLL_STRONGHOLD, 8, 10)) { produceGroundItem(killer, Items.CELL_KEY_1_3136, 1, this.location) } super.finalizeDeath(killer) diff --git a/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt b/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt index d059afb09..054dbf209 100644 --- a/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt +++ b/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt @@ -49,7 +49,8 @@ class MasterCrafterDialogue(player: Player? = null) : DialoguePlugin(player) { ).also { stage++ } 101 -> options( - "99000 gold! Are you mad?", "That's fine." + "99000 gold! Are you mad?", + "That's fine." ).also { stage++ } 102 -> { @@ -59,26 +60,10 @@ class MasterCrafterDialogue(player: Player? = null) : DialoguePlugin(player) { "99000 gold! Are you mad?" ).also { stage++ } - 2 -> { - when { - !inInventory(player, Items.COINS_995, 99000) -> playerl( - FacialExpression.NEUTRAL, - "But, unfortunately, I don't have enough money with me." - ).also { stage = 111 } - - freeSlots(player) < 2 -> npcl( - FacialExpression.FRIENDLY, - "Unfortunately all Skillcapes are only available with a free hood, it's part " + - "of a skill promotion deal; buy one get one free, you know. So you'll need " + - "to free up some inventory space before I can sell you one." - ).also { stage = END_DIALOGUE } - - else -> playerl( - FacialExpression.FRIENDLY, - "That's fine." - ).also { stage = 112 } - } - } + 2 -> playerl( + FacialExpression.FRIENDLY, + "That's fine." + ).also { stage = 110 } } } @@ -88,19 +73,34 @@ class MasterCrafterDialogue(player: Player? = null) : DialoguePlugin(player) { "such a prestigious item! You can find me here if you change your mind." ).also { stage = END_DIALOGUE } + 110 -> { + when { + !inInventory(player, Items.COINS_995, 99000) -> playerl( + FacialExpression.NEUTRAL, + "But, unfortunately, I don't have enough money with me." + ).also { stage = 111 } + + freeSlots(player) < 2 -> npcl( + FacialExpression.FRIENDLY, + "Unfortunately all Skillcapes are only available with a free hood, it's part " + + "of a skill promotion deal; buy one get one free, you know. So you'll need " + + "to free up some inventory space before I can sell you one." + ).also { stage = END_DIALOGUE } + + else -> { + Skillcape.purchase(player, Skills.CRAFTING) + npcl( + FacialExpression.FRIENDLY, + "Excellent! Wear that cape with pride my friend." + ).also { stage = END_DIALOGUE } + } + } + } 111 -> npcl( FacialExpression.FRIENDLY, "Well, come back and see me when you do." ).also { stage = END_DIALOGUE } - - 112 -> { - Skillcape.purchase(player, Skills.CRAFTING) - npcl( - FacialExpression.FRIENDLY, - "Excellent! Wear that cape with pride my friend." - ).also { stage = END_DIALOGUE } - } } return true } diff --git a/Server/src/main/content/region/asgarnia/dialogue/OracleDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/OracleDialogue.java index a4a58570c..b811dbc74 100644 --- a/Server/src/main/content/region/asgarnia/dialogue/OracleDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/OracleDialogue.java @@ -5,6 +5,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the oracle dialogue plugin related to dragon slayer. @@ -43,7 +44,7 @@ public final class OracleDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Dragon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER); switch (quest.getStage(player)) { case 20: player("I seek a piece of the map to the island of Crandor."); diff --git a/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt b/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt index fe7c09060..b4d949075 100644 --- a/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt +++ b/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt @@ -1,56 +1,80 @@ package content.region.asgarnia.dialogue import content.global.skill.crafting.TanningProduct +import core.api.amountInInventory +import core.api.inInventory import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC import core.plugin.Initializable +import org.rs09.consts.Items import org.rs09.consts.NPCs import core.tools.END_DIALOGUE /** + * Handles the Crafting Guild Tanner's dialogue. * @author bushtail */ @Initializable class TannerDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player) : DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return TannerDialogue(player) } - override fun open(vararg args: Any?) : Boolean { + override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC npcl(FacialExpression.NEUTRAL, "Greetings friend. I am a manufacturer of leather.") stage = 0 return true } - override fun handle(interfaceId: Int, buttonId: Int) : Boolean { - when(stage) { - 0 -> options("Can I buy some leather then?", "Leather is rather weak stuff.").also{ stage++ } - 1 -> when(buttonId) { - 1 -> player(FacialExpression.ASKING,"Can I buy some leather then?").also{ stage = 10 } - 2 -> player(FacialExpression.SUSPICIOUS, "Leather is rather weak stuff.").also { stage = 20 } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> { + var hasHides = false + + for (tanningProduct in TanningProduct.values()) { + if (inInventory(player, tanningProduct.item)) { + hasHides = true + break + } + } + + if(hasHides) { + npcl(FacialExpression.FRIENDLY, "I see you have brought me some hides. Would you like me to tan them for you?").also { stage = 10 } + } else { + options("Can I buy some leather?", "Leather is rather weak stuff.").also { stage = 20 } + } } - 10 -> npcl(FacialExpression.FRIENDLY, "Certainly!").also { stage = 30 } - 20 -> npcl(FacialExpression.NOD_YES, "Normal leather may be quite weak, but it's very cheap - I " + - "make it from cowhides for only 1 gp per hide - and it's so easy to craft that anyone can work with it.").also{ stage++ } - 21 -> npcl(FacialExpression.HALF_THINKING, "Alternatively you could try hard leather. It's not so easy " + - "to craft, but I only charge 3 gp per cowhide to prepare it, and it makes much sturdier armour.").also{ stage++ } - 22 -> npcl(FacialExpression.FRIENDLY, "I can also tan snake hides and dragonhides, suitable for crafting" + - "into the highest quality armour for rangers.").also{ stage++ } - 23 -> player(FacialExpression.NEUTRAL, "Thanks, I'll bear it in mind.").also { stage = END_DIALOGUE } - 30 -> { - end() - TanningProduct.open(player, npc.id) + 10 -> options("Yes please.", "No thanks.").also { stage++ } + 11 -> when (buttonId) { + 1 -> playerl(FacialExpression.HAPPY, "Yes please.").also { stage = 12 } + 2 -> playerl(FacialExpression.NEUTRAL, "No thanks.").also { stage = 13 } } + + 12 -> end().also { TanningProduct.open(player, NPCs.TANNER_804) } + 13 -> npcl(FacialExpression.FRIENDLY, "Very well, @g[sir,madam], as you wish.").also { stage = END_DIALOGUE } + + 20 -> when (buttonId) { + 1 -> playerl(FacialExpression.ASKING, "Can I buy some leather?").also { stage = 21 } + 2 -> playerl(FacialExpression.SUSPICIOUS, "Leather is rather weak stuff.").also { stage = 22 } + } + + 21 -> npcl(FacialExpression.FRIENDLY, "I make leather from animal hides. Bring me some cowhides and one gold coin per hide, and I'll tan them into soft leather for you.").also { stage = END_DIALOGUE } + + 22 -> npcl(FacialExpression.NOD_YES, "Normal leather may be quite weak, but it's very cheap - I make it from cowhides for only 1 gp per hide - and it's so easy to craft that anyone can work with it.").also { stage++ } + 23 -> npcl(FacialExpression.HALF_THINKING, "Alternatively you could try hard leather. It's not so easy to craft, but I only charge 3 gp per cowhide to prepare it, and it makes much sturdier armour.").also { stage++ } + 24 -> npcl(FacialExpression.FRIENDLY, "I can also tan snake hides and dragonhides, suitable for crafting into the highest quality armour for rangers.").also { stage++ } + 25 -> playerl(FacialExpression.NEUTRAL, "Thanks, I'll bear it in mind.").also { stage = END_DIALOGUE } } return true } - override fun getIds() : IntArray { + + override fun getIds(): IntArray { return intArrayOf(NPCs.TANNER_804) } diff --git a/Server/src/main/content/region/asgarnia/dialogue/ThuroDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/ThuroDialogue.java index 761c0977c..af9e253f3 100644 --- a/Server/src/main/content/region/asgarnia/dialogue/ThuroDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/ThuroDialogue.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for thurgo. @@ -77,7 +78,7 @@ public final class ThuroDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("The Knight's Sword"); + quest = player.getQuestRepository().getQuest(Quests.THE_KNIGHTS_SWORD); player.removeAttribute("thurgo:1"); switch (quest.getStage(player)) { default: diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.kt index a829c840b..0d8e029eb 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.kt @@ -16,12 +16,13 @@ import core.game.world.map.Location import core.plugin.Initializable import core.tools.END_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests @Initializable class DoricDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - val qStage = getQuestStage(player, "Doric's Quest") + val qStage = getQuestStage(player, Quests.DORICS_QUEST) if(qStage == 0) { npcl(FacialExpression.OLD_NORMAL, "Hello traveller, what brings you to my humble smithy?").also { stage = 0 } } else if(qStage in 1..99) { diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorSquireDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorSquireDialogue.java index 5d026893d..8fe25153e 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorSquireDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorSquireDialogue.java @@ -10,6 +10,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.Initializable; import core.game.world.GameWorld; +import content.data.Quests; /** * Represents the falador squire dialogue plugin. @@ -59,7 +60,7 @@ public final class FaladorSquireDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("The Knight's Sword"); + quest = player.getQuestRepository().getQuest(Quests.THE_KNIGHTS_SWORD); interpreter.sendOptions("What do you want to do?", "Chat", "Talk about the Falador Achievement Diary"); stage = -1; replacementReward = AchievementDiary.canReplaceReward(player, DiaryType.FALADOR, level); diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/SirTiffyCashienDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/SirTiffyCashienDialogue.java deleted file mode 100644 index 0332c725b..000000000 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/SirTiffyCashienDialogue.java +++ /dev/null @@ -1,89 +0,0 @@ -package content.region.asgarnia.falador.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue used for sir tiffy. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SirTiffyCashienDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code SirTiffyCashienDialogue} {@code Object}. - */ - public SirTiffyCashienDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code SirTiffyCashienDialogue} {@code Object}. - * @param player the player. - */ - public SirTiffyCashienDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SirTiffyCashienDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What ho, sirrag.", "Spiffing day for a walk in the park, what?"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Spiffing?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Absolutely, top-hole!", "Well, can't stay and chat all day, dontchaknow!", "Ta-ta for now!"); - stage = 10; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Erm...goodbye."); - stage = 4; - break; - case 4: - end(); - break; - case 10: - npc("Would you like to look at my wares?"); - stage++; - break; - case 11: - player("Yes, please."); - stage++; - break; - case 12: - npc.openShop(player); - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2290 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/SirTiffyCashienDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/SirTiffyCashienDialogue.kt new file mode 100644 index 000000000..70abbe778 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/SirTiffyCashienDialogue.kt @@ -0,0 +1,120 @@ +package content.region.asgarnia.falador.dialogue + +import content.data.Quests +import content.region.asgarnia.falador.quest.recruitmentdrive.SirTiffyCashienDialogueFile +import core.ServerConstants +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class SirTiffyCashienDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + + // Completed Recruitment Drive & Start Wanted!! Quest + if (isQuestComplete(player!!, Quests.RECRUITMENT_DRIVE)) { + openDialogue(player, SirTiffyCashienAfterRecruitmentDriveQuestDialogueFile(), npc) + return true + } + + // Recruitment Drive Quest + if (isQuestInProgress(player!!, Quests.RECRUITMENT_DRIVE, 1, 99)) { + openDialogue(player, SirTiffyCashienDialogueFile(), npc) + return true + } + + // Fallback to default. + when (stage) { + START_DIALOGUE -> player("Hello.").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "What ho, ${if (player.isMale) "sirrah" else "milady"}.", "Spiffing day for a walk in the park, what?").also { stage++ } + 2 -> player(FacialExpression.THINKING, "Spiffing?").also { stage++ } + 3 -> npc(FacialExpression.FRIENDLY, "Absolutely, top-hole!", "Well, can't stay and chat all day, dontchaknow!", "Ta-ta for now!").also { stage++ } + 4 -> player(FacialExpression.THINKING, "Erm...goodbye.").also { stage = END_DIALOGUE } + } + + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return SirTiffyCashienDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.SIR_TIFFY_CASHIEN_2290) + } +} + +// Move this to Wanted!! Quest. +class SirTiffyCashienAfterRecruitmentDriveQuestDialogueFile : DialogueBuilderFile() { + private fun dialogueChangeSpawnPoint(builder: DialogueBuilder, place: String, location: Location, tiffyLine1: String, tiffyLine2: String): DialogueBuilder { + return builder.npcl("${tiffyLine1} Are you sure?") + .options().let { optionBuilder -> + optionBuilder.option("Yes, I want to respawn in $place.") + .playerl("Yes, I want to respawn in $place.") + .npcl(tiffyLine2) + .endWith { _, player -> + setAttribute(player, "/save:spawnLocation", location) + player.properties.spawnLocation = location + } + optionBuilder.option("Actually, no thanks. I like my respawn point.") + .playerl("Actually, no thanks. I like my respawn point.") + .npcl("As you wish, what? Ta-ta for now.") + } + } + + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .npc(FacialExpression.HAPPY, "What ho, @g[sirrah,milady].", "Jolly good show on the old training grounds thingy,", "what?") + .options().let { optionBuilder -> + optionBuilder.option_playerl("Do you have any jobs for me yet?") + .npcl("Sorry dear @g[boy,gal] but we are still in the process of organising.") + .npcl("I'm sure that we will have something for you soon, so please feel free to check back later.") + // Started of Wanted! quest + .end() + optionBuilder.option("Can you explain the Gaze of Saradomin to me?") + .playerl("I don't really understand this 'Gaze of Saradomin' thing... Do you think you could explain what it does for me?") + .npcl("Certainly @g[sirrah,milady]! As you know, we Temple Knights are personally favoured by Saradomin himself.") + .npcl("And when I say personally favoured, I don't mean that sometime off in the future he's going to buy us all a drink!") + .npcl("He watches over each of us, and when we die he catches us as we fall, and ensures we arrive back at Falador castle safe and sound.") + .npcl("We usually lose some equipment when he does so, but it's a small price to pay to be hale and hearty again, what?") + .npcl("Some lucky fellows have a similar system going already, but when they die they spawn in that squalid little swamp village Lumbridge.") + .playerl("Yeah, what kind of person would want to spawn there... Certainly not me, and I never have! Honest!") + .npcl("Well, you should be glad that we offer you a step up then! Falador is clearly a far superior town to spend your time in!") + .npcl("Was there something else you wanted to ask good old Tiffy, @g[sirrah,milady]?") + .end() + optionBuilder.option("Can I buy some armour?") + .playerl("Can I buy some armour?") + // Recruitment Drive -> Initiate level, Slug Menace -> Proselyte level + .npcl("Of course dear @g[boy,gal]. I can sell you up to Initiate level items only I'm afraid.") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + optionBuilder.option("Can I switch respawns please?") + .branch { player -> if (player.properties.spawnLocation == ServerConstants.HOME_LOCATION) { 1 } else { 0 } } + .let { branch -> + dialogueChangeSpawnPoint( + branch.onValue(1), + "Falador", Location(2971, 3340, 0), //https://www.youtube.com/watch?v=Mm15dHuIaVg + "Ah, so you'd like to respawn in Falador, the good old homestead!", + "Top-hole, what? Good old Fally is definitely the hot-spot nowadays!" + ) + dialogueChangeSpawnPoint( + branch.onValue(0), + "Lumbridge", ServerConstants.HOME_LOCATION ?: Location(3222, 3218, 0), + "What? You're saying you want to respawn in Lumbridge?", + "Why anyone would want to visit that smelly little swamp village of oiks is quite beyond me, I'm afraid, but the deed is done now." + ) + } + optionBuilder.option("Goodbye.") + .playerl("Well, see you around Tiffy.") + .npcl(FacialExpression.HAPPY,"Ta-ta for now, old bean!") + .end() + } + } +} diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt index 4d42d5d9f..b26643bba 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt @@ -3,12 +3,12 @@ package content.region.asgarnia.falador.dialogue import content.data.tables.BirdNest import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items -import core.game.diary.DiaryLevel +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.link.diary.* /** * Represents the Wyson the gardener dialogue. @@ -16,46 +16,53 @@ import core.game.diary.DiaryLevel * @version 1.0 */ @Initializable -class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { - /** - * If its a bird nest reward. - */ - private var birdNest = false - - /** - * Constructs a new `WysonTheGardenerDialogue` `Object`. - */ - constructor() { - /** - * empty. - */ - } +class WysonTheGardenerDialogue(player: Player? = null) : DialoguePlugin(player) { /** * Constructs a new `WysonTheGardenerDialogue` `Object`. + * Mole part dialogue source: https://www.youtube.com/watch?v=Dw-P9T7EhZk and https://www.youtube.com/watch?v=krZiIRupKbs * @param player the player. */ - constructor(player: Player?) : super(player) {} + //constructor(player: Player?) : super(player) {} - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return WysonTheGardenerDialogue(player) } + /** + * Choose greeting. Either you have mole parts or just the normal greeting. + */ override fun open(vararg args: Any): Boolean { npc = args[0] as NPC - birdNest = player.inventory.containsItem(MOLE_CLAW) || player.inventory.containsItem(MOLE_SKIN) - if (birdNest) { - npc("If I'm not mistaken, you've got some mole bits there!", "I'll trade 'em for bird nest if ye likes.") + if (inInventory(player, Items.MOLE_CLAW_7416, 1) && inInventory(player, Items.MOLE_SKIN_7418, 1)) { + npc("If I'm not mistaken, you've got some claws and skin", " from a big mole there! I'll trade 'em for bird nests if ye", "likes. Or was ye wantin' some woad leaves instead?") + stage = 102 + return true + } + if (inInventory(player, Items.MOLE_SKIN_7418, 1)) { + npc("If I'm not mistaken, you've got some skin from a big", "mole there! I'll trade it for bird nests if ye likes. Or", "was ye wantin' some woad leaves instead?") stage = 100 return true } + if (inInventory(player, Items.MOLE_CLAW_7416, 1)) { + npc("If I'm not mistaken, you've got some claws from a big", "mole there! I'll trade it for bird nests if ye likes. Or", "was ye wantin' some woad leaves instead?") + stage = 101 + return true + } npc("I'm the head gardener around here.", "If you're looking for woad leaves, or if you need help", "with owt, I'm yer man.") stage = 0 return true } + /** + * Dialogue. + */ override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { + + /** + * Dialogue options: woad leaves. + */ 0 -> { options("Yes please, I need woad leaves.", "Sorry, but I'm not interested.") stage = 1 @@ -105,15 +112,15 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { npc("Mmmm... ok, that sounds fair.") stage = 131 } - 131 -> if (player.inventory.contains(995, 15)) { - player.inventory.remove(COINS[0]) - player.inventory.add(WOAD_LEAF) + 131 -> if (removeItem(player,Item(Items.COINS_995, 15) ,Container.INVENTORY)) { + addItemOrDrop(player, Items.WOAD_LEAF_1793, 1) + player("Thanks.") - player.packetDispatch.sendMessage("You buy a woad leaf from Wyson.") + sendMessage(player, "You buy a woad leaf from Wyson.") stage = 132 } else { end() - player.packetDispatch.sendMessage("You need 15 cold coins to buy a woad leaf.") + sendMessage(player, "You need 15 gold coins to buy a woad leaf.") } 132 -> { npc("I'll be around if you have any more gardening needs.") @@ -121,35 +128,70 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { } 133 -> end() 140 -> { - npc("Thanks for being generous", "here's an extra woad leave.") + npc("Thanks for being generous", "here's an extra woad leaf.") stage = 141 } - 141 -> if (player.inventory.contains(995, 20)) { - player.inventory.remove(COINS[1]) - var i = 0 - while (i < 2) { - player.inventory.add(WOAD_LEAF, player) - i++ - } + 141 -> if (removeItem(player,Item(Items.COINS_995, 20) ,Container.INVENTORY)) { + addItemOrDrop(player, Items.WOAD_LEAF_1793, 2) player("Thanks.") - player.packetDispatch.sendMessage("You buy two woad leaves from Wyson.") + sendMessage(player, "You buy two woad leaves from Wyson.") stage = 132 } else { end() - player.packetDispatch.sendMessage("You need 15 cold coins to buy a woad leaf.") + sendMessage(player, "You need 20 gold coins to buy a woad leaf.") } 200 -> { npc("Fair enough.") stage = 201 } 201 -> end() + + /** + * Dialogue options: mole parts. + */ 100 -> { - options("Yes, I will trade the mole claws.", "Okay, I will trade the mole skin.", "I'd like to trade both.", "No, thanks.") + options("Ok, I will trade the mole skin.", "Yes please, I need woad leaves.", "Sorry, but I'm not interested.") stage = 900 } + 101 -> { + options("Yeah, I will trade the mole claws.", "Yes please, I need woad leaves.", "Sorry, but I'm not interested.") + stage = 901 + } + 102 -> { + options("Yeah, I will trade the mole claws.", "Okay, I will trade the mole skin.", "Alright, I'll trade the claws and skin.", "Yes please, I need woad leaves.", "Sorry, but I'm not interested.") + stage = 902 + } 900 -> when (buttonId) { 1 -> { - player("Yes, I will trade the mole claws.") + player("Ok, I will trade the mole skin.") + stage = 920 + } + 2 -> { + player("Yes please, I need woad leaves.") + stage = 10 + } + 3 -> { + player("Sorry, but I'm not interested.") + stage = 200 + } + } + 901 -> when (buttonId) { + 1 -> { + player("Yeah, I will trade the mole claws.") + stage = 910 + } + 2 -> { + player("Yes please, I need woad leaves.") + stage = 10 + } + 3 -> { + player("Sorry, but I'm not interested.") + stage = 200 + } + } + 902 -> when (buttonId) { + 1 -> { + player("Yeah, I will trade the mole claws.") stage = 910 } 2 -> { @@ -157,37 +199,48 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { stage = 920 } 3 -> { - player("I'd like to trade both.") + player("Alright, I'll trade the claws and skin.") stage = 930 } 4 -> { - player("No, thanks.") - stage = 999 + player("Yes please, I need woad leaves.") + stage = 10 + } + 5 -> { + player("Sorry, but I'm not interested.") + stage = 200 } } 910 -> { - if (!player.inventory.containsItem(MOLE_CLAW)) { + if (!inInventory(player, Items.MOLE_CLAW_7416, 1)) { player("Sorry, I don't have any mole claws.") stage = 999 + } else { + addClawRewards() + npc("Pleasure doing business with ya.") + stage = 999 } - end() - addRewards() } 920 -> { - if (!player.inventory.containsItem(MOLE_SKIN)) { + if (!inInventory(player, Items.MOLE_SKIN_7418, 1)) { player("Sorry, I don't have any mole skins.") stage = 999 + } else { + addSkinRewards() + npc("Pleasure doing business with ya.") + stage = 999 } - end() - addRewards() } 930 -> { - if (!player.inventory.containsItem(MOLE_CLAW) && !player.inventory.containsItem(MOLE_SKIN)) { + if (!inInventory(player, Items.MOLE_CLAW_7416, 1) || !inInventory(player, Items.MOLE_SKIN_7418, 1)) { player("Sorry, I don't have any.") stage = 999 + } else { + addClawRewards() + addSkinRewards() + npc("Pleasure doing business with ya.") + stage = 999 } - addRewards() - end() } 999 -> end() } @@ -196,26 +249,31 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { /** * Adds nests. - * @param nestAmount the amount. */ - private fun addRewards() { - val moleClaws = player.inventory.getAmount(Items.MOLE_CLAW_7416) - val moleSkin = player.inventory.getAmount(Items.MOLE_SKIN_7418) - val nestAmount = moleClaws + moleSkin - - //Remove claws and skins - player.inventory.remove(Item(Items.MOLE_CLAW_7416,moleClaws)) - player.inventory.remove(Item(Items.MOLE_SKIN_7418, moleSkin)) - - //Add white lily seeds if the player has the hard diary done - if(moleSkin > 0 && player.achievementDiaryManager.getDiary(DiaryType.FALADOR).checkComplete(DiaryLevel.HARD)) { - player.inventory.add(Item(14589, moleSkin), player) + private fun addClawRewards() { + // count the number of claws + val nestAmount = amountInInventory(player, Items.MOLE_CLAW_7416) + // remove the counted number of skins + if(removeItem(player, Item(Items.MOLE_CLAW_7416, nestAmount), Container.INVENTORY)){ + // add the counted number of nests. one by one so they each have random contents + for (i in 0 until nestAmount) { + addItemOrDrop(player, BirdNest.getRandomNest(true).nest.id, 1) + } } + } - //Add nests - for (i in 0 until nestAmount) { - if(!player.inventory.add(Item(BirdNest.getRandomNest(true).nest.id, 1), player)){ - GroundItemManager.create(Item(BirdNest.getRandomNest(true).nest.id,1),player.location,player) + private fun addSkinRewards() { + // count the number of skins + val nestAmount = amountInInventory(player, Items.MOLE_SKIN_7418) + // remove the counted number of skins + if(removeItem(player, Item(Items.MOLE_SKIN_7418, nestAmount), Container.INVENTORY)) { + // add the counted number of nests. one by one so they each have random contents + // if Falador Hard diary is complete, add a white lilly seed + for (i in 0 until nestAmount) { + addItemOrDrop(player, BirdNest.getRandomNest(true).nest.id, 1) + if (player.achievementDiaryManager.getDiary(DiaryType.FALADOR).isComplete(2)) { + addItemOrDrop(player, Items.WHITE_LILY_SEED_14589, 1) + } } } } @@ -224,25 +282,4 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { return intArrayOf(36) } - companion object { - /** - * Represents the coins item that can be used. - */ - private val COINS = arrayOf(Item(995, 15), Item(995, 20)) - - /** - * Represents the woad leaf item. - */ - private val WOAD_LEAF = Item(1793, 1) - - /** - * The mole claw item. - */ - private val MOLE_CLAW = Item(7416) - - /** - * The mole skin. - */ - private val MOLE_SKIN = Item(7418) - } } \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt b/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt index 4578a23ee..4831afd9c 100644 --- a/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt +++ b/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt @@ -19,6 +19,7 @@ import core.game.diary.DiaryEventHookBase import core.game.diary.DiaryLevel import core.game.event.* import core.game.node.entity.skill.Skills +import core.game.world.map.Location class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { companion object { @@ -211,6 +212,9 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { ) } } + if (event.npc.id == NPCs.MOGRE_114){ + finishTask(player, DiaryLevel.HARD, HardTasks.MUDSKIPPER_POINT_KILL_MOGRE) + } } override fun onItemPurchasedFromShop(player: Player, event: ItemShopPurchaseEvent) { @@ -283,4 +287,14 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { } } } + + override fun onPrayerPointsRecharged(player: Player, event: PrayerPointsRechargeEvent) { + if (event.altar.id == Scenery.ALTAR_39842 && event.altar.location == Location(2995, 3177, 0)) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.PORT_SARIM_RECHARGE_PRAYER_POINTS + ) + } + } } \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/handlers/BankNotices.kt b/Server/src/main/content/region/asgarnia/falador/handlers/BankNotices.kt new file mode 100644 index 000000000..11a124e8b --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/handlers/BankNotices.kt @@ -0,0 +1,48 @@ +package content.region.asgarnia.falador.handlers + +import content.global.handlers.iface.ScrollInterface +import content.global.handlers.iface.ScrollLine +import core.game.interaction.InteractionListener +import org.rs09.consts.Components +import org.rs09.consts.Scenery + +class BankNotices : InteractionListener { + companion object { + val CONTENTS_PIN = arrayOf( + ScrollLine("If you're worried about someone stealing items from your",3), + ScrollLine("bank, why not protect yourself with a Bank PIN?",4), + + ScrollLine("A Bank PIN is a four-digit number. It's like a password that",6), + ScrollLine("protects your bank account. If you set one, you'll be asked to",7), + ScrollLine("enter it before you can take items out of the bank.",8), + + ScrollLine("If you're interested, speak to the bankers and ask about Bank",10), + ScrollLine("PINs.",11), + + ScrollLine("But remember, KEEP YOUR PIN SECRET!",13), + ) + + val CONTENTS_PASSWORD = arrayOf( + ScrollLine("The Bank of RuneScape would like to remind customers that",4), + ScrollLine("they should NEVER tell ANYONE their password.",5), + + ScrollLine("If someone asks you to say your password, please report",7), + ScrollLine("them using the 'Report Abuse' button at the bottom of your",8), + ScrollLine("screen. ",9), + + ScrollLine("Change your password regularly, and make sure no-one else",11), + ScrollLine("could easily guess it!",12), + ) + } + + override fun defineListeners() { + on(Scenery.NOTICEBOARD_11755, SCENERY, "read") { player, _ -> + ScrollInterface.scrollSetup(player, Components.MESSAGESCROLL_220, CONTENTS_PIN) + return@on true + } + on(Scenery.NOTICEBOARD_11756, SCENERY, "read") { player, _ -> + ScrollInterface.scrollSetup(player, Components.MESSAGESCROLL_220, CONTENTS_PASSWORD) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/TheKnightsSword.java b/Server/src/main/content/region/asgarnia/falador/quest/TheKnightsSword.java index 535885464..70ca79515 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/TheKnightsSword.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/TheKnightsSword.java @@ -5,6 +5,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents The KnightSword quest. @@ -23,7 +24,7 @@ public class TheKnightsSword extends Quest { * Constructs a new {@code TheKnightsSword} {@code Object}. */ public TheKnightsSword() { - super("The Knight's Sword", 22, 21, 1, 122, 0, 1, 7); + super(Quests.THE_KNIGHTS_SWORD, 22, 21, 1, 122, 0, 1, 7); } @Override diff --git a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKCabbagePlugin.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKCabbagePlugin.java index 36684ff28..1fdcd3565 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKCabbagePlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKCabbagePlugin.java @@ -5,6 +5,7 @@ import core.game.interaction.UseWithHandler; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the plugin used to send the cabbage down the hole. @@ -29,7 +30,7 @@ public class BKCabbagePlugin extends UseWithHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - final Quest quest = player.getQuestRepository().getQuest("Black Knights' Fortress"); + final Quest quest = player.getQuestRepository().getQuest(Quests.BLACK_KNIGHTS_FORTRESS); if (quest.getStage(player) == 20) { if (event.getUsedItem().getId() == 1967) { player.getDialogueInterpreter().sendDialogue("This is the wrong sort of cabbage!"); diff --git a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKListenDialogue.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKListenDialogue.java index df55f800b..b8d4dd509 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKListenDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKListenDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Initializable; import core.game.world.update.flag.context.Animation; +import content.data.Quests; /** * Represents the dialogue of listening throug a grill during the black knights' @@ -117,7 +118,7 @@ public final class BKListenDialogue extends DialoguePlugin { stage = 8; break; case 8: - player.getQuestRepository().getQuest("Black Knights' Fortress").setStage(player, 20); + player.getQuestRepository().getQuest(Quests.BLACK_KNIGHTS_FORTRESS).setStage(player, 20); end(); break; case 10: @@ -142,7 +143,7 @@ public final class BKListenDialogue extends DialoguePlugin { break; case 15: if (player.getInventory().remove(CABBAGE)) { - player.getQuestRepository().getQuest("Black Knights' Fortress").setStage(player, 30); + player.getQuestRepository().getQuest(Quests.BLACK_KNIGHTS_FORTRESS).setStage(player, 30); interpreter.sendDialogues(player, FacialExpression.HAPPY, "Looks like my work here is done. Seems like that's", "successfully sabotaged their little secret weapon plan."); stage = 16; } diff --git a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightsFortress.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightsFortress.java index 6e5da0e7d..a3a1bf405 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightsFortress.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightsFortress.java @@ -5,6 +5,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the black knights fortress quest. @@ -23,7 +24,7 @@ public final class BlackKnightsFortress extends Quest { * Constructs a new {@Code BlackKnightsFortress} {@Code Object} */ public BlackKnightsFortress() { - super("Black Knights' Fortress", 14, 13, 3, 130, 0, 1, 4); + super(Quests.BLACK_KNIGHTS_FORTRESS, 14, 13, 3, 130, 0, 1, 4); } @Override diff --git a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/SirAmikVarzeDialogue.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/SirAmikVarzeDialogue.java index 33f2a41c5..93901c19e 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/SirAmikVarzeDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/SirAmikVarzeDialogue.java @@ -1,5 +1,6 @@ package content.region.asgarnia.falador.quest.blackknightsfortress; +import content.region.asgarnia.falador.quest.recruitmentdrive.SirAmikVarzeDialogueFile; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; @@ -8,6 +9,9 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; +import static core.api.ContentAPIKt.openDialogue; +import content.data.Quests; + /** * Represents the sir amik varze dialogue. * @author Vexia @@ -45,7 +49,7 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Black Knights' Fortress"); + quest = player.getQuestRepository().getQuest(Quests.BLACK_KNIGHTS_FORTRESS); switch (quest.getStage(player)) { case 30: interpreter.sendDialogues(player, FacialExpression.HAPPY, "I have ruined the Black Knights' invincibility potion."); @@ -72,35 +76,7 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (quest.getStage(player)) { case 100: - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Hello, friend!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Do you have any other quests for me to do?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_THINKING, "Quests, eh?", "Well, I don't have anything on the go at the moment,", "but there is an organisation that is always looking for", "capable adventurers to assist them."); - stage = 3; - break; - case 3: - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Your excellent work sorting out those Black Knights", "means I will happily write you a letter of", "recommendation."); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Would you like me to put your name forwards to", "them?"); - stage = 5; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No thanks."); - stage = 6; - break; - case 6: - end(); - break; - } + openDialogue(player, new SirAmikVarzeDialogueFile(), npc); break; case 30: switch (stage) { diff --git a/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricDoricsQuestDialogue.kt b/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricDoricsQuestDialogue.kt index 9857961b6..3c34fca1f 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricDoricsQuestDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricDoricsQuestDialogue.kt @@ -4,12 +4,11 @@ import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression import core.game.dialogue.Topic -import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.item.Item import core.tools.END_DIALOGUE import org.rs09.consts.Items -import org.rs09.consts.NPCs +import content.data.Quests class DoricDoricsQuestDialogue(private val dStage: Int) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { @@ -40,7 +39,7 @@ class DoricDoricsQuestDialogue(private val dStage: Int) : DialogueFile() { 40 -> npc(FacialExpression.OLD_NORMAL, "Clay is what I use more than anything, to make casts.", "Could you get me 6 clay, 4 copper ore, and 2 iron ore,", "please? I could pay a little, and let you use my anvils.", "Take this pickaxe with you just in case you need it.").also { stage++ } 41 -> { playerl(FacialExpression.FRIENDLY, "Certainly, I'll be right back!") - startQuest(player, "Doric's Quest") + startQuest(player, Quests.DORICS_QUEST) if(!inInventory(player, Items.BRONZE_PICKAXE_1265)) addItemOrDrop(player, Items.BRONZE_PICKAXE_1265) stage = END_DIALOGUE } @@ -65,7 +64,7 @@ class DoricDoricsQuestDialogue(private val dStage: Int) : DialogueFile() { 3 -> { if(removeItem(player, Item(Items.CLAY_434, 6)) && removeItem(player, Item(Items.COPPER_ORE_436, 4)) && removeItem(player, Item(Items.IRON_ORE_440, 2))) { sendItemDialogue(player, Items.COPPER_ORE_436, "You hand the clay, copper, and iron to Doric.") - finishQuest(player, "Doric's Quest") + finishQuest(player, Quests.DORICS_QUEST) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricsQuest.kt b/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricsQuest.kt index 5c04c3983..0bc49f173 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricsQuest.kt +++ b/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricsQuest.kt @@ -7,26 +7,32 @@ import core.game.node.entity.skill.Skills import core.plugin.Initializable import org.rs09.consts.Components import org.rs09.consts.Items +import content.data.Quests @Initializable -class DoricsQuest : Quest("Doric's Quest", 17, 16, 1, 31, 0, 1, 100) { +class DoricsQuest : Quest(Quests.DORICS_QUEST, 17, 16, 1, 31, 0, 1, 100) { override fun newInstance(`object`: Any?): Quest { return this } override fun drawJournal(player: Player?, stage: Int) { super.drawJournal(player, stage) player ?: return - var line = 11 + var line = 12 if(stage == 0) { line(player, "I can start this quest by speaking to !!Doric?? who is !!North of??", line++) line(player, "!!Falador??.", line++) + line++ line(player, "There aren't any requirements but !!Level 15 Mining?? will help.", line++) } else { if(stage in 1..99) { - line(player, "I have spoken to !!Doric??.", line++) - line(player, "I need to collect some items and bring them to !!Doric??:", line++) - line(player, "6 Clay", line++, inInventory(player, Items.CLAY_434, 6)) - line(player, "4 Copper Ore", line++, inInventory(player, Items.COPPER_ORE_436, 4)) - line(player, "2 Iron Ore", line++, inInventory(player, Items.IRON_ORE_440, 2)) + // https://www.youtube.com/watch?v=vm4BEXtMoO0 + line(player, "I have spoken to Doric. He agreed to let me use his anvils", line++, true) + line(player, "if I bring him some materials.", line++, true) + line++ + line(player, "I need to collect the following materials and bring them all", line++) + line(player, "to !!Doric??:", line++) + line(player, "!!6 Clay.??", line++, inInventory(player, Items.CLAY_434, 6)) + line(player, "!!4 Copper Ore.??", line++, inInventory(player, Items.COPPER_ORE_436, 4)) + line(player, "!!2 Iron Ore.??", line++, inInventory(player, Items.IRON_ORE_440, 2)) } if(stage == 100) { diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/AlchemicalNotes.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/AlchemicalNotes.kt new file mode 100644 index 000000000..56eaa77d1 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/AlchemicalNotes.kt @@ -0,0 +1,248 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import content.global.handlers.iface.BookInterface +import content.global.handlers.iface.BookLine +import content.global.handlers.iface.Page +import content.global.handlers.iface.PageSet +import core.api.setAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items + +// https://www.youtube.com/watch?v=o-bAoxIYT-A 7:27 +@Initializable +class AlchemicalNotes : InteractionListener { + companion object { + private val TITLE = "Alchemical Reactions Study" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Acetic acid and Cupric", 55), + BookLine("Sulphate:", 56), + BookLine("Endothermic.", 57), + BookLine("The Cupric is in ", 58), + BookLine("insufficient quantities to", 59), + BookLine("cause any noticeable", 60), + BookLine("reaction.", 61), + ), + Page( + BookLine("Acetic acid and Gypsum:", 66), + BookLine("Endothermic.", 67), + BookLine("Made a particularly bad", 68), + BookLine("smell, but little else that", 69), + BookLine("was productive.", 70), + ) + ), + PageSet( + Page( + BookLine("Acetic acid and Sodium", 55), + BookLine("Chloride:", 56), + BookLine("Endothermic.", 57), + BookLine("Very tasty when", 58), + BookLine("combined with fried", 59), + BookLine("potatoes at room", 60), + BookLine("temperature.", 61), + ), + Page( + BookLine("Acetic acid and", 66), + BookLine("Dihydrogen Monoxide:", 67), + BookLine("Endothermic.", 68), + BookLine("The Dihydrogen", 69), + BookLine("Monoxide served only to", 70), + BookLine("dilute the Acetic acid at", 71), + BookLine("room temperature.", 72), + ) + ), + PageSet( + Page( + BookLine("Acetic acid and Cupric", 55), + BookLine("Ore Powder:", 56), + BookLine("Endothermic.", 57), + BookLine("The powdered form of", 58), + BookLine("Cupric Ore allowed a", 59), + BookLine("lower than usual melting", 60), + BookLine("temperature, but the end", 61), + BookLine("product was non-usable.", 62), + ), + Page( + BookLine("Acetic acid and Tin Ore", 66), + BookLine("powder:", 67), + BookLine("Endothermic.", 68), + BookLine("Similar results to those", 69), + BookLine("made using Cupric Ore.", 70), + ) + ), + PageSet( + Page( + BookLine("Cupric Sulphate and", 55), + BookLine("Dihyrdogen Monoxide:", 56), + BookLine("Exothermic.", 57), + BookLine("A blue compound was", 58), + BookLine("produced, along with heat.", 59), + ), + Page( + BookLine("Cupric Sulphate and", 66), + BookLine("Gypsum:", 67), + BookLine("Endothermic.", 68), + BookLine("At room temperature, no", 69), + BookLine("useful product was", 70), + BookLine("created.", 71), + ) + ), + PageSet( + Page( + BookLine("Cupric Sulphate and", 55), + BookLine("Sodium Chloride:", 56), + BookLine("Endothermic.", 57), + BookLine("A pungent odour was", 58), + BookLine("released when combined.", 59), + ), + Page( + BookLine("Cupric Sulphate and", 66), + BookLine("Cupric Ore powder:", 67), + BookLine("Endothermic.", 68), + BookLine("The Cupric did not react", 69), + BookLine("with each other at room", 70), + BookLine("temperature.", 71), + ) + ), + PageSet( + Page( + BookLine("Cupric Sulphate and Tin", 55), + BookLine("Ore powder:", 56), + BookLine("Endothermic.", 57), + BookLine("Similar results to those", 58), + BookLine("shown with Cupric Ore,", 59), + BookLine("despite the increased", 60), + BookLine("solubility involved with", 61), + BookLine("the powdered form.", 62), + ), + Page( + BookLine("Gypsum and Dihydrogen", 66), + BookLine("Monoxide:", 67), + BookLine("Exothermic.", 68), + BookLine("A white liquid compound", 69), + BookLine("was formed, that quickly", 70), + BookLine("cooled at room", 71), + BookLine("temperature to a white", 72), + BookLine("heat resistant solid very", 73), + BookLine("similar to plaster.", 74), + BookLine("Heat was also produced,", 75), + BookLine("although not in the same", 76), + ) + ), + PageSet( + Page( + BookLine("quantity as Cupric", 55), + BookLine("Sulphate with Dihydrogen", 56), + BookLine("Monoxide", 57), + ), + Page( + BookLine("Gypsum and Sodium", 66), + BookLine("Chloride:", 67), + BookLine("Endothermic.", 68), + BookLine("The two did not seem to", 69), + BookLine("noticably mix together at", 70), + BookLine("room temperature.", 71), + ) + ), + PageSet( + Page( + BookLine("Gypsum and Culpric Ore:", 55), + BookLine("Endothermic.", 56), + BookLine("The gypsum seems quite", 57), + BookLine("resistant to most", 58), + BookLine("compounds at normal", 59), + BookLine("room temperature.", 60), + ), + Page( + BookLine("Gypsum and Tin Ore:", 66), + BookLine("Endothermic.", 67), + BookLine("Again, very similar results", 68), + BookLine("as those shown with", 69), + BookLine("Cupric Ore.", 70), + ) + ), + + PageSet( + Page( + BookLine("Sodium Chloride and", 55), + BookLine("Dihydrogen Monoxide:", 56), + BookLine("Endothermic.", 57), + BookLine("At room temperature, the", 58), + BookLine("Sodium Chloride dissolves", 59), + BookLine("quite easily. Dissolution is", 60), + BookLine("faster at higher", 61), + BookLine("temperatures.", 62), + ), + Page( + BookLine("Sodium Chloride and", 66), + BookLine("Cupric Ore:", 67), + BookLine("Endothermic.", 68), + BookLine("No visible combination at", 69), + BookLine("room temperature.", 70), + ) + ), + PageSet( + Page( + BookLine("Sodium Chloride and Tin", 55), + BookLine("Ore:", 56), + BookLine("Endothermic.", 57), + BookLine("Another very similar ", 58), + BookLine("result as with Cupric Ore.", 59), + ), + Page( + BookLine("Cupric Ore Powder and", 66), + BookLine("Tin Ore Powder:", 67), + BookLine("Endothermic.", 68), + BookLine("When both ores are in", 69), + BookLine("particulate form, a much", 70), + BookLine("lower than usual bonding", 71), + BookLine("temperature can be", 72), + BookLine("obtained.", 73), + BookLine("When combined at a", 74), + BookLine("moderate heat, (my", 75), + BookLine("laboratory heating", 76), + ) + ), + PageSet( + Page( + BookLine("apparatus) I was able to", 55), + BookLine("form liquid Bronze quite", 56), + BookLine("easily, which cooled to", 57), + BookLine("form a standard Bronze", 58), + BookLine("Bar at a temperature far", 60), + BookLine("lower than that required", 61), + BookLine("to produce in mass at a", 62), + BookLine("furnace.", 63), + ), + Page( + BookLine("Nitrous Monoxide:", 66), + BookLine("Was not able to perform", 67), + BookLine("an experimentation using", 68), + BookLine("this substance, as the", 69), + BookLine("gaseous form would", 70), + BookLine("always escape when the", 71), + BookLine("vial was opened.", 72), + ) + ) + ) + } + + private fun display(player:Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) + return true + } + + + override fun defineListeners() { + on(Items.ALCHEMICAL_NOTES_5588, IntType.ITEM, "read") { player, _ -> + setAttribute(player, "bookInterfaceCallback", ::display) + setAttribute(player, "bookInterfaceCurrentPage", 0) + display(player, 0, 0) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/LadyTableDialogue.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/LadyTableDialogue.kt new file mode 100644 index 000000000..b1386266f --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/LadyTableDialogue.kt @@ -0,0 +1,114 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import org.rs09.consts.Components +import org.rs09.consts.NPCs + +class LadyTableDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, LadyTableDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return LadyTableDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.LADY_TABLE_2283) + } +} + +class LadyTableDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile(), InteractionListener { + companion object { + const val statueVarbit = 658 + const val attributeStatueStateNumber = "quest:recruitmentdrive-statuestatenumber" + val statueArray = intArrayOf(0, 7308, 7307, 7306, 7305, 7304, 7303, 7312, 7313, 7314, 7311, 7310, 7309) + } + + override fun defineListeners() { + + on(statueArray, IntType.SCENERY, "touch") { player, node -> + if( node.id == statueArray[getAttribute(player, attributeStatueStateNumber, 0)]) { + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 0) { + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, 1) + sendNPCDialogueLines(player, NPCs.LADY_TABLE_2283, FacialExpression.NEUTRAL, false, "Excellent work, @name.", "Please step through the portal to meet your next", "challenge.") + return@on true + } + } else { + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 0) { + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, -1) + openDialogue(player, LadyTableDialogueFile(2), NPC(NPCs.LADY_TABLE_2283)) + return@on true + } + } + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 1) { + sendNPCDialogueLines(player, NPCs.LADY_TABLE_2283, FacialExpression.NEUTRAL, false, "Please step through the portal to meet your next", "challenge.") + } + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == -1) { + openDialogue(player, LadyTableDialogueFile(2), NPC(NPCs.LADY_TABLE_2283)) + } + return@on true + } + + } + override fun create(b: DialogueBuilder) { + b.onPredicate { player -> dialogueNum == 1 } + .endWith { _, player -> + submitWorldPulse(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 0 -> { + lock(player, 15) + setAttribute(player, attributeStatueStateNumber, (1..12).random()) + setVarbit(player, statueVarbit, getAttribute(player, attributeStatueStateNumber, 0)) + sendNPCDialogueLines(player, NPCs.LADY_TABLE_2283, FacialExpression.NEUTRAL, true,"Welcome, @name.", "This room will test your observation skills.") + } + 5 -> { + sendNPCDialogueLines(player, NPCs.LADY_TABLE_2283, FacialExpression.NEUTRAL, true, "Study the statues closely.", "There is one missing statue in this room.") + } + 10 -> { + sendNPCDialogueLines(player, NPCs.LADY_TABLE_2283, FacialExpression.NEUTRAL, true, "We will also mix the order up a little, to make things", "interesting for you!") + } + 15 -> { + sendNPCDialogueLines(player, NPCs.LADY_TABLE_2283, FacialExpression.NEUTRAL, true,"You have 10 seconds to memorise the statues... starting", "NOW!") + } + 20 -> { + closeDialogue(player) + } + 31 -> { // From 15 -> 16 * 600ms = 10 seconds + openOverlay(player,Components.FADE_TO_BLACK_120) + sendNPCDialogueLines(player, NPCs.LADY_TABLE_2283, FacialExpression.NEUTRAL, true,"We will now dim the lights and bring the missing statue", "back in.") + } + 34 -> { // From 15 -> 16 * 600ms = 10 seconds + setVarbit(player, statueVarbit, 0) + openOverlay(player,Components.FADE_FROM_BLACK_170) + sendNPCDialogueLines(player, NPCs.LADY_TABLE_2283, FacialExpression.NEUTRAL, true,"Please touch the statue you think has been added.") + return true + } + } + return false + } + }) + } + + b.onPredicate { player -> dialogueNum == 2 || (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == -1) } + .betweenStage { _, player, _, _ -> + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, -1) + } + .npc(FacialExpression.SAD, "No... I am very sorry.", "Apparently you are not up to the challenge.", "I will return you where you came from, better luck in the", "future.") + .endWith { _, player -> + removeAttribute(player, attributeStatueStateNumber) + removeAttribute(player, RecruitmentDrive.attributeStagePassFailState) + RecruitmentDriveListeners.FailTestCutscene(player).start() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/MissCheeversDialogue.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/MissCheeversDialogue.kt new file mode 100644 index 000000000..21fed50ba --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/MissCheeversDialogue.kt @@ -0,0 +1,512 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +@Initializable +class MissCheeversDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MissCheeversDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return MissCheeversDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.MISS_CHEEVERS_2288) + } +} + +class MissCheeversDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> dialogueNum == 0 } + .playerl(FacialExpression.FRIENDLY,"Can you give me any help?") + .npcl(FacialExpression.FRIENDLY,"No, I am sorry, but that is forbidden by our rules.") + .npcl(FacialExpression.FRIENDLY,"If you are having a particularly tough time of it, I suggest you leave and come back later when you are in a more receptive frame of mind.") + .npcl(FacialExpression.FRIENDLY,"Sometimes a break from concentration will yield fresh insight. Our aim is to test you, but not to the point of frustration!") + .playerl(FacialExpression.FRIENDLY,"Okay, thanks!") + .end() + + + b.onPredicate { _ -> dialogueNum == 1 } + .betweenStage { _, player, _, _ -> + setVarbit(player, MissCheeversRoomListeners.doorVarbit, 0) + removeAttribute(player, MissCheeversRoomListeners.attributebook) + removeAttribute(player, MissCheeversRoomListeners.attributemagnet) + removeAttribute(player, MissCheeversRoomListeners.attributeKnife) + removeAttribute(player, MissCheeversRoomListeners.attributeShears) + removeAttribute(player, MissCheeversRoomListeners.attributeTin) + removeAttribute(player, MissCheeversRoomListeners.attributeChisel) + removeAttribute(player, MissCheeversRoomListeners.attributeWire) + + removeAttribute(player, MissCheeversRoomListeners.attribute3VialsOfLiquid) + + MissCheeversRoomListeners.Companion.Vials.vialMap.map { + removeAttribute(player, it.value.attribute) + } + + MissCheeversRoomListeners.Companion.DoorVials.doorVialsRequiredMap.map { + removeAttribute(player, it.value.attribute) + } + } + .npcl(FacialExpression.FRIENDLY,"Greetings, @name. Welcome to my challenge.") + .npcl(FacialExpression.FRIENDLY,"All you need to do is leave from the opposite door to where you came in by.") + .npcl(FacialExpression.FRIENDLY,"I will warn you that this is more complicated than it may at first appear.") + .npcl(FacialExpression.FRIENDLY,"I should also warn you that there are limited supplies of the items in this room, so think carefully before using them, you may find yourself stuck and have to leave to start again!") + .npcl(FacialExpression.FRIENDLY,"Best of luck!") + .end() + } +} + +class MissCheeversRoomListeners : InteractionListener { + companion object { + + const val doorVarbit = 686 + + const val attributebook = "quest:recruitmentdrive-book" + const val attributemagnet = "quest:recruitmentdrive-magnet" + const val attributeKnife = "quest:recruitmentdrive-knife" + const val attributeShears = "quest:recruitmentdrive-shears" + const val attributeTin = "quest:recruitmentdrive-tin" + const val attributeChisel = "quest:recruitmentdrive-chisel" + const val attributeWire = "quest:recruitmentdrive-wire" + + const val attribute3VialsOfLiquid = "quest:recruitmentdrive-3vialsofliquid" + + /** Enums to map canoes to related properties. */ + enum class Vials(val itemId: Int, val attribute: String) { + CUPRIC_SULPHATE_5577(Items.CUPRIC_SULPHATE_5577, "quest:recruitmentdrive-cupricsulphate"), + ACETIC_ACID_5578(Items.ACETIC_ACID_5578, "quest:recruitmentdrive-aceticacid"), + GYPSUM_5579(Items.GYPSUM_5579, "quest:recruitmentdrive-gypsum"), + SODIUM_CHLORIDE_5580(Items.SODIUM_CHLORIDE_5580, "quest:recruitmentdrive-sodiumchloride"), + NITROUS_OXIDE_5581(Items.NITROUS_OXIDE_5581, "quest:recruitmentdrive-nitrousoxide"), + VIAL_OF_LIQUID_5582(Items.VIAL_OF_LIQUID_5582, "quest:recruitmentdrive-vialofliquid"), + TIN_ORE_POWDER_5583(Items.TIN_ORE_POWDER_5583, "quest:recruitmentdrive-tinorepowder"), + CUPRIC_ORE_POWDER_5584(Items.CUPRIC_ORE_POWDER_5584, "quest:recruitmentdrive-cupricorepowder"); + + companion object { + @JvmField + val vialMap = Vials.values().associateBy { it.itemId } + } + } + + + /** Enums to map canoes to related properties. */ + enum class DoorVials(val itemId: Int, val attribute: String) { + CUPRIC_SULPHATE_5577(Items.CUPRIC_SULPHATE_5577, "quest:recruitmentdrive-doorcupricsulphate"), + ACETIC_ACID_5578(Items.ACETIC_ACID_5578, ""), + SODIUM_CHLORIDE_5580(Items.SODIUM_CHLORIDE_5580, ""), + VIAL_OF_LIQUID_5582(Items.VIAL_OF_LIQUID_5582, "quest:recruitmentdrive-doorvialofliquid"); + + companion object { + @JvmField + val doorVialsArray = DoorVials.values().map { it.itemId }.toIntArray() + val doorVialsMap = DoorVials.values().associateBy { it.itemId } + val doorVialsRequiredMap = DoorVials.values().associateBy { it.itemId }.filter { it.value.attribute != "" } + } + } + + + fun searchingHelper(player: Player, attributeCheck: String, item: Int, searchingDescription: String, objectDescription: String) { + queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, searchingDescription) + return@queueScript delayScript(player, 2) + } + 1 -> { + if (attributeCheck != "" && !getAttribute(player, attributeCheck, false)) { + setAttribute(player, attributeCheck, true) + addItem(player, item) + sendMessage(player, objectDescription) + } else { + sendMessage(player, "You don't find anything interesting.") + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + } + + override fun defineListeners() { + + /** Obtainable Items */ + + on(Scenery.OLD_BOOKSHELF_7327, IntType.SCENERY, "search") { player, _ -> + searchingHelper(player, attributemagnet, Items.MAGNET_5604, "You search the bookshelves...", "Hidden amongst the books you find a magnet.") + return@on true + } + + on(Scenery.OLD_BOOKSHELF_7328, IntType.SCENERY, "search") { player, _ -> + searchingHelper(player, attributebook, Items.ALCHEMICAL_NOTES_5588, "You search the bookshelves...", "You find a book that looks like it might be helpful.") + return@on true + } + + on(Scenery.OLD_BOOKSHELF_7329, IntType.SCENERY, "search") { player, _ -> + searchingHelper(player, attributeKnife, Items.KNIFE_5605, "You search the bookshelves...", "Hidden amongst the books you find a knife.") + return@on true + } + + on(Scenery.OLD_BOOKSHELF_7330, IntType.SCENERY, "search") { player, _ -> + searchingHelper(player, "", 0, "You search the bookshelves...", "") + return@on true + } + + on(Scenery.SHELVES_7333, IntType.SCENERY, "search") { player, _ -> + val vialList = ArrayList() + if (!getAttribute(player, Vials.vialMap[Items.ACETIC_ACID_5578]!!.attribute, false)) { vialList.add(Items.ACETIC_ACID_5578) } + if (!getAttribute(player, Vials.vialMap[Items.VIAL_OF_LIQUID_5582]!!.attribute, false)) { vialList.add(Items.VIAL_OF_LIQUID_5582) } + openDialogue(player, VialShelfDialogueFile(vialList.toIntArray())) + return@on true + } + + on(Scenery.SHELVES_7334, IntType.SCENERY, "search") { player, _ -> + val vialList = ArrayList() + if (!getAttribute(player, Vials.vialMap[Items.CUPRIC_SULPHATE_5577]!!.attribute, false)) { vialList.add(Items.CUPRIC_SULPHATE_5577) } + openDialogue(player, VialShelfDialogueFile(vialList.toIntArray())) + return@on true + } + + on(Scenery.SHELVES_7335, IntType.SCENERY, "search") { player, _ -> + val vialList = ArrayList() + if (!getAttribute(player, Vials.vialMap[Items.GYPSUM_5579]!!.attribute, false)) { vialList.add(Items.GYPSUM_5579) } + openDialogue(player, VialShelfDialogueFile(vialList.toIntArray())) + return@on true + } + + on(Scenery.SHELVES_7336, IntType.SCENERY, "search") { player, _ -> + val vialList = ArrayList() + if (!getAttribute(player, Vials.vialMap[Items.SODIUM_CHLORIDE_5580]!!.attribute, false)) { vialList.add(Items.SODIUM_CHLORIDE_5580) } + openDialogue(player, VialShelfDialogueFile(vialList.toIntArray())) + return@on true + } + + on(Scenery.SHELVES_7337, IntType.SCENERY, "search") { player, _ -> + val vialList = ArrayList() + if (!getAttribute(player, Vials.vialMap[Items.NITROUS_OXIDE_5581]!!.attribute, false)) { vialList.add(Items.NITROUS_OXIDE_5581) } + openDialogue(player, VialShelfDialogueFile(vialList.toIntArray())) + return@on true + } + + on(Scenery.SHELVES_7338, IntType.SCENERY, "search") { player, _ -> + val vialList = ArrayList() + if (!getAttribute(player, Vials.vialMap[Items.TIN_ORE_POWDER_5583]!!.attribute, false)) { vialList.add(Items.TIN_ORE_POWDER_5583) } + openDialogue(player, VialShelfDialogueFile(vialList.toIntArray())) + return@on true + } + + on(Scenery.SHELVES_7339, IntType.SCENERY, "search") { player, _ -> + val vialList = ArrayList() + if (!getAttribute(player, Vials.vialMap[Items.CUPRIC_ORE_POWDER_5584]!!.attribute, false)) { vialList.add(Items.CUPRIC_ORE_POWDER_5584) } + openDialogue(player, VialShelfDialogueFile(vialList.toIntArray())) + return@on true + } + + on(Scenery.SHELVES_7340, IntType.SCENERY, "search") { player, _ -> + val vialList = ArrayList() + val total = getAttribute(player, attribute3VialsOfLiquid, 3) + for (i in 1..total) { vialList.add(Items.VIAL_OF_LIQUID_5582) } + openDialogue(player, VialShelfDialogueFile(vialList.toIntArray(), attribute3VialsOfLiquid)) + return@on true + } + + on(Scenery.CRATE_7347, IntType.SCENERY, "search") { player, node -> + if (node.location == Location(2476, 4943)) { + searchingHelper(player, attributeTin, Items.TIN_5600, "You search the crate...", "Inside the crate you find a tin.") + } else { + searchingHelper(player, "", 0, "You search the crate...", "") + } + return@on true + } + + on(Scenery.CRATE_7348, IntType.SCENERY, "search") { player, node -> + if (node.location == Location(2476, 4937)) { + searchingHelper(player, attributeChisel, Items.CHISEL_5601, "You search the crate...", "Inside the crate you find a chisel.") + } else { + searchingHelper(player, "", 0, "You search the crate...", "") + } + return@on true + } + + on(Scenery.CRATE_7349, IntType.SCENERY, "search") { player, node -> + if (node.location == Location(2475, 4943)) { + searchingHelper(player, attributeWire, Items.BRONZE_WIRE_5602, "You search the crate...", "Inside the crate you find some wire.") + } else { + searchingHelper(player, "", 0, "You search the crate...", "") + } + return@on true + } + + on(Scenery.CLOSED_CHEST_7350, IntType.SCENERY, "open") { player, node -> + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.OPEN_CHEST_7351, 100) + return@on true + } + + on(Scenery.OPEN_CHEST_7351, IntType.SCENERY, "search") { player, _ -> + searchingHelper(player, attributeShears, Items.SHEARS_5603, "You search the chest...", "Inside the chest you find some shears.") + return@on true + } + + on(Scenery.OPEN_CHEST_7351, IntType.SCENERY, "close") { player, node -> + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.CLOSED_CHEST_7350, -1) + return@on true + } + + /** Combining Items the correct way */ + + onUseWith(ITEM, Items.TIN_5600, Items.GYPSUM_5579) { player, used, with -> + if(removeItem(player, used.id) && removeItem(player, with.id)) { + sendMessage(player, "You empty the vial into the tin.") + addItemOrDrop(player, Items.TIN_5592) + addItemOrDrop(player, Items.VIAL_229) + } + return@onUseWith true + } + + onUseWith(ITEM, Items.TIN_5592, Items.VIAL_OF_LIQUID_5582) { player, used, with -> + if(removeItem(player, used.id) && removeItem(player, with.id)) { + sendMessage(player, "You empty the vial into the tin.") + sendMessage(player, "You notice the tin gets quite warm as you do this.") + sendMessage(player, "A lumpy white mixture is made, that seems to be hardening.") + addItemOrDrop(player, Items.TIN_5593) + addItemOrDrop(player, Items.VIAL_229) + } + return@onUseWith true + } + + onUseWith(SCENERY, Items.TIN_5593, Scenery.KEY_7346) { player, used, _ -> + if(removeItem(player, used.id)) { + sendMessage(player, "You make an impression of the key as the white mixture hardens.") + addItemOrDrop(player, Items.TIN_5594) + } + return@onUseWith true + } + + onUseWith(ITEM, Items.TIN_5594, Items.TIN_ORE_POWDER_5583) { player, used, with -> + if(removeItem(player, used.id) && removeItem(player, with.id)) { + sendMessage(player, "You pour the vial into the impression of the key.") + addItemOrDrop(player, Items.TIN_5595) + addItemOrDrop(player, Items.VIAL_229) + } + return@onUseWith true + } + + onUseWith(ITEM, Items.TIN_5595, Items.CUPRIC_ORE_POWDER_5584) { player, used, with -> + if(removeItem(player, used.id) && removeItem(player, with.id)) { + sendMessage(player, "You pour the vial into the impression of the key.") + addItemOrDrop(player, Items.TIN_5597) + addItemOrDrop(player, Items.VIAL_229) + } + return@onUseWith true + } + + onUseWith(ITEM, Items.TIN_5594, Items.CUPRIC_ORE_POWDER_5584) { player, used, with -> + if(removeItem(player, used.id) && removeItem(player, with.id)) { + sendMessage(player, "You pour the vial into the impression of the key.") + addItemOrDrop(player, Items.TIN_5596) + addItemOrDrop(player, Items.VIAL_229) + } + return@onUseWith true + } + + onUseWith(ITEM, Items.TIN_5596, Items.TIN_ORE_POWDER_5583) { player, used, with -> + if(removeItem(player, used.id) && removeItem(player, with.id)) { + sendMessage(player, "You pour the vial into the impression of the key.") + addItemOrDrop(player, Items.TIN_5597) + addItemOrDrop(player, Items.VIAL_229) + } + return@onUseWith true + } + + onUseWith(SCENERY, Items.TIN_5597, Scenery.BUNSEN_BURNER_7332) { player, used, _ -> + if(removeItem(player, used.id)) { + sendMessage(player, "You heat the two powdered ores together in the tin.") + sendMessage(player, "You make a duplicate of the key in bronze.") + addItemOrDrop(player, Items.TIN_5598) + } + return@onUseWith true + } + + onUseWith(ITEM, Items.TIN_5598, Items.BRONZE_WIRE_5602, Items.CHISEL_5601, Items.KNIFE_5605) { player, used, with -> + if(removeItem(player, used.id)) { + sendMessage(player, "You prise the duplicate key out of the tin.") + addItemOrDrop(player, Items.TIN_5594) + addItemOrDrop(player, Items.BRONZE_KEY_5585) + } + return@onUseWith true + } + + onUseWith(SCENERY, Items.METAL_SPADE_5586, Scenery.BUNSEN_BURNER_7332) { player, used, _ -> + if(removeItem(player, used.id)) { + sendMessage(player, "You burn the wooden handle away from the spade...") + sendMessage(player, "...and are left with a metal spade with no handle.") + addItemOrDrop(player, Items.ASHES_592) + addItemOrDrop(player, Items.METAL_SPADE_5587) + } + return@onUseWith true + } + + + on(Scenery.STONE_DOOR_7343, SCENERY, "study") { player, node -> + sendDialogueLines(player, "There is a stone slab here obstructing the door.", "There is a small hole in the slab that looks like it might be for a handle.") + sendMessage(player, "It's nearly a perfect fit!") + return@on true + } + + onUseWith(SCENERY, Items.METAL_SPADE_5587, Scenery.STONE_DOOR_7343) { player, used, _ -> + if(removeItem(player, used.id)) { + sendMessage(player, "You slide the spade into the hole in the stone...") + sendMessage(player, "It's nearly a perfect fit!") + setVarbit(player, doorVarbit, 1) + } + return@onUseWith true + } + + onUseWith(SCENERY, DoorVials.doorVialsArray, Scenery.STONE_DOOR_7344) { player, used, _ -> + if(removeItem(player, used.id)) { + setAttribute(player, DoorVials.doorVialsMap[used.id]!!.attribute, true) + sendMessage(player, "You pour the vial onto the flat part of the spade.") + } + if (DoorVials.doorVialsRequiredMap.all { getAttribute(player, it.value.attribute, false) }) { + sendMessage(player, "Something caused a reaction when mixed!") + sendMessage(player, "The spade gets hotter, and expands slightly.") + setVarbit(player, doorVarbit, 2) + } + return@onUseWith true + } + + on(Scenery.STONE_DOOR_7344, SCENERY, "pull-spade") { player, node -> + if (DoorVials.doorVialsRequiredMap.all { getAttribute(player, it.value.attribute, false) }) { + sendMessage(player, "You pull on the spade...") + sendMessage(player, "It works as a handle, and you swing the stone door open.") + setVarbit(player, doorVarbit, 3) + } else { + sendMessage(player, "You pull on the spade...") + sendMessage(player, "It comes loose, and slides out of the hole in the stone.") + addItemOrDrop(player, Items.METAL_SPADE_5587) + setVarbit(player, doorVarbit, 0) + } + return@on true + } + + on(Scenery.OPEN_DOOR_7345, SCENERY, "walk-through") { player, node -> + if(player.location.x <= 2477) { + player.walkingQueue.addPath(2477, 4940) + player.walkingQueue.addPath(2478, 4940) + } else { + player.walkingQueue.addPath(2477, 4940) + } + return@on true + } + + + } + +} + +private class VialShelfDialogueFile(private val flaskIdsArray: IntArray, private val specialAttribute: String? = null) : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true }.branch { _ -> flaskIdsArray.size }.let { branch -> + + branch.onValue(3) + // This is the only shelf with 3 vials of water. + .line("There are three vials on this shelf.") + .options("Take the vials?").let { optionBuilder -> + optionBuilder.option("Take one vial.") + .endWith { _, player -> + addItemOrDrop(player, flaskIdsArray[0]) + if (specialAttribute != null) { + setAttribute(player, specialAttribute, getAttribute(player, specialAttribute, 3) - 1) + print(getAttribute(player, specialAttribute, 3)) + } + } + optionBuilder.option("Take two vials.") + .endWith { _, player -> + addItemOrDrop(player, flaskIdsArray[0]) + addItemOrDrop(player, flaskIdsArray[1]) + if (specialAttribute != null) { + setAttribute(player, specialAttribute, getAttribute(player, specialAttribute, 3) - 2) + } + } + optionBuilder.option("Take all three vials.") + .endWith { _, player -> + addItemOrDrop(player, flaskIdsArray[0]) + addItemOrDrop(player, flaskIdsArray[1]) + addItemOrDrop(player, flaskIdsArray[2]) + if (specialAttribute != null) { + setAttribute(player, specialAttribute, getAttribute(player, specialAttribute, 3) - 3) + } + } + optionBuilder.option("Don't take a vial.") + .end() + } + branch.onValue(2) + .line("There are two vials on this shelf.") + .options("Take the vials?").let { optionBuilder -> + optionBuilder.option("Take the first vial.") + .endWith { _, player -> + addItemOrDrop(player, flaskIdsArray[0]) + if (specialAttribute != null) { + setAttribute(player, specialAttribute, getAttribute(player, specialAttribute, 2) - 1) + } else { + setAttribute(player, MissCheeversRoomListeners.Companion.Vials.vialMap[flaskIdsArray[0]]!!.attribute, true) + } + } + optionBuilder.option("Take the second vial.") + .endWith { _, player -> + addItemOrDrop(player, flaskIdsArray[1]) + if (specialAttribute != null) { + setAttribute(player, specialAttribute, getAttribute(player, specialAttribute, 2) - 1) + } else { + setAttribute(player, MissCheeversRoomListeners.Companion.Vials.vialMap[flaskIdsArray[1]]!!.attribute, true) + } + } + optionBuilder.option("Take both vials.") + .endWith { _, player -> + addItemOrDrop(player, flaskIdsArray[0]) + addItemOrDrop(player, flaskIdsArray[1]) + if (specialAttribute != null) { + setAttribute(player, specialAttribute, getAttribute(player, specialAttribute, 2) - 2) + } else { + setAttribute(player, MissCheeversRoomListeners.Companion.Vials.vialMap[flaskIdsArray[0]]!!.attribute, true) + setAttribute(player, MissCheeversRoomListeners.Companion.Vials.vialMap[flaskIdsArray[1]]!!.attribute, true) + } + } + } + + branch.onValue(1) + .line("There is a vial on this shelf.") + .options("Take the vial?").let { optionBuilder -> + optionBuilder.option("YES") + .endWith { _, player -> + addItemOrDrop(player, flaskIdsArray[0]) + if (specialAttribute != null) { + setAttribute(player, specialAttribute, getAttribute(player, specialAttribute, 1) - 1) + } else { + setAttribute(player, MissCheeversRoomListeners.Companion.Vials.vialMap[flaskIdsArray[0]]!!.attribute, true) + } + } + optionBuilder.option("NO") + .end() + } + + branch.onValue(0) + .line("There is nothing of interest on these shelves.") + } + } +} diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/MsHynnTerprettDialogue.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/MsHynnTerprettDialogue.kt new file mode 100644 index 000000000..91be2f7a7 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/MsHynnTerprettDialogue.kt @@ -0,0 +1,154 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class MsHynnTerprettDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MsHynnTerprettDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return MsHynnTerprettDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.MS_HYNN_TERPRETT_2289) + } +} + +class MsHynnTerprettDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + companion object { + const val attributeRandomRiddle = "quest:recruitmentdrive-randomriddle" + const val attributeRecentlyCorrect = "quest:recruitmentdrive-recentlycorrect" + } + + override fun create(b: DialogueBuilder) { + + b.onPredicate { player -> true }.branch { player -> + if (getAttribute(player, attributeRecentlyCorrect, false)) { + return@branch 3 + } else if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == -1) { + return@branch 2 + } else if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 1) { + return@branch 1 + } else { + return@branch 0 + } + }.let { branch -> + /** Failed Branch */ + val failedStage = b.placeholder() + failedStage.builder() + .npc(FacialExpression.SAD, "No... I am very sorry.", "Apparently you are not up to the challenge.", "I will return you where you came from, better luck in the", "future.") + .endWith { _, player -> + removeAttribute(player, attributeRandomRiddle) + removeAttribute(player, attributeRecentlyCorrect) + removeAttribute(player, RecruitmentDrive.attributeStagePassFailState) + RecruitmentDriveListeners.FailTestCutscene(player).start() + } + /** Passed Branch */ + val passedStage = b.placeholder() + passedStage.builder() + .betweenStage { _, player, _, _ -> + removeAttribute(player, attributeRandomRiddle) + removeAttribute(player, attributeRecentlyCorrect) + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 0) { + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, 1) + } + } + .npc("Excellent work, @name.", "Please step through the portal to meet your next", "challenge.") + .end() + + branch.onValue(3) // Passed stage + .goto(passedStage) + branch.onValue(2) // Failed stage + .goto(failedStage) + branch.onValue(1) // Already passed stage + .npc("You certainly have the wits to be a Temple Knight.", "Pass on through the portal to find your next challenge.") + .end() + branch.onValue(0) + .betweenStage { _, player, _, _ -> + if (getAttribute(player, attributeRandomRiddle, -1) !in 0..4) { + setAttribute(player, attributeRandomRiddle, (0..4).random()) + } + } + .npc("Greetings, @name.", "I am here to test your wits with a simple riddle.") + .branch { player -> getAttribute(player, attributeRandomRiddle, 0) } + .let { branch -> + branch.onValue(0) + .npc(FacialExpression.THINKING, "Here is my riddle:", "I estimate there to be one million inhabitants in the world", "of @servername, creatures and people both.") + .npc(FacialExpression.THINKING, "What number would you get if you multiply", "the number of fingers on everything's left hand, to the", "nearest million?") + .manualStage { _, player, _, _ -> + sendInputDialogue(player, false, "Enter the amount:") { value: Any -> + if(value == "0") { + setAttribute(player, attributeRecentlyCorrect, true) + } else { + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, -1) + } + openDialogue(player, MsHynnTerprettDialogueFile(), NPC(NPCs.MS_HYNN_TERPRETT_2289)) + return@sendInputDialogue + } + } + .end() + + branch.onValue(1) + .npc(FacialExpression.THINKING, "Here is my riddle:", "Which of the following statements is true?") + .options().let { optionBuilder -> + optionBuilder.option("The number of false statements here is one.").goto(failedStage) + optionBuilder.option("The number of false statements here is two.").goto(failedStage) + optionBuilder.option("The number of false statements here is three.").goto(passedStage) + optionBuilder.option("The number of false statements here is four.").goto(failedStage) + } + + branch.onValue(2) + .npc(FacialExpression.THINKING, "Here is my riddle:", "I have both a husband and daughter.") + .npc(FacialExpression.THINKING, "My husband is four times older than my daughter. ", "In twenty years time, he will be twice as old as my", "daughter.") + .npc(FacialExpression.THINKING, "How old is my daughter now?") + .manualStage { _, player, _, _ -> + sendInputDialogue(player, true, "Enter the amount:") { value: Any -> + if(value == 10) { + setAttribute(player, attributeRecentlyCorrect, true) + } else { + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, -1) + } + openDialogue(player, MsHynnTerprettDialogueFile(), NPC(NPCs.MS_HYNN_TERPRETT_2289)) + return@sendInputDialogue + } + } + .end() + + branch.onValue(3) + .npc(FacialExpression.THINKING, "Here is my riddle:", "Imagine that you have been captured by an enemy.", "You are to be killed, but in a moment of mercy, the", "enemy has allowed you to pick your own demise.") + .npc(FacialExpression.THINKING, "Your first choice is to be drowned in a lake of acid.") + .npc(FacialExpression.THINKING, "Your second choice is to be burned on a fire.") + .npc(FacialExpression.THINKING, "Your third choice is to be thrown to a pack of wolves", "that have not been fed in over a month.") + .npc(FacialExpression.THINKING, "Your final choice of fate is to be thrown from the walls", "of a castle, many hundreds of feet high.") + .npc(FacialExpression.THINKING, "Which fate would you be wise to choose?") + .options().let { optionBuilder -> + optionBuilder.option("The lake of acid.").goto(failedStage) + optionBuilder.option("The large fire.").goto(failedStage) + optionBuilder.option("The wolves.").goto(passedStage) + optionBuilder.option("The castle walls.").goto(failedStage) + } + + branch.onValue(4) + .npc(FacialExpression.THINKING, "Here is my riddle:", "I dropped four identical stones, into four identical", "buckets, each containing an identical amount of water.") + .npc(FacialExpression.THINKING, "The first bucket's water was at 32 degrees Fahrenheit,", "the second was at 33 degrees, the third at 34 and the", "fourth was at 35 degrees.") + .npc(FacialExpression.THINKING, "Which bucket's stone dropped to the bottom of the bucket", "last?") + .options().let { optionBuilder -> + optionBuilder.option("Bucket A (32 degrees)").goto(passedStage) + optionBuilder.option("Bucket B (33 degrees)").goto(failedStage) + optionBuilder.option("Bucket C (34 degrees)").goto(failedStage) + optionBuilder.option("Bucket D (35 degrees)").goto(failedStage) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/RecruitmentDrive.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/RecruitmentDrive.kt new file mode 100644 index 000000000..709256772 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/RecruitmentDrive.kt @@ -0,0 +1,135 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +/** + * Recruitment Drive Quest + * + * https://www.youtube.com/watch?v=0yvFREeXNn0 - Quest start log only. + * https://www.youtube.com/watch?v=lNlSiUvPL1o - Very good + * https://www.youtube.com/watch?v=OGWpX1WqpKM 10:12 - Final congrats page. + * https://www.youtube.com/watch?v=nu4OAswRcGg - Speaking to Tiffy after the quest (IMPORTANT!) + * https://www.youtube.com/watch?v=srFMJa4nuX0 1:47 blur ass quest log again + * https://www.youtube.com/watch?v=L7NdDTWa-1Q HAZEEL's CULT + * 1 - Speak to Sir Amik Varze. + * 2 - Sent to secret training ground. + * 3 - Finish all stages. + * 100 - Finish by talking to Tiffy. + */ +@Initializable +class RecruitmentDrive : Quest(Quests.RECRUITMENT_DRIVE, 103, 102, 1, 496, 0, 1, 2) { + companion object { + const val attributeOriginalGender = "/save:quest:recruitmentdrive-originalgender" + + // Stage state: (0: reset), (1: passed), (-1: failed) + const val attributeStagePassFailState = "/save:quest:recruitmentdrive-stagestate" + const val attributeCurrentStage = "/save:quest:recruitmentdrive-currentstage" + const val attributeStage1 = "/save:quest:recruitmentdrive-stage1" + const val attributeStage2 = "/save:quest:recruitmentdrive-stage2" + const val attributeStage3 = "/save:quest:recruitmentdrive-stage3" + const val attributeStage4 = "/save:quest:recruitmentdrive-stage4" + const val attributeStage5 = "/save:quest:recruitmentdrive-stage5" + val attributeStageArray = arrayOf(attributeStage1, attributeStage2, attributeStage3, attributeStage4, attributeStage5) + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, Quests.RECRUITMENT_DRIVE) > 0 + + if(!started){ + line(player, "I can start this quest by speaking to !!Sir Amik Varze??,", line++) + line(player, "upstairs in !!Falador Castle,??", line++) + if (isQuestComplete(player, Quests.DRUIDIC_RITUAL)) { + line(player, "with the Druidic Ritual Quest completed,", line++, true) + } else { + line(player, "with the !!Druidic Ritual Quest?? completed,", line++) + } + if (isQuestComplete(player, Quests.BLACK_KNIGHTS_FORTRESS)) { + line(player, "and since I have completed the Black Knights' Fortress", line++, true) + line(player, "Quest.", line++, true) + } else { + line(player, "and after I have completed the !!Black Knights' Fortress??", line++) + line(player, "Quest.", line++) + } + } else { + line(player, "Sir Amik Varze told me that he had put my name forward as", line++, true) + line(player, "a potential member of some mysterious organisation.", line++, true) + + if (stage >= 2) { + } else if (stage >= 1) { + line(player, "I should head to !!Falador Park?? to meet my !!Contact?? so that I", line++, false) + line(player, "can begin my !!testing for the job??", line++, false) + } + + if (stage >= 3) { + line(player, "I went to Falador Park, and met a strange old man named", line++, true) + line(player, "Tiffy.", line++, true) + line(player, "He sent me to a secret training ground, where my wits", line++, true) + line(player, "were thoroughly tested.", line++, true) + line(player, "Luckily, I was too smart to fall for any of their little tricks,", line++, true) + line(player, "and passed the test with flying colours.", line++, true) + } else if (stage >= 2) { + // http://youtu.be/Otc7ATq3tik 4:17 - I guess this is why no one opens their quest log + line(player, "A man named !!Tiffy?? brought me !!here, to the secret training??", line++, false) + line(player, "!!grounds?? so that I could be tested for the job.", line++, false) + line++ + line(player, "I should !!work out?? what I am supposed to do to complete", line++, false) + line(player, "these rooms...", line++, false) + } + + if (stage >= 4) { + line(player, "I am now an official member of the Temple Knights,", line++, true) + line(player, "although I have to wait for the paperwork to go through", line++, true) + line(player, "before I can commence working for them.", line++, true) + } else if (stage >= 3) { + line(player, "I should talk to !!Tiffy?? to become a Temple Knight.", line++, false) + } + if (stage >= 100) { + line++ + line(player,"QUEST COMPLETE!", line) + } + } + } + + override fun reset(player: Player) { + removeAttribute(player, attributeOriginalGender) + removeAttribute(player, attributeStagePassFailState) + removeAttribute(player, attributeCurrentStage) + removeAttribute(player, attributeStage1) + removeAttribute(player, attributeStage2) + removeAttribute(player, attributeStage3) + removeAttribute(player, attributeStage4) + removeAttribute(player, attributeStage5) + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have passed the Recruitment Drive!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.INITIATE_SALLET_5574, 230, 277, 5) + + drawReward(player, "1 Quest Point", ln++) + drawReward(player, "1000 Prayer, Herblore and", ln++) + drawReward(player, "Agility XP", ln++) + drawReward(player, "Gaze of Saradomin", ln++) + drawReward(player, "Temple Knight's Initiate Helm", ln) + + rewardXP(player, Skills.PRAYER, 1000.0) + rewardXP(player, Skills.HERBLORE, 1000.0) + rewardXP(player, Skills.AGILITY, 1000.0) + addItem(player, Items.INITIATE_SALLET_5574) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/RecruitmentDriveListeners.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/RecruitmentDriveListeners.kt new file mode 100644 index 000000000..39afdf1f4 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/RecruitmentDriveListeners.kt @@ -0,0 +1,316 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import content.data.Quests +import core.api.* +import core.game.activity.Cutscene +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import core.net.packet.PacketRepository +import core.net.packet.context.MinimapStateContext +import core.net.packet.out.MinimapState +import org.rs09.consts.* + +class RecruitmentDriveListeners : InteractionListener { + companion object { + + enum class Stages(val npc: Int, val startLocation: Location, val startWalkLocation: Location, val quitPortal: Int, val successDoor: Int) { + SIR_SPISHYUS(NPCs.SIR_SPISHYUS_2282, Location(2490, 4972), Location(2489, 4972), Scenery.PORTAL_7272, Scenery.DOOR_7274), + LADY_TABLE(NPCs.LADY_TABLE_2283, Location(2460, 4979), Location(2459, 4979), Scenery.PORTAL_7288, Scenery.DOOR_7302), + SIR_KUAM_FERENTSE(NPCs.SIR_KUAM_FERENTSE_2284, Location(2455, 4964), Location(2456, 4964), Scenery.PORTAL_7315, Scenery.DOOR_7317), + SIR_TINLEY(NPCs.SIR_TINLEY_2286, Location(2471, 4956), Location(2472, 4956), Scenery.PORTAL_7318, Scenery.DOOR_7320), + SIR_REN_ITCHOOD(NPCs.SIR_REN_ITCHOOD_2287, Location(2439, 4956), Location(2440, 4956), Scenery.PORTAL_7321, Scenery.DOOR_7323), + MISS_CHEEVERS(NPCs.MISS_CHEEVERS_2288, Location(2467, 4940), Location(2468, 4940), Scenery.PORTAL_7324, Scenery.DOOR_7326), + MS_HYNN_TERPRETT(NPCs.MS_HYNN_TERPRETT_2289, Location(2451, 4935), Location(2451, 4936), Scenery.PORTAL_7352, Scenery.DOOR_7354); + + companion object { + @JvmField + val indexMap = Stages.values().associateBy { it.ordinal } + val indexArray = Stages.indexMap.keys.map { it } + val quitPortalArray = Stages.indexMap.values.map { it.quitPortal }.toIntArray() + val successDoorArray = Stages.indexMap.values.map { it.successDoor }.toIntArray() + } + } + + fun shuffleStages(player: Player) { + // Obtain an array to shuffle. Must be at least [5] long. + val stagesArrayToShuffle = intArrayOf(0,1,2,3,4,5,6) // Stages.indexArray.toIntArray() + stagesArrayToShuffle.shuffle() + setAttribute(player, RecruitmentDrive.attributeStage1, stagesArrayToShuffle[0]) + setAttribute(player, RecruitmentDrive.attributeStage2, stagesArrayToShuffle[1]) + setAttribute(player, RecruitmentDrive.attributeStage3, stagesArrayToShuffle[2]) + setAttribute(player, RecruitmentDrive.attributeStage4, stagesArrayToShuffle[3]) + setAttribute(player, RecruitmentDrive.attributeStage5, stagesArrayToShuffle[4]) + setAttribute(player, RecruitmentDrive.attributeCurrentStage, 0) + removeAttribute(player, RecruitmentDrive.attributeStagePassFailState) + } + + fun callStartingDialogues (player: Player, npc: Int) { + when (npc) { + NPCs.SIR_SPISHYUS_2282 -> openDialogue(player, SirSpishyusDialogueFile(1), NPC(npc)) + NPCs.LADY_TABLE_2283 -> openDialogue(player, LadyTableDialogueFile(1), NPC(npc)) + NPCs.SIR_KUAM_FERENTSE_2284 -> openDialogue(player, SirKuamFerentseDialogueFile(1), NPC(npc)) + NPCs.SIR_TINLEY_2286 -> openDialogue(player, SirTinleyDialogueFile(1), NPC(npc)) + NPCs.SIR_REN_ITCHOOD_2287 -> openDialogue(player, SirRenItchwoodDialogueFile(1), NPC(npc)) + NPCs.MISS_CHEEVERS_2288 -> openDialogue(player, MissCheeversDialogueFile(1), NPC(npc)) + NPCs.MS_HYNN_TERPRETT_2289 -> openDialogue(player, MsHynnTerprettDialogueFile(1), NPC(npc)) + } + } + } + + override fun defineListeners() { + + on(Stages.quitPortalArray, IntType.SCENERY, "use") { player, node -> + FailTestCutscene(player).start() + return@on true + } + + on(Stages.successDoorArray, IntType.SCENERY, "open") { player, node -> + // This is specially for Miss Cheevers + if (inInventory(player, Items.BRONZE_KEY_5585)) { + sendMessage(player, "You use the duplicate key you made to unlock the door.") + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, 1) + } + // Success Door + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 1) { + removeAttribute(player, RecruitmentDrive.attributeStagePassFailState) + setAttribute(player, RecruitmentDrive.attributeCurrentStage, getAttribute(player, RecruitmentDrive.attributeCurrentStage, 0) + 1) + DoorActionHandler.handleAutowalkDoor(player, node as core.game.node.scenery.Scenery) + val currentLevel = getAttribute(player, RecruitmentDrive.attributeCurrentStage, 0) + if (currentLevel >= 5) { + CompleteTestCutscene(player).start() + return@on true + } + val currentStage = getAttribute(player, RecruitmentDrive.attributeStageArray[currentLevel], 0) + val currentStageEnum = Stages.indexMap[currentStage]!! + closeDialogue(player) + + // This is specifically for Sir Spishyus to reset the fox, chicken, grain + SirSpishyusRoomListeners.resetStage(player) + + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + player.inventory.clear() + player.equipment.clear() + openOverlay(player, Components.FADE_TO_BLACK_120) + return@queueScript delayScript(player, 6) + } + 1 -> { + teleport(player, currentStageEnum.startLocation) + return@queueScript delayScript(player, 2) + } + 2 -> { + openOverlay(player, Components.FADE_FROM_BLACK_170) + return@queueScript delayScript(player, 2) + } + 3 -> { + forceWalk(player, currentStageEnum.startWalkLocation, "dumb") + return@queueScript delayScript(player, 2) + } + 4 -> { + callStartingDialogues(player, currentStageEnum.npc) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } else { + if(node.id == Scenery.DOOR_7323) { + // This is specifically for SirRenItchwood + openInterface(player, Components.RD_COMBOLOCK_285) + } else { + sendMessage(player, "You have not completed this room's puzzle yet.") + } + } + return@on true + } + } + + /** Starting Recruitment Drive test cutscene */ + class StartTestCutscene(player: Player) : Cutscene(player) { + override fun setup() { + loadRegion(9805) + val currentStage = getAttribute(player, RecruitmentDrive.attributeStageArray[0], 0) + setExit(Stages.indexMap[currentStage]!!.startLocation) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + timedUpdate(6) + } + 1 -> { + dialogueLinesUpdate(NPCs.SIR_TIFFY_CASHIEN_2290, FacialExpression.HAPPY, "Here we go!", "Mind your head!") + timedUpdate(3) + } + 2 -> { + dialogueLinesUpdate(NPCs.SIR_TIFFY_CASHIEN_2290, FacialExpression.HAPPY, "Oops. Ignore the smell!", "Nearly there!") + timedUpdate(3) + } + 3 -> { + dialogueLinesUpdate(NPCs.SIR_TIFFY_CASHIEN_2290, FacialExpression.HAPPY, "And...", "Here we are!", "Best of luck!") + timedUpdate(3) + } + 4 -> { + player.inventory.clear() + player.equipment.clear() + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + dialogueClose() + endWithoutFade { + val currentStage = getAttribute(player, RecruitmentDrive.attributeStageArray[0], 0) + val firstStage = Stages.indexMap[currentStage]!! + + // This is specifically for Sir Spishyus to reset the fox, chicken, grain + SirSpishyusRoomListeners.resetStage(player) + + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + fadeFromBlack() + return@queueScript delayScript(player, 2) + } + 1 -> { + forceWalk(player, firstStage.startWalkLocation, "dumb") + return@queueScript delayScript(player, 2) + } + 2 -> { + callStartingDialogues(player, firstStage.npc) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + + } + } + } + } + } + + /** Failed Recruitment Drive test cutscene */ + class FailTestCutscene(player: Player) : Cutscene(player) { + override fun setup() { + loadRegion(9805) + setExit(Location(2997, 3374)) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + closeDialogue(player) + fadeToBlack() + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + timedUpdate(6) + } + 1 -> { + var clearBoss = getAttribute(player, SirKuamFerentseDialogueFile.attributeGeneratedSirLeye, NPC(0)) + if (clearBoss.id != 0) { + clearBoss.clear() + } + player.inventory.clear() + player.equipment.clear() + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + fadeFromBlack() + return@queueScript delayScript(player, 2) + } + 1 -> { + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + openDialogue(player, SirTiffyCashienFailedDialogueFile(), NPC(NPCs.SIR_TIFFY_CASHIEN_2290)) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + endWithoutFade { + face(player, Location(2997, 3373)) + fadeFromBlack() + } + } + } + } + } + + /** Complete Recruitment Drive test cutscene */ + class CompleteTestCutscene(player: Player) : Cutscene(player) { + override fun setup() { + loadRegion(9805) + setExit(Location(2996, 3375)) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + if (getQuestStage(player, Quests.RECRUITMENT_DRIVE) == 2) { + setQuestStage(player, Quests.RECRUITMENT_DRIVE, 3) + } + closeDialogue(player) + fadeToBlack() + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + timedUpdate(6) + } + 1 -> { + player.inventory.clear() + player.equipment.clear() + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + fadeFromBlack() + return@queueScript delayScript(player, 2) + } + 1 -> { + openDialogue(player, SirTiffyCashienDialogueFile(), NPC(NPCs.SIR_TIFFY_CASHIEN_2290)) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + endWithoutFade { + face(player, Location(2997, 3373)) + fadeFromBlack() + } + } + } + } + } + + class LogoutRecruitmentDrive : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(9805)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS) + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + // This is specifically for Sir Spishyus to reset the fox, chicken, grain + SirSpishyusRoomListeners.resetStage(entity) + // Clear inventory whenever you leave the recruitment drive area + entity.inventory.clear() + entity.equipment.clear() + // Restore player normal tabs on leave + entity.interfaceManager.openDefaultTabs() + // Teleport you out if you log out. You should do this in one sitting. + if (logout) { + PacketRepository.send(MinimapState::class.java, MinimapStateContext(entity, 0)) + teleport(entity, Location(2996, 3375)) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirAmikVarzeDialogueFile.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirAmikVarzeDialogueFile.kt new file mode 100644 index 000000000..a9aacdc6b --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirAmikVarzeDialogueFile.kt @@ -0,0 +1,75 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.dialogue.* +import content.data.Quests + +class SirAmikVarzeDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.RECRUITMENT_DRIVE, 0) + .npcl(FacialExpression.FRIENDLY,"Hello, friend!") + .playerl(FacialExpression.THINKING, "Do you have any other quests for me to do?") + .branch { player -> if(isQuestComplete(player, Quests.BLACK_KNIGHTS_FORTRESS) && isQuestComplete(player, Quests.DRUIDIC_RITUAL)) { 1 } else { 0 } } + .let{ branch -> + // Failure branch + branch.onValue(0) + .npcl(FacialExpression.THINKING, "A quest? Alas I do not have any quests I can offer you at this time.") + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) // Success branch + .npc("Quests, eh?", "Well, I don't have anything on the go at the moment,", "but there is an organisation that is always looking for", "capable adventurers to assist them.") + .npc(FacialExpression.FRIENDLY,"Your excellent work sorting out those Black Knights", "means I will happily write you a letter of", "recommendation.") + .npc("Would you like me to put your name forwards to", "them?") + .options().let { optionBuilder -> + optionBuilder.option ("Yes please") + .playerl("Sure thing Sir Amik, sign me up!") + .npc(FacialExpression.SUSPICIOUS,"Erm, well, this is a little embarrassing, I already HAVE", "put you forward as a potential member.") + .npc("They are the Temple Knights, and you are to", "meet Sir Tiffy Cashien in Falador park for testing", "immediately.") + .playerl("Okey dokey, I'll go do that then.") + .endWith { _, player -> + if(getQuestStage(player, Quests.RECRUITMENT_DRIVE) == 0) { + setAttribute(player, RecruitmentDrive.attributeOriginalGender, player.isMale) + setQuestStage(player, Quests.RECRUITMENT_DRIVE, 1) + } + } + optionBuilder.option_playerl("No thanks") + .end() + optionBuilder.option("Tell me about this organization...") + .npc(FacialExpression.SUSPICIOUS,"I cannot tell you much...", "They are called the Temple Knights, and are an", "organisation that was founded by Saradomin personally", "many centuries ago.") + .npc("There are many rumours and fables about their works and", "actions, but official records of their presence are non-", "existent.") + .npc("It is a secret organisation of extraordinary power and", "resourcefulness...") + .npc("Let me put it this way:", "Should you decide to take them up on their generous", "offer to join, you will find yourself in an advantageous", "position that many in this world would envy, and that few") + .npc("are called to occupy.") + .playerl("Well, that wasn't quite as helpful as I thought it would be...but thanks anyway, I guess.") + .end() + } + + b.onQuestStages(Quests.RECRUITMENT_DRIVE, 1, 2, 3, 4) + .npcl(FacialExpression.FRIENDLY,"Hello, friend!") + .playerl(FacialExpression.THINKING, "Can I just skip the test to become a Temple Knight?") + .npcl("No, I'm afraid not. I suggest you go meet Sir Tiffy in Falador Park, he will be expecting you.") + .end() + + // This should be after the Wanted Quest, but is the placeholder until that quest is implemented. + b.onQuestStages(Quests.RECRUITMENT_DRIVE, 100) + .npcl(FacialExpression.FRIENDLY,"Hello, friend!") + .npcl(FacialExpression.FRIENDLY,"Well @name, now that you are a White Knight, I expect you should be out there hunting Black Knights for us!") + .options().let { optionBuilder -> + optionBuilder.option_playerl("Can you explain the White Knight honour system again?") + .npcl("Sadly we are not as rich as we once were, and there are many White Knights who foolishly lose their combat equipment.") + .npcl("We do not think it fair to make a profit from our brethren, so we will sell you equipment at cost, and rebuy it at the same cost, but we will only sell equipment to those we consider responsible enough to") + .npcl("wield it correctly.") + .npcl("By killing Black Knights, you will increase your reputation with us, by killing White Knights we will obviously think less of you.") + .npcl("You can check your White Knight reputation level by looking at your quest journal for the Wanted! Quest, or Sir Vyvin will let you know what level you are at when you go to purchase equipment.") + .npcl("Sir Vyvin can be found in Falador Castle, and he will sell you any equipment appropriate to your reputation level.") + .npcl("Have fun, and go kill some Black Knights for me!") + .playerl("Okay Amik, thanks for explaining!") + .end() + + optionBuilder.option("Okay, bye!") + .playerl("Okay, 'bye then Amik!") + .end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirKuamFerentseDialogue.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirKuamFerentseDialogue.kt new file mode 100644 index 000000000..055845ca2 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirKuamFerentseDialogue.kt @@ -0,0 +1,58 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class SirKuamFerentseDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, SirKuamFerentseDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return SirKuamFerentseDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.SIR_KUAM_FERENTSE_2284) + } +} + +class SirKuamFerentseDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + companion object { + const val attributeGeneratedSirLeye = "quest:recruitmentdrive-generatedsirleye" + } + + override fun create(b: DialogueBuilder) { + b.onPredicate { player -> getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 1 } + .npc(FacialExpression.FRIENDLY, "Excellent work, @name.", "Please step through the portal to meet your next", "challenge.") + .end() + + // You can't fail unless you quit the room. + b.onPredicate { _ -> true } + .npc("Ah, @name, you're finally here.", "Your task for this room is to defeat Sir Leye.", "He has been blessed by Saradomin to be undefeatable", "by any man, so it should be quite the challenge for you.") + .npc("If you are having problems, remember", "A true warrior uses his wits as much as his brawn.", "Fight smarter, not harder.") + .endWith { _, player -> + var boss = getAttribute(player, attributeGeneratedSirLeye, NPC(0)) + if (boss.id != 0) { + boss.clear() + } + boss = NPC(NPCs.SIR_LEYE_2285, player.location) + setAttribute(player, attributeGeneratedSirLeye, boss) + boss.isRespawn = false + boss.isAggressive = false + boss.isWalks = true + boss.location = Location(2460, 4963) + boss.init() + registerHintIcon(player, boss) + sendChat(boss, "No man may defeat me!") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirLeyeBehavior.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirLeyeBehavior.kt new file mode 100644 index 000000000..3f91752f7 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirLeyeBehavior.kt @@ -0,0 +1,49 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import org.rs09.consts.NPCs + +class SirLeyeBehavior : NPCBehavior(NPCs.SIR_LEYE_2285) { + var clearTime = 0 + + override fun tick(self: NPC): Boolean { + // You have 400 ticks to kill Sir Leye + if (clearTime++ > 400) { + clearTime = 0 + poofClear(self) + } + return true + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + val lifepoints = self.skills.lifepoints + if (attacker is Player) { + // If you are male, Sir Leye will recover to full health. + if (attacker.isMale) { + if (state.estimatedHit + Integer.max(state.secondaryHit, 0) > lifepoints - 1) { + self.skills.lifepoints = self.getSkills().getStaticLevel(Skills.HITPOINTS) + } + } + } + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + clearHintIcon(killer) + setAttribute(killer, RecruitmentDrive.attributeStagePassFailState, 1) + removeAttribute(killer, SirKuamFerentseDialogueFile.attributeGeneratedSirLeye) + } + } + + // No xp from attacking this dude. + override fun getXpMultiplier(self: NPC, attacker: Entity): Double { + return 0.0 + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirRenItchwoodDialogue.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirRenItchwoodDialogue.kt new file mode 100644 index 000000000..d5ddd45a0 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirRenItchwoodDialogue.kt @@ -0,0 +1,199 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.InterfaceListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Components +import org.rs09.consts.NPCs + +@Initializable +class SirRenItchwoodDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, SirRenItchwoodDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return SirRenItchwoodDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.SIR_REN_ITCHOOD_2287) + } +} + +class SirRenItchwoodDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + companion object { + const val attributeClueNumber = "quest:recruitmentdrive-cluenumber" + } + + override fun create(b: DialogueBuilder) { + b.onPredicate { player -> dialogueNum in 0..1 && getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) != -1 } + .betweenStage { _, player, _, _ -> + if (getAttribute(player, attributeClueNumber, -1) !in 0..5) { + setAttribute(player, attributeClueNumber, (0..5).random()) + } + } + .npc("Greetings friend, and welcome here,", "you'll find my puzzle not so clear.", "Hidden amongst my words, it's true,", "the password for the door as a clue.") + .options().let { optionBuilder -> + optionBuilder.option_playerl ("Can I have the clue for the door?") + .branch { player -> getAttribute(player, attributeClueNumber, 0) } + .let{ branch -> + // Note: all the "I" in here are written in small case "i" (sic) + branch.onValue(0) + .npc("Better than me, you'll not find", "In rhyming and in puzzles.", "This clue so clear will tax your mind", "Entirely as it confuzzles!") + .end() + branch.onValue(1) + .npc("Feel the aching of your mind", "In puzzlement, confused.", "See the clue hidden behind", "His words, as you perused.") + .end() + branch.onValue(2) + .npc("Look closely at the words i speak;", "And study closely every part.", "See for yourself the word you seek", "Trapped for you if you're smart.") + .end() + branch.onValue(3) + .npc("More than words, i have not for you", "Except the things i say today.", "Aware are you, this is a clue?", "Take note of what i say!") + .end() + branch.onValue(4) + .npc("Rare it is that you will see", "A puzzle such as this!", "In many ways it tickles me", "Now, watching you hit and miss!") + .end() + branch.onValue(5) + .npc("This riddle of mine may confuse,", "I am quite sure of that.", "Mayhap you should closely peruse", "Every word i have spat?") + .end() + } + return@let optionBuilder.option("Can I have a different clue?") + .player("I don't get that riddle...", "Can I have a different one?") + .branch { player -> getAttribute(player, attributeClueNumber, 0) } + .let{ branch -> + branch.onValue(0) + .npc("Before you hurry through that door", "Inspect the words i spoke.", "There is a simple hidden flaw", "Ere you think my rhyme a joke.") + .end() + branch.onValue(1) + .npc("First my clue you did not see,", "I really wish you had.", "Such puzzling wordplay devilry", "Has left you kind of mad!") + .end() + branch.onValue(2) + .npc("Last time my puzzle did not help", "Apparently, so you've bidden.", "Study my speech carefully, whelp", "To find the answer, hidden.") + .end() + branch.onValue(3) + .npc("Many types have passed through here", "Even such as you amongst their sort.", "And in the end, the puzzles clear;", "The hidden word you saught.") + .end() + branch.onValue(4) + .npc("Repetition, once again", "Against good sense it goes.", "In my words, the answers plain", "Now that you see rhyme flows.") + .end() + branch.onValue(5) + .npc("Twice it is now, i have stated", "In a rhyme, what is the pass.", "Maybe my words obfuscated", "Entirely beyond your class.") + .end() + } + /* + // I'm too goddamned lazy to implement the final clue dialogue + return@let optionBuilder.option("Can I have the final clue?") + .branch { player -> getAttribute(player, attributeClueNumber, 0) } + .let{ branch -> + branch.onValue(0) + .npc("Betrayed by words the answer is", "In that what i say is the key", "There is no more help after this", "Especially no more from me.") + .end() + branch.onValue(1) + .npc("For the last time i will state", "In simple words, the clue.", "Such tricky words make you irate", "Having no idea what to do...") + .end() + branch.onValue(2) + .npc("Lo! my final speech is now", "Attended to by you.", "Study my words, and find out how", "To understand my clue!") + .end() + branch.onValue(3) + .npc("Many types have passed through here", "Even such as you amongst their sort.", "And in the end, the puzzles clear;", "The hidden word you saught.") + .end() + branch.onValue(4) + .npc("Repetition, once again", "Against good sense it goes.", "In my words, the answers plain", "Now that you see rhyme flows.") + .end() + branch.onValue(5) + .npc("Twice it is now, i have stated", "In a rhyme, what is the pass.", "Maybe my words obfuscated", "Entirely beyond your class.") + .end() + } + */ + + } + b.onPredicate { player -> dialogueNum == 2 || getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == -1 } + .betweenStage { _, player, _, _ -> + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, -1) + } + .npc(FacialExpression.SAD, "It's sad to say,", "this test beat you.", "I'll send you to Tiffy,", "what to do?") + .endWith { _, player -> + removeAttribute(player, attributeClueNumber) + removeAttribute(player, RecruitmentDrive.attributeStagePassFailState) + RecruitmentDriveListeners.FailTestCutscene(player).start() + } + + } +} + +class DoorLockPuzzleInterfaceListener : InterfaceListener { + + companion object { + const val temp = Components.RD_COMBOLOCK_285 + const val attributeLock1 = "quest:recruitmentdrive-lock1" + const val attributeLock2 = "quest:recruitmentdrive-lock2" + const val attributeLock3 = "quest:recruitmentdrive-lock3" + const val attributeLock4 = "quest:recruitmentdrive-lock4" + val lockArray = arrayOf(attributeLock1, attributeLock2, attributeLock3, attributeLock4) + val answers = arrayOf("BITE", "FISH", "LAST", "MEAT", "RAIN", "TIME") + } + + override fun defineInterfaceListeners() { + + onOpen(Components.RD_COMBOLOCK_285) { player, _ -> + setAttribute(player, attributeLock1, 65) + setAttribute(player, attributeLock2, 65) + setAttribute(player, attributeLock3, 65) + setAttribute(player, attributeLock4, 65) + return@onOpen true + } + + onClose(Components.RD_COMBOLOCK_285) { player, _ -> + removeAttribute(player, attributeLock1) + removeAttribute(player, attributeLock2) + removeAttribute(player, attributeLock3) + removeAttribute(player, attributeLock4) + return@onClose true + } + + on(Components.RD_COMBOLOCK_285) { player, component, opcode, buttonID, slot, itemID -> + // Child IDs for respective locks: + // 6 7 8 9 + // 10 < Lock1 > 11 12 < Lock2 > 13 14 < Lock3 > 15 16 < Lock4 > 17 + if (buttonID in 10..17) { + val position = (buttonID - 10) / 2 + val backForth = (buttonID - 10) % 2 + var newValue = getAttribute(player, lockArray[position], 65) + (if (backForth == 0) { -1 } else { 1 }) + if (newValue < 65) { newValue = 90 } // If char number is under A(65), loop back to Z(90) + if (newValue > 90) { newValue = 65 } // If char number is over Z(90), loop back to A(65) + setAttribute(player, lockArray[position], newValue) + setInterfaceText(player, newValue.toChar().toString(), Components.RD_COMBOLOCK_285, position + 6) + } + // Enter Button + if (buttonID == 18) { + val lock1 = getAttribute(player, attributeLock1, 65).toChar() + val lock2 = getAttribute(player, attributeLock2, 65).toChar() + val lock3 = getAttribute(player, attributeLock3, 65).toChar() + val lock4 = getAttribute(player, attributeLock4, 65).toChar() + val answer = arrayOf(lock1, lock2, lock3, lock4).joinToString("") + closeInterface(player) + if (answers[getAttribute(player, SirRenItchwoodDialogueFile.attributeClueNumber, 0)] == answer){ + removeAttribute(player, SirRenItchwoodDialogueFile.attributeClueNumber) + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) != -1) { + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, 1) + } + sendNPCDialogue(player, NPCs.SIR_REN_ITCHOOD_2287, "Your wit is sharp, your brains quite clear; You solved my puzzle with no fear. At puzzles I rank you quite the best, now enter the portal for your next test.") + } else { + removeAttribute(player, SirRenItchwoodDialogueFile.attributeClueNumber) + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 0) { + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, -1) + openDialogue(player, SirRenItchwoodDialogueFile(2), NPC(NPCs.SIR_REN_ITCHOOD_2287)) + } + } + } + return@on true + } + } +} diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirSpishyusDialogue.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirSpishyusDialogue.kt new file mode 100644 index 000000000..8f6df7708 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirSpishyusDialogue.kt @@ -0,0 +1,221 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.container.impl.EquipmentContainer +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.zone.ZoneBorders +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +@Initializable +class SirSpishyusDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, SirSpishyusDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return SirSpishyusDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.SIR_SPISHYUS_2282) + } +} + +class SirSpishyusDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { player -> getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 1 } + .npc(FacialExpression.FRIENDLY, "Excellent work, @name.", "Please step through the portal to meet your next", "challenge.") + .end() + + b.onPredicate { player -> dialogueNum == 2 || getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == -1 } + .betweenStage { _, player, _, _ -> + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, -1) + } + .npc(FacialExpression.SAD, "No... I am very sorry.", "Apparently you are not up to the challenge.", "I will return you where you came from, better luck in the", "future.") + .endWith { _, player -> + removeAttribute(player, SirTinleyDialogueFile.attributeDoNotMove) + removeAttribute(player, RecruitmentDrive.attributeStagePassFailState) + RecruitmentDriveListeners.FailTestCutscene(player).start() + } + b.onPredicate { _ -> true } + .npcl(FacialExpression.FRIENDLY, "Ah, welcome @name.") + .playerl(FacialExpression.FRIENDLY, "Hello there." + " What am I supposed to be doing in this room?") + .npcl(FacialExpression.FRIENDLY, "Well, your task is to take this fox, this chicken and this bag of grain across that bridge there to the other side of the room.") + .npcl(FacialExpression.FRIENDLY, "When you have done that, your task is complete.") + .playerl(FacialExpression.FRIENDLY, "Is that it?") + .npcl(FacialExpression.FRIENDLY, "Well, it is not quite as simple as that may sound.") + .npcl(FacialExpression.FRIENDLY, "Firstly, you may only carry one of the objects across the room at a time, for the bridge is old and fragile.") + .npcl(FacialExpression.FRIENDLY, "Secondly, the fox wants to eat the chicken, and the chicken wants to eat the grain. Should you ever leave the fox unattended with the chicken, or the grain unattended with the chicken, then") + .npcl(FacialExpression.FRIENDLY, "one of them will be eaten, and you will be unable to complete the test.") + .playerl(FacialExpression.FRIENDLY, "Okay, I'll see what I can do.") + .end() + } +} +class SirSpishyusRoomListeners : InteractionListener { + companion object { + const val foxFromVarbit = 680 + const val foxToVarbit = 681 + const val chickenFromVarbit = 682 + const val chickenToVarbit = 683 + const val grainFromVarbit = 684 + const val grainToVarbit = 685 + + val fromZoneBorder = ZoneBorders(2479, 4967, 2490, 4977) + val toZoneBorder = ZoneBorders(2471, 4967, 2478, 4977) + + fun countEquipmentItems(player: Player): Int { + var count = 0 + if(inEquipment(player, Items.GRAIN_5607)) { count++ } + if(inEquipment(player, Items.FOX_5608)) { count++ } + if(inEquipment(player, Items.CHICKEN_5609)) { count++ } + return count + } + + fun checkFinished(player: Player) { + if (getVarbit(player, foxToVarbit) == 1 && getVarbit(player, chickenToVarbit) == 1 && getVarbit(player, grainToVarbit) == 1) { + sendMessage(player, "Congratulations! You have solved this room's puzzle!") + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, 1) + } + } + + fun checkFail(player: Player): Boolean { + return ((getVarbit(player, foxFromVarbit) == 0 && getVarbit(player, chickenFromVarbit) == 0 && getVarbit(player, grainFromVarbit) == 1) || + (getVarbit(player, foxFromVarbit) == 1 && getVarbit(player, chickenFromVarbit) == 0 && getVarbit(player, grainFromVarbit) == 0) || + (getVarbit(player, foxToVarbit) == 1 && getVarbit(player, chickenToVarbit) == 1 && getVarbit(player, grainToVarbit) == 0) || + (getVarbit(player, foxToVarbit) == 0 && getVarbit(player, chickenToVarbit) == 1 && getVarbit(player, grainToVarbit) == 1)) + } + + fun resetStage(player: Player) { + setVarbit(player, foxFromVarbit, 0) + setVarbit(player, chickenFromVarbit, 0) + setVarbit(player, grainFromVarbit, 0) + setVarbit(player, foxToVarbit, 0) + setVarbit(player, chickenToVarbit, 0) + setVarbit(player, grainToVarbit, 0) + removeItem(player, Items.GRAIN_5607, Container.EQUIPMENT) + removeItem(player, Items.FOX_5608, Container.EQUIPMENT) + removeItem(player, Items.CHICKEN_5609, Container.EQUIPMENT) + } + } + + override fun defineListeners() { + on(Scenery.PRECARIOUS_BRIDGE_7286, SCENERY, "cross") { player, node -> + if (countEquipmentItems(player) > 1) { + sendDialogue(player, "I really don't think I should be carrying more than 5Kg across that rickety bridge...") + } else if (checkFail(player)) { + openDialogue(player, SirTinleyDialogueFile(2), NPC(NPCs.SIR_SPISHYUS_2282)) // Fail + } else { + lock(player, 5) + sendMessage(player, "You carefully walk across the rickety bridge...") + player.walkingQueue.reset() + player.walkingQueue.addPath(2476, 4972) + } + return@on true + } + + on(Scenery.PRECARIOUS_BRIDGE_7287, SCENERY, "cross") { player, node -> + if (countEquipmentItems(player) > 1) { + sendDialogue(player, "I really don't think I should be carrying more than 5Kg across that rickety bridge...") + } else if (checkFail(player)) { + openDialogue(player, SirTinleyDialogueFile(2), NPC(NPCs.SIR_SPISHYUS_2282)) // Fail + } else { + lock(player, 5) + sendMessage(player, "You carefully walk across the rickety bridge...") + player.walkingQueue.reset() + player.walkingQueue.addPath(2484, 4972) + } + return@on true + } + + on(Scenery.GRAIN_7284, SCENERY, "pick-up") { player, _ -> + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 0) { + if (fromZoneBorder.insideBorder(player)) { + replaceSlot(player, EquipmentSlot.CAPE.ordinal, Item(Items.GRAIN_5607), null, Container.EQUIPMENT) + setVarbit(player, grainFromVarbit, 1) + } + if (toZoneBorder.insideBorder(player)) { + replaceSlot(player, EquipmentSlot.CAPE.ordinal, Item(Items.GRAIN_5607), null, Container.EQUIPMENT) + setVarbit(player, grainToVarbit, 0) + } + } + return@on true + } + onUnequip(Items.GRAIN_5607) { player, _ -> + if (fromZoneBorder.insideBorder(player)) { + removeItem(player, Items.GRAIN_5607, Container.EQUIPMENT) + setVarbit(player, grainFromVarbit, 0) + } + if (toZoneBorder.insideBorder(player)) { + removeItem(player, Items.GRAIN_5607, Container.EQUIPMENT) + setVarbit(player, grainToVarbit, 1) + checkFinished(player) + } + return@onUnequip true + } + + + on(Scenery.FOX_7277, SCENERY, "pick-up") { player, _ -> + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 0) { + if (fromZoneBorder.insideBorder(player)) { + replaceSlot(player, EquipmentSlot.WEAPON.ordinal, Item(Items.FOX_5608), null, Container.EQUIPMENT) + setVarbit(player, foxFromVarbit, 1) + } + if (toZoneBorder.insideBorder(player)) { + replaceSlot(player, EquipmentSlot.WEAPON.ordinal, Item(Items.FOX_5608), null, Container.EQUIPMENT) + setVarbit(player, foxToVarbit, 0) + } + } + return@on true + } + onUnequip(Items.FOX_5608) { player, _ -> + if (fromZoneBorder.insideBorder(player)) { + removeItem(player, Items.FOX_5608, Container.EQUIPMENT) + setVarbit(player, foxFromVarbit, 0) + } + if (toZoneBorder.insideBorder(player)) { + removeItem(player, Items.FOX_5608, Container.EQUIPMENT) + setVarbit(player, foxToVarbit, 1) + checkFinished(player) + } + return@onUnequip true + } + + + on(Scenery.CHICKEN_7281, SCENERY, "pick-up") { player, _ -> + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 0) { + if (fromZoneBorder.insideBorder(player)) { + replaceSlot(player, EquipmentSlot.SHIELD.ordinal, Item(Items.CHICKEN_5609), null, Container.EQUIPMENT) + setVarbit(player, chickenFromVarbit, 1) + } + if (toZoneBorder.insideBorder(player)) { + replaceSlot(player, EquipmentSlot.SHIELD.ordinal, Item(Items.CHICKEN_5609), null, Container.EQUIPMENT) + setVarbit(player, chickenToVarbit, 0) + } + } + return@on true + } + onUnequip(Items.CHICKEN_5609) { player, _ -> + if (fromZoneBorder.insideBorder(player)) { + removeItem(player, Items.CHICKEN_5609, Container.EQUIPMENT) + setVarbit(player, chickenFromVarbit, 0) + } + if (toZoneBorder.insideBorder(player)) { + removeItem(player, Items.CHICKEN_5609, Container.EQUIPMENT) + setVarbit(player, chickenToVarbit, 1) + checkFinished(player) + } + return@onUnequip true + } + + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirTiffyCashienDialogueFile.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirTiffyCashienDialogueFile.kt new file mode 100644 index 000000000..9bd2ecf93 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirTiffyCashienDialogueFile.kt @@ -0,0 +1,140 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import org.rs09.consts.Items + +class SirTiffyCashienDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.RECRUITMENT_DRIVE, 1) + .player(FacialExpression.FRIENDLY, "Sir Amik Varze sent me to meet you here for some", "sort of testing...") + .npc(FacialExpression.FRIENDLY, "Ah, @name!", "Amik told me all about you, dontchaknow!", "Spliffing job you you did with the old Black Knights there,", "absolutely first class.") + .playerl(FacialExpression.GUILTY, "...Thanks I think.") + // .npcl(FacialExpression.FRIENDLY, "Well, not in those exact words, but you get my point, what?") + .npc(FacialExpression.FRIENDLY, "Well, a top-notch filly like yourself is just the right sort", "we've been looking for for our organisation.") + .npcl(FacialExpression.FRIENDLY, "So, are you ready to begin testing?") + .let { path -> + val originalPath = b.placeholder() + path.goto(originalPath) + return@let originalPath.builder().options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option("Testing..?") + .playerl(FacialExpression.FRIENDLY, "Testing? What exactly do you mean by testing?") + .npcl(FacialExpression.FRIENDLY, "Jolly bad show! Varze was supposed to have informed you about all this before sending you here!") + .npcl(FacialExpression.FRIENDLY, "Well, not your fault I suppose, what? Anywho, our organisation is looking for a certain specific type of person to join.") + .playerl(FacialExpression.FRIENDLY, "So... You want me to go kill some monster or something for you?") + .npcl(FacialExpression.FRIENDLY, "Not at all, old bean. There's plenty of warriors around should we require dumb muscle.") + .npcl(FacialExpression.FRIENDLY, "That's really not the kind of thing our organisation is after, what?") + .playerl(FacialExpression.FRIENDLY, "So you want me to go and fetch you some kind of common item, and then take it for delivery somewhere on the other side of the country?") + .playerl(FacialExpression.FRIENDLY, "Because I really hate doing that!") + .npcl(FacialExpression.FRIENDLY, "Haw, haw, haw! What a dull thing to ask of someone, what?") + .npcl(FacialExpression.FRIENDLY, "I know what you mean, though. I did my fair share of running errands when I was a young adventurer, myself!") + .playerl(FacialExpression.FRIENDLY, "So what exactly will this test consist of?") + .npcl(FacialExpression.FRIENDLY, "Can't let just any old riff-raff in, what? The mindless thugs and bully boys are best left in the White Knights or the city guard. We look for the top-shelf brains to join us.") + .playerl(FacialExpression.FRIENDLY, "So you want to test my brains? Will it hurt?") + .npcl(FacialExpression.FRIENDLY, "Haw, haw, haw! That's a good one!") + .npcl(FacialExpression.FRIENDLY, "Not in the slightest.. Well, maybe a bit, but we all have to make sacrifices occasionally, what?") + .playerl(FacialExpression.FRIENDLY, "What do you want me to do then?") + .npcl(FacialExpression.FRIENDLY, "It's a test of wits, what? I'll take you to our secret training grounds, and you will have to pass through a series of five separate intelligence test to prove you're our sort of adventurer.") + .npcl(FacialExpression.FRIENDLY, "Standard puzzle room rules will apply.") + .playerl(FacialExpression.THINKING, "Erm... What are standard puzzle room rules exactly?") + .npcl(FacialExpression.HAPPY, "Never done this sort of thing before, what?") + .npc("The simple rules are:", "No items or equipment to be brought with you.", "Each room is a self-contained puzzle.", "You may quit at any time.") + .npcl(FacialExpression.HAPPY, "Of course, if you quit a room, then all your progress up to that point will be cleared, and you'll have to start again from scratch.") + .npc(FacialExpression.HAPPY, "Our organisation manages to filter all the top-notch", "adventurers this way.", "So, are you ready to go?") + .goto(originalPath) + optionBuilder.option("Organisation?") + .playerl(FacialExpression.THINKING, "This organisation you keep mentioning.. Perhaps you could tell me a little about it?") + .npcl(FacialExpression.FRIENDLY, "Oh, that Amik! Jolly bad form. Did he not tell you anything that he was supposed to?") + .playerl(FacialExpression.FRIENDLY, "No. He didn't really tell me anything except to come here and meet you.") + .npcl(FacialExpression.FRIENDLY, "Well, now, old sport, let me give you the heads up and the low down, what?") + .npcl(FacialExpression.FRIENDLY, "I represent the Temple Knights. We are the premier order of Knights in Asgarnia, if not the world. Saradomin himself personally founded our order centuries ago, and we answer only to him.") + .npcl(FacialExpression.FRIENDLY, "Only the very best of the best are permitted to join, and the powers we command are formidable indeed.") + .npcl(FacialExpression.FRIENDLY, "You might say that we are the front line of defence for the entire kingdom!") + .playerl(FacialExpression.THINKING, "So what's the difference between you and the White Knights?") + .npcl(FacialExpression.FRIENDLY, "Well, in simple terms, we're better! Any fool with a sword can manage to get into the White Knights, which is mostly the reason they are so very, very incompetent, what?") + .npcl(FacialExpression.FRIENDLY, "The Temple Knights, on the other hand, have to be smarter, stronger and better than all others. We are the elite. No man controls us, for our orders come directly from Saradomin himself!") + .npcl(FacialExpression.FRIENDLY, "According to Sir Vey Lance, our head of operations, that is. He claims that everything he tells us to do is done with Saradomin's implicit permission.") + .npcl(FacialExpression.FRIENDLY, "It's not every job where you have more authority than the king, though, is it?") + .playerl(FacialExpression.THINKING, "Wait... You can order the King around?") + .npcl(FacialExpression.FRIENDLY, "Well, not me personally. I'm only in the recruitment side of things, dontchaknow, but the higher ranking members of the organisation have almost absolute power over the kingdom.") + .npcl(FacialExpression.FRIENDLY, "Plus a few others, so I hear...") + .npcl(FacialExpression.FRIENDLY, "Anyway, this is why we keep our organisation shrouded in secrecy, and why we demand such rigorous testing for all potential recruits. Speaking of which, are you ready to begin your testing?") + .goto(originalPath) + optionBuilder.option("Yes, let's go!") + .player(FacialExpression.FRIENDLY, "Yeah. this sounds right up my street.", "Let's go!") + .branch { player -> if(player.inventory.isEmpty && player.equipment.isEmpty && !player.familiarManager.hasFamiliar()) { 1 } else { 0 } } + .let { branch -> + branch.onValue(0) + .npcl(FacialExpression.NEUTRAL, "Well, bad luck, old @g[guy,gal]. You'll need to have a completely empty inventory and you can't be wearing any equipment before we can accurately test you.") + .npcl(FacialExpression.HAPPY, "Don't want people cheating by smuggling stuff in, what? That includes things carried by familiars, too! Come and see me again after you've been to the old bank to drop your stuff off, what?") + .end() + return@let branch + } + .onValue(1) + .npc(FacialExpression.HAPPY, "Jolly good show!", "Now the training grounds location is a secret, so...") + .goto(continuePath) + optionBuilder.option("No, I've changed my mind.") + .player("No, I've changed my mind.") + .end() + + return@let continuePath.builder() + } + + }.endWith { _, player -> + if (getQuestStage(player, Quests.RECRUITMENT_DRIVE) == 1) { + setQuestStage(player, Quests.RECRUITMENT_DRIVE, 2) + } + RecruitmentDriveListeners.shuffleStages(player) + RecruitmentDriveListeners.StartTestCutscene(player).start() + } + b.onQuestStages(Quests.RECRUITMENT_DRIVE, 2) + .npc(FacialExpression.FRIENDLY, "Ah, what ho!", "Back for another go at the old testing, what?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option("Yes, let's go!") + .player(FacialExpression.FRIENDLY, "Yeah. this sounds right up my street.", "Let's go!") + .branch { player -> if(player.inventory.isEmpty && player.equipment.isEmpty && !player.familiarManager.hasFamiliar()) { 1 } else { 0 } } + .let { branch -> + branch.onValue(0) + .npcl(FacialExpression.NEUTRAL, "Well, bad luck, old @g[guy,gal]. You'll need to have a completely empty inventory and you can't be wearing any equipment before we can accurately test you.") + .npcl(FacialExpression.HAPPY, "Don't want people cheating by smuggling stuff in, what? That includes things carried by familiars, too! Come and see me again after you've been to the old bank to drop your stuff off, what?") + .end() + return@let branch + } + .onValue(1) + .npc(FacialExpression.FRIENDLY, "Jolly good show!", "Now the training grounds location is a secret, so...") + .endWith { _, player -> + RecruitmentDriveListeners.shuffleStages(player) + RecruitmentDriveListeners.StartTestCutscene(player).start() + } + optionBuilder.option("No, I've changed my mind.") + .player("No, I've changed my mind.") + .end() + return@let continuePath.builder() + } + b.onQuestStages(Quests.RECRUITMENT_DRIVE, 3) + .npc(FacialExpression.HAPPY, "Oh, jolly well done!", "Your performance will need to be evaluated by Sir Vey", "personally, but I don't think it's going too far ahead of", "myself to welcome you to the team!") + .endWith { _, player -> + // Get a voucher and $3000 to change gender if you did do it during the quest. + if (getAttribute(player, RecruitmentDrive.attributeOriginalGender, true) != player.isMale) { + addItemOrDrop(player, Items.MAKEOVER_VOUCHER_5606) + addItemOrDrop(player, Items.COINS_995, 3000) + } + removeAttribute(player, RecruitmentDrive.attributeOriginalGender) + finishQuest(player, Quests.RECRUITMENT_DRIVE) + } + } +} + +class SirTiffyCashienFailedDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .npc(FacialExpression.SAD, "Oh, jolly bad luck, what?", "Not quite the brainbox you thought you were, eh?") + .npc(FacialExpression.HAPPY, "Well, never mind!", "You have an open invitation to join our organization, so", "when you're feeling a little smarter, come back and talk", "to me again.") + + } +} diff --git a/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirTinleyDialogue.kt b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirTinleyDialogue.kt new file mode 100644 index 000000000..d00cae73a --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/recruitmentdrive/SirTinleyDialogue.kt @@ -0,0 +1,100 @@ +package content.region.asgarnia.falador.quest.recruitmentdrive + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.impl.Projectile +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +@Initializable +class SirTinleyDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, SirTinleyDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return SirTinleyDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.SIR_TINLEY_2286) + } +} + +class SirTinleyDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile(), MapArea { + companion object { + const val attributeDoNotMove = "quest:recruitmentdrive-donotmove" + } + + override fun create(b: DialogueBuilder) { + b.onPredicate {player -> dialogueNum == 0 && !getAttribute(player, attributeDoNotMove, false) && getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 0 } + .npc("Ah, welcome @name.", "I have but one clue for you to pass this room's puzzle:", "'Patience'.") + .endWith { _, player -> + setAttribute(player, attributeDoNotMove, true) + queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + return@queueScript delayScript(player, 15) + } + 1 -> { + if (getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) != -1) { + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, 1) + setAttribute(player, attributeDoNotMove, false) + npc(FacialExpression.FRIENDLY, "Excellent work, @name.", "Please step through the portal to meet your next", "challenge.") + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + b.onPredicate {player -> dialogueNum == 0 && !getAttribute(player, attributeDoNotMove, false) && getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == 1 } + .npc(FacialExpression.FRIENDLY, "Excellent work, @name.", "Please step through the portal to meet your next", "challenge.") + .end() + + // If you talk to him before time is up, you fail. + b.onPredicate { player -> dialogueNum == 0 && getAttribute(player, attributeDoNotMove, false) || dialogueNum == 2 || getAttribute(player, RecruitmentDrive.attributeStagePassFailState, 0) == -1 } + .betweenStage { _, player, _, _ -> + setAttribute(player, RecruitmentDrive.attributeStagePassFailState, -1) + } + .npc(FacialExpression.SAD, "No... I am very sorry.", "Apparently you are not up to the challenge.", "I will return you where you came from, better luck in the", "future.") + .endWith { _, player -> + removeAttribute(player, attributeDoNotMove) + removeAttribute(player, RecruitmentDrive.attributeStagePassFailState) + RecruitmentDriveListeners.FailTestCutscene(player).start() + } + + b.onPredicate { _ -> dialogueNum == 1 } + .npc("Ah, @name, you have arrived.", "Speak to me to begin your task.") + .endWith { _, player -> + setAttribute(player, attributeDoNotMove, false) + } + } + + + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2474, 4959, 2478, 4957)) + } + + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + if(getAttribute(entity, attributeDoNotMove, false)) { + setAttribute(entity, attributeDoNotMove, false) + setAttribute(entity, RecruitmentDrive.attributeStagePassFailState, -1) + openDialogue(entity, SirTinleyDialogueFile(2), NPC(NPCs.SIR_TINLEY_2286)) + } + } + } +} diff --git a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GDiplomacyCutscene.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GDiplomacyCutscene.java index 487467ae2..3743a5c2c 100644 --- a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GDiplomacyCutscene.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GDiplomacyCutscene.java @@ -1,5 +1,6 @@ package content.region.asgarnia.goblinvillage.quest.goblindiplomacy; +import content.data.Quests; import core.game.component.Component; import core.game.node.entity.player.link.emote.Emotes; import core.game.activity.ActivityManager; @@ -54,7 +55,7 @@ public final class GDiplomacyCutscene extends CutscenePlugin { @Override public boolean start(final Player player, final boolean login, Object... args) { - Quest quest = player.getQuestRepository().getQuest(GoblinDiplomacy.NAME); + Quest quest = player.getQuestRepository().getQuest(Quests.GOBLIN_DIPLOMACY); final NPC grubfoot = NPC.create(quest.getStage(player) == 10 ? 4495 : quest.getStage(player) == 20 ? 4497 : quest.getStage(player) == 30 ? 4498 : 4496, getBase().transform(10, 55, 0)); grubfoot.setWalks(false); npcs.add(grubfoot); @@ -182,8 +183,8 @@ public final class GDiplomacyCutscene extends CutscenePlugin { type = GrubFoot.forConfig(player); dialIndex = RandomFunction.random(DIALOGUES.length); other = Repository.findNPC(npc.getId() == 4494 ? 4493 : 4494); - quest = player.getQuestRepository().getQuest(GoblinDiplomacy.NAME); - if(player.getQuestRepository().getQuest("Lost Tribe").getStage(player) == 43){ + quest = player.getQuestRepository().getQuest(Quests.GOBLIN_DIPLOMACY); + if(player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 43){ player("Have you heard of the Dorgeshuun?"); stage = 5000; return true; @@ -367,8 +368,8 @@ public final class GDiplomacyCutscene extends CutscenePlugin { player.setAttribute("/save:tlt-goblin-emotes",true); player.getEmoteManager().unlock(Emotes.GOBLIN_BOW); player.getEmoteManager().unlock(Emotes.GOBLIN_SALUTE); - setVarbit(player, 532, 7, true); - player.getQuestRepository().getQuest("Lost Tribe").setStage(player,44); + setVarbit(player, 532, 7, true); + player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).setStage(player,44); stage++; break; case 5055: diff --git a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java index a3e744b86..ad3d9c910 100644 --- a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the demon slayer quest. @@ -15,12 +16,6 @@ import core.plugin.ClassScanner; */ @Initializable public class GoblinDiplomacy extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "Goblin Diplomacy"; - /** * Represents the orange goblin mail. */ @@ -45,7 +40,7 @@ public class GoblinDiplomacy extends Quest { * Constructs a new {@Code GoblinDiplomacy} {@Code Object} */ public GoblinDiplomacy() { - super("Goblin Diplomacy", 20, 19, 5); + super(Quests.GOBLIN_DIPLOMACY, 20, 19, 5); } @Override @@ -87,7 +82,7 @@ public class GoblinDiplomacy extends Quest { line(player, BLUE + "I have some " + RED + "Blue Goblin Armour. " + BLUE + "I should show it to the", 12+ 7); line(player, BLUE + "generals.", 13+ 7); } else { - line(player, BLUE + "I should bring the goblins s+ 7+ 7);e " + RED + "Blue Goblin Armour", 12+ 7); + line(player, BLUE + "I should bring the goblins some " + RED + "Blue Goblin Armour", 12+ 7); line(player, BLUE + "Maybe the generals will know where to get some.", 13+ 7); } break; diff --git a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubfootDialogue.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubfootDialogue.java index 0eaed3038..ca562e9c6 100644 --- a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubfootDialogue.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubfootDialogue.java @@ -1,5 +1,6 @@ package content.region.asgarnia.goblinvillage.quest.goblindiplomacy; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -42,7 +43,7 @@ public final class GrubfootDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(GoblinDiplomacy.NAME); + quest = player.getQuestRepository().getQuest(Quests.GOBLIN_DIPLOMACY); switch (quest.getStage(player)) { case 100: npc("Me lonely."); diff --git a/Server/src/main/content/region/asgarnia/handlers/CraftingGuildListeners.kt b/Server/src/main/content/region/asgarnia/handlers/CraftingGuildListeners.kt index 9ca8b163a..32059ef91 100644 --- a/Server/src/main/content/region/asgarnia/handlers/CraftingGuildListeners.kt +++ b/Server/src/main/content/region/asgarnia/handlers/CraftingGuildListeners.kt @@ -2,28 +2,25 @@ package content.region.asgarnia.handlers import content.global.skill.crafting.TanningProduct import core.api.* +import core.game.interaction.IntType import core.game.node.entity.skill.Skills import core.game.world.map.Location import org.rs09.consts.Items +import org.rs09.consts.NPCs import org.rs09.consts.Scenery import content.region.asgarnia.dialogue.TheDoorDialogues import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import org.rs09.consts.NPCs /** - * @author bushtail + * @author bushtail, Player Name */ class CraftingGuildListeners : InteractionListener { - private val GUILD_DOOR = Scenery.GUILD_DOOR_2647 - private val REQUIRED_ITEMS = intArrayOf(Items.BROWN_APRON_1757, Items.CRAFTING_CAPE_9780, Items.CRAFTING_CAPET_9781) - override fun defineListeners() { - on(GUILD_DOOR, IntType.SCENERY, "open") { player, door -> + on(Scenery.GUILD_DOOR_2647, IntType.SCENERY, "open") { player, door -> if (player.location == Location.create(2933, 3289, 0)) { if (hasLevelStat(player, Skills.CRAFTING, 40)) { - if (anyInEquipment(player, *REQUIRED_ITEMS)) { + if (anyInEquipment(player, Items.BROWN_APRON_1757, Items.CRAFTING_CAPE_9780, Items.CRAFTING_CAPET_9781)) { openDialogue(player, TheDoorDialogues(0)) core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door.asScenery()) return@on true @@ -46,4 +43,4 @@ class CraftingGuildListeners : InteractionListener { return@on true } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/asgarnia/mudskipperpoint/MudSkipperPointListeners.kt b/Server/src/main/content/region/asgarnia/mudskipperpoint/MudSkipperPointListeners.kt new file mode 100644 index 000000000..eed56b4bf --- /dev/null +++ b/Server/src/main/content/region/asgarnia/mudskipperpoint/MudSkipperPointListeners.kt @@ -0,0 +1,151 @@ +package content.region.asgarnia.mudskipperpoint + +import content.global.skill.slayer.MogreNPC +import content.region.asgarnia.mudskipperpoint.dialogue.SkippyBootDialogue +import content.region.asgarnia.mudskipperpoint.dialogue.SkippyBucketDialogue +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.impl.Projectile +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import org.rs09.consts.Vars + +@Initializable +class MudSkipperPointListeners : InteractionListener { + + companion object { + private val MESSAGES = arrayOf( + "Da boom-boom kill all da fishies!", + "I smack you good!", + "Smash stupid human!", + "Tasty human!", + "Human hit me on the head!", + "I get you!", + "Human scare all da fishies!" + ) + + private val FISHING_SPOTS = intArrayOf( + Scenery.OMINOUS_FISHING_SPOT_10087, + Scenery.OMINOUS_FISHING_SPOT_10088, + Scenery.OMINOUS_FISHING_SPOT_10089 + ) + } + + private fun handleExplosives(player: Player, explosives: Item, target: Location): Boolean{ + // Check if the player has unlocked the ability to summon Mogres + if (getVarbit(player, Vars.VARBIT_MINI_QUEST_MOGRE) != SkippyBucketDialogue.DONE_QUEST){ + sendDialogueLines(player, "Sinister as that fishing spot is, why would I want to", "explode it?") + return false + } + val distance = player.location.getDistance(target) + if (distance < 5){ + //too close + sendPlayerDialogue(player, "If this thing explodes, I think I should stand a liiiiitle further away.") + return false + } + if (distance > 14){ + // too far + sendPlayerDialogue(player, "I can't throw that far.") + return false + } + removeItem(player, explosives.id) + animate(player, Animation(385)) // Throw the vial + sendMessage(player, "You hurl the shuddering vial into the water...") + val projectile = Projectile.create(player, null, 49, 30, 20, 30, Projectile.getSpeed(player, target.location)) + projectile.endLocation = target.location + projectile.send() + + val delay = (2+ distance * 0.5).toInt() + queueScript(player, delay){ + val mogre = MogreNPC() + val xOffset = (player.location.x - target.location.x) % 2 + val yOffset = (player.location.y - target.location.y) % 2 + mogre.location = Location.create(target.location.x + xOffset, target.location.y + yOffset) + mogre.init() + mogre.moveStep() + mogre.isRespawn = false + mogre.attack(player) + registerHintIcon(player, mogre) + mogre.sendChat(MESSAGES[RandomFunction.random(MESSAGES.size)]) + if (explosives.id == Items.SUPER_FISHING_EXPLOSIVE_12633){ + impact(mogre, 15) + } + mogre.graphics(Graphics(68)) + sendMessage(player, "...and a Mogre appears!") + return@queueScript stopExecuting(player) + } + return true + } + + private fun soberSkippy(player: Player): Boolean { + if (getVarbit(player, Vars.VARBIT_MINI_QUEST_MOGRE) > SkippyBucketDialogue.DRUNK){ + sendDialogue(player, "I think he's sober enough. And I don't want to use another bucket of water.") + return false + } + else{ + if (hasAnItem(player, Items.BUCKET_OF_WATER_1929).exists()) { + openDialogue(player, SkippyBucketDialogue()) + return true + } + else{ + sendDialogue(player, "You know, I could shock him out of it if I could find some cold water...") + return false + } + } + } + + override fun defineListeners() { + + onUseWith(IntType.SCENERY, Items.FISHING_EXPLOSIVE_6664, *FISHING_SPOTS) { player, used, with -> + handleExplosives(player, used.asItem(), with.location) + } + + onUseWith(IntType.SCENERY, Items.SUPER_FISHING_EXPLOSIVE_12633, *FISHING_SPOTS) { player, used, with -> + handleExplosives(player, used.asItem(), with.location) + } + + on(FISHING_SPOTS, IntType.SCENERY, "Lure", "Bait") { player, _ -> + sendMessage(player, "Something seems to have scared all the fishes away...") + return@on true + } + + on(Scenery.SIGNPOST_10090, IntType.SCENERY, "read") { player, _ -> + setInterfaceText(player, "Mudskipper Point.", 220, 2) + setInterfaceText(player, "WARNING! BEWARE OF THE MUDSKIPPERS!", 220, 4) + openInterface(player, 220) + return@on true + } + + // For some reason the 2795 wrapper does not work for sober-up + on(intArrayOf(NPCs.SKIPPY_2796, NPCs.SKIPPY_2797, NPCs.SKIPPY_2798, NPCs.SKIPPY_2799), + IntType.NPC, "sober-up") { player, _ -> + return@on soberSkippy(player) + } + + onUseWith(IntType.NPC, Items.BUCKET_OF_WATER_1929, NPCs.SKIPPY_2795) { player, _, _ -> + return@onUseWith soberSkippy(player) + } + + onUseWith(IntType.NPC, Items.FORLORN_BOOT_6663, NPCs.SKIPPY_2795){ player, _, _ -> + openDialogue(player, SkippyBootDialogue()) + return@onUseWith true + } + } + + override fun defineDestinationOverrides() { + + // Don't run towards the fishing spots when trying to lure or bait or throw explosives + setDest(IntType.SCENERY, FISHING_SPOTS, "Lure", "Bait", "use"){ entity, _ -> + return@setDest entity.location + } + } +} diff --git a/Server/src/main/content/region/asgarnia/mudskipperpoint/dialogue/SkippyDialogue.kt b/Server/src/main/content/region/asgarnia/mudskipperpoint/dialogue/SkippyDialogue.kt new file mode 100644 index 000000000..2106d6856 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/mudskipperpoint/dialogue/SkippyDialogue.kt @@ -0,0 +1,267 @@ +package content.region.asgarnia.mudskipperpoint.dialogue + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Vars + + +@Initializable +class SkippyDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, SkippyBucketDialogue(0, getVarbit(player, Vars.VARBIT_MINI_QUEST_MOGRE))) + return true + } + + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SKIPPY_2795) + } + +} + + +class SkippyBucketDialogue(override var stage: Int = 0, val questStage: Int = 0) : DialogueFile() { + + companion object{ + // Skippy will progress through these stages as you help him + const val DRUNK = 0 + const val TENDER = 1 + const val HUNGOVER = 2 + const val DONE_QUEST = 3 + + // Dialogue branches + const val THROW_WATER = 10 + const val DRUNK_RAMBLING = 30 + const val CRAZY_PEOPLE = 40 + const val WHO_ARE_THEY = 50 + // Tea state + const val LEARN_RECIPE = 170 + const val PLAGUE_CITY_RECIPE = 180 + const val NO_TEA = 190 + // Hangover cure state + const val CURE = 220 + const val NO_CURE = 250 + const val RECIPE = 260 + const val NAME_ORIGIN = 270 + + const val ALL_DONE = 300 + } + + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.SKIPPY_2796) + when (questStage){ + DRUNK -> when (stage) { + 0 -> if(hasAnItem(player!!, Items.BUCKET_OF_WATER_1929).exists()){ + playerl(FacialExpression.THINKING, "Well, I could dump this bucket of water over him. That would sober him up a little.").also { stage = THROW_WATER } + } + else { + playerl(FacialExpression.ASKING, "Are you all right? You seem a little...incoherent.").also { stage = DRUNK_RAMBLING } + } + THROW_WATER -> showTopics( + Topic("Throw the water!", THROW_WATER+ 1, true), + Topic("I think I'll leave it for now.", END_DIALOGUE) + ) + THROW_WATER + 1 -> playerl(FacialExpression.NEUTRAL, "Hey, Skippy!").also { stage++ } + THROW_WATER + 2 -> npcl(FacialExpression.DRUNK, "What?").also { + stage = END_DIALOGUE + queueScript(player!!, 1){ animationStage : Int -> + when(animationStage){ + 1 -> { + // todo get the right animation + animate(player!!, 2283) + removeItem(player!!, Items.BUCKET_OF_WATER_1929) + addItem(player!!, Items.BUCKET_1925) + setVarbit(player!!, Vars.VARBIT_MINI_QUEST_MOGRE, TENDER, true) + return@queueScript delayScript(player!!, 1) + } + 2 -> { + openDialogue(player!!, SkippyBucketDialogue(THROW_WATER + 4)) + return@queueScript delayScript(player!!, 1) + } + 3 -> return@queueScript stopExecuting(player!!) + else -> return@queueScript delayScript(player!!, 1) + } + } } + THROW_WATER + 4 -> npcl(FacialExpression.ANGRY, "Ahhhhhhhhhhgh! That's cold! Are you trying to kill me?").also { stage++ } + THROW_WATER + 5 -> playerl(FacialExpression.NEUTRAL, "Nope. Just sober you up. Memory coming back yet?").also { stage++ } + THROW_WATER + 6 -> npcl(FacialExpression.ANGRY, "No. But I could do with a bowl of tea to warm myself up a bit. Go grab me one and we'll talk.").also { stage++ } + THROW_WATER + 7 -> playerl(FacialExpression.ASKING, "Any particular type of tea?").also { stage++ } + THROW_WATER + 8 -> npcl(FacialExpression.ANGRY, "Nettle for preference. Just make sure it's hot.").also { stage++ } + THROW_WATER + 9 -> npcl(FacialExpression.ANGRY, "And don't throw it at me!").also { stage++ } + THROW_WATER + 10 -> playerl(FacialExpression.HALF_GUILTY, "What's your problem? You're all clean now.").also { stage = END_DIALOGUE } + + + DRUNK_RAMBLING -> npcl(FacialExpression.DRUNK, "Inc'hearnt? Inc'herant! You...you...with yer fancy book- lernin' words. You'd be more than inc'herant if youd seen...").also { stage++ } + DRUNK_RAMBLING + 1 -> npcl(FacialExpression.DRUNK, "(Dramatic pause)").also { stage++ } + DRUNK_RAMBLING + 2 -> npcl(FacialExpression.DRUNK, "THEM!").also { stage++ } + DRUNK_RAMBLING + 3 -> showTopics( + Topic("I'm sure I would as well.", CRAZY_PEOPLE), + Topic("Who are (Dramatic pause) THEY?", WHO_ARE_THEY, true) + ) + + CRAZY_PEOPLE -> playerl(FacialExpression.NEUTRAL, "I'm going over here to talk to non-crazy people now.").also { stage++ } + CRAZY_PEOPLE + 1 -> npcl(FacialExpression.DRUNK, "Yeah? Yeah? Well when THEY come floppin' into your house and eat your furniture you'll be sorry!").also { stage = END_DIALOGUE } + + WHO_ARE_THEY -> playerl(FacialExpression.NEUTRAL, "Who are").also { stage++ } + WHO_ARE_THEY + 1 -> playerl(FacialExpression.NEUTRAL, "(Dramatic pause)").also { stage++ } + WHO_ARE_THEY + 2 -> playerl(FacialExpression.NEUTRAL, "THEY?").also { stage++ } + WHO_ARE_THEY + 3 -> npcl(FacialExpression.DRUNK, "They! Those bloodthirsty, flesh-tearing devils! They are the reason I'm out here every day hurlin' bottles into the sea!").also { stage++ } + WHO_ARE_THEY + 4 -> npcl(FacialExpression.DRUNK, "They are the reason I've lost everything, except the horrifying memory of what THEY look like...").also { stage++ } + WHO_ARE_THEY + 5 -> playerl(FacialExpression.ASKING, "And what do THEY look like?").also { stage++ } + WHO_ARE_THEY + 6 -> npcl(FacialExpression.DRUNK, "Mudskippers!").also { stage++ } + WHO_ARE_THEY + 7 -> playerl(FacialExpression.ASKING, "Mudskippers?").also { stage++ } + WHO_ARE_THEY + 8 -> npcl(FacialExpression.DRUNK, "Aye, Mudskippers! Those ferocious, ravening, evil, beady-eyed terrors of the deep!").also { stage++ } + WHO_ARE_THEY + 9 -> playerl(FacialExpression.SUSPICIOUS, "I...see...").also{ stage++ } + WHO_ARE_THEY + 10 -> npcl(FacialExpression.DRUNK, " I was ambushed by them way back, see. They got the drop on me...I can't remember where, somewhere around here though.").also { stage++ } + WHO_ARE_THEY + 11 -> playerl(FacialExpression.ASKING, "These would be the mudskippers, right?").also{ stage++ } + WHO_ARE_THEY + 12 -> npcl(FacialExpression.DRUNK, "Aye! The mudskippers! Huge they were! Ten feet of glistening, muddy flesh floppin' towards me with white foam flying from their gnashing fangs!").also { stage++ } + WHO_ARE_THEY + 13 -> npcl(FacialExpression.DRUNK, "I fought them up and down the beach, with the tide rising and more of them leaping towards me with cutlasses drawn!").also { stage++ } + WHO_ARE_THEY + 14 -> playerl(FacialExpression.HALF_GUILTY, "This is fascinating, but I have to be...").also{ stage++ } + WHO_ARE_THEY + 15 -> npcl(FacialExpression.DRUNK, "Shut yer' word-hole and listen! I can't remember all the details, as I'm sure they must have hit me quite hard, but the last thing I remember before it all went black...").also { stage++ } + WHO_ARE_THEY + 16 -> npcl(FacialExpression.DRUNK, "...was one of those devils rearing over me, its eyes glowin' red with the fires of hell!").also { stage++ } + WHO_ARE_THEY + 17 -> playerl(FacialExpression.ROLLING_EYES, "Fires of hell...right. I believe you.").also { stage++ } + WHO_ARE_THEY + 18 -> npcl(FacialExpression.DRUNK, "No you don't! You think I'm crazy, like all the rest! Well, if I'm crazy, how did I get these?").also { stage++ } + WHO_ARE_THEY + 19 -> playerl(FacialExpression.ASKING, "Get what?").also { stage++ } + WHO_ARE_THEY + 20 -> sendDialogue(player!!, "Skippy shows you what appears to be massive bite scars on his legs. You're no expert, but they look like... giant mudskipper bites!").also { stage++ } + WHO_ARE_THEY + 21 -> playerl(FacialExpression.ASKING, "Giant mudskipper bites! Where did you get those?").also { stage++ } + WHO_ARE_THEY + 22 -> npcl(FacialExpression.DRUNK, "I can't remember... I've been drinking to forget the horror, and all I seem to have forgotten is where it all happened...").also { stage++ } + WHO_ARE_THEY + 23 -> playerl(FacialExpression.THINKING, "Hmmm...I suppose if I sober you up you may well start to recall.").also{ stage++ } + WHO_ARE_THEY + 24 -> npcl(FacialExpression.DRUNK, "You'll have a job. I've been drinking this for a week.").also { stage++ } + WHO_ARE_THEY + 25 -> playerl(FacialExpression.ASKING, "'Captain Braindeath's Extra Strength Rum/Drain Cleaner. Now 50% more debilitating'?").also { stage++ } + WHO_ARE_THEY + 26 -> npcl(FacialExpression.DRUNK, "It's the extra sheep tranquilizers that gives it that added kick!").also { stage++ } + WHO_ARE_THEY + 27 -> playerl(FacialExpression.NEUTRAL, "Stay here and I'll be right back. Try not to move. Or go near any open flames.").also { stage = END_DIALOGUE } + } + + TENDER -> when(stage){ + 0 -> playerl(FacialExpression.HAPPY, "Hey, Skippy!").also { stage++ } + 1 -> npcl(FacialExpression.PANICKED, "Gaah! Don't drench me again!").also { stage++ } + 2 -> playerl(FacialExpression.HALF_GUILTY, "Hey! I only did that once! Try not to be such a big baby!").also { stage++ } + 3 -> npcl(FacialExpression.HALF_THINKING, "So what are you here for then?").also { stage = if (hasAnItem(player!!, Items.NETTLE_TEA_4239).exists()) 4 else NO_TEA } + 4 -> playerl(FacialExpression.HAPPY, "I've come to give you your tea.").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY, "Excellent! I was thinking I was going to freeze out here!").also { stage++ } + 6 -> sendItemDialogue(player!!, Items.NETTLE_TEA_4239, "Skippy drinks the tea and clutches his forehead in pain.").also { + stage++ + removeItem(player!!, Items.NETTLE_TEA_4239) + setVarbit(player!!, Vars.VARBIT_MINI_QUEST_MOGRE, HUNGOVER, true) + } + 7 -> npcl(FacialExpression.ANNOYED, "Ohhhhh...").also { stage++ } + 8 -> playerl(FacialExpression.ASKING, "What? What's wrong?").also { stage++ } + 9 -> npcl(FacialExpression.ANNOYED, "Not so loud...I think I have a hangover...").also { stage++ } + 10 -> playerl(FacialExpression.HALF_WORRIED, "Great...Well, I doubt you can remember anything through a hangover. What a waste of nettle tea...").also { stage++ } + 11 -> npcl(FacialExpression.FURIOUS, "Hey! A little sympathy here?").also { stage++ } + 12 -> npcl(FacialExpression.ANNOYED, "Owwowwoww... must remember not to shout...").also { stage++ } + 13 -> npcl(FacialExpression.ASKING, "Look, I do know a hangover cure. If you can get me a bucket of the stuff I think I'll be okay.").also { stage = if(getQuestStage(player!!, Quests.PLAGUE_CITY) >= 14 ) PLAGUE_CITY_RECIPE else LEARN_RECIPE } + + PLAGUE_CITY_RECIPE -> playerl(FacialExpression.HALF_THINKING, "Wait... is this cure a bucket of chocolate milk and snape grass?").also { stage++ } + PLAGUE_CITY_RECIPE + 1 -> npcl(FacialExpression.HAPPY, "Yes! That's the stuff!").also { stage++ } + PLAGUE_CITY_RECIPE + 2 -> playerl(FacialExpression.HALF_THINKING, "Ahhh. Yes, I've made some of that stuff before. I should be able to get you some, no problem.").also { stage = END_DIALOGUE } + + LEARN_RECIPE -> playerl(FacialExpression.ASKING, "So what is it you need?").also { stage++ } + LEARN_RECIPE + 1 -> npcl(FacialExpression.NEUTRAL, "A bucket of milk with chocolate ground into it, with a handful of snape grass thrown in on top.").also { stage++ } + LEARN_RECIPE + 2 -> playerl(FacialExpression.ASKING, "What? Run that past me again?").also { stage ++ } + LEARN_RECIPE + 3 -> npcl(FacialExpression.NEUTRAL, "Take a bucket of milk, a bar of chocolate and some snape grass.").also { stage++ } + LEARN_RECIPE + 4 -> npcl(FacialExpression.NEUTRAL, "Grind the chocolate with a mortar and pestle.").also { stage++ } + LEARN_RECIPE + 5 -> npcl(FacialExpression.NEUTRAL, "Add the chocolate powder to the milk, then add the snape grass.").also { stage++ } + LEARN_RECIPE + 6 -> npcl(FacialExpression.NEUTRAL, "Then bring it here and I will drink it.").also { stage++ } + LEARN_RECIPE + 7 -> npcl(FacialExpression.NEUTRAL, "The end, and we all live happily ever after. Got it?").also { stage = END_DIALOGUE } + + NO_TEA -> playerl(FacialExpression.HALF_GUILTY, "No real reason. I just thought I would check up on you is all.").also { stage++ } + NO_TEA + 1 -> npcl(FacialExpression.ANGRY, "Well, I'm still wet, still cold and still waiting on that nettle tea.").also { stage = END_DIALOGUE } + } + HUNGOVER -> when(stage){ + 0 -> playerl(FacialExpression.HAPPY, "Hey, Skippy!").also{ stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "Egad! Don't you know not to shout around a guy with a hangover?").also { stage++ } + 2 -> npcl(FacialExpression.HALF_GUILTY, "Ahhhhhg...No more shouting for me...").also { stage++ } + 3 -> npcl(FacialExpression.ASKING, "What is it anyway?").also { stage = if (hasAnItem(player!!, Items.HANGOVER_CURE_1504).exists()) CURE else NO_CURE } + + CURE -> playerl(FacialExpression.HAPPY, "Well Skippy, you will no doubt be glad to hear that I got you your hangover cure!").also { stage++ } + CURE + 1 ->npcl(FacialExpression.HAPPY, "Gimme!").also { stage++ } + CURE + 2 -> sendDialogue(player!!, "Skippy chugs the hangover cure... very impressive.").also { + removeItem(player!!, Items.HANGOVER_CURE_1504) + stage++ + setVarbit(player!!, 1344, DONE_QUEST, true) + } + CURE + 3 -> npcl(FacialExpression.HAPPY, "Ahhhhhhhhhhhhhhh...").also { stage ++ } + CURE + 4 -> npcl(FacialExpression.HAPPY, "Much better...").also { stage ++ } + CURE + 5 -> playerl(FacialExpression.ASKING, "Feeling better?").also { stage ++ } + CURE + 6 -> npcl(FacialExpression.HAPPY, "Considerably.").also { stage ++ } + CURE + 7 -> playerl(FacialExpression.ASKING, "Then tell me where the mudskippers are!").also { stage++ } + CURE + 8 -> npcl(FacialExpression.NEUTRAL, "I wish you wouldn't go looking for them. Those vicious killers will tear you apart.").also { stage ++ } + CURE + 9 -> npcl(FacialExpression.HALF_THINKING, "It's all becoming clear to me now...").also { stage ++ } + CURE + 10 -> npcl(FacialExpression.HAPPY, "I was fishing using a Fishing Explosive...").also { stage ++ } + CURE + 11 -> playerl(FacialExpression.ASKING, "A Fishing Explosive?").also { stage++ } + CURE + 12 -> npcl(FacialExpression.NEUTRAL, "Well, Slayer Masters sell these highly volatile potions for killing underwater creatures.").also { stage ++ } + CURE + 13 -> npcl(FacialExpression.NEUTRAL, "If you don't feel like lobbing a net about all day you can use them to fish with...").also { stage ++ } + CURE + 14 -> npcl(FacialExpression.NEUTRAL, "But this time I was startled by what I thought was a giant mudskipper.").also { stage ++ } + CURE + 15 -> npcl(FacialExpression.NEUTRAL, "What it was, in fact, was a...").also { stage ++ } + CURE + 16 -> npcl(FacialExpression.NEUTRAL, "Dramatic Pause...").also { stage ++ } + CURE + 17 -> npcl(FacialExpression.PANICKED, "A Mogre!").also { stage ++ } + CURE + 18 -> playerl(FacialExpression.ASKING, "What exactly is a Mogre?").also { stage++ } + CURE + 19 -> npcl(FacialExpression.NEUTRAL, "A Mogre is a type of Ogre that spends most of its time underwater.").also { stage ++ } + CURE + 20 -> npcl(FacialExpression.NEUTRAL, "They hunt giant mudskippers by wearing their skins and swimming close until they can attack them.").also { stage ++ } + CURE + 21 -> npcl(FacialExpression.NEUTRAL, "When I used the Fishing Explosive I scared off all the fish, and so the Mogre got out of the water to express its extreme displeasure.").also { stage ++ } + CURE + 22 -> npcl(FacialExpression.NEUTRAL, "With an iron mace.").also { stage ++ } + CURE + 23 -> playerl(FacialExpression.ASKING, "I take it the head injury is responsible for the staggering and yelling?").also { stage++ } + CURE + 24 -> npcl(FacialExpression.NEUTRAL, "No, no.").also { stage ++ } + CURE + 25 -> npcl(FacialExpression.NEUTRAL, "My addiction to almost-lethal alcohol did that.").also { stage ++ } + CURE + 26 -> npcl(FacialExpression.NEUTRAL, "But if you are set on finding these Mogres just head south from here until you find Mudskipper Point.").also { stage ++ } + CURE + 27 -> playerl(FacialExpression.ASKING, "The mudskipper-eating monsters are to be found at Mudskipper point?").also { stage++ } + CURE + 28 -> playerl(FacialExpression.ROLLING_EYES, "Shock!").also { stage++ } + CURE + 29 -> playerl(FacialExpression.NEUTRAL, "Thanks. I'll be careful.").also { stage = END_DIALOGUE } + + NO_CURE -> playerl(FacialExpression.HALF_GUILTY, "I just came back to ask you something.").also { stage++ } + NO_CURE + 1 -> npcl(FacialExpression.ANNOYED, "What?").also { stage++ } + NO_CURE + 2 -> showTopics( + Topic("How do I make that hangover cure again?", RECIPE), + Topic("Why do they call you 'Skippy'?", NAME_ORIGIN) + ) + + RECIPE -> npcl(FacialExpression.ANGRY, "Give me strength...Here's what you do. Pay attention!").also { stage++ } + RECIPE + 1 -> npcl(FacialExpression.ANGRY, "You take a bucket of milk, a bar of chocolate and some snape grass.").also { stage++ } + RECIPE + 2 -> npcl(FacialExpression.ANGRY, "Grind the chocolate with a pestle and mortar.").also { stage++ } + RECIPE + 3 -> npcl(FacialExpression.ANGRY, "Add the chocolate powder to the milk, then add the snape grass.").also { stage++ } + RECIPE + 4 -> npcl(FacialExpression.ANGRY, "Then bring it here and I will drink it.").also { stage++ } + RECIPE + 5 -> npcl(FacialExpression.ANGRY, "Are you likely to remember that or should I go get some crayons and draw you a picture?").also { stage++ } + RECIPE + 6 -> playerl(FacialExpression.ANGRY, "Hey! I remember it now, ok! See you in a bit.").also { stage = END_DIALOGUE } + + NAME_ORIGIN -> npcl(FacialExpression.THINKING, "I think it may have something to do with my near- constant raving about mudskippers.").also { stage++ } + NAME_ORIGIN + 1 -> npcl(FacialExpression.THINKING, "That or it's something to do with that time with the dress and the field full of daisies...").also { stage = END_DIALOGUE } + } + DONE_QUEST -> when(stage){ + 0 -> playerl(FacialExpression.NEUTRAL, "Hey, Skippy.").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY, "Hey you!").also { stage++ } + 2 -> playerl(FacialExpression.ASKING, "How do I annoy the Mogres again?").also { stage++ } + 3 -> npcl(FacialExpression.HAPPY, "Go south to Mudskipper Point and lob a Fishing Explosive into the sea. You can grab them from the Slayer masters.").also { stage++ } + 4 -> playerl(FacialExpression.ASKING, "Thanks! So, what are you going to do now?").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY, "Well, I was planning on continuing my hobby of wandering up and down this bit of coastline, bellowing random threats and throwing bottles.").also { stage++ } + 6 -> npcl(FacialExpression.ASKING, "And you?").also { stage++ } + 7 -> playerl(FacialExpression.NEUTRAL, "I was planning on wandering up and down the landscape, bugging people to see if they had mindblowingly dangerous quests for me to undertake.").also { stage++ } + 8 -> npcl(FacialExpression.HAPPY, "Well, good luck with that!").also { stage++ } + 9 -> playerl(FacialExpression.HAPPY, "You too!").also { stage++ } + 10 -> npcl(FacialExpression.ROLLING_EYES, "Weirdo...").also { stage++ } + 11 -> playerl(FacialExpression.ROLLING_EYES, "Loony..").also { stage = END_DIALOGUE } + } + } + } +} + +class SkippyBootDialogue: DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.SKIPPY_2796) + val expression = if (getVarbit(player!!, Vars.VARBIT_MINI_QUEST_MOGRE) > SkippyBucketDialogue.DRUNK) FacialExpression.ROLLING_EYES else FacialExpression.DRUNK + npcl(expression, "Thanks! Now I have two right boots!").also { stage = END_DIALOGUE } + } + +} diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/AhabDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/AhabDialogue.java index 493bc1973..5f5446083 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/AhabDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/AhabDialogue.java @@ -5,6 +5,7 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; +import content.data.Quests; /** * Represents the dialogue plugin used for the npc Ahav. @@ -117,7 +118,7 @@ public final class AhabDialogue extends DialoguePlugin { stage = 28; break; case 28: - if (player.getQuestRepository().isComplete("Dragon Slayer")) { + if (player.getQuestRepository().isComplete(Quests.DRAGON_SLAYER)) { player("Well, I do have a ship that I'm not using.", "It's the Lady Lumbridge."); stage = 29; } else { diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/GerrantDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/GerrantDialogue.java deleted file mode 100644 index 1d00b97d0..000000000 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/GerrantDialogue.java +++ /dev/null @@ -1,82 +0,0 @@ -package content.region.asgarnia.portsarim.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used for the gerrant npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GerrantDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GerrantDialogue} {@code Object}. - */ - public GerrantDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GerrantDialogue} {@code Object}. - * @param player the player. - */ - public GerrantDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GerrantDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Welcome! You can buy fishing equipment at my store.", "We'll also buy anything you catch off you."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Choose an option:", "Let's see what you've got then.", "Sorry, I'm not interested."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HAPPY, "Let's see what you've got then."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I'm not interested."); - stage = 20; - break; - - } - break; - case 10: - end(); - npc.openShop(player); - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 558 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/KlarenseDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/KlarenseDialogue.java index e09ea0149..6c5e14b1e 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/KlarenseDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/KlarenseDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue used for the klarense npc. @@ -51,7 +52,7 @@ public final class KlarenseDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Dragon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER); if (args.length > 1) { interpreter.sendDialogues(npc, FacialExpression.ANGRY, "Hey, stay off my ship! That's private property!"); stage = 0; diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java index 87842b4c6..e8c921159 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java @@ -41,11 +41,16 @@ public final class MonkOfEntranaDialogue extends DialoguePlugin { return new MonkOfEntranaDialogue(player); } + public void sail(Player player, Ships ship) { + ship.sail(player); + playJingle(player, 172); + } + @Override public boolean open(Object... args) { npc = (NPC) args[0]; if (npc.getId() == 2730 || npc.getId() == 658 || npc.getId() == 2731) { - interpreter.sendDialogues(npc, null, "Do you wish to leave holy entrana?"); + interpreter.sendDialogues(npc, null, "Do you wish to leave holy Entrana?"); stage = 500; return true; } @@ -98,7 +103,7 @@ public final class MonkOfEntranaDialogue extends DialoguePlugin { stage = 25; break; case 23: - interpreter.sendDialogues(npc, null, "Do not try and decieve us again. Come back when you", "have liad down your Zamorakian instruments of death."); + interpreter.sendDialogues(npc, null, "Do not try to deceive us again. Come back when you", "have laid down your Zamorakian instruments of death."); stage = 24; break; case 24: @@ -106,8 +111,7 @@ public final class MonkOfEntranaDialogue extends DialoguePlugin { break; case 25: end(); - Ships.PORT_SARIM_TO_ENTRANA.sail(player); - playJingle(player, 172); + sail(player, Ships.PORT_SARIM_TO_ENTRANA); if (!player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).isComplete(0, 14)) { player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).updateTask(player, 0, 14, true); } @@ -133,9 +137,7 @@ public final class MonkOfEntranaDialogue extends DialoguePlugin { stage = 511; break; case 511: - end(); - Ships.ENTRANA_TO_PORT_SARIM.sail(player); - playJingle(player, 172); + sail(player, Ships.ENTRANA_TO_PORT_SARIM); break; case 520: end(); diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/RedbeardFrankDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/RedbeardFrankDialogue.java index 214b5f9f4..e3a46166c 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/RedbeardFrankDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/RedbeardFrankDialogue.java @@ -12,6 +12,7 @@ import core.plugin.Initializable; import core.game.node.item.Item; import static core.tools.DialogueConstKt.END_DIALOGUE; +import content.data.Quests; /** * Represents the dialogue to handle Rebeard Frank. @@ -59,7 +60,7 @@ public class RedbeardFrankDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Pirate's Treasure"); + quest = player.getQuestRepository().getQuest(Quests.PIRATES_TREASURE); npc("Arr, Matey!"); stage = 0; replacementReward = AchievementDiary.canReplaceReward(player, DiaryType.FALADOR, level); diff --git a/Server/src/main/content/region/asgarnia/portsarim/handlers/PortSarimPlugin.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/PortSarimPlugin.java index 47df99e03..8746b7e93 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/handlers/PortSarimPlugin.java +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/PortSarimPlugin.java @@ -15,6 +15,7 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import content.data.Quests; /** * Represents the port sarim plugin. @@ -72,7 +73,7 @@ public final class PortSarimPlugin extends OptionHandler { player.getDialogueInterpreter().open(238284); break; case "attack": - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) != 20) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 20) { player.getPacketDispatch().sendMessage("The goblin is already in prison. You have no reason to attack him."); } else { player.getProperties().getCombatPulse().attack(node); diff --git a/Server/src/main/content/region/asgarnia/portsarim/handlers/PortsObjectPlugin.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/PortsObjectPlugin.java index 206b3860c..7f45a94f4 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/handlers/PortsObjectPlugin.java +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/PortsObjectPlugin.java @@ -11,6 +11,7 @@ import core.game.world.repository.Repository; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the option handler for port sarim. @@ -206,7 +207,7 @@ public class PortsObjectPlugin extends OptionHandler { player.getPacketDispatch().sendMessage("You disembark the ship."); break; case 2593: - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) == 100) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) == 100) { player.getDialogueInterpreter().open(744, Repository.findNPC(744), true);// lady // lumbridge. return true; diff --git a/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasure.java b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasure.java index ae22be400..65ab84c0e 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasure.java +++ b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasure.java @@ -7,6 +7,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the pirates treasure quest. @@ -50,7 +51,7 @@ public final class PiratesTreasure extends Quest { * Constructs a new {@Code PiratesTreasure} {@Code Object} */ public PiratesTreasure() { - super("Pirate's Treasure", 23, 22, 2, 71, 0, 1, 4); + super(Quests.PIRATES_TREASURE, 23, 22, 2, 71, 0, 1, 4); } @Override diff --git a/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasurePlugin.java b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasurePlugin.java index a4f64a4dc..ce41de603 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasurePlugin.java +++ b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasurePlugin.java @@ -15,6 +15,7 @@ import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the pirates treasure plugin. @@ -92,7 +93,7 @@ public final class PiratesTreasurePlugin extends OptionHandler { @Override public void run(Player player) { - final Quest quest = player.getQuestRepository().getQuest("Pirate's Treasure"); + final Quest quest = player.getQuestRepository().getQuest(Quests.PIRATES_TREASURE); player.lock(2); if (quest.getStage(player) == 20) { if (player.getSavedData().getQuestData().isGardenerAttack()) { diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/AnjaDialogue.kt b/Server/src/main/content/region/asgarnia/rimmington/dialogue/AnjaDialogue.kt new file mode 100644 index 000000000..317f96478 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/dialogue/AnjaDialogue.kt @@ -0,0 +1,87 @@ +package content.region.asgarnia.rimmington.dialogue + +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC +import core.game.world.map.RegionManager +import core.tools.RandomFunction +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Handles Anja's dialogue. + */ +@Initializable +class AnjaDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.NEUTRAL, "Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.ASKING, "Hello @g[sir,madam]. What are you doing in my house?").also { stage++ } + 1 -> options("I'm just wandering around.", "I was hoping you'd give me some free stuff.", "I've come to kill you.").also { stage++ } + 2 -> when (buttonId) { + 1 -> playerl(FacialExpression.NEUTRAL, "I'm just wondering around.").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "I was hoping you'd give me some free stuff.").also { stage = 20 } + 3 -> playerl(FacialExpression.ANGRY_WITH_SMILE, "I've come to kill you.").also { stage = 30 } + } + + 10 -> npcl(FacialExpression.NEUTRAL, "Oh dear, are you lost?").also { stage++ } + 11 -> options("Yes, I'm lost.", "No, I know where I am.").also { stage++ } + 12 -> when (buttonId) { + 1 -> playerl(FacialExpression.NEUTRAL, "Yes, I'm lost.").also { stage = 13 } + 2 -> playerl(FacialExpression.NEUTRAL, "No, I know where I am.").also { stage = 15 } + } + + 13 -> npcl(FacialExpression.FRIENDLY, "Okay, just walk north-east when you leave this house and soon you'll reach the big city of Falador.").also { stage++ } + 14 -> playerl(FacialExpression.FRIENDLY, "Thanks a lot.").also { stage = END_DIALOGUE } + 15 -> npcl(FacialExpression.ASKING, "Oh? Well, would you mind wandering somewhere else?").also { stage++ } + 16 -> npcl(FacialExpression.NEUTRAL, "This is my house.").also { stage++ } + 17 -> playerl(FacialExpression.NEUTRAL, "Meh!").also { stage = END_DIALOGUE } + + 20 -> { + val dialogues = arrayOf("Do you REALLY need it?", "I don't have much on me...", "I don't know...") + npcl(FacialExpression.NEUTRAL, dialogues[RandomFunction.random(0, 3)]) + stage++ + } + + 21 -> playerl(FacialExpression.ASKING, "I promise I'll stop bothering you!").also { stage++ } + 22 -> playerl(FacialExpression.ASKING, "Pleeease!").also { stage++ } + 23 -> playerl(FacialExpression.ASKING, "Pwetty pleathe wiv thugar on top!").also { stage++ } + 24 -> { + npcl(FacialExpression.NEUTRAL, "Oh, alright. Here you go.") + addItemOrDrop(player, Items.COINS_995, RandomFunction.random(1, 3)) + stage = END_DIALOGUE + } + + 30 -> { + stage = END_DIALOGUE + close() + + val hengel = findLocalNPC(npc, NPCs.HENGEL_2683) + if(hengel != null) + sendChat(hengel, "Aaaaarrgh!") + + sendChat(npc, "Eeeek!") + } + } + + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return AnjaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ANJA_2684) + } +} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/AnjaDialoguePlugin.java b/Server/src/main/content/region/asgarnia/rimmington/dialogue/AnjaDialoguePlugin.java deleted file mode 100644 index 36ed82c7b..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/dialogue/AnjaDialoguePlugin.java +++ /dev/null @@ -1,144 +0,0 @@ -package content.region.asgarnia.rimmington.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.map.RegionManager; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the dialogue plugin used for the anja npc. - * @author jamix77 - */ -@Initializable -public final class AnjaDialoguePlugin extends DialoguePlugin { - - /** - * - * Constructs a new @{Code AnjaDialoguePlugin} object. - */ - public AnjaDialoguePlugin() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code AjjatDialoguePlugin} {@code Object}. - * @param player the player. - */ - public AnjaDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AnjaDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - player("Hello."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("Hello sir. What are you doing in my house?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "I'm just wandering around.", "I was hoping you'd give me some free stuff.", "I've come to kill you."); - stage = 2; - break; - case 2: - if (buttonId == 1) { - player("I'm just wondering around."); - stage = 3; - } else if (buttonId == 2) { - player("I was hoping you'd give me some free stuff."); - stage = 10; - } else if (buttonId == 3) { - player("I've come to kill you."); - stage = 13; - } - break; - case 3: - npc("Oh dear are you lost?"); - stage++; - break; - case 4: - interpreter.sendOptions("Select an Option.", "Yes, I'm lost.", "No, I know where I am."); - stage++; - break; - case 5: - switch (buttonId) { - case 1: - player("Yes, I'm lost."); - stage =6; - break; - case 2: - player("No I know where I am."); - stage = 8; - break; - } - break; - case 6: - npc("Okay, just walk north-east when you leave this house,","and soon you'll reach the big city of Falador."); - stage++; - break; - case 7: - player("Thanks a lot."); - stage = 605; - break; - case 8: - npc("Oh? Well, would you mind wandering somewhere else?", "This is my house."); - stage++; - break; - case 9: - player("Meh!"); - stage = 605; - break; - case 10: - String[] dialogues = {"Do you REALLY need it","I don't have much on me...", "I don't know..."}; - npc(dialogues[RandomFunction.random(0, 2)]); - stage++; - break; - case 11: - interpreter.sendDialogues(player, FacialExpression.ASKING, "I promise I'll stop bothering you!", "Pleeease!","Pwetty pleathe wiv thugar on top!"); - stage++; - break; - case 12: - npc("Oh, alright. Here you go."); - player.getInventory().add(new Item(995,RandomFunction.random(1, 3))); - stage = 605; - break; - case 13: - npc.sendChat("Eeeek!"); - for (NPC npc1 : RegionManager.getLocalNpcs(player)) { - if (npc1.getName().equalsIgnoreCase("Hengel")) { - npc1.sendChat("Aaaaarrgh!"); - break; - } - } - end(); - break; - case 605: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2684 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/BrianArcheryDialogue.java b/Server/src/main/content/region/asgarnia/rimmington/dialogue/BrianArcheryDialogue.java deleted file mode 100644 index faa29da41..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/dialogue/BrianArcheryDialogue.java +++ /dev/null @@ -1,81 +0,0 @@ -package content.region.asgarnia.rimmington.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used to handle the brain archery npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BrianArcheryDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BrianArcheryDialogue} {@code Object}. - */ - public BrianArcheryDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BrianArcheryDialogue} {@code Object}. - * @param player the player. - */ - public BrianArcheryDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BrianArcheryDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Would you like to buy some archery equipment?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "No thanks, I've got all the archery equipment I need.", "Let's see what you've got, then."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks, I've got all the archery equipment I need."); - stage = 10; - break; - case 2: - end(); - npc.openShop(player); - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Okay. Fare well on your travels."); - stage = 11; - break; - case 11: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1860 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/BrianArcheryDialogue.kt b/Server/src/main/content/region/asgarnia/rimmington/dialogue/BrianArcheryDialogue.kt new file mode 100644 index 000000000..0598231cc --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/dialogue/BrianArcheryDialogue.kt @@ -0,0 +1,47 @@ +package content.region.asgarnia.rimmington.dialogue + +import core.api.openNpcShop +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * Handles the BrianArchery's dialogue. + */ +@Initializable +class BrianArcheryDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HAPPY, "Would you like to buy some archery equipment?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("Let's see what you've got, then.", "No thanks, I've got all the archery equipment I need.").also { stage++ } + 1 -> when (buttonId) { + 1 -> end().also { openNpcShop(player, npc.id) } + 2 -> playerl(FacialExpression.HALF_GUILTY, "No thanks, I've got all the archery equipment I need.").also { stage = 10 } + } + + 10 -> { + npcl(FacialExpression.FRIENDLY, "Okay. Fare well on your travels.") + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BrianArcheryDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BRIAN_1860) + } +} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/HengelDialogue.kt b/Server/src/main/content/region/asgarnia/rimmington/dialogue/HengelDialogue.kt new file mode 100644 index 000000000..3bae6f108 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/dialogue/HengelDialogue.kt @@ -0,0 +1,67 @@ +package content.region.asgarnia.rimmington.dialogue + +import core.api.findLocalNPC +import core.api.sendChat +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC +import core.game.world.map.RegionManager +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * Handles the Hengel's dialogue. + */ +@Initializable +class HengelDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + playerl(FacialExpression.NEUTRAL, "Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.ASKING, "What are you doing here?").also { stage++ } + 1 -> options("I'm just wandering around.", "I was hoping you'd give me some free stuff.", "I've come to kill you.").also { stage++ } + 2 -> when (buttonId) { + 1 -> playerl(FacialExpression.NEUTRAL, "I'm just wondering around.").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "I was hoping you'd give me some free stuff.").also { stage = 20 } + 3 -> playerl(FacialExpression.ANGRY_WITH_SMILE, "I've come to kill you.").also { stage = 30 } + } + + 10 -> npcl(FacialExpression.ASKING, "You do realise you're wandering around in my house?").also { stage++ } + 11 -> playerl(FacialExpression.NEUTRAL, "Yep.").also { stage++ } + 12 -> npcl(FacialExpression.ANNOYED, "Well please get out!").also { stage++ } + 13 -> playerl(FacialExpression.NEUTRAL, "Sheesh, keep your wig on!").also { stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.ANNOYED, "No, I jolly well wouldn't!").also { stage++ } + 21 -> npcl(FacialExpression.ANNOYED, "Get out of my house!").also { stage++ } + 22 -> playerl(FacialExpression.NEUTRAL, "Meanie!").also { stage = END_DIALOGUE } + + 30 -> { + stage = END_DIALOGUE + close() + + val anja = findLocalNPC(npc, NPCs.ANJA_2684) + if(anja != null) + sendChat(anja, "Eeeek!") + + sendChat(npc, "Aaaaarrgh!") + } + } + + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return HengelDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HENGEL_2683) + } +} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/HengelDialoguePlugin.java b/Server/src/main/content/region/asgarnia/rimmington/dialogue/HengelDialoguePlugin.java deleted file mode 100644 index dcdc489bc..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/dialogue/HengelDialoguePlugin.java +++ /dev/null @@ -1,115 +0,0 @@ -package content.region.asgarnia.rimmington.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.world.map.RegionManager; -import core.plugin.Initializable; - -/** - * Represents the dialogue plugin used for the hengel npc. - * @author jamix77 - */ -@Initializable -public final class HengelDialoguePlugin extends DialoguePlugin { - - /** - * - * Constructs a new @{Code HengelDialoguePlugin} object. - */ - public HengelDialoguePlugin() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code AjjatDialoguePlugin} {@code Object}. - * @param player the player. - */ - public HengelDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HengelDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - player("Hello."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("What are you doing here?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "I'm just wandering around.", "I was hoping you'd give me some free stuff.", "I've come to kill you."); - stage = 2; - break; - case 2: - if (buttonId == 1) { - player("I'm just wondering around."); - stage = 3; - } else if (buttonId == 2) { - player("I was hoping you'd give me some free stuff."); - stage = 7; - } else if (buttonId == 3) { - player("I've come to kill you."); - stage = 9; - } - break; - case 3: - npc("You do realise you're wandering around in my house?"); - stage++; - break; - case 4: - player("Yep."); - stage++; - break; - case 5: - npc("Well please get out!"); - stage++; - break; - case 6: - player("Sheesh, keep your wig on!"); - stage = 605; - break; - case 7: - npc("No, I jolly well wouldn't!","Get out of my house"); - stage++; - break; - case 8: - player("Meanie!"); - stage = 605; - break; - case 9: - npc.sendChat("Aaaaarrgh!"); - for (NPC npc1 : RegionManager.getLocalNpcs(player)) { - if (npc1.getName().equalsIgnoreCase("anja")) { - npc1.sendChat("Eeeek!"); - break; - } - } - end(); - break; - case 605: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2683 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/HettyDialogue.java b/Server/src/main/content/region/asgarnia/rimmington/dialogue/HettyDialogue.java deleted file mode 100644 index 8c32a718f..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/dialogue/HettyDialogue.java +++ /dev/null @@ -1,226 +0,0 @@ -package content.region.asgarnia.rimmington.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the dialogue plugin used for the hetty npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HettyDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code HettyDialogue} {@code Object}. - */ - public HettyDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HettyDialogue} {@code Object}. - * @param player the player. - */ - public HettyDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HettyDialogue(player); - } - - @Override - public boolean open(Object... args) { - Quest quest = player.getQuestRepository().getQuest("Witch's Potion"); - if (quest.isCompleted(player)) { - interpreter.sendDialogues(307, FacialExpression.ASKING, "How's your magic coming along?"); - stage = 0; - } - switch (quest.getStage(player)) { - case 0: - interpreter.sendDialogues(307, FacialExpression.NEUTRAL, "What could you want with an old woman like me?"); - stage = 11; - break; - case 20: - interpreter.sendDialogues(307, FacialExpression.HAPPY, "So have you found the things for the potion?"); - stage = 100; - break; - case 40: - if (args.length == 2) { - interpreter.sendDialogue("You drink from the cauldron, it tastes horrible! You feel yourself", "imbued with power."); - stage = 41; - } else { - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "Well are you going to drink the potion or not?"); - stage = 500; - } - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - Quest quest = player.getQuestRepository().getQuest("Witch's Potion"); - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm practicing and slowly getting better."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "Good, good."); - stage = 2; - break; - case 2: - end(); - break; - case 11: - interpreter.sendOptions("Select an Option", "I am in search of a quest.", "I've heard that you are a witch."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I am in search of a quest."); - stage = 13; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I've heard that you are a witch."); - stage = 20; - break; - } - break; - case 13: - interpreter.sendDialogues(307, FacialExpression.HAPPY, "Hmmm... Maybe I can think of something for you."); - stage = 14; - break; - case 14: - interpreter.sendDialogues(307, FacialExpression.HAPPY, "Would you like to become more proficient in the dark", "arts?"); - stage = 15; - break; - case 15: - interpreter.sendOptions("Select an Option", "Yes help me become one with my darker side.", "No I have my principles and honour."); - stage = 16; - break; - case 16: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes help me become one with my darker side."); - stage = 30; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No I have my principles and honour."); - stage = 17; - break; - } - break; - case 17: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "Suit yourself, but you're missing out."); - stage = 18; - break; - case 18: - end(); - break; - case 20: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "Yes it does seem to be getting fairly common", "knowledge."); - stage = 21; - break; - case 21: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "I fear I may get a visit from the witch hunter of", "Falador before long."); - stage = 22; - break; - case 22: - end(); - break; - case 30: - interpreter.sendDialogues(307, FacialExpression.NEUTRAL, "Ok I'm going to make a potion to help bring out your", "darker self."); - stage = 31; - break; - case 31: - interpreter.sendDialogues(307, FacialExpression.NEUTRAL, "You will need certain ingredients."); - stage = 32; - break; - case 32: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "What do I need?"); - stage = 33; - break; - case 33: - interpreter.sendDialogues(307, FacialExpression.NEUTRAL, "You need an eye of newt, a rat's tail, and onion... Oh", "and a piece of burnt meat."); - stage = 34; - break; - case 34: - interpreter.sendDialogues(player, FacialExpression.HAPPY, "Great, I'll go get them."); - stage = 35; - break; - case 35: - quest.start(player); - quest.setStage(player, 20); - end(); - break; - case 100: - // Her:Well I can't make the potion without them! - // Remember.../You - // need an eye of newt, a rat's tail, an onion, and a/piece of - // burnt - // meat. Off you go dear! Me:end(); - if (!player.getInventory().containItems(1957, 300, 2146, 221)) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm afraid I don't have all of them yet."); - stage = 101; - } else { - interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes I have everything!"); - stage = 110; - } - break; - case 110: - interpreter.sendDialogues(307, FacialExpression.HAPPY, "Excellent, can I have them then?"); - stage = 111; - break; - case 111: - interpreter.sendDialogue("You pass the ingredients to Hetty and she puts them all into her", "cauldron. Hetty closes her eyes and begins to chant. The cauldron", "bubbles mysteriously."); - stage = 112; - break; - case 112: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Well, is it ready?"); - stage = 113; - break; - case 113: - if (player.getInventory().remove(new Item(1957), new Item(300), new Item(2146), new Item(221))) { - quest.setStage(player, 40); - interpreter.sendDialogues(307, FacialExpression.HAPPY, "Ok, now drink from the cauldron."); - stage = 114; - } - break; - case 114: - end(); - break; - case 101: - interpreter.sendDialogues(307, FacialExpression.NEUTRAL, "Well I can't make the potion without them! Remember...", "You need an eye of newt, a rat's tail, an onion, and a", "piece of burnt meat. Off you go dear!"); - stage = 102; - break; - case 102: - end(); - break; - case 500: - end(); - break; - case 41: - end(); - quest.finish(player); - player.getQuestRepository().syncronizeTab(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 307 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/HettyDialogue.kt b/Server/src/main/content/region/asgarnia/rimmington/dialogue/HettyDialogue.kt new file mode 100644 index 000000000..39a15588a --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/dialogue/HettyDialogue.kt @@ -0,0 +1,83 @@ +package content.region.asgarnia.rimmington.dialogue + +import content.data.Quests +import content.region.asgarnia.rimmington.quest.witchpotion.HettyWitchsPotionDialogue +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * Handles the Hetty's dialogue. + */ +@Initializable +class HettyDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + val questStage = getQuestStage(player, Quests.WITCHS_POTION) + + when(questStage) { + 0 -> npcl(FacialExpression.NEUTRAL, "What could you want with an old woman like me?").also { stage = 0 } + in 1..99 -> openDialogue(player, HettyWitchsPotionDialogue(questStage), npc) + else -> npcl(FacialExpression.ASKING, "How's your magic coming along?").also { stage = 30 } + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("I am in search of a quest.", "I've heard that you are a witch.").also { stage++ } + 1 -> when (buttonId) { + 1 -> playerl(FacialExpression.NEUTRAL, "I am in search of a quest.").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "I've heard that you are a witch.").also { stage = 20 } + } + + 10 -> npcl(FacialExpression.HAPPY, "Hmmm... Maybe I can think of something for you.").also { stage++ } + 11 -> npcl(FacialExpression.HAPPY, "Would you like to become more proficient in the dark arts?").also { stage++ } + 12 -> options("Yes help me become one with my darker side.", "No I have my principles and honour.", "What, you mean improve my magic?").also { stage++ } + 13 -> when (buttonId) { + 1 -> playerl(FacialExpression.HAPPY, "Yes help me become one with my darker side.").also { stage = 14 } + 2 -> playerl(FacialExpression.NEUTRAL, "No I have my principles and honour.").also { stage = 15 } + 3 -> playerl(FacialExpression.ASKING, "What, you mean improve my magic?").also { stage = 40 } + } + + 14 -> openDialogue(player, HettyWitchsPotionDialogue(0), npc) + 15 -> npcl(FacialExpression.HALF_GUILTY, "Suit yourself, but you're missing out.").also { stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.HALF_GUILTY, "Yes it does seem to be getting fairly common knowledge").also { stage++ } + 21 -> npcl(FacialExpression.HALF_GUILTY, "I fear I may get a visit from the witch hunter of Falador before long.").also { stage = END_DIALOGUE } + + 30 -> playerl(FacialExpression.HALF_GUILTY, "I'm practicing and slowly getting better.").also { stage++ } + 31 -> npcl(FacialExpression.HALF_GUILTY, "Good, good.").also { stage = END_DIALOGUE } + + 40 -> sendDialogue("The witch sighs.").also { stage++ } + 41 -> npcl(FacialExpression.ANNOYED, "Yes, improve your magic...").also { stage++ } + 42 -> npcl(FacialExpression.ASKING, "Do you have no sense of drama?").also { stage++ } + 43 -> options("Yes I'd like to improve my magic.", "No I'm not interested.", "Show me the mysteries of the dark arts...").also { stage++ } + 44 -> when(buttonId) { + // TODO: Find authentic source for dialogue option + 1 -> playerl(FacialExpression.NEUTRAL, "Yes I'd like to improve my magic.").also { stage = 14 } + // TODO: Find authentic source for dialogue option + 2 -> playerl(FacialExpression.NEUTRAL, "No I'm not interested.").also { stage = 15 } + 3 -> playerl(FacialExpression.NEUTRAL, "Show me the mysteries of the dark arts...").also { stage++ } + } + 45 -> sendDialogue("The witch smiles mysteriously.").also { stage = 14 } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return HettyDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HETTY_307) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.java b/Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.java deleted file mode 100644 index 4197fe202..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.java +++ /dev/null @@ -1,74 +0,0 @@ -package content.region.asgarnia.rimmington.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the RimmingtonShopKeeperDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class RimmingtonShopKeeperDialogue extends DialoguePlugin { - - public RimmingtonShopKeeperDialogue() { - - } - - public RimmingtonShopKeeperDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 531, 530 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, please. What are you selling?", "How should I use your shop?", "No, thanks."); - stage = 1; - break; - case 1: - - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "I'm glad you ask! You can buy as many of the items", "stocked as you wish. You can also sell most items to the", "shop."); - stage = 10; - break; - case 3: - end(); - break; - } - - break; - case 10: - end(); - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new RimmingtonShopKeeperDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Can I help you at all?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.kt b/Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.kt new file mode 100644 index 000000000..1c637f5a5 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.kt @@ -0,0 +1,44 @@ +package content.region.asgarnia.rimmington.dialogue + +import core.api.openNpcShop +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * Handles the RimmingtonShopKeeper dialogue. + */ +@Initializable +class RimmingtonShopKeeperDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HAPPY, "Can I help you at all?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("Yes, please. What are you selling?", "How should I use your shop?", "No, thanks.").also { stage++ } + 1 -> when (buttonId) { + 1 -> end().also { openNpcShop(player, npc.id) } + 2 -> npcl(FacialExpression.HAPPY, "I'm glad you ask! You can buy as many of the items stocked as you wish. You can also sell most items to the shop.").also { stage = END_DIALOGUE } + 3 -> end() + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return RimmingtonShopKeeperDialogue(player) + } + + override fun getIds(): IntArray = intArrayOf( + NPCs.SHOPKEEPER_530, + NPCs.SHOP_ASSISTANT_531 + ) +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/RommikDialogue.kt b/Server/src/main/content/region/asgarnia/rimmington/dialogue/RommikDialogue.kt new file mode 100644 index 000000000..3cb8514cc --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/dialogue/RommikDialogue.kt @@ -0,0 +1,48 @@ +package content.region.asgarnia.rimmington.dialogue + +import core.api.openNpcShop +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * Handles the Rommik's dialogue. + */ +@Initializable +class RommikDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HAPPY, "Would you like to buy some crafting equipment?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("Let's see what you've got, then.", "No thanks, I've got all the crafting equipment I need.").also { stage++ } + 1 -> when (buttonId) { + 1 -> end().also { openNpcShop(player, npc.id) } + 2 -> playerl(FacialExpression.HALF_GUILTY, "No thanks, I've got all the crafting equipment I need.").also { stage = 10 } + } + + 10 -> { + npcl(FacialExpression.FRIENDLY, "Okay. Fare well on your travels.") + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return RommikDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ROMMIK_585) + } + +} diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/RoomikDialogue.java b/Server/src/main/content/region/asgarnia/rimmington/dialogue/RoomikDialogue.java deleted file mode 100644 index 90962c374..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/dialogue/RoomikDialogue.java +++ /dev/null @@ -1,73 +0,0 @@ -package content.region.asgarnia.rimmington.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the RoomikDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class RoomikDialogue extends DialoguePlugin { - - public RoomikDialogue() { - - } - - public RoomikDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 585 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Choose an option:", "Let's see what you've got, then.", "No thanks, I've got all the crafting equipment I need."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks, I've got all the crafting equipment I need."); - stage = 20; - break; - - } - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Okay. Fare well on your travels."); - stage = 21; - break; - case 21: - end(); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new RoomikDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Would you like to buy some Crafting equipment?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/content/region/asgarnia/rimmington/handler/RimmingtonListeners.kt b/Server/src/main/content/region/asgarnia/rimmington/handler/RimmingtonListeners.kt new file mode 100644 index 000000000..5dc727594 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/handler/RimmingtonListeners.kt @@ -0,0 +1,29 @@ +package content.region.asgarnia.rimmington.handler + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item +import core.game.node.scenery.Scenery +import org.rs09.consts.Items +import org.rs09.consts.Sounds + +/** + * @author Player Name + */ + +class RimmingtonListeners : InteractionListener { + override fun defineListeners() { + on(9662, IntType.SCENERY, "take") { player, node -> + if (!hasSpaceFor(player, Item(Items.SPADE_952))) { + sendMessage(player, "You don't have enough inventory space to hold that item.") + return@on true + } + animate(player, 535) + playAudio(player, Sounds.PICK2_2582) + addItem(player, Items.SPADE_952) + replaceScenery(node as Scenery, 0, 50) + return@on true + } + } +} diff --git a/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/HettyWitchsPotionDialogue.kt b/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/HettyWitchsPotionDialogue.kt new file mode 100644 index 000000000..50169689b --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/HettyWitchsPotionDialogue.kt @@ -0,0 +1,77 @@ +package content.region.asgarnia.rimmington.quest.witchpotion + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.item.Item +import core.game.node.entity.player.Player +import core.tools.END_DIALOGUE +import org.rs09.consts.Items + +/** + * Handles Hetty's dialogue for the Witch's Potion Quest. + */ +class HettyWitchsPotionDialogue(private val dStage: Int) : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + when (dStage) { + 0 -> handleQuestStartDialogue(player) + 20 -> handleGiveItemsDialogue(player) + 40 -> npcl(FacialExpression.ANNOYED, "Well are you going to drink the potion or not?").also { stage = END_DIALOGUE } + } + } + + private fun handleQuestStartDialogue(player: Player?) { + player ?: return + when (stage) { + 0 -> npcl(FacialExpression.HAPPY, "Ok I'm going to make a potion to help bring out your darker self.").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "You will need certain ingredients.").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "What do I need?").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "You need an eye of newt, a rat's tail, an onion... Oh and a piece of burnt meat.").also { stage++ } + 4 -> { + playerl(FacialExpression.HAPPY, "Great, I'll go and get them.") + startQuest(player, Quests.WITCHS_POTION) + setQuestStage(player, Quests.WITCHS_POTION, 20) + stage = END_DIALOGUE + } + } + } + + private fun handleGiveItemsDialogue(player: Player?) { + player ?: return + when (stage) { + 0 -> npcl(FacialExpression.HAPPY, "So have you found the things for the potion?").also { stage++ } + 1 -> { + if (inInventory(player, Items.ONION_1957, 1) && + inInventory(player, Items.RATS_TAIL_300, 1) && + inInventory(player, Items.BURNT_MEAT_2146, 1) && + inInventory(player, Items.EYE_OF_NEWT_221, 1)) { + playerl(FacialExpression.HAPPY, "Yes I have everything!").also { stage = 20 } + } else { + playerl(FacialExpression.HALF_GUILTY, "I'm afraid I don't have all of them yet.").also { stage = 10 } + } + } + + 10 -> npcl(FacialExpression.ANNOYED, "Well I can't make the potion without them! Remember...").also { stage++ } + 11 -> npcl(FacialExpression.NEUTRAL, "You need an eye of newt, a rat's tail, an onion, and a piece of burnt meat.").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "Off you go dear!").also { stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.HAPPY, "Excellent, can I have them then?").also { stage++ } + 21 -> sendDialogue(player, "You pass the ingredients to Hetty and she puts them all into her cauldron. Hetty closes her eyes and begins to chant. The cauldron bubbles mysteriously.").also { stage++ } + + 22 -> playerl(FacialExpression.NEUTRAL, "Well, is it ready?").also { stage++ } + 23 -> { + // Removing the items at this stage is authentic behavior + if (removeItem(player, Item(Items.ONION_1957, 1)) && + removeItem(player, Item(Items.RATS_TAIL_300, 1)) && + removeItem(player, Item(Items.BURNT_MEAT_2146, 1)) && + removeItem(player, Item(Items.EYE_OF_NEWT_221, 1))) { + npcl(FacialExpression.HAPPY, "Ok, now drink from the cauldron.") + setQuestStage(player, Quests.WITCHS_POTION, 40) + stage = END_DIALOGUE + } + } + } + } +} diff --git a/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotion.java b/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotion.java deleted file mode 100644 index 9b5738527..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotion.java +++ /dev/null @@ -1,90 +0,0 @@ -package content.region.asgarnia.rimmington.quest.witchpotion; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.node.entity.player.link.quest.Quest; - -/** - * Represents the witch's potion quest. - * @author 'Vexia - */ -@Initializable -public class WitchsPotion extends Quest { - - /** - * Constructs a new {@code WitchsPotion} {@code Object}. - */ - public WitchsPotion() { - super("Witch's Potion", 31, 30, 1, 67, 0, 1, 3); - } - - @Override - public void drawJournal(Player player, int stage) { - super.drawJournal(player, stage); - switch (getStage(player)) { - case 0: - line(player, BLUE + "I can start this quest by speaking to " + RED + "Hetty " + BLUE + "in her house in", 4+ 7); - line(player, RED + "Rimmington" + BLUE + ", West of " + RED + "Port Sarim", 5+ 7); - break; - case 20: - line(player, "I spoke to Hetty in her house at Rimmington. hetty told me", 4+ 7); - line(player, "she could increase my magic power if I can bring her", 5+ 7); - line(player, "certain ingredients for a potion.", 6+ 7); - line(player, BLUE + "Hetty needs me to bring her the following:", 8+ 7); - if (player.getInventory().contains(1957, 1)) { - line(player, "I have an onion with me", 9+ 7); - } else { - line(player, RED + "An onion", 9+ 7); - } - if (player.getInventory().contains(1957, 1)) { - line(player, "I have an onion with me", 9+ 7); - } else { - line(player, RED + "An onion", 9+ 7); - } - if (player.getInventory().contains(300, 1)) { - line(player, "I have a rat's tail with me", 10+ 7); - } else { - line(player, RED + "A rat's tail", 10+ 7); - } - if (player.getInventory().contains(2146, 1)) { - line(player, "I have a piece of burnt meat with me", 11+ 7); - } else { - line(player, RED + "A piece of burnt meat", 11+ 7); - } - if (player.getInventory().contains(221, 1)) { - line(player, "I have an eye of newt with me", 12+ 7); - } else { - line(player, RED + "An eye of newt", 12+ 7); - } - break; - case 40: - line(player, "I brought her an onion, a rat's tail, a piece of burnt meat", 4+ 7); - line(player, "and eye of newt which she used to make a potion.", 5+ 7); - line(player, BLUE + "I should drink from the " + RED + "cauldron" + BLUE + " and improve my magic!", 7+ 7); - break; - case 100: - line(player, "I brought her an onion, a rat's tail, a piece of burnt meat", 4+ 7); - line(player, "and an eye of newt which she used to make a potion.", 5+ 7); - line(player, "I drank from the cauldron and my magic power increased!", 7+ 7); - line(player, "QUEST COMPLETE!", 9+ 7); - break; - } - } - - @Override - public void finish(Player player) { - super.finish(player); - player.getPacketDispatch().sendString("1 Quest Point", 277, 8 + 2); - player.getPacketDispatch().sendString("325 Magic XP", 277, 9 + 2); - player.getSkills().addExperience(Skills.MAGIC, 325); - player.getInterfaceManager().closeChatbox(); - player.getPacketDispatch().sendItemZoomOnInterface(221, 240, 277, 3 + 2); - } - - @Override - public Quest newInstance(Object object) { - // TODO Auto-generated method stub - return this; - } -} diff --git a/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotion.kt b/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotion.kt new file mode 100644 index 000000000..0ada42b79 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotion.kt @@ -0,0 +1,86 @@ +package content.region.asgarnia.rimmington.quest.witchpotion + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Components +import org.rs09.consts.Items +import content.data.Quests + +/** + * Represents the Witch's Potion Quest + */ +@Initializable +class WitchsPotion : Quest(Quests.WITCHS_POTION, 31, 30, 1, 67, 0, 1, 3) { + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + player ?: return + var line = 11 + if (stage == 0) { + line(player, "I can start this quest by speaking to !!Hetty?? who in her house in", line++) + line(player, "!!Rimmington??, West of !!Port Sarim??.", line++) + } else if (stage in 1..39) { + line(player, "I spoke to !!Hetty?? in her house at Rimmington. Hetty told me", line++, true) + line(player, "she could increase my magic power if I can bring her", line++, true) + line(player, "certain ingredients for a potion.", line++, true) + line++ + line(player, "Hetty needs me to bring her the following:", line++) + + if(inInventory(player, Items.ONION_1957, 1)) + line(player, "I have an onion with me", line++, true) + else + line(player, "!!An onion??", line++) + + if(inInventory(player, Items.RATS_TAIL_300, 1)) + line(player, "I have a rat's tail with me", line++, true) + else + line(player, "!!A rat's tail??", line++) + + if(inInventory(player, Items.BURNT_MEAT_2146, 1)) + line(player, "I have a piece of burnt meat with me", line++, true) + else + line(player, "!!A piece of burnt meat??", line++) + + if(inInventory(player, Items.EYE_OF_NEWT_221, 1)) + line(player, "I have an eye of newt with me", line++, true) + else + line(player, "!!An eye of newt??", line++) + } else if (stage in 40..99) { + line(player, "I spoke to !!Hetty?? in her house at Rimmington. Hetty told me.", line++, true) + line(player, "she could increase my magic power if I can bring her", line++, true) + line(player, "certain ingredients for a potion.", line++, true) + line(player, "I brought her an onion, a rat's tail, a piece of burnt meat", line++, true) + line(player, "and an eye of newt which she used to make a potion.", line++, true) + line(player, "I should drink from the !!cauldron?? and improve my magic!", line++) + } else if (stage == 100) { + line(player, "I have spoken to !!Hetty??.", line++, true) + line(player, "I spoke to !!Hetty?? in her house at Rimmington. Hetty told me.", line++, true) + line(player, "she could increase my magic power if I can bring her", line++, true) + line(player, "certain ingredients for a potion.", line++, true) + line(player, "I brought her an onion, a rat's tail, a piece of burnt meat", line++, true) + line(player, "and an eye of newt which she used to make a potion.", line++, true) + line(player, "I drank from the cauldron and my magic power increased!", line++, true) + line++ + line(player, "%%QUEST COMPLETE!&&", line++) + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var line = 10 + + sendItemZoomOnInterface(player, Components.QUEST_COMPLETE_SCROLL_277, 5, Items.EYE_OF_NEWT_221) + drawReward(player, "1 Quest Point", line++) + drawReward(player, "325 Magic XP", line++) + + rewardXP(player, Skills.MAGIC, 325.0) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotionListeners.kt b/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotionListeners.kt new file mode 100644 index 000000000..0d00cee47 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotionListeners.kt @@ -0,0 +1,26 @@ +package content.region.asgarnia.rimmington.quest.witchpotion + +import content.data.Quests +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Scenery + +/** + * Interaction Listener for Witch's Potion Quest. + */ +class WitchsPotionListener : InteractionListener { + + override fun defineListeners() { + + on(Scenery.CAULDRON_2024, IntType.SCENERY, "drink from") { player, node -> + if (getQuestStage(player, Quests.WITCHS_POTION) == 40) { + sendDialogue(player, "You drink from the cauldron, it tastes horrible! You feel yourself imbued with power.") + finishQuest(player, Quests.WITCHS_POTION) + } else { + sendDialogue(player, "As nice as that looks I think I'll give it a miss for now.") + } + return@on true + } + } +} diff --git a/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotionPlugin.java b/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotionPlugin.java deleted file mode 100644 index 745f0837c..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotionPlugin.java +++ /dev/null @@ -1,41 +0,0 @@ -package content.region.asgarnia.rimmington.quest.witchpotion; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the witchs potion plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class WitchsPotionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(2024).getHandlers().put("option:drink from", this); - SceneryDefinition.forId(2024).getHandlers().put("option:Drink From", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Witch's Potion"); - switch (quest.getStage(player)) { - case 20: - case 100: - player.getDialogueInterpreter().sendDialogues(player, null, "As nice as that looks I think I'll give it a miss for now."); - break; - case 40: - player.getDialogueInterpreter().open(307, true, 1); - break; - } - return true; - } - -} diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/KaqemeexDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/KaqemeexDialogue.java index e5ab79fd7..487939a33 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/KaqemeexDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/KaqemeexDialogue.java @@ -7,6 +7,7 @@ import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; +import content.data.Quests; /** * Represents the kaqemeex dialogue plugin. @@ -55,17 +56,17 @@ public final class KaqemeexDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getQuestRepository().isComplete("Druidic Ritual")) { + if (player.getQuestRepository().isComplete(Quests.DRUIDIC_RITUAL)) { interpreter.sendDialogues(npc, null, "Hello again. How is the Herblore going?"); stage = 600; break; } - if (player.getQuestRepository().getQuest("Druidic Ritual").getStage(player) == 10) { + if (player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).getStage(player) == 10) { interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Hello again."); stage = 40; break; } - if (player.getQuestRepository().getQuest("Druidic Ritual").getStage(player) == 99) { + if (player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).getStage(player) == 99) { interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "I have word from Sanfew that you have been very", "helpful in assisting him with his preparations for the", "purification ritual. As promised I will now teach you the", "ancient arts of Herblore."); stage = 200; break; @@ -74,7 +75,7 @@ public final class KaqemeexDialogue extends DialoguePlugin { stage = 1; break; case 1: - if (player.getQuestRepository().getQuest("Druidic Ritual").isStarted(player)) { + if (player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).isStarted(player)) { if (Skillcape.isMaster(player, Skills.HERBLORE)) { interpreter.sendOptions("Select an Option", "Can I buy a Skillcape of Herblore?", "Who are you?", "Did you build this?"); stage = 800; @@ -164,7 +165,7 @@ public final class KaqemeexDialogue extends DialoguePlugin { stage = 13; break; case 26: - player.getQuestRepository().getQuest("Druidic Ritual").start(player); + player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).start(player); interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Excellent. Go to the village south of this place and speak", "to my fellow Sanfew who is working on the purification", "ritual. He knows better than I what is required to", "complete it."); stage = 27; break; @@ -184,7 +185,7 @@ public final class KaqemeexDialogue extends DialoguePlugin { break; case 200: end(); - player.getQuestRepository().getQuest("Druidic Ritual").finish(player); + player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).finish(player); break; case 500: switch (buttonId) { diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java index 3162dbdf1..f953d2783 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java @@ -11,6 +11,7 @@ import core.plugin.Initializable; import core.game.node.item.Item; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Handles the PikkupstixDialogue dialogue. @@ -85,7 +86,7 @@ public final class PikkupstixDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Wolf Whistle"); + quest = player.getQuestRepository().getQuest(Quests.WOLF_WHISTLE); switch (quest.getStage(player)) { case 0: npc("You there! What are you doing here, as if I didn't have", "enough troubles?"); diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/SanfewDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/SanfewDialogue.java index acf1f9f8b..0c5d09444 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/SanfewDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/SanfewDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Handles the SanfewDialogue dialogue. @@ -40,12 +41,12 @@ public class SanfewDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getQuestRepository().getQuest("Druidic Ritual").getStage(player) == 20) { + if (player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).getStage(player) == 20) { interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Did you bring me the required ingredients for the", "potion?"); stage = 100; break; } - if (player.getQuestRepository().getQuest("Druidic Ritual").getStage(player) == 10) { + if (player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).getStage(player) == 10) { interpreter.sendOptions("Select an Option", "I've been sent to help purify the Varrock stone circle.", "Actually, I don't need to speak to you."); stage = 2; break; @@ -89,7 +90,7 @@ public class SanfewDialogue extends DialoguePlugin { break; case 8: interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "It is located somewhere in the mysterious underground", "halls which are located somewhere in the woods just", "South of here. They are too dangerous for me to go", "myself however."); - player.getQuestRepository().getQuest("Druidic Ritual").setStage(player, 20); + player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).setStage(player, 20); stage = 9; break; case 9: @@ -129,7 +130,7 @@ public class SanfewDialogue extends DialoguePlugin { break; case 202: player.getInventory().remove(new Item(522, 1), new Item(523, 1), new Item(524, 1), new Item(525, 1)); - player.getQuestRepository().getQuest("Druidic Ritual").setStage(player, 99); + player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).setStage(player, 99); player.getQuestRepository().syncronizeTab(player); interpreter.sendDialogues(npc, null, "Now go and talk to Kaqemeex and he will introduce", "you to the wonderful world of herblore and potion", "making!"); stage = 203; diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt b/Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt index b53370950..90063d64c 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt @@ -21,7 +21,6 @@ class VelrakDialogue(player: Player? = null) : DialoguePlugin(player) { playerl(FacialExpression.HALF_THINKING, "Are you still here?").also { stage = 100 } } else { npcl(FacialExpression.FRIENDLY, "Thank you for rescuing me! It isn't very comfy in this cell.") - removeItem(player, Items.JAIL_KEY_1591, Container.INVENTORY) } return true } @@ -42,7 +41,7 @@ class VelrakDialogue(player: Player? = null) : DialoguePlugin(player) { 2 -> playerl(FacialExpression.NEUTRAL, "No, it's too dangerous for me too.").also { stage = 15 } } - 14 -> sendItemDialogue(player, Items.DUSTY_KEY_1590, "Velrak reaches somewhere mysterious and passes you a key.").also { addItem(player, Items.DUSTY_KEY_1590, 1); stage = END_DIALOGUE } + 14 -> sendItemDialogue(player, Items.DUSTY_KEY_1590, "Velrak reaches somewhere mysterious and passes you a key.").also { addItemOrDrop(player, Items.DUSTY_KEY_1590, 1); stage = END_DIALOGUE } 15 -> npcl(FacialExpression.FRIENDLY, "I don't blame you!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/DruidicRitual.java b/Server/src/main/content/region/asgarnia/taverley/quest/DruidicRitual.java deleted file mode 100644 index 83afb86c1..000000000 --- a/Server/src/main/content/region/asgarnia/taverley/quest/DruidicRitual.java +++ /dev/null @@ -1,83 +0,0 @@ -package content.region.asgarnia.taverley.quest; - -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.node.entity.player.link.quest.Quest; - -/** - * The main type for the druidic ritual quest. - * @author Vexia - * - */ -@Initializable -public class DruidicRitual extends Quest { - - /** - * Constructs a new {@code DruidicRitual} {@code Object}. - */ - public DruidicRitual() { - super("Druidic Ritual", 48, 47, 4, 80, 0, 3, 4); - } - - @Override - public void drawJournal(Player player, int stage) { - super.drawJournal(player, stage); - player.getPacketDispatch().sendString("I can start this quest by speaking to the Kaqemeex who is at", 275, 4+ 7); - player.getPacketDispatch().sendString("the Druids Circle just North of Taverley.", 275, 5+ 7); - if (stage == 10) { - player.getPacketDispatch().sendString("I told Kaqemeex I would help them prepare their ceremony.", 275, 4+ 7); - player.getPacketDispatch().sendString("I should speak to Sanfew in the village to the South", 275, 5+ 7); - } - if (stage == 20) { - player.getPacketDispatch().sendString("", 275, 5+ 7); - player.getPacketDispatch().sendString("I told Kaqemeex I would help them prepare their ceremony.", 275, 4+ 7); - player.getPacketDispatch().sendString("Sanfew told me for the ritual they would need me to place", 275, 6+ 7); - player.getPacketDispatch().sendString("raw bear meat, raw chicken, raw rat meat, and raw beef in", 275, 7+ 7); - player.getPacketDispatch().sendString("the Cauldron of Thunder in the dungeon South of Taverley", 275, 8+ 7); - } - if (stage == 99) { - player.getPacketDispatch().sendString("I told Kaqemeex I would help them prepare their ceremony.", 275, 4+ 7); - player.getPacketDispatch().sendString("The ceremony required various meats being placed in the.", 275, 5+ 7); - player.getPacketDispatch().sendString("Cauldron of Thunder. I did this and gave them to Sanfew.", 275, 6+ 7); - player.getPacketDispatch().sendString("I should speak to Kaqemeex again and claim my reward", 275, 7+ 7); - } - if (stage == 100) { - player.getPacketDispatch().sendString("I told Kaqemeex I would help them prepare their ceremony.", 275, 4+ 7); - player.getPacketDispatch().sendString("The ceremony required various meats being placed in the.", 275, 5+ 7); - player.getPacketDispatch().sendString("Cauldron of Thunder. I did this and gave them to Sanfew.", 275, 6+ 7); - player.getPacketDispatch().sendString("Kaqemeex then taught me the basics of the skill Herblore", 275, 7+ 7); - player.getPacketDispatch().sendString("QUEST COMPLETE!", 275, 11+ 7); - } - } - - @Override - public void finish(Player player) { - super.finish(player); - player.getInterfaceManager().open(new Component(277).setCloseEvent(new CloseEvent() { - @Override - public boolean close(Player player, Component c) { - if (player != null) { - player.getDialogueInterpreter().open(455, NPC.create(455, player.getLocation()), true); - } - return true; - } - })); - player.getPacketDispatch().sendString("You have completed the Druidic Ritual Quest!", 277, 4); - player.getPacketDispatch().sendItemZoomOnInterface(249, 240, 277, 3 + 2); - player.getPacketDispatch().sendString("4 Quest Points", 277, 8 + 2); - player.getPacketDispatch().sendString("250 Herblore XP", 277, 9 + 2); - player.getPacketDispatch().sendString("Access to Herblore skill", 277, 10 + 2); - player.getSkills().addExperience(Skills.HERBLORE, 250); - player.getInterfaceManager().closeChatbox(); - } - - @Override - public Quest newInstance(Object object) { - // TODO Auto-generated method stub - return this; - } -} diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/DruidicRitual.kt b/Server/src/main/content/region/asgarnia/taverley/quest/DruidicRitual.kt new file mode 100644 index 000000000..15440a4bd --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/quest/DruidicRitual.kt @@ -0,0 +1,93 @@ +package content.region.asgarnia.taverley.quest + +import core.api.* +import core.game.component.Component +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.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +/** + * Druidic Ritual Quest + */ +@Initializable +class DruidicRitual : Quest(Quests.DRUIDIC_RITUAL, 48, 47, 4, 80, 0, 3, 4) { + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, Quests.DRUIDIC_RITUAL) > 0 + + if (!started) { + line(player, "I can start this quest by speaking to !!Kaqemeex?? who is at", line++) + line(player, "the !!Druids Circle?? just !!north?? of !!Taverly??.", line++) + } else { + line(player, "Kaqemeex and the druids are preparing a ceremony to", line++, true) + line(player, "purify the Varrock stone circle. I told Kaqemeex I would", line++, true) + line(player, "help them.", line++, true) + + if (stage >= 20) { + // Line disappears. + } else if (stage >= 10) { + line++ + line(player, "I should speak to !!Sanfew?? in the village to the !!south??.", line++) + } + + if (stage >= 99) { + line(player, "The ceremony required various meats being placed in the", line++, true) + line(player, "Cauldron of Thunder. I did this and gave them to Sanfew.", line++, true) + } else if (stage >= 20) { + line++ + line(player, "!!Sanfew?? told me for the ritual they would need me to place", line++) + line(player, "!!raw bear meat??, !!raw chicken??, !!raw rat meat??, and !!raw beef?? in", line++) + line(player, "the !!Cauldron of Thunder?? in the !!dungeon south?? of !!Taverley??.", line++) + } + + if (stage >= 100) { + line(player, "Kaqemeex then taught me the basics of the Heblore skill.", line++, true) + } else if (stage >= 99) { + line++ + line(player, "I should speak to !!Kaqemeex?? again and claim my !!reward??.", line++) + } + + if (stage >= 100) { + line++ + line(player,"QUEST COMPLETE!", line++) + line(player, "I gained !!4 quest points??, !!250 Heblore XP?? and access to", line++) + line(player, "the !!Heblore skill??.", line) + } + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Druidic Ritual Quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.CLEAN_GUAM_249, 230, 277, 5) + + drawReward(player,"4 Quest Points", ln++) + drawReward(player,"250 Herblore XP", ln++) + drawReward(player,"Access to Herblore skill", ln++) + + rewardXP(player, Skills.HERBLORE, 250.0) + + player.interfaceManager.closeChatbox() + } + + override fun questCloseEvent(player: Player?, component: Component?) { + queueScript(player!!, 1, QueueStrength.SOFT) { + openDialogue(player,455, NPC.create(455, player.location), true) + return@queueScript stopExecuting(player) + } + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/WolfWhistle.java b/Server/src/main/content/region/asgarnia/taverley/quest/WolfWhistle.java index a9aeecf48..ec4661ccf 100644 --- a/Server/src/main/content/region/asgarnia/taverley/quest/WolfWhistle.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/WolfWhistle.java @@ -5,8 +5,10 @@ import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; +import org.rs09.consts.Items; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** @@ -25,38 +27,128 @@ public class WolfWhistle extends Quest { * Constructs a new {@code WolfWhistle} {@code Object}. */ public WolfWhistle() { - super("Wolf Whistle", 146, 145, 1); + super(Quests.WOLF_WHISTLE, 146, 145, 1); } @Override public void drawJournal(Player player, int stage) { super.drawJournal(player, stage); - switch (stage) { - case 0: - line(player, BLUE + "I can begin this quest by talking to " + RED + "Pikkupstix" + BLUE + ", who lives in", 4+ 7); - line(player, RED + "Taverley.", 5+ 7); - break; - case 10: - line(player, "Having spoken to Pikkupstix, it seems that all I have to do

is get rid of the little rabbit upstairs in his house.", 4+ 7); - break; - case 20: - line(player, "Having spoken to Pikkupstix, it seems that all I have to do

is get rid of the little rabbit upstairs in his house.



It appears that I have underestimated the rabbit in this

case; it is some huge rabbit-wolf-monster-bird-thing. I

think I should speak to Pikkupstix to find out what is going

on.", 4+ 7); - break; - case 30: - line(player, "Having spoken to Pikkupstix, it seems that all I have to do

is get rid of the little rabbit upstairs in his house.

It appears that I have underestimated the rabbit in this

case; it is some huge rabbit-wolf-monster-bird-thing. I

think I should speak to Pikkupstix to find out what is going

on.



I have spoken to Pikkupstix, who has promised to teach me

the secrets of Summoning if I can help dismiss the giant

wolpertinger. To do this, I need to bring him 2 lots of wolf

bones.

" + (player.getInventory().containsItem(WOLF_BONES) ? "" : "") + "I need to get 2 lots of wolf bones.", 4+ 7); - break; - case 40: - line(player, "Having spoken to Pikkupstix, it seems that all I have to do

is get rid of the little rabbit upstairs in his house.

It appears that I have underestimated the rabbit in this

case; it is some huge rabbit-wolf-monster-bird-thing. I

think I should speak to Pikkupstix to find out what is going

on.



I have spoken to Pikkupstix, who has promised to teach me

the secrets of Summoning if I can help dismiss the giant

wolpertinger. To do this, I need to bring him 2 lots of wolf

bones.

I have given Pikkupstix all of the items he requested.



Pikkupstix has given me some gold charms, spirit shards

and pouches, with which to make a spirit wolf pouch and

some Howl scrolls. I will then be able to use them to dismiss

the giant wolpertinger.

I need to open the trapdoor with the trapdoor key that I

have been given. ", 4+ 7); - break; - case 50: - line(player, "Having spoken to Pikkupstix, it seems that all I have to do

is get rid of the little rabbit upstairs in his house.

It appears that I have underestimated the rabbit in this

case; it is some huge rabbit-wolf-monster-bird-thing. I

think I should speak to Pikkupstix to find out what is going

on.



I have spoken to Pikkupstix, who has promised to teach me

the secrets of Summoning if I can help dismiss the giant

wolpertinger. To do this, I need to bring him 2 lots of wolf

bones.



Pikkupstix has given me some gold charms, spirit shards

and pouches, with which to make a spirit wolf pouch and

some Howl scrolls. I will then be able to use them to dismiss

the giant wolpertinger.

I have infused the 2 spirit wolf pouches, but I need to

transform one of them into scrolls at the obelisk.", 4+ 7); - break; - case 60: - line(player, "Having spoken to Pikkupstix, it seems that all I have to do

is get rid of the little rabbit upstairs in his house.

It appears that I have underestimated the rabbit in this

case; it is some huge rabbit-wolf-monster-bird-thing. I

think I should speak to Pikkupstix to find out what is going

on.



I have spoken to Pikkupstix, who has promised to teach me

the secrets of Summoning if I can help dismiss the giant

wolpertinger. To do this, I need to bring him 2 lots of wolf

bones.



Pikkupstix has given me some gold charms, spirit shards

and pouches, with which to make a spirit wolf pouch and

some Howl scrolls. I will then be able to use them to dismiss

the giant wolpertinger.

I have infused the 2 spirit wolf pouches, but I need to

transform one of them into scrolls at the obelisk.

I have dismissed the giant wolpertinger.", 4+ 7); - break; - case 100: - line(player, "Having spoken to Pikkupstix, it seems that all I have to do

is get rid of the little rabbit upstairs in his house.

It appears that I have underestimated the rabbit in this

case; it is some huge rabbit-wolf-monster-bird-thing. I

think I should speak to Pikkupstix to find out what is going

on.



I have spoken to Pikkupstix, who has promised to teach me

the secrets of Summoning if I can help dismiss the giant

wolpertinger. To do this, I need to bring him 2 lots of wolf

bones.



Pikkupstix has given me some gold charms, spirit shards

and pouches, with which to make a spirit wolf pouch and

some Howl scrolls. I will then be able to use them to dismiss

the giant wolpertinger.

I have infused the 2 spirit wolf pouches, but I need to

transform one of them into scrolls at the obelisk.

I have dismissed the giant wolpertinger.



QUEST COMPLETE!", 4+ 7); - break; + var line = 12; + + if(stage == 0){ + line(player, "I can begin this quest by talking to !!Pikkupstix??, who lives in", line++, false); + line(player, "!!Taverly??.", line++, false); + limitScrolling(player, line, true); + } else { + if (stage >= 10) { + line(player, "Having spoken to !!Pikkupstix??, it seems that all I have to do", line++, stage >= 20); + line(player, "is get rid of the !!little rabbit upstairs in his house??.", line++, stage >= 20); + line++; + } + if (stage >= 20) { + line(player, "It appears that I have underestimated the rabbit in this", line++, stage >= 30); + line(player, "case; it is some !!huge rabbit-wolf-monster-bird-thing??. I", line++, stage >= 30); + line(player, "think I should speak to !!Pikkupstix?? to find out what is going", line++, stage >= 30); + line(player, "on.", line++, stage >= 30); + line++; + } + // Clicking on the ladder - sendMessage("There is no reason to go up there and face that thing again.") + if (stage >= 30) { + line(player, "I have spoken to !!Pikkupstix??, who has promised to teach me ", line++, stage >= 40); + line(player, "the secrets of !!Summoning?? if I can help dismiss the !!giant??", line++, stage >= 40); + line(player, "!!wolpertinger??. To do this, I need to bring him !!2 lots of wolf??", line++, stage >= 40); + line(player, "!!bones??.", line++, stage >= 40); + + if (stage == 30) { + line(player, "!!I need to get 2 lots of wolf bones.??", line++, inInventory(player, Items.WOLF_BONES_2859, 2)); + } else { + line(player, "I have given Pikkupstix all of the items he requested.", line++, true); + line++; + } + } + if (stage >= 40) { + line(player, "Pikkupstix has given me some !!gold charms??, !!spirit shards??", line++, stage >= 50); + line(player, "and !!pouches??, with which to make a !!spirit wolf pouch?? and", line++, stage >= 50); + line(player, "some !!Howl scrolls??. I will then be able to use them to dismiss", line++, stage >= 50); + line(player, "the !!giant wolpertinger??.", line++, stage >= 50); + } + if (stage == 40 && inInventory(player, Items.TRAPDOOR_KEY_12528, 1)) { + line(player, "I need to open the !!trapdoor?? with the !!trapdoor key?? that I", line++, false); + line(player, "have been given.", line++, false); + } else if (stage >= 50 || player.getAttribute("has-key", false)) { + line(player, "I have unlocked the trapdoor.", line++, true); + } + + // This part is a shitshow. + if (stage >= 50 || (stage >= 40 && (inInventory(player, Items.SPIRIT_WOLF_POUCH_12047, 1) || inInventory(player, Items.HOWL_SCROLL_12425, 1)))) { + line(player, "I need to go into Pikkupstix's !!cellar?? and !!infuse a pouch?? at", line++, stage >= 50); + line(player, "the obelisk, using the items I have been given.", line++, stage >= 50); + line++; + line(player, "I have infused the spirit wolf pouch and made some Howl", line++, stage >= 50); + line(player, "scrolls. I should speak with !!Pikkupstix?? about how to use", line++, stage >= 50); + line(player, "them.", line++, stage >= 50); + line++; + } else if (stage >= 40 && inInventory(player, Items.SPIRIT_WOLF_POUCH_12047, 2)) { + line(player, "I have infused the 2 spirit wolf pouches, but I need to", line++, false); + line(player, "transform one of them into scrolls at the obelisk.", line++, false); + } else if (stage >= 40 && player.getAttribute("has-key", false)) { + line(player, "I need to go into Pikkupstix's !!cellar?? and !!infuse a pouch?? at", line++); + line(player, "the obelisk, using the items I have been given.", line++); + line(player, "!!I need to bring 2 lots of wolf bones.??", line++, inInventory(player, Items.WOLF_BONES_2859, 2)); + line(player, "!!I need to bring the pouches.??", line++, inInventory(player, Items.POUCH_12155, 2)); + line(player, "!!I need to bring the gold charms.??", line++, inInventory(player, Items.GOLD_CHARM_12158, 2)); + line(player, "!!I need to bring the spirit shards.??", line++, inInventory(player, Items.SPIRIT_SHARDS_12183, 14)); + } + + if (stage >= 50) { + line(player, "I have been told how to use the spirit wolf pouch and Howl", line++, stage >= 60); + line(player, "scrolls. I should go back upstairs and confront the !!giant??", line++, stage >= 60); + line(player, "!!wolpertinger??.", line++, stage >= 60); + line++; + } + if (stage == 50) { // Does not stay. + if (inInventory(player, Items.SPIRIT_WOLF_POUCH_12047, 1)) { + line(player, "I have the spirit wolf pouch on me.", line++, false); + } else { + line(player, "!!I have lost the spirit wolf pouch.??", line++, false); + } + if (inInventory(player, Items.HOWL_SCROLL_12425, 1)) { + line(player, "I have the Howl scroll on me.", line++, false); + } else { + line(player, "!!I have lost the Howl scroll.??", line++, false); + } + } + + if (stage >= 60) { + // Technically, there should be an extra stage speaking to Pikkupstix here, but it is not available. + line(player, "I have banished the giant !!wolpertinger??. I should speak with", line++, true); + line(player, "!!Pikkupstix?? to get my reward.", line++, true); + line++; + if (player.getSkills().getLevel(Skills.SUMMONING) >= player.getSkills().getStaticLevel(Skills.SUMMONING) || stage >= 100) { + line(player, "I am feeling drained of Summoning skill points and need to", line++, true); + line(player, "recharge at the !!obelisk??.", line++, true); + line++; + line(player, "I have banished the giant !!wolpertinger?? and refreshed my", line++, stage >= 100); + line(player, "Summoning skill points. I should speak with !!Pikkupstix?? to", line++, stage >= 100); + line(player, "get my reward.", line++, stage >= 100); + line++; + } else { + line(player, "I am feeling drained of Summoning skill points and need to", line++); + line(player, "recharge at the !!obelisk??.", line++); + line++; + } + } + + if (stage >= 100) { + line(player, "I have been given access to the secrets of Summoning.", line++, true); + line(player,"QUEST COMPLETE!", line++); + line(player, "!!Reward:??", line++); + line(player, "1 Quest Point,", line++); + line(player, "access to the Summoning skill", line++); + line(player, "275 gold charms", line++); + line(player, "and 276 Summoning XP", line++); + } + limitScrolling(player, line, false); } } diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/BoyDialoguePlugin.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/BoyDialoguePlugin.java index 3a92d309f..2574498c4 100644 --- a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/BoyDialoguePlugin.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/BoyDialoguePlugin.java @@ -6,6 +6,7 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; +import content.data.Quests; /** * Created for 2009Scape @@ -32,10 +33,10 @@ public class BoyDialoguePlugin extends DialoguePlugin { @Override public boolean open(Object... args) { - final Quest quest = player.getQuestRepository().getQuest("Witch's House"); + final Quest quest = player.getQuestRepository().getQuest(Quests.WITCHS_HOUSE); player.debug(quest.isStarted(player) + " " + quest.getStage(player) ); if (!quest.isStarted(player) && quest.getStage(player) < 10) { - player("Hello young man."); + player(FacialExpression.FRIENDLY, "Hello young man."); setStage(1); return true; } @@ -45,9 +46,9 @@ public class BoyDialoguePlugin extends DialoguePlugin { return true; } if (!player.getInventory().containsItem(BALL)) { - npc( FacialExpression.OLD_NORMAL, "Have you gotten my ball back yet?"); + npc( FacialExpression.CHILD_THINKING, "Have you gotten my ball back yet?"); } else { - player("Hi, I have got your ball back. It was MUCH harder", "than I thought it would be."); + player(FacialExpression.NEUTRAL, "Hi, I have got your ball back. It was MUCH harder", "than I thought it would be."); } setStage(11); return true; @@ -55,7 +56,7 @@ public class BoyDialoguePlugin extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Witch's House"); + final Quest quest = player.getQuestRepository().getQuest(Quests.WITCHS_HOUSE); switch(stage) { case -1: end(); @@ -71,11 +72,11 @@ public class BoyDialoguePlugin extends DialoguePlugin { case 3: switch(buttonId) { case 1: - player("What's the matter?"); + player(FacialExpression.THINKING, "What's the matter?"); setStage(5); break; case 2: - player("Well if you're not going to answer then I'll go."); + player(FacialExpression.NEUTRAL, "Well if you're not going to answer then I'll go."); next(); break; } @@ -85,7 +86,7 @@ public class BoyDialoguePlugin extends DialoguePlugin { finish(); break; case 5: - npc(FacialExpression.OLD_NORMAL, "I've kicked my ball over that hedge, into that garden!", "The old lady who lives there is scary... She's locked the","ball in her wooden shed! Can you get my ball back for", "me please?"); + npc(FacialExpression.CHILD_SAD, "I've kicked my ball over that hedge, into that garden!", "The old lady who lives there is scary... She's locked the","ball in her wooden shed! Can you get my ball back for", "me please?"); next(); break; case 6: @@ -95,17 +96,17 @@ public class BoyDialoguePlugin extends DialoguePlugin { case 7: switch(buttonId) { case 1: - player("Ok, I'll see what I can do."); + player(FacialExpression.NEUTRAL, "Ok, I'll see what I can do."); setStage(10); break; case 2: - player("Get it back yourself."); + player(FacialExpression.NEUTRAL, "Get it back yourself."); next(); break; } break; case 8: - npc(FacialExpression.OLD_NORMAL, "You're a meany."); + npc(FacialExpression.CHILD_SAD, "You're a meany."); next(); break; case 9: @@ -113,13 +114,13 @@ public class BoyDialoguePlugin extends DialoguePlugin { finish(); break; case 10: - npc(FacialExpression.OLD_NORMAL, "Thanks mister!"); + npc(FacialExpression.CHILD_FRIENDLY, "Thanks mister!"); finish(); quest.start(player); break; case 11: if (!player.getInventory().containsItem(BALL)) { - player("Not yet."); + player(FacialExpression.NEUTRAL, "Not yet."); next(); } else { if (player.getInventory().remove(BALL)) @@ -128,11 +129,11 @@ public class BoyDialoguePlugin extends DialoguePlugin { } break; case 12: - npc(FacialExpression.OLD_NORMAL, "Well it's in the shed in that garden."); + npc(FacialExpression.CHILD_ANGRY, "Well it's in the shed in that garden."); finish(); break; case 13: - npc(FacialExpression.OLD_NORMAL, "Thank you so much!"); + npc(FacialExpression.CHILD_FRIENDLY, "Thank you so much!"); next(); break; case 14: diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHouse.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHouse.java index 7d142974d..ab2b32e00 100644 --- a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHouse.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHouse.java @@ -4,10 +4,12 @@ import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; +import content.data.Quests; /** * Created for 2009Scape * User: Ethan Kyle Millard + * https://www.youtube.com/watch?v=-RuHho3NbWg * Date: March 15, 2020 * Time: 9:21 AM */ @@ -18,31 +20,30 @@ public class WitchsHouse extends Quest { * Constructs a new {@code WitchsHouse} {@code Object}. */ public WitchsHouse() { - super("Witch's House", 124, 123, 4, 226, 0, 1, 7); + super(Quests.WITCHS_HOUSE, 124, 123, 4, 226, 0, 1, 7); } @Override public void drawJournal(Player player, int stage) { super.drawJournal(player, stage); - switch (getStage(player)) { - case 0: - line(player, "I can start this quest by speaking to the little boy", 4+ 7); - line(player, "standing by the long garden just north of Taverley", 5+ 7); - line(player, "I must be able to defeat a level 53 enemy.", 6+ 7); - break; - case 10: - line(player, "A small boy has kicked his ball over the fence into the", 4+ 7); - line(player, "nearby garden, and I have agreed to retrieve it for him.", 5+ 7); - line(player, "I should find a way into the garden where the ball is.", 6+ 7); - break; - case 100: - line(player, "A small boy has kicked his ball over the fence into the", 4+ 7); - line(player, "nearby garden, and I have agreed to retrieve it for him.", 5+ 7); - line(player, "After puzzling through the strangely elaborate security", 6+ 7); - line(player, "system, and defeating a very strange monster, I returned", 7+ 7); - line(player, "the child's ball to him, and he thanked me for my help.", 8+ 7); - line(player, "QUEST COMPLETE!", 10+ 7); - break; + var line = 12; + if(stage == 0){ + line(player, "I can start this quest by speaking to the !!little boy??", line++); + line(player, "standing by the long garden just !!north of Taverly??.", line++); + line(player, "I must be able to defeat a !!level 53 enemy??.", line++); + } else { + line(player, "A small boy kicked his ball over the fence into the nearby", line++, true); + line(player, "garden, and I have agreed to retrieve it for him.", line++, true); + if (stage == 10) { + line(player, "I should find a way into the !!garden?? where the !!ball?? is.", line++); + } + if (stage >= 100) { + line(player, "After puzzling through the strangely elaborate security", line++, true); + line(player, "system, and defeating a very strange monster, I returned", line++, true); + line(player, "the child's ball to him, and he thanked me for my help.", line++, true); + line++; + line(player,"QUEST COMPLETE!", line); + } } } diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHousePlugin.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHousePlugin.java index 3600be5c6..5e8ccb1d9 100644 --- a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHousePlugin.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHousePlugin.java @@ -18,6 +18,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.ClassScanner; import core.tools.RandomFunction; +import content.data.Quests; /** * Created for 2009Scape @@ -39,7 +40,7 @@ public class WitchsHousePlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Witch's House"); + final Quest quest = player.getQuestRepository().getQuest(Quests.WITCHS_HOUSE); final int id = node instanceof Item ? ((Item) node).getId() : node instanceof Scenery ? ((Scenery) node).getId() : node instanceof NPC ? ((NPC) node).getId() : node.getId(); // boolean killedExperiment = player.getAttribute("witchs_house:experiment_killed",false); // boolean experimentAlive = !player.getAttribute("witchs_house:experiment_killed", false); diff --git a/Server/src/main/content/region/asgarnia/trollheim/dialogue/SabaDialogue.kt b/Server/src/main/content/region/asgarnia/trollheim/dialogue/SabaDialogue.kt index f904fe12e..9eb7daf39 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/dialogue/SabaDialogue.kt +++ b/Server/src/main/content/region/asgarnia/trollheim/dialogue/SabaDialogue.kt @@ -1,6 +1,6 @@ package content.region.asgarnia.trollheim.dialogue -import content.region.asgarnia.burthorpe.quest.deathplateau.DeathPlateau +import content.data.Quests import content.region.asgarnia.burthorpe.quest.deathplateau.SabaDialogueFile import core.api.getQuestStage import core.api.openDialogue @@ -20,7 +20,7 @@ import org.rs09.consts.NPCs @Initializable class SabaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - if (getQuestStage(player!!, DeathPlateau.questName) >= 19) { + if (getQuestStage(player!!, Quests.DEATH_PLATEAU) >= 19) { openDialogue(player!!, SabaDialogueFile(), npc) return true } diff --git a/Server/src/main/content/region/asgarnia/trollheim/dialogue/TenzingDialogue.kt b/Server/src/main/content/region/asgarnia/trollheim/dialogue/TenzingDialogue.kt index a657189c2..2101a9b77 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/dialogue/TenzingDialogue.kt +++ b/Server/src/main/content/region/asgarnia/trollheim/dialogue/TenzingDialogue.kt @@ -1,6 +1,6 @@ package content.region.asgarnia.trollheim.dialogue -import content.region.asgarnia.burthorpe.quest.deathplateau.DeathPlateau +import content.data.Quests import content.region.asgarnia.burthorpe.quest.deathplateau.TenzingDialogueFile import core.api.* import core.game.dialogue.DialoguePlugin @@ -21,7 +21,7 @@ import org.rs09.consts.NPCs @Initializable class TenzingDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - if (isQuestInProgress(player!!, DeathPlateau.questName, 20, 29)) { + if (isQuestInProgress(player!!, Quests.DEATH_PLATEAU, 20, 29)) { openDialogue(player!!, TenzingDialogueFile(), npc) return true } diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDTsutsarothSwingHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDTsutsarothSwingHandler.java index d90bd3f35..8a4de99a8 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDTsutsarothSwingHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDTsutsarothSwingHandler.java @@ -73,7 +73,7 @@ public final class GWDTsutsarothSwingHandler extends CombatSwingHandler { hit = RandomFunction.random(max); state.setMaximumHit(max); if (style == CombatStyle.MELEE) { - applyPoison(victim, entity, 16); + applyPoison(victim, entity, 80); } if (special) { ((Player) victim).getSkills().decrementPrayerPoints((double) hit / 2); diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java index c1be4295f..8803d9d88 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java @@ -18,6 +18,7 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Handles the entrance hole to the godwars dungeon. @@ -67,7 +68,7 @@ public final class GodwarsEntranceHandler extends OptionHandler { }); return true; case 26338: - if (!hasRequirement(player, "Troll Stronghold")) + if (!hasRequirement(player, Quests.TROLL_STRONGHOLD)) return true; if (player.getSkills().getStaticLevel(Skills.STRENGTH) < 60) { player.getPacketDispatch().sendMessage("You need a Strength level of 60 to move this boulder."); diff --git a/Server/src/main/content/region/asgarnia/whitewolfmountain/WhiteWolfMountainListener.kt b/Server/src/main/content/region/asgarnia/whitewolfmountain/WhiteWolfMountainListener.kt new file mode 100644 index 000000000..4e84f83a4 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/whitewolfmountain/WhiteWolfMountainListener.kt @@ -0,0 +1,64 @@ +package content.region.asgarnia.whitewolfmountain + +import content.data.skill.SkillingTool +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.skill.Skills +import org.rs09.consts.Scenery + +class WhiteWolfMountainListener : InteractionListener { + override fun defineListeners() { + + on(Scenery.ROCK_SLIDE_2634, SCENERY, "investigate") { player, node -> + // dtWpLjw4X0A + sendMessage(player, "These rocks contain nothing interesting.") + sendMessage(player, "They are just in the way.") + return@on true + } + + on(Scenery.ROCK_SLIDE_2634, SCENERY, "mine") { player, node -> + val pickaxe = SkillingTool.getPickaxe(player) + val rockScenery = node as core.game.node.scenery.Scenery + if (getDynLevel(player, Skills.MINING) < 50) { + sendMessage(player, "You need a mining level of 50 to mine this rock slide.") + return@on true + } + if (pickaxe == null) { + sendMessage(player, "You do not have a pickaxe to use.") + return@on true + } + animate(player, pickaxe.animation) + lock(player, 6) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + // Scenery.ROCKSLIDE_471 is the original rock. + 0 -> { + replaceScenery(rockScenery, Scenery.ROCKSLIDE_472, 2) + return@queueScript delayScript(player, 2) + } + + 1 -> { + replaceScenery(rockScenery, Scenery.ROCKSLIDE_473, 2) + return@queueScript delayScript(player, 2) + } + + 2 -> { + replaceScenery(rockScenery, 476, 2) + player.walkingQueue.reset() + if (player.location.x < 2839) { + player.walkingQueue.addPath(2840, 3517) + } else { + player.walkingQueue.addPath(2837, 3518) + } + return@queueScript delayScript(player, 2) + } + + else -> return@queueScript stopExecuting(player) + } + } + return@on true + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.kt index 50f351f37..a0bc89b30 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.kt @@ -8,6 +8,7 @@ import core.plugin.Initializable import org.rs09.consts.NPCs import core.api.* +import content.data.Quests /** * Represents the ali morrisane dialogue. @@ -36,7 +37,7 @@ class AliMorrisaneDialogue(player: Player? = null) : DialoguePlugin(player) { 1 -> playerl(FacialExpression.ASKING, "If you are, then why are you still selling goods from a stall?").also { stage = 10 } 2 -> { end() - if (!hasRequirement(player, "The Feud")) + if (!hasRequirement(player, Quests.THE_FEUD)) return true npc.openShop(player) } @@ -59,7 +60,7 @@ class AliMorrisaneDialogue(player: Player? = null) : DialoguePlugin(player) { } 2 -> { end() - if (!hasRequirement(player, "The Feud")) + if (!hasRequirement(player, Quests.THE_FEUD)) return true npc.openShop(player) } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/BorderGuardDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/BorderGuardDialogue.java index c31f6d468..4331b9370 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/BorderGuardDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/BorderGuardDialogue.java @@ -9,6 +9,7 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.plugin.Initializable; import core.game.world.map.RegionManager; +import content.data.Quests; /** * Represents the border guard dialogue plugin. @@ -80,7 +81,7 @@ public final class BorderGuardDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getQuestRepository().getQuest("Prince Ali Rescue").getStage(player) > 50) { + if (player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE).getStage(player) > 50) { npc("You may pass for free, you are a friend of Al-Kharid."); stage = 100; } else { diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/EllisDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/EllisDialogue.kt new file mode 100644 index 000000000..6dc4a3418 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/EllisDialogue.kt @@ -0,0 +1,75 @@ +package content.region.desert.alkharid.dialogue + +import content.global.skill.crafting.TanningProduct +import core.api.inInventory +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * Handles Ellis's dialogue. + */ +@Initializable +class EllisDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.FRIENDLY, "Greetings friend. I am a manufacturer of leather.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> { + var hasHides = false + + for (tanningProduct in TanningProduct.values()) { + if (inInventory(player, tanningProduct.item)) { + hasHides = true + break + } + } + + if(hasHides) { + npcl(FacialExpression.FRIENDLY, "I see you have brought me some hides. Would you like me to tan them for you?").also { stage = 10 } + } else { + options("Can I buy some leather?", "Leather is rather weak stuff.").also { stage = 20 } + } + } + + 10 -> options("Yes please.", "No thanks.").also { stage++ } + 11 -> when (buttonId) { + 1 -> playerl(FacialExpression.HAPPY, "Yes please.").also { stage = 12 } + 2 -> playerl(FacialExpression.NEUTRAL, "No thanks.").also { stage = 13 } + } + + 12 -> end().also { TanningProduct.open(player, NPCs.ELLIS_2824) } + 13 -> npcl(FacialExpression.FRIENDLY, "Very well, @g[sir,madam], as you wish.").also { stage = END_DIALOGUE } + + 20 -> when (buttonId) { + 1 -> playerl(FacialExpression.ASKING, "Can I buy some leather?").also { stage = 21 } + 2 -> playerl(FacialExpression.SUSPICIOUS, "Leather is rather weak stuff.").also { stage = 22 } + } + + 21 -> npcl(FacialExpression.FRIENDLY, "I make leather from animal hides. Bring me some cowhides and one gold coin per hide, and I'll tan them into soft leather for you.").also { stage = END_DIALOGUE } + + 22 -> npcl(FacialExpression.NOD_YES, "Normal leather may be quite weak, but it's very cheap - I make it from cowhides for only 1 gp per hide - and it's so easy to craft that anyone can work with it.").also { stage++ } + 23 -> npcl(FacialExpression.HALF_THINKING, "Alternatively you could try hard leather. It's not so easy to craft, but I only charge 3 gp per cowhide to prepare it, and it makes much sturdier armour.").also { stage++ } + 24 -> npcl(FacialExpression.FRIENDLY, "I can also tan snake hides and dragonhides, suitable for crafting into the highest quality armour for rangers.").also { stage++ } + 25 -> playerl(FacialExpression.NEUTRAL, "Thanks, I'll bear it in mind.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return EllisDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ELLIS_2824) + } +} diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt index 65e45420c..cd6bd5d5e 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt @@ -1,11 +1,13 @@ package content.region.desert.alkharid.dialogue import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs +import content.data.Quests +import core.api.getQuestStage +import core.api.setQuestStage /** * Represents the gem trader Dialogue plugin @@ -21,17 +23,8 @@ class GemTraderDialogue (player: Player? = null): DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player?.questRepository?.getStage("Family Crest") ?: -1 - if(qstage == 12){ - npc("Good day to you, traveller. ", - "Would you be interested in buying some gems?") - stage = 1 - } - else{ - npc("Good day to you, traveller. ", - "Would you be interested in buying some gems?") - stage = 2 - } + npc("Good day to you, traveller.", "Would you be interested in buying some gems?") + stage = if (getQuestStage(player, Quests.FAMILY_CREST) == 12) 1 else 2 return true } @@ -74,7 +67,7 @@ class GemTraderDialogue (player: Player? = null): DialoguePlugin(player){ 103 -> npc("Well, maybe we'll all get lucky ", "and the scorpions will deal with him.").also{ stage = 1000 - player.questRepository.getQuest("Family Crest").setStage(player, 13) + setQuestStage(player, Quests.FAMILY_CREST, 13) } 1000 -> end() diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/HassanDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/HassanDialogue.java index ce0192046..7c711edab 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/HassanDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/HassanDialogue.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue used to handle the Hassan npc. @@ -52,7 +53,7 @@ public final class HassanDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Prince Ali Rescue"); + quest = player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE); switch (quest.getStage(player)) { case 100: interpreter.sendDialogues(npc, null, "You are a friend of the town of Al-Kharid. If we have", "more tasks to complete, we will ask you. Please, keep in", "contact. Good employees are not easy to find."); diff --git a/Server/src/main/content/region/desert/alkharid/handlers/TanningNPC.java b/Server/src/main/content/region/desert/alkharid/handlers/TanningNPC.java deleted file mode 100644 index 8fdf05cc1..000000000 --- a/Server/src/main/content/region/desert/alkharid/handlers/TanningNPC.java +++ /dev/null @@ -1,30 +0,0 @@ -package content.region.desert.alkharid.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.plugin.Initializable; -import content.global.skill.crafting.TanningProduct; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; - -/** - * @author 'Vexia - */ -@Initializable -public class TanningNPC extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(1041).getHandlers().put("option:trade", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - TanningProduct.open(player, ((NPC) node).getId()); - return true; - } - -} diff --git a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java index 5cc974b9b..038567099 100644 --- a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java @@ -10,6 +10,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.world.GameWorld; import core.game.world.map.RegionManager; +import content.data.Quests; /** * Represents the dialogue which handles the lady keli transcript. @@ -58,7 +59,7 @@ public final class LadyKeliDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Prince Ali Rescue"); + quest = player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE); switch (quest.getStage(player)) { case 60: case 100: diff --git a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescue.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescue.java index 4cd5acffa..3df88587a 100644 --- a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescue.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescue.java @@ -6,6 +6,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the prince ali rescue quest. @@ -44,7 +45,7 @@ public class PrinceAliRescue extends Quest { * Constructs a new {@Code PrinceAliRescue} {@Code Object} */ public PrinceAliRescue() { - super("Prince Ali Rescue", 24, 23, 3, 273, 0, 1, 110); + super(Quests.PRINCE_ALI_RESCUE, 24, 23, 3, 273, 0, 1, 110); } @Override diff --git a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescuePlugin.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescuePlugin.java index 7327333fb..3f38c3489 100644 --- a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescuePlugin.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescuePlugin.java @@ -13,6 +13,7 @@ import core.game.world.GameWorld; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the plugin used to handle prince ali rescue quest interaction nodes. @@ -35,7 +36,7 @@ public class PrinceAliRescuePlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Prince Ali Rescue"); + final Quest quest = player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE); final int id = node instanceof Scenery ? ((Scenery) node).getId() : node instanceof NPC ? ((NPC) node).getId() : 0; switch (id) { case 925: diff --git a/Server/src/main/content/region/desert/bandits/handlers/BanditBehavior.kt b/Server/src/main/content/region/desert/bandits/handlers/BanditBehavior.kt deleted file mode 100644 index b918f675b..000000000 --- a/Server/src/main/content/region/desert/bandits/handlers/BanditBehavior.kt +++ /dev/null @@ -1,46 +0,0 @@ -package content.region.desert.bandits.handlers - -import core.api.* -import core.game.node.entity.Entity -import core.game.node.entity.combat.BattleState -import core.game.node.entity.npc.NPC -import core.game.node.entity.npc.NPCBehavior -import core.game.world.map.RegionManager -import core.tools.RandomFunction -import org.rs09.consts.NPCs - -class BanditBehavior : NPCBehavior(NPCs.BANDIT_1926) { - override fun tick(self: NPC): Boolean { - if (!self.inCombat() && RandomFunction.roll(3) && getWorldTicks() % 5 == 0) { - val players = RegionManager.getLocalPlayers(self, 5) - for (player in players) { - if (player.inCombat()) continue - if (hasGodItem(player, God.SARADOMIN)) { - sendChat(self, "Prepare to die, Saradominist scum!") - self.attack(player) - break - } - else if (hasGodItem(player, God.ZAMORAK)) { - sendChat(self, "Prepare to die, Zamorakian scum!") - self.attack(player) - break - } - } - } - return true - } - - override fun afterDamageReceived(self: NPC, attacker: Entity, state: BattleState) { - if (getAttribute(self, "alerted-others", false)) return - val otherBandits = RegionManager.getLocalNpcs(self, 3).filter { it.id == self.id } - for (bandit in otherBandits) { - if (!bandit.inCombat()) - bandit.attack(attacker) - } - setAttribute(self, "alerted-others", true) - } - - override fun onDeathStarted(self: NPC, killer: Entity) { - removeAttribute(self, "alerted-others") - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/bandits/handlers/BanditDialogue.kt b/Server/src/main/content/region/desert/bandits/handlers/BanditDialogue.kt new file mode 100644 index 000000000..2b6d8f8ac --- /dev/null +++ b/Server/src/main/content/region/desert/bandits/handlers/BanditDialogue.kt @@ -0,0 +1,63 @@ +package content.region.desert.bandits.handlers + +import content.region.desert.quest.deserttreasure.DesertTreasure +import core.api.getAttribute +import core.api.inInventory +import core.api.openDialogue +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class BanditDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return BanditDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, BanditDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.BANDIT_1926) + } +} +class BanditDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 100) + .npcl("So you're the one who freed Azzanadra from his prison? Thank you, kind @g[sir,lady]!") + .end() + + b.onQuestStages(DesertTreasure.questName, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) + .npcl("What do you want @g[lad,lass]?") + .playerl("I'm here on an archaeological expedition for the Museum of Varrock. I believe there may be some interesting artefacts in the area.") + .branch { player -> + return@branch (0..4).random() + }.let { branch -> + branch.onValue(0) + .npcl("You are a crazy @g[man,woman]. The only thing you will find out here in the desert is your death.") + .end() + branch.onValue(1) + .npcl("I have no interest in the world that betrayed my people. Search where you will, you will find nothing.") + .end() + branch.onValue(2) + .npcl("The gods forsake us, and drove us to this place. Anything of worth has been long gone.") + .end() + branch.onValue(3) + .npcl("I'm sure there are many secrets buried beneath the sands here. The thing about this being a desert, is that they're likely to stay that way.") + .end() + branch.onValue(4) + .npcl("Do I look like I care who you are or where you came from?") + .end() + } + + b.onPredicate { _ -> true } + .npcl("Get out of this village. You are not welcome here.") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt b/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt new file mode 100644 index 000000000..29a7809d1 --- /dev/null +++ b/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt @@ -0,0 +1,64 @@ +package content.region.desert.bandits.handlers + +import core.api.God +import core.api.hasGodItem +import core.api.inEquipment +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class BanditNPC(id: Int = NPCs.BANDIT_1926, location: Location? = null) : AbstractNPC(id, location) { + private val supportRange: Int = 3 + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return BanditNPC(id, location) + } + + override fun tick() { + if (!inCombat()) { + val players = RegionManager.getLocalPlayers(this, 5) + for (player in players) { + if (player.inCombat()) continue + if (hasGodItem(player, God.SARADOMIN)) { + sendChat("Time to die, Saradominist filth!") + attack(player) + break + } else if (hasGodItem(player, God.ZAMORAK) || inEquipment(player, Items.DAGONHAI_HAT_14499) || inEquipment(player, Items.DAGONHAI_ROBE_TOP_14497) || inEquipment(player, Items.DAGONHAI_ROBE_BOTTOM_14501)) { + sendChat("Prepare to suffer, Zamorakian scum!") + attack(player) + break + } + } + } + super.tick() + } + + //Clear target on death + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + } + + override fun onImpact(entity: Entity?, state: BattleState?) { + if (entity is Player) { + RegionManager.getLocalNpcs(entity, supportRange).forEach { + if (it.id == NPCs.BANDIT_1926 && !it.properties.combatPulse.isAttacking && it != this) { + it.sendChat("You picked the wrong place to start trouble!") + it.attack(entity) + } + } + } + super.onImpact(entity, state) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BANDIT_1926) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java b/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java index 577cb1151..a4a3c626a 100644 --- a/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java +++ b/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java @@ -23,6 +23,7 @@ import core.plugin.Plugin; import core.plugin.ClassScanner; import org.rs09.consts.Items; import org.rs09.consts.Sounds; +import content.data.Quests; /** @@ -158,11 +159,11 @@ public final class RugMerchantDialogue extends DialoguePlugin { } end(); destination = options.length == 1 ? options[0] : options[buttonId - 1]; - if (destination == RugDestination.UZER && !hasRequirement(player, "The Golem")) + if (destination == RugDestination.UZER && !hasRequirement(player, Quests.THE_GOLEM)) break; - else if (destination == RugDestination.BEDABIN_CAMP && !hasRequirement(player, "The Tourist Trap")) + else if (destination == RugDestination.BEDABIN_CAMP && !hasRequirement(player, Quests.THE_TOURIST_TRAP)) break; - else if (destination == RugDestination.SOPHANEM && !hasRequirement(player, "Icthlarin's Little Helper")) + else if (destination == RugDestination.SOPHANEM && !hasRequirement(player, Quests.ICTHLARINS_LITTLE_HELPER)) break; if(player.getEquipment().get(EquipmentContainer.SLOT_WEAPON) != null){ player.sendMessage(colorize("%RYou must unequip all your weapons before you can fly on a carpet.")); diff --git a/Server/src/main/content/region/desert/handlers/KalphiteQueenArea.kt b/Server/src/main/content/region/desert/handlers/KalphiteQueenArea.kt new file mode 100644 index 000000000..e59ae6c86 --- /dev/null +++ b/Server/src/main/content/region/desert/handlers/KalphiteQueenArea.kt @@ -0,0 +1,10 @@ +package content.region.desert.handlers + +import core.api.* +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction + +class KalphiteQueenArea : MapArea { + override fun defineAreaBorders() : Array { return arrayOf(ZoneBorders(3456, 9472, 3519, 9535, 0, true)) } + override fun getRestrictions() : Array { return arrayOf(ZoneRestriction.RANDOM_EVENTS) } +} diff --git a/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java b/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java index cff2ae212..be677c52d 100644 --- a/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java +++ b/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java @@ -95,8 +95,8 @@ public final class KalphiteQueenNPC extends AbstractNPC { public void finalizeDeath(Entity killer) { if (getId() == 1160) { removeAttribute("disable:drop"); - reTransform(); super.finalizeDeath(killer); + reTransform(); BossKillCounter.addtoKillcount((Player) killer, 1160); return; } diff --git a/Server/src/main/content/region/desert/handlers/KhardianDesertPlugin.java b/Server/src/main/content/region/desert/handlers/KhardianDesertPlugin.java deleted file mode 100644 index ed0ba3d8c..000000000 --- a/Server/src/main/content/region/desert/handlers/KhardianDesertPlugin.java +++ /dev/null @@ -1,47 +0,0 @@ -package content.region.desert.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.global.action.DoorActionHandler; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles interactions in the khardian desert. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KhardianDesertPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(6481).getHandlers().put("option:enter", this); - SceneryDefinition.forId(6545).getHandlers().put("option:open", this); - SceneryDefinition.forId(6547).getHandlers().put("option:open", this); - SceneryDefinition.forId(6551).getHandlers().put("option:use", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (node.getId()) { - case 6481: - player.teleport(new Location(3233, 9313, 0)); - break; - case 6545: - case 6547: - // player.getPacketDispatch().sendMessage("A mystical power has sealed this door..."); - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()); - break; - case 6551: - player.teleport(new Location(3233, 2887, 0)); - break; - } - return true; - } - -} diff --git a/Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java b/Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java index 73a4d96dd..74ae051e1 100644 --- a/Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java +++ b/Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java @@ -92,7 +92,7 @@ public class ShantayPassPlugin extends OptionHandler { case "quick-pass": if (player.getLocation().getY() > 3116) { if (!inInventory(player, Items.SHANTAY_PASS_1854, 1)) { - sendNPCDialogue(player, 838, "You need a Shantay pass to get through this gate. See Shantay, he will sell you one for a very reasonable price.", FacialExpression.NEUTRAL); + sendNPCDialogue(player, 838, "You need a Shantay pass to get through this gate. See Shantay, he will sell you one for a very reasonable price.", FacialExpression.NEUTRAL, false); return true; } if (!removeItem(player, Items.SHANTAY_PASS_1854, Container.INVENTORY)) return true; diff --git a/Server/src/main/content/region/desert/handlers/TollGateOptionPlugin.java b/Server/src/main/content/region/desert/handlers/TollGateOptionPlugin.java index a07dcbed6..2c9f338a9 100644 --- a/Server/src/main/content/region/desert/handlers/TollGateOptionPlugin.java +++ b/Server/src/main/content/region/desert/handlers/TollGateOptionPlugin.java @@ -13,6 +13,7 @@ import core.plugin.Plugin; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_ALKHARID_GATE; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_BASE; +import content.data.Quests; @Initializable public class TollGateOptionPlugin extends OptionHandler { @@ -20,7 +21,7 @@ public class TollGateOptionPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { if (option.equals("pay-toll(10gp)")) { - if (player.getQuestRepository().getQuest("Prince Ali Rescue").getStage(player) > 50) { + if (player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE).getStage(player) > 50) { player.getPacketDispatch().sendMessage("The guards let you through for free."); DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); } else { diff --git a/Server/src/main/content/region/desert/quest/curseofzaros/CurseOfZaros.kt b/Server/src/main/content/region/desert/quest/curseofzaros/CurseOfZaros.kt new file mode 100644 index 000000000..a57abef78 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/curseofzaros/CurseOfZaros.kt @@ -0,0 +1,189 @@ +package content.region.desert.quest.curseofzaros + +import core.game.dialogue.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player + +/** + * Curse of Zaros is a miniquest with no quest points or final dialogue + * + * Players get the ghostly set for fashionscape. + */ +class CurseOfZaros { + companion object { + const val attributePathNumber = "/save:miniquest:curseofzaros-pathnumber" // 1 of 3. + const val attributeValdezSpoke = "/save:miniquest:curseofzaros-valdezspoke" + const val attributeRennardSpoke = "/save:miniquest:curseofzaros-rennardspoke" + const val attributeKharrimSpoke = "/save:miniquest:curseofzaros-kharrimspoke" + const val attributeLennissaSpoke = "/save:miniquest:curseofzaros-lennissaspoke" + const val attributeDhalakSpoke = "/save:miniquest:curseofzaros-dhalakspoke" + const val attributeViggoraSpoke = "/save:miniquest:curseofzaros-viggoraspoke" + + // Ghostly Robes lost: http://youtu.be/YcwYOqfG1Ys + + fun withoutGhostspeak(d: DialogueLabeller) { + fun label(label: String) { d.label(label) } + fun loadLabel(player: Player, label: String) { d.loadLabel(player, label) } + fun player(vararg messages: String) { d.player(ChatAnim.NEUTRAL, *messages) } + fun npc(vararg messages: String) { d.npc(ChatAnim.NEUTRAL, *messages) } + fun exec(callback: (player: Player, npc: NPC) -> Unit) { d.exec(callback) } + + label("noghostspeak") + exec { player, npc -> + loadLabel(player, "noghostspeak" + (1..8).random()) + } + + label("noghostspeak1") + player("Hello there.") + npc("Wooo? Woooo woo woooooo wooooo woooowooo wooo woooooo woo!") + player("You don't say?") + npc("Woo! WOO WOOOOO WOOWOOWOO WOO WOOOOO!") + player("You don't say!") + npc("Wooowoowoooooo.... Woowoowoo? Woooo, wooowoo wooowooowooo!") + player("Well, I guess you didn't say.") + + label("noghostspeak2") + player("Hello there.") + npc("Wooo? Woooo woo woooooo wooooo woooowooo wooo woooooo woo!") + player("Yeah, I don't want to brag, but seriously: I am SOOOO rich....") + npc("Woo! WOO WOOOOO WOOWOOWOO WOO WOOOOO!") + player("Yeah, I know they say money doesn't bring you happiness, but sometimes I just like to open my bank account and look at all of the stuff I own and just think to myself:") + player("Wow. I am soooooo rich.") + npc("Wooowoowoooooo.... Woowoowoo? Woooo, wooowoo wooowooowooo!") + player("Well, us rich alive people don't want to waste all day spending time with you poor dead people, because I'm just sooooo rich I have to go now, and very possibly make myself even richer.") + player("See ya around ghosty!") + + label("noghostspeak3") + player("Hello there.") + npc("Wooo? Woooo woo woooooo wooooo woooowooo wooo woooooo woo!") + player("Yeah, I heard about that, ha-ha-ha!") + npc("Woo! WOO WOOOOO WOOWOOWOO WOO WOOOOO!") + player("With a MACKEREL? Ouch!") + npc("Wooowoowoooooo.... Woowoowoo? Woooo, wooowoo wooowooowooo!") + player("Well, it was fun. Let's do it again sometime.") + + label("noghostspeak4") + player("Hello there.") + npc("Wooo? Woooo woo woooooo wooooo woooowooo wooo woooooo woo!") + player("Why, thank you very much!") + npc("Woo! WOO WOOOOO WOOWOOWOO WOO WOOOOO!") + player("I know, but what are you going to do?") + npc("Wooowoowoooooo.... Woowoowoo? Woooo, wooowoo wooowooowooo!") + player("Well, I guess that's always true in the long run. See you around, weird invisible dead person!") + + label("noghostspeak5") + player("Hello there.") + npc("Wooo? Woooo woo woooooo wooooo woooowooo wooo woooooo woo!") + player("Yeah it's not bad, but I prefer cooked chicken.") + npc("Woo! WOO WOOOOO WOOWOOWOO WOO WOOOOO!") + player("Maybe, but nothing beats a home cooked pie!") + player("Man, I love pie!") + npc("Wooowoowoooooo.... Woowoowoo? Woooo, wooowoo wooowooowooo!") + player("You don't say? I never knew that. Well, I must be going, see you around.") + + // This is peak 2009 memez + label("noghostspeak6") + player("Hello there.") + npc("Wooo? Woooo woo woooooo wooooo woooowooo wooo woooooo woo!") + player("We get signal!") + npc("Woo! WOO WOOOOO WOOWOOWOO WOO WOOOOO!") + player("Somebody set up us the bomb!") + npc("Wooowoowoooooo.... Woowoowoo? Woooo, wooowoo wooowooowooo!") + player("You have no chance to survive make your time.") + npc("Woo?") + player("All your base are belong to us.") + + label("noghostspeak7") + player("Hey, don't think you can talk to me like that!") + npc("Woo! WOO WOOOOO WOOWOOWOO WOO WOOOOO!") + player("Are you threatening me?") + npc("Wooowoowoooooo.... Woowoowoo? Woooo, wooowoo wooowooowooo!") + player("Just because you're already dead, doesn't mean I can't find a way to hurt you ghosty!") + + label("noghostspeak8") + player("No, I've never been there in my life and CERTAINLY didn't steal anything when I was!") + npc("Woo! WOO WOOOOO WOOWOOWOO WOO WOOOOO!") + player("Are you calling me a liar?!?! I have never stolen a thing in my life, and I resent the implication that I am the kind of morally depraved individual that would steal someone else's hard earned") + player("money from their very pockets!") + player("Or cakes. Or fur. Repeatedly. For long periods at a time.") + npc("Wooowoowoooooo.... Woowoowoo? Woooo, wooowoo wooowooowooo!") + player("Well if you are going to take that attitude, then I have nothing further to say on the matter, and bid you good day!") + } + + + fun wrongPath(d: DialogueLabeller) { + fun label(label: String) { d.label(label) } + fun loadLabel(player: Player, label: String) { d.loadLabel(player, label) } + fun player(vararg messages: String) { d.player(ChatAnim.NEUTRAL, *messages) } + fun npc(vararg messages: String) { d.npc(ChatAnim.NEUTRAL, *messages) } + fun exec(callback: (player: Player, npc: NPC) -> Unit) { d.exec(callback) } + + label("wrongpath") + exec { player, npc -> + loadLabel(player, "wrongpath" + (1..7).random()) + } + + label("wrongpath1") + player("Hello there.") + npc("The endless tragedy of fate...", "Why must you torment me so?") + player("Alright, alright, calm down, calm down. All I said was 'hello'!") + + label("wrongpath2") + player("Hello there.") + npc("You can see me?") + player("Uh... yes?") + npc("And you understand my words?") + player("Well, most of them...") + npc("This is incredible! How can such a thing have come to pass?") + player("What can I say? I'm a professional.") + + label("wrongpath3") + player("Hello there.") + npc("Hello back at you.") + player("So what's a nice ghost like you doing in a place like this?") + npc("I suppose you think that's funny?") + player("Well... Mildly amusing I guess.") + npc("I don't think I want to talk to you anymore.") + + label("wrongpath4") + player("Hello there.") + npc("Hello stranger. It is rare indeed that I meet one who can see my presence, let alone one who can understand my words.") + player("Soooo.... Is it fun being a ghost?") + npc("Does it look like fun to you?") + player("Erm... Well, yes actually.") + npc("Then you are a fool, and I will waste no more words upon you.") + player("What, not even 'goodbye'?") + npc(" ") + player("Sheesh, what a grouch. You'd think you'd have more of a sense of humour being dead and all.") + + label("wrongpath5") + player("Hello there.") + npc("Mortal... Take heed of my example, and waste not your life, lest you may suffer the same fate as myself...") + player("Huh? You mean someone did this to you?") + npc("You have ascertained the truth in my words...") + player("So who did it to you? And why?") + npc("Events of moons past, I remember not clearly...") + player("Fine help you are then.") + + label("wrongpath6") + player("Hello there.") + npc("Hello stranger.") + player("So.... Invisible ghost haunting the same place for thousands of years, huh?") + npc("You have no idea...") + player("Well, bad luck and all that. See ya around!") + + label("wrongpath7") + player("Hello there.") + npc("Hello.") + player("So you're a ghost, huh?") + npc("Apparently.") + player("In which case I only have one thing to say to you:") + npc("...what?") + player("Guess you don't have the amulet of humanspeak, huh?") + npc("...huh?") + player("Yeah, that's right. WOO! WOOOWOOO WOO WOOOOWOO! Got no comeback for that, have you?") + npc("You are very strange...") + + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostDhalakDialogue.kt b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostDhalakDialogue.kt new file mode 100644 index 000000000..d8c89c1d0 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostDhalakDialogue.kt @@ -0,0 +1,149 @@ +package content.region.desert.quest.curseofzaros + +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class MysteriousGhostDhalakDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return MysteriousGhostDhalakDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MysteriousGhostDhalakDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(2385, 2386, 2387) + } +} + +class MysteriousGhostDhalakDialogueFile : DialogueLabeller() { + override fun addConversation() { + exec { player, npc -> + if (inEquipment(player, Items.GHOSTSPEAK_AMULET_552) || inEquipment(player, Items.GHOSTSPEAK_AMULET_4250)){ + if (2384 + getAttribute(player, CurseOfZaros.attributePathNumber, 0) == npc.id) { + if (getAttribute(player, CurseOfZaros.attributeDhalakSpoke, false)) { + if (inInventory(player, Items.GHOSTLY_HOOD_6109) || inEquipment(player, Items.GHOSTLY_HOOD_6109)) { + loadLabel(player, "subsequenttime") + } else { + loadLabel(player, "lostghostlything") + } + } else { + loadLabel(player, "firsttime") + } + } else { + loadLabel(player, "wrongpath") + } + } else { + loadLabel(player, "noghostspeak") + } + } + + CurseOfZaros.withoutGhostspeak(this) + + CurseOfZaros.wrongPath(this) + + label("firsttime") + player("Hello Dhalak.") + npc("You see my form, hear my words, and know my name, yet your face I recognise not...") + npc("Be you some mighty sorcerer to bind me so?") + player("Um... Well, not really...") // player("Well, I don't mean to brag, but I guess I am with my level 99 magic...") if you are 99 lvl magic but calm the fuck down showoff + player("But that is besides the point. It is not I who has trapped you here as a ghost.") + npc("Then how comes it to be that you know my name stranger?") + player("Lennissa told me about you, and where to find you?") + npc("Lennissa? Oh that poor sweet girl... Has my foolishness cursed her as well as myself???") + player("Your foolishness?") + npc("The story shames me stranger, I wouldst rather keep it unto myself.") + options( + DialogueOption("tellmeyourstory", "Tell me your story", skipPlayer = true), + DialogueOption("goodbye", "Goodbye then", skipPlayer = true), + ) + + label("goodbye") + player("Well, that's all fascinating, but I just don't particularly care. Bye-bye.") + + label("tellmeyourstory") + player("Look, I don't want to force you into telling me, but perhaps sharing it with someone might relieve your guilt?") + npc("Aye... Perhaps it might at that.") + npc("So what has Lennissa told you of the events of the day this curse befell me?") + player("Well, she told me that she was working as an undercover agent of Saradomin amongst the followers of some 'Empty Lord', and when news of the theft of a god-weapon reached her, she passed the message on to") + player("you instead of taking it to Saradomin because she was scared her cover might be blown.") + npc("Aye, that is a fair account of events...") + player("But I don't understand why you didn't take her message to Saradomin?") + npc("Stranger, my foolishness was a result of my respect for Saradomin, not as a result of any attempted treachery!") + player("So why didn't you pass on Lennissa's message? As I understand it something happened with that staff that could have been avoided if you had passed her message on!") + npc("(sigh) I know not what occurred with that god-weapon, but I have my suspicions...") + npc("Let me explain myself. I was Lennissa's immediate superior, and I was often her contact for missions.") + npc("Because of this role, I had access to a larger picture of what was happening than she herself did, and I was not only well aware that her presence amongst the enemy camp had been detected, but I was also well aware that") + npc("there was a growing faction amongst them who were plotting to overthrow their master.") + player("Their master being...?") + npc("That I will not tell you. I will tempt the fates no more than I already have done.") + npc("But anyway, it had come to my attention that the Mahjarrat who had been liberated from the control of Icthlarin did not much appreciate one form of slavery to another, and under the leadership of the mighty") + npc("Zamorak were making plans to overthrow their master and take his power for themselves.") + npc("Now, as powerful, long-lived and evil as they were, they were still just mortal, and I made the decision that it would be of benefit to my Lord Saradomin for his mightiest rival to be distracted by such internal conflicts.") + player("So that is why you decided not to pass the report from Lennissa on?") + npc("Yes, but my guilt is more than simply inaction...") + npc("I knew that with such a weapon, Zamorak would be capable of launching an attack that could actually stand a chance of success, but I also knew that he would never be able to get a chance to use it in a battle for") + npc("being a god-weapon its very presence would have sung out to their leader.") + player("I'm guessing you did something about that, then?") + npc("Indeed I did. To my eternal shame, I decided that I would assist Zamorak and his henchmen in their battle, by secretly casting a spell of concealment upon the staff so that") + npc("they might use it secretly against their master.") + player("So Zamorak knew about this?") + npc("No, nobody except myself, and now you, knew that I cast such a spell....") + npc("Had I known what a threat to my Lord Saradomin Zamorak would later become, I wouldst have taken the message to Saradomin immediately! Alas, it is all too easy to see your mistakes after you") + npc("have made them...") + player("I'm confused. What exactly happened with this staff anyway? And why have all these various random people been cursed because of it?") + npc("I cannot answer your question with anything other than my own suppositions, but I do know of one who might be able to, and if any man deserved to be cursed for their actions that day, it was he!") + player("Who are you speaking of?") + npc("His name was Viggora. He was an evil man, brutal and vicious, and deadly with a blade.") + npc("He was one of the few humans Zamorak allowed to rise to a position of power amongst his rebels, possibly because he imitated those same qualities of Zamorak.") + npc("If anyone knows what Zamorak did with that god- weapon to have caused this curse to have befallen us, it would have been he, for he would have been fighting on Zamorak's very right hand side in their rebellion.") + npc("Please, if this curse can be lifted, find Viggora and find out what he has wrought upon us! I have no wealth nor magic to aid you, but take my hood as reward;") + exec { player, npc -> + setAttribute(player, CurseOfZaros.attributeDhalakSpoke, true) + addItemOrDrop(player, Items.GHOSTLY_HOOD_6109) + } + npc("it has served me well these centuries past, and may bring you luck.") + player("Where would you suggest I look for Viggora?") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("subsequenttime") + player("Dhalak, where can I find the swordsman Viggora?") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("curseofzaros1") + npc("Ah, the evil swordsman Viggora... A rogue like him would probably flock to his own kind.") + player("Okay, well I'll try and find him for you then.") + + label("curseofzaros2") + npc("Ah, the evil swordsman Viggora... Perhaps he has returned to his castle in the dark lands?") + player("Okay, well I'll try and find him for you then.") + + label("curseofzaros3") + npc("Ah, the evil swordsman Viggora... Paddewwa was where he fought many battles, perhaps he has returned to one of his old haunts?") + player("Okay, well I'll try and find him for you then.") + + + label("lostghostlything") + + player(ChatAnim.SAD, "Could I have that hat again? I seem to have misplaced it", "somewhere...") + exec { player, npc -> + addItemOrDrop(player, Items.GHOSTLY_HOOD_6109) + } + npc("Certainly, I am not sure how, but it returned to me by", "some magic or other.") + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostKharrimDialogue.kt b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostKharrimDialogue.kt new file mode 100644 index 000000000..4d825f193 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostKharrimDialogue.kt @@ -0,0 +1,143 @@ +package content.region.desert.quest.curseofzaros + +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class MysteriousGhostKharrimDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return MysteriousGhostKharrimDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MysteriousGhostKharrimDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(2388, 2389, 2390) + } +} + +class MysteriousGhostKharrimDialogueFile : DialogueLabeller() { + override fun addConversation() { + exec { player, npc -> + if (inEquipment(player, Items.GHOSTSPEAK_AMULET_552) || inEquipment(player, Items.GHOSTSPEAK_AMULET_4250)){ + if (2387 + getAttribute(player, CurseOfZaros.attributePathNumber, 0) == npc.id) { + if (getAttribute(player, CurseOfZaros.attributeKharrimSpoke, false)) { + if (inInventory(player, Items.GHOSTLY_BOOTS_6106) || inEquipment(player, Items.GHOSTLY_BOOTS_6106)) { + loadLabel(player, "subsequenttime") + } else { + loadLabel(player, "lostghostlything") + } + } else { + loadLabel(player, "firsttime") + } + } else { + loadLabel(player, "wrongpath") + } + } else { + loadLabel(player, "noghostspeak") + } + } + + CurseOfZaros.withoutGhostspeak(this) + + CurseOfZaros.wrongPath(this) + + label("firsttime") + player("Hello. So you must be Kharrim the messenger.") + npc("How do you know my name, stranger?") + player("Well now... I had a very interesting chat with Rennard the thief.") + player("It seems you redirected his message regarding a certain god-weapon for your own ends.") + npc("So THAT is what this is about... I should have known the deal was too good to have no repercussions...") + player("It seems as though you might be responsible for this curse that has befallen you by not delivering Rennard's message to the correct person.") + npc("Ha! That is not a truthful assessment of the story... You might think differently if you had heard my side of events.") + options( + DialogueOption("tellmeyourstory", "Tell me your story", skipPlayer = true), + DialogueOption("goodbye", "Goodbye then", skipPlayer = true), + ) + + label("goodbye") + player("Well, that's all fascinating, but I just don't particularly care. Bye-bye.") + + label("tellmeyourstory") + player("Please let me hear your side of the story then...") + npc("Well, if you have spoken to Rennard, then you will know that he had somehow managed to obtain a very valuable weapon, and was looking for buyers.") + npc("What he probably didn't tell you, was that he met me in a drunken stupor in some smoke filled tavern, and I offered to arrange a purchaser for his item, in exchange for a small finders fee.") + player("So you knew what the staff was?") + npc("The god-staff of Armadyl? Well, of course I did.") + npc("Honestly, you would have to be pretty slow-witted to not recognise a legendary artefact such as that.") + player("Wait, I don't understand, Rennard said that he had a buyer already in mind, and that you diverted his message to a General Zamorak instead?") + npc("Ha! Here is a word of advice for you adventurer; Never trust the words of a drunk.") + npc("Whatever he might have thought he was doing with it, in the end all that happened was he left me to arrange a purchaser for the item.") + player("So you thought you would offer it to General Zamorak?") + npc("Ah yes, Lord Zamorak. He was merely a mortal back then, you know?") + npc("Yet I could see great things in store for him even then. He had a kind of brilliant ruthlessness... And that special kind of vicious streak you see so rarely...") + npc("Well anyway, when given the task of selling a weapon forged by the very gods themselves, I naturally thought of Zamorak as a potential buyer.") + npc("I was a messenger in his employ anyway, so it was a mere trifle to find him and deliver the news, and I knew of his particular interest in armour and weaponry of all kinds.") + npc("Yes, he was always quite the connoisseur when it came to weaponry...") + npc("But I digress. I let Lord Zamorak know that there was some drunken fool with an artefact of incredible power that could probably be bought off with a few jewels and trinkets,") + npc("and he escorted me to the tavern and made the purchase there and then.") + npc("It was a satisfactory deal all around, I got a share of the sale price from Rennard, and I greatly increased my prestige amongst Zamorak and his followers.") + npc("But maybe... Perhaps the events that followed were responsible for my cursed state...") + player("Events that followed?") + npc("I can not tell you of them precisely, for I myself was not there to witness them.") + npc("I am after all, simply a messenger. When... 'it' happened, I was busy elsewhere delivering a message from Zamorak to the rest of his Mahjarrat ilk.") + player("When 'it' happened? What was 'it'?") + npc("As I have explained, I was not there, and I do not know what Zamorak did with the staff, but whatever it was resulted in his banishment by the other gods for many years.") + npc("The very strange thing was that Saradomin must have known about it, whatever it was, before it even happened...") + player("Really? Why do you say that?") + npc("Well, it was the contents of the message I was returning to Zamorak;") + npc("Lucien seemed quite certain that there was a spy for Saradomin somewhere amongst his followers named Lennissa.") + npc("If whatever happened to the staff caused this curse to befall me, then it is certain that she too would have been afflicted, because she would have been in the very heart of the action.") + npc("Hmmm....") + npc("You have given me much to think on adventurer. I would like to reward you with my sturdy messenger boots, may they aid you in your travels.") + exec { player, npc -> + setAttribute(player, CurseOfZaros.attributeKharrimSpoke, true) + addItemOrDrop(player, Items.GHOSTLY_BOOTS_6106) + } + player("But where can I find this Lennissa?") + npc("Ah yes, the whereabouts of the treacherous Lennissa...") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("subsequenttime") + player("Hello again Kharrim.") + player("Can you remind me where to find Lennissa?") + npc("Ah yes, the whereabouts of the treacherous Lennissa...") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("curseofzaros1") + npc("Well, she was always sickeningly obedient to Saradomin, so I would expect her to have run to some great place of worship of him if she was affected by the curse to try and gain his blessing.") + player("Okay, well I'll try and find her for you then.") + + label("curseofzaros2") + npc("According to Lucien's intelligence report, she had been uncovered as a spy by her constant use of ships to ferry information...") + npc("It is entirely possible she would be located somewhere coastal to this day!") + player("Okay, well I'll try and find her for you then.") + + label("curseofzaros3") + npc("Well, we knew little about her or she would have been caught and exposed as a traitor and a spy, but Lucien did mention that he had evidence that she was a great fan of ball games...") + player("Okay, well I'll try and find her for you then.") + + + label("lostghostlything") + player(ChatAnim.SAD, "I lost those boots you gave me...", "Can I have some more please?") + exec { player, npc -> + addItemOrDrop(player, Items.GHOSTLY_BOOTS_6106) + } + npc(ChatAnim.SAD, "How strange...", "They seemed to return to me when you lost them...") + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostLennissaDialogue.kt b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostLennissaDialogue.kt new file mode 100644 index 000000000..74c3657d7 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostLennissaDialogue.kt @@ -0,0 +1,155 @@ +package content.region.desert.quest.curseofzaros + +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class MysteriousGhostLennissaDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return MysteriousGhostLennissaDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MysteriousGhostLennissaDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(2391, 2392, 2393) + } +} + +class MysteriousGhostLennissaDialogueFile : DialogueLabeller() { + override fun addConversation() { + exec { player, npc -> + if (inEquipment(player, Items.GHOSTSPEAK_AMULET_552) || inEquipment(player, Items.GHOSTSPEAK_AMULET_4250)){ + if (2390 + getAttribute(player, CurseOfZaros.attributePathNumber, 0) == npc.id) { + if (getAttribute(player, CurseOfZaros.attributeLennissaSpoke, false)) { + if (inInventory(player, Items.GHOSTLY_ROBE_6108) || inEquipment(player, Items.GHOSTLY_ROBE_6108)) { + loadLabel(player, "subsequenttime") + } else { + loadLabel(player, "lostghostlything") + } + } else { + loadLabel(player, "firsttime") + } + } else { + loadLabel(player, "wrongpath") + } + } else { + loadLabel(player, "noghostspeak") + } + } + + CurseOfZaros.withoutGhostspeak(this) + + CurseOfZaros.wrongPath(this) + + label("firsttime") + player("Hello. You would be Lennissa, I take it?") + npc("Who are you? Where did you hear that name? How comes it that you can see and speak to me?") + player("Well, a ghost called Kharrim directed me towards you.") + npc("So that weasel Kharrim has been blighted by this curse too?") + npc("Ha, a good thing too. If anybody deserved such a fate it would be one such as him.") + player("I guess you didn't get along then?") + npc("No, evil scum such as he should never have been allowed to walk this world.") + npc("What lies has he told you to come here? Have you come to try and kill me?") + player("Well, I'm not sure I could if I tried, but that is not why I have come to you.") + npc("Then speak, and speak well, for I may yet be dead, but am still a danger to those who cross me.") + player("Actually, I'm trying to work out why all of you invisible ghosts seem to have been cursed.") + player("I'm not sure how exactly, but the trail seems to have led me to you...") + npc("That makes no sense... I served Saradomin faithfully my entire life, then all of a sudden I find myself reduced to this state!") + player("Well, it seems as though that may have been the cause...") + npc("What? Explain yourself.") + player("As I understand it, it was something to do with the Staff of Armadyl, and your refusal to tell Saradomin that it had been stolen...") + npc("What? But... But that is not how it happened at all!") + options( + DialogueOption("tellmeyourstory", "Tell me your story", skipPlayer = true), + DialogueOption("goodbye", "Goodbye then", skipPlayer = true), + ) + + label("goodbye") + player("Well, that's all fascinating, but I just don't particularly care. Bye-bye.") + + label("tellmeyourstory") + player("Then please, go ahead and tell me the events of that day in your own words...") + npc("Let me see... I had been working as a spy for my Lord Saradomin, amongst the forces of... the Empty Lord.") + player("'The Empty Lord'? Who is that?") + npc("I will not give you his name, for to do so would give him power here.") + npc("Let us just say that he was a fearsome deity, whose strength was greater than all the gods we knew of on this realm at the time.") + npc("It is probably worth mentioning that at this time we had no knowledge of the mysterious nature god Guthix.") + player("So he was stronger than Saradomin?") + npc("As Saradomin was, yes. As Saradomin is now? Who can say?") + player("I see... Please, continue.") + npc("As I say, I was working as a spy within the very camp of my Lord's enemies.") + npc("I knew that should I have been caught, I risked being killed upon the spot, but my combat skills were always formidable, and if truth be told, there was a fair amount of dissent amongst... 'his' followers anyway.") + player("How do you mean 'dissent'?") + npc("Ah, to not understand this, you must have led a sheltered life...") + npc("Let me tell you this: Evil will always breed more evil, and will never be satisfied with what it has.") + npc("The Empty Lord chose to ally himself with the dark creatures of this world, fully aware that their own natures would cause them to rally against his rule, and take every opportunity they could to betray him.") + npc("This has always been the nature of evil. Perhaps he thought his power could prevent such treachery?") + npc("This allowed me freedom amongst their camp, for it was always easy to point the finger of suspicion at some unsuspecting necromancer or foolish Mahjarrat if it seemed as though my activities had been discovered.") + npc("Similarly, should I ever be caught in the act of my sabotage, it was all too easy to bribe whoever found me or persuade them into believing it was just some minor treachery of my own, rather than my work for my") + npc("Lord Saradomin.") + player("Okay... Well, that makes sense, but I don't understand what the Staff of Armadyl had to do with this...?") + npc("As I have told you, the Empty Lord was extremely powerful, but not so powerful that he could rule over the other deities of this world without opposition.") + npc("Should he have made a move against any other god, then he could still have been easily brought down by the combined efforts of the others.") + npc("The theft of Armadyl's staff changed this however.") + npc("If he had taken possession of this god-weapon, then his power would have been so great that he could have overthrown all on this world, and made it into his own image!") + npc("I could not allow such a thing to happen!") + npc("I went immediately to my comrade Dhalak, the mage, and told him that a message had come to the lair offering this weapon for sale!") + npc("I knew that as soon as my Lord Saradomin heard this, he would contact Armadyl to inform them of the theft, and the matter would have been resolved quickly and discreetly.") + player("So you passed this information to Dhalak instead of taking it to Saradomin yourself?") + npc("To my eternal shame, I indeed failed my Lord Saradomin...") + npc("I could not risk taking the message directly, for I feared my disguise had been uncovered.") + npc("Lucien particularly had been taking an unhealthy interest in my activities, and I had a gut feeling that to make any obvious moves against the Empty Lord would have been my undoing.") + npc("But Dhalak was a noble man! I cannot believe that he would not have taken my message immediately to Lord Saradomin!") + player("Well, it seems like he didn't, but I don't know why not...") + npc("Please adventurer, discover what foul fate must have befallen him for him to have neglected his duty!") + npc("I have not much to offer as reward, but for these spare robes I wore while on assignment...") + exec { player, npc -> + setAttribute(player, CurseOfZaros.attributeLennissaSpoke, true) + addItemOrDrop(player, Items.GHOSTLY_ROBE_6108) + } + npc("Please find him and discover why I am cursed like this!") + player("Where would I be able to find this Dhalak then?") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("subsequenttime") + player("Hello Lennissa. Can you remind me where to find Dhalak?") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("curseofzaros1") + npc("Dhalak? Well, he was always a knowlegeable mage, so if this curse has befallen him as well, I would suspect he would be researching how to free himself of it.") + npc("I would look for a library to find him if I were you.") + player("Okay, well I'll try and find him for you then.") + + label("curseofzaros2") + npc("Dhalak? He was always a loyal follower of Saradomin... I think he would have found an altar to Saradomin so that he may pray for this curse to be lifted.") + player("Okay, well I'll try and find him for you then.") + + label("curseofzaros3") + npc("Dhalak? I know not where, but he would try and make the most of his situation if he has been cursed, and find a place to lift his spirits!") + player("Okay, well I'll try and find him for you then.") + + + label("lostghostlything") + player(ChatAnim.SAD, "Could I have that rob bottom again? I seem to have", "misplaced it somewhere...") + exec { player, npc -> + addItemOrDrop(player, Items.GHOSTLY_ROBE_6108) + } + npc(ChatAnim.SAD, "Certainly, I am not sure how, but it returned to me by", "some magic or other.") + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostRennardDialogue.kt b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostRennardDialogue.kt new file mode 100644 index 000000000..83f8383ef --- /dev/null +++ b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostRennardDialogue.kt @@ -0,0 +1,151 @@ +package content.region.desert.quest.curseofzaros + +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class MysteriousGhostRennardDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return MysteriousGhostRennardDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MysteriousGhostRennardDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(2382, 2383, 2384) + } +} + +class MysteriousGhostRennardDialogueFile : DialogueLabeller() { + override fun addConversation() { + exec { player, npc -> + if (inEquipment(player, Items.GHOSTSPEAK_AMULET_552) || inEquipment(player, Items.GHOSTSPEAK_AMULET_4250)){ + if (2381 + getAttribute(player, CurseOfZaros.attributePathNumber, 0) == npc.id) { + if (getAttribute(player, CurseOfZaros.attributeRennardSpoke, false)) { + if (inInventory(player, Items.GHOSTLY_GLOVES_6110) || inEquipment(player, Items.GHOSTLY_GLOVES_6110)) { + loadLabel(player, "subsequenttime") + } else { + loadLabel(player, "lostghostlything") + } + } else { + loadLabel(player, "firsttime") + } + } else { + loadLabel(player, "wrongpath") + } + } else { + loadLabel(player, "noghostspeak") + } + } + + CurseOfZaros.withoutGhostspeak(this) + + CurseOfZaros.wrongPath(this) + + label("firsttime") + player("Hello. You must be Rennard.") + npc("What be this? You both see me and hear me, and also know my name?") + npc("Tell me what devilry brings you here, and be quick about it afore I gut you like a fish!") + player("Well, apart from the fact I ain't scared of no ghost, I am here because I have spoken to Valdez.") + npc("Valdez? Who be that? Some foul necromancer?") + player("No, he was a ghost I met near Glarial's tomb.") + player("He seems convinced that the artefact you stole from him is responsible for him becoming cursed to be an invisible ghost.") + player("Seems like he might be onto something too, given the state of you.") + npc("A curse ye say... Aye, that makes sense...") + npc("And there was I thinking my fate be the fault of the thieving and murdering I spent me life a-doing...") + npc("So it all began the day I stole that staff, ye say? Aye, that be a story I have never told another soul...") + options( + DialogueOption("tellmeyourstory", "Tell me your story", skipPlayer = true), + DialogueOption("goodbye", "Goodbye then", skipPlayer = true), + ) + + label("goodbye") + player("Well, that's all fascinating, but I just don't particularly care. Bye-bye.") + + label("tellmeyourstory") + player("Why don't you tell me what happened? I might be able to help...") + npc("Well, I was making me merry way along, having just pulled off a glorious jewellery heist from a bunch of stinking dwarves...") + player("Hey, that's no way to talk about dwarves! Some of my best friends are short!") + npc("Ah, yer misunderstand me [lad/lass], I wasn't generalising about the whole dwarf species, I had just stolen a bundle of jewels from a very specific group of dwarves who happened to have an odious stench about them!") + player("Oh. Well I guess that's okay then. Please continue.") + npc("Well, as I headed on me merry way, hoping the foul odours that lingered in me nostrils would soon pass, I see in front of me this explorer fella, all decked out in in his fine clothing, and carrying some long package") + npc("bundled in rags.") + npc("So I says to meself, 'Rennard', I says, 'Rennard, why would some fella all dressed in his finery be carrying something wrapped in dirty rags?'.") + npc("So I thinks to meself a little more, 'Rennard', I thinks, 'Rennard, maybe that fella has something valuable in there, and he covered it in dirty rags so it don't look so valuable'.") + npc("So I coshed this fella round the back of his head with me bag of jewels, picked up his package and was on me merry way afore he comes to.") + player("So what happened then?") + npc("Well, I makes me way to the closest tavern I knew of that catered to my sort of people...") + player("You mean thieves?") + npc("Right ye are, so I makes me way to the nearest friendly tavern, and unwraps the bundle to see what it had inside.") + player("The Staff of Armadyl?") + npc("Was it? Ah, I never knew that...") + npc("Anysways, I unwraps this staff, and sees it be a god- weapon; I may be just a common thief, but I recognises a weapon not made by mortal hands when I sees one.") + player("So what did you do then?") + npc("Well, I knew such a weapon would be of great value to...") + npc("Now that's funny. Can't remember his name, now. The powerful god, lived in the North-east. Took the Mahjarrat away from under Icthlarin's") + npc("control.") + npc("Anyway, I hired me a messenger to go off and let him know I had something I was prepared to sell that I thought he'd be interested in...") + npc("Now WHY can't I remember his name? Very odd that...") + player("So you sold the staff to this god you can't remember?") + npc("Well, that's the other funny thing... He never showed up, he sent some General or other instead.") + npc("Hmmm... You know... Thinking back on that, I'm getting the feeling that messenger did a little doublecross of his own, and took") + npc("me message to the wrong fella.") + player("So what was this General's name?") + npc("His name was Zamorak. I remember thinking at the time it was odd, because the fella was a mighty powerful warrior, but was never fully trusted by...") + npc("WHY can't I remember his name???") + player("So you suspect the messenger might have taken the message to the wrong person? So you think it was an accident or deliberate?") + npc("Well that I can't tell ya, but if something happened to get me cursed, it's likely the messenger would know what more than me.") + npc("His name was Kharrim, and if he caused me to be stuck like this, I'm gonna fillet him like a dog, ghost or no!") + npc("I tell ye what, you've given me much to think about so I'd like to offer yer a gift; Here, take these, they were the gloves I stole me first cake with, they might bring yer some luck.") + exec { player, npc -> + setAttribute(player, CurseOfZaros.attributeRennardSpoke, true) + addItemOrDrop(player, Items.GHOSTLY_GLOVES_6110) + } + player("Where can I find this Kharrim then?") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("subsequenttime") + player("Hello again Rennard.") + npc("Ah, it be you again! What can I do fer ya?") + player("Can you tell me where I can find Kharrim again?") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("curseofzaros1") + npc("Kharrim the messenger... Well, he was always a devoted follower of old General Zamorak, and if I remember rightly Zamorak set up a small base in an old temple near Dareeyak...") + npc("You might want to check around there.") + player("Okay, well I'll try and find him for you then.") + + label("curseofzaros2") + npc("Kharrim the messenger... Last I'd heard of him, he'd headed off to Carrallagar to seek his fortune. Ya might want to check around there somewhere.") + player("Okay, well I'll try and find him for you then.") + + label("curseofzaros3") + npc("Kharrim the messenger... The last I'd heard of that weasel he was claiming he'd found some underground deposit of runite ore guarded by demons and dragons.") + npc("I suspect he was pulling some scam or other, but if you know of such a place, that might be a good place to start checking.") + player("Okay, well I'll try and find him for you then.") + + + label("lostghostlything") + player(ChatAnim.SAD, "I lost those gloves you gave me...", "Can I have some more please?") + exec { player, npc -> + addItemOrDrop(player, Items.GHOSTLY_GLOVES_6110) + } + npc(ChatAnim.SAD, "It seems as though the curse that keeps me here", "extends to my very clothing...") + npc("Here, take them, some evil power returned them to", "me...") + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostValdezDialogue.kt b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostValdezDialogue.kt new file mode 100644 index 000000000..0383402f7 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostValdezDialogue.kt @@ -0,0 +1,136 @@ +package content.region.desert.quest.curseofzaros + +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class MysteriousGhostValdezDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return MysteriousGhostValdezDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MysteriousGhostValdezDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(2381) // wrong const SICK_LOOKING_SHEEP_4_2381 -> should be correct MYSTERIOUS_GHOST_2381 + } +} + +class MysteriousGhostValdezDialogueFile : DialogueLabeller() { + override fun addConversation() { + exec { player, npc -> + if (inEquipment(player, Items.GHOSTSPEAK_AMULET_552) || inEquipment(player, Items.GHOSTSPEAK_AMULET_4250)){ + if (getAttribute(player, CurseOfZaros.attributeValdezSpoke, false)) { + if (inInventory(player, Items.GHOSTLY_ROBE_6107) || inEquipment(player, Items.GHOSTLY_ROBE_6107)) { + loadLabel(player, "subsequenttime") + } else { + loadLabel(player, "lostghostlything") + } + } else { + loadLabel(player, "firsttime") + } + } else { + loadLabel(player, "noghostspeak") + } + } + + CurseOfZaros.withoutGhostspeak(this) + + label("firsttime") + player("Hello.") + npc(ChatAnim.EXTREMELY_SHOCKED, "H-hello!") + player(ChatAnim.THINKING, "So what's up?") + npc(ChatAnim.EXTREMELY_SHOCKED, "I cannot believe it!", "You can see me?", "You understand my words?") + player("Sure can.", "So why are you hanging around here?") + npc(ChatAnim.SAD, "My tale is one of woe...", "No doubt you will have little interest in hearing it...") + npc(ChatAnim.SAD, "Though it has been so many moons since last I had", "company in this endless non-life...") + options( + DialogueOption("tellmeyourstory", "Tell me your story", skipPlayer = true), + DialogueOption("goodbye", "Goodbye then", skipPlayer = true), + ) + + label("goodbye") + player("Well, that's all fascinating, but I just don't particularly care. Bye-bye.") + + label("tellmeyourstory") + player("Well, actually I would like to know what happened to you to turn you into an invisible ghost.") + player(ChatAnim.SUSPICIOUS, "If only so I can make sure it doesn't happen to me...") + npc("My name is Valdez. I served my Lord Saradomin faithfully for many years, as an explorer of this strange land we had been brought to.") + npc("I remember the day this curse fell upon me clearly... I had just discovered a huge temple, hidden below the ground, of one of Saradomin's compatriots.") + npc("I am unsure who had built it, or why they had left it seemingly abandoned, but inside I located a great treasure...") + npc("It was the godstaff of Armadyl.", "Oh, how I rue my choice that day!") + player("Choice?") + npc("Aye, stranger.", "I chose that day to take it so that my Lord Saradomin's", "power and prestige could be increased by its possession.") + npc("A god-weapon!", "Do you have any comprehension of the difficulty and", "rarity in obtaining such a thing?") + npc("To find such an artefact of power just lying around, it is almost incomprehensible...") + npc("So it was there in that deserted temple that I made my", "choice.", "I took the staff, and left that temple for Entrana", "immediately.") + npc("This was the cause of my cursed state.") + player("What, you mean you gave it to Saradomin and in return he cursed you???") + player("Seems kind of ungrateful if you ask me...") + npc("No stranger, you misunderstand completely...", "Firstly my gracious Lord would never treat anyone in", "such a manner;", "If he felt it was beyond my bounds as a mere mortal") + npc("to hold such an artefact, he would simply have commanded me to return it to whence I had claimed it, and I being eternally loyal would have obeyed without question...") + player("And secondly?") + npc("And secondly, I never managed to pass the artefact on to my Lord...") + npc("The vile thief Rennard accosted me as I made my way to Entrana, and after defeating me with a sneak attack, plundered the staff from my person, and left me for dead...") + npc("I do not know what became of the staff, but I can feel in my very bones that whatever its final fate was, it is somehow related to this curse upon me...") + player("Wow", "Tough break.") + npc("I am sorry to bore you with my tale stranger, please allow me to compound my rudeness by asking you for one favour, small to perform?") + player("Eh, I won't make any promises, but if it's nothing too annoying I guess I can help you out.") + npc("Many thanks stranger, this existence tortures me...") + npc("I need you to find Rennard and if he has the staff yet reclaim it, or find out what hideous deed he performed to curse me so!") + npc("I have nothing I may offer you save this piece of clothing, please take it as payment...") + exec { player, npc -> + setAttribute(player, CurseOfZaros.attributeValdezSpoke, true) + // Set the path to follow. + setAttribute(player, CurseOfZaros.attributePathNumber, (1..3).random()) + addItemOrDrop(player, Items.GHOSTLY_ROBE_6107) + } + player("Where can I find this Rennard then?") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("subsequenttime") + npc("Thank you for hearing my tale...", "It has been so lonely here...") + player("Can you remind me where to find the thief Rennard who caused this curse to befall you again?") + npc("Of course...") + exec { player, npc -> + // 1 of 3 paths. + loadLabel(player, "curseofzaros" + getAttribute(player, CurseOfZaros.attributePathNumber, 0)) + } + + label("curseofzaros1") + npc("Ah, the infamous Rennard...", "The last I had heard of him, he had sought passage on", "a ship crewed by none but the most dastardly lowly", "pirates...") + npc("I also heard that this ship had been caught in a violent", "storm, and stranded upon rocks, where the pirates then", "made their home...") + player("Okay, well I'll try and find him for you then.") + + label("curseofzaros2") + npc("Ah, the infamous Rennard...", "The last I had heard of that vile thief, he had joined a", "group of bandits in an evil land to the North-east of", "here, where they had made their home living outside of") + npc("the reach of the authorities that pursued them...") + player("Okay, well I'll try and find him for you then.") + + label("curseofzaros3") + npc("Ah, the infamous Rennard...", "The last I had heard of that vile thief, he had joined a", "group of bandits in a barren land to the South-east of", "here, where they prey upon the unsuspecting visitors to") + npc("the desert awaiting the return of their dark master...") + player("Okay, well I'll try and find him for you then.") + + + label("lostghostlything") + player(ChatAnim.SAD, "I lost that Robe top you gave me...", "Can I have another please?") + exec { player, npc -> + addItemOrDrop(player, Items.GHOSTLY_ROBE_6107) + } + npc(ChatAnim.SAD, "It seems as though the curse that keeps me here", "extends to my very clothing...") + npc("Here, take it, the moment you lost it, it returned to", "me...") + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostViggoraDialogue.kt b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostViggoraDialogue.kt new file mode 100644 index 000000000..d5464ee19 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/curseofzaros/MysteriousGhostViggoraDialogue.kt @@ -0,0 +1,188 @@ +package content.region.desert.quest.curseofzaros + +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class MysteriousGhostViggoraDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return MysteriousGhostViggoraDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MysteriousGhostViggoraDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(2394, 2395, 2396) + } +} + +class MysteriousGhostViggoraDialogueFile : DialogueLabeller() { + override fun addConversation() { + exec { player, npc -> + if (inEquipment(player, Items.GHOSTSPEAK_AMULET_552) || inEquipment(player, Items.GHOSTSPEAK_AMULET_4250)){ + if (2393 + getAttribute(player, CurseOfZaros.attributePathNumber, 0) == npc.id) { + if (getAttribute(player, CurseOfZaros.attributeViggoraSpoke, false)) { + if (inInventory(player, Items.GHOSTLY_CLOAK_6111) || inEquipment(player, Items.GHOSTLY_CLOAK_6111)) { + loadLabel(player, "subsequenttime") + } else { + loadLabel(player, "lostghostlything") + } + } else { + loadLabel(player, "firsttime") + } + } else { + loadLabel(player, "wrongpath") + } + } else { + loadLabel(player, "noghostspeak") + } + } + + CurseOfZaros.withoutGhostspeak(this) + + CurseOfZaros.wrongPath(this) + + label("firsttime") + player("So... You must be the infamous Viggora.") + npc("Hold thy tongue varlet! Speak fast, how come you to find me here, and how doth you understand mine speech?") + player("You want me to hold my tongue and tell you how I found you?") + npc("Cease thy chatter and respond to my demand!") + player("Cease my chatter AND respond to your demand?") + npc("I warn thee knave, this curse upon me hath not improved my temper, these centuries past...") + player("Well, it's actually about that curse that I have come to speak to you.") + npc("Oh, be that so? Then forgive my swift anger, and speak to me of how you plan to break this curse.") + player("Erm... I didn't actually mention anything about breaking the curse...") + npc("Then what lets you dare speak to me?") + player("Well, I heard of your name from a mage called Dhalak, and I'm trying to find out what exactly caused the curse to befall him, and you as well apparently.") + npc("Ha! So the weak-willed mage was cursed along with me?") + npc("Well now, that is an interesting turn of events... Then am I to assume that Valdez, Rennard, Kharrim and Lennissa were also cursed along with me?") + player("...How did you know that?") + npc("Ha ha ha! Oh, the curse cut deeper than I had previously thought!") + npc("Stranger, this news has brought me a ray of sunshine in an otherwise dreary millennium! Please, ask any question you wish!") + options( + DialogueOption("tellmeyourstory", "Tell me your story", skipPlayer = true), + DialogueOption("goodbye", "Goodbye then", skipPlayer = true), + ) + + label("goodbye") + player("Well, that's all fascinating, but I just don't particularly care. Bye-bye.") + + label("tellmeyourstory") + player("Erm... Thanks, I think. So what exactly happened on that day you were all cursed?") + player("I know that Valdez discovered the Staff of Armadyl, was robbed by Rennard, who then sent Kharrim to tell Zamorak of it.") + player("Meanwhile Lennissa heard of the sale, and informed Dhalak who placed an enchantment upon it so that its power would be hidden.") + player("I still don't know what happened with the staff to cause this curse, or what you had to do with it though...") + npc("Well stranger, rest yourself awhile, and I will recount a tale of the events of that day, for I was one of the few actually there when it happened...") + player("When what happened?") + npc("When my Lord Zamorak first got his taste of godhood!") + player("Wow. Sounds like quite the dinner party anecdote.") + npc("You can take the snide venom out of your voice whelp, you came to me; 'twas not the other way round.") + player("Okay, okay. Please continue.") + npc("Well now, let us see... As you may have heard tell, my affiliation lay with General Zamorak, a mighty warrior of the Mahjarrat tribe, and my skill on the battlefield had quickly brought") + npc("me to his attention.") + npc("So pleased was he with my bloodlust that he promoted me on the battlefield once to serve in his honour guard, and let me tell you, this was a rare honour indeed, for I was the only human chosen to take such a position.") + player("Really?") + npc("Oh yes, the dragon riders, Mahjarrat, demons and vampyre warriors made up the bulk of the force, but I wager I was their equal in all ways of combat.") + npc("Ha, when I think of someone like Lucien struggling to lift a blade, in some ways I was even their better!") + player("Please continue.") + npc("Well anyway... Myself and the rest of Zamoraks honour guard were formulating stratagems in our battle-tent, when that sneaky messenger Kharrim came in offering to sell us") + npc("the god-staff of Armadyl!") + npc("Naturally, we suspected that this was some trick by our Lord to test our loyalty...") + player("Yes, who was your lord? Everyone has been very evasive about that...") + npc("Quiet fool, all things in their course; You are disrupting my train of thought!") + player("Sorry...") + npc("Well anyway, we thought it was too good to be true, yet when we visited this scummy tavern we were amazed to discover there was no trick, no test of loyalty, no hidden trap:") + npc("Somehow this fool had actually managed to obtain the god-staff of Armadyl!") + npc("Its power was incredible, you could almost feel the energies crackling around it in the air!") + player("So what happened then?") + npc("Well, with such a weapon, the plans we had been developing for a rebellion against our lord could finally be put into action, but we knew that we would have to act swiftly, before he heard that we had a weapon") + npc("capable of defeating him, and we would have to act decisively, for even amongst our group there were still those loyal to the lord - such as that pathetic fool Azzanadra.") + player("So JUST WHO WAS this lord you speak of?") + npc("And I tell thee again, I will say when it is appropriate, now do not disrupt my tale!") + player("Okay, carry on then...") + npc("So anyway, Lord Zamorak and his most trusted compatriots, namely myself, Hazeel, Drakan, Thammaron and Zemouregal made plans to overthrow our lord using the god-weapon, and by pledging") + npc("allegiance to Zamorak as our master, were each to be given a large piece of land as our own in return.") + npc("We decided to move immediately, before anyone got cold feet, or any other parties could interfere in our work, and made haste towards the castle where our Lord lived.") + npc("If Lucien had not been otherwise occupied, he would have probably accompanied us with his magicks, but it turned out the foolish Dhalak had made his involvement unnecessary with some spells of his own allowing us to") + npc("get close enough to the castle with the staff without the Empty Lord being able to sense its presence.") + player("So your lord was the one that cursed you?") + npc("I am coming to that... So anyway, we made our way to the castle, under the pretense that we had war plans against Saradomin and the other deities to discuss.") + npc("As usual, our lord was guarded well, but this was why Zamorak had brought his most trusted fighters with him.") + npc("While we distracted the Empty Lord with our feints and attacks, and kept his bodyguards busy, Lord Zamorak outflanked him, unsheathed the staff and plunged it into his back!") + npc("Ah, it was a glorious sight... At that moment I was reminded for whom I fought, and why General Zamorak had earned his nickname 'the scourge' upon the battlefield...") + player("And the next thing you know you were cursed?") + npc("No, it was not quite that simple... The Empty Lord turned away from our battle, eyes burning with hatred, and towards Zamorak instead. Seeing this, we all fought with extra vigour, so that") + npc("General Zamorak would not face our lord alone, but we were outnumbered by many hundreds of warriors and demons, and could not reach him to assist him!") + player("So what then?") + npc("Why, it was the Empty Lord versus Zamorak, in single combat! And the sight of the battle will be with me forever more...") + npc("The Empty Lord was a powerful god, stronger than any of the others awake at that time, possibly even as strong as Guthix is, and Zamorak was but a mortal: A Mahjarrat warrior all the same, with all of the") + npc("strength and power that that entails, but mortal nonetheless, but to see him fight, you would not think of him as a 'mere' anything...") + npc("He was war itself! Flurry after flurry of blows he rained upon the Empty Lord, and the very castle walls shook and quivered with their power, but the Empty Lord would not fall!") + npc("Even with the weapon of a god embedded in his back, he fought on, and with each blow our victory seemed less and less certain...") + player("So what then?") + npc("Well, then a miracle happened. Or luck. Or natural justice.") + npc("You can call it what you want, but as the Empty Lords hands wrapped tightly around Zamorak's throat, Lord Zamorak, kicking and screaming defiantly and radiant in his anger until the very last, plunged towards the") + npc("Empty Lord, who seemed to lose his footing slightly, and fell in such a way so that the staff plunged deeper into his body, but also impaled Lord Zamorak with it at the same time...") + npc("And then...") + player("And then what?") + npc("And then nothing. There was a sudden flash of bright light, and then a sudden blink of cold darkness, and it was over.") + npc("Zamorak stood over the Empty Lord who was slowly... fading from existence... And as he faded, it seemed as though... It almost seemed as though Zamorak became more real,") + npc("more solid than he had been before...") + npc("And as the Empty Lord faded from this world completely, I heard his voice, almost a whisper upon the wind, cursing all who had helped Zamorak in his victory, which as you now tell me seems to have been all who") + npc("were responsible for the staff ending up in Zamorak's hands at the castle.") + npc("As I heard it, I saw that I too was fading, just as the Empty Lord had, and I called to my brethren for their assistance, but they could no longer hear my words, nor see my form.") + npc("It was then that the other gods appeared and banished Zamorak from the world completely for daring to kill one of their kind, although as it turned out it didn't quite work out that way for them, when he returned") + npc("stronger than ever, a god himself.") + npc("But the god wars were another story entirely...") + player("But... I don't understand... If it was Zamorak who used the weapon, then why was it only you who were cursed?") + player("And the other people who were cursed, why them? Why not the other Mahjarrat for example?") + player("And just who was this 'Empty Lord' you keep speaking of?") + npc("Well, in my life I was nothing but a warrior. I had no hidden knowledge, I didn't especially care about the gods or their magics, and I certainly didn't respect them.") + npc("Now in my... I suppose this is my death. I am but a shade on the wind, unnoticed by all who pass me, until today anyway, and the only answer I can give you is that the others who were with me, the") + npc("Mahjarrats and the Vampyre, they were beings of magic.") + npc("It runs through their very veins, and ebbs through their bones.") + npc("Who knows why the curse fell as it did? Perhaps as a mere human I was more susceptible to it, when they were not. Perhaps they too are cursed, but their life spans are so") + npc("long that it will be millennia before they feel it upon them. Perhaps because it did not affect them, it extended backwards through time, to the moment the staff was") + npc("taken from its rightful place, and all who had known of its theft were cursed too. Perhaps there are others also cursed, who played no part in this tale, and were merely unlucky enough to be") + npc("in the wrong place at the wrong time...") + npc("I don't know. Things do not always happen for a reason, just as tales do not always end with all of the loose ends neatly tied up and all answers supplied.") + npc("I have simply told you the events I was witness to, for I can do no more.") + npc("You have cheered me no end to let me know that this curse that has afflicted me has not left me alone here, in this void between worlds.") + exec { player, npc -> + setAttribute(player, CurseOfZaros.attributeViggoraSpoke, true) + addItemOrDrop(player, Items.GHOSTLY_CLOAK_6111) + } + npc("Perhaps I will hunt down these others who have also been cursed as I was, but I feel I must reward you for your efforts; Here, take my cloak, it is drenched in the blood of a") + npc("thousand foes, and may bring you luck in battle.") + player("So how can I break this curse?") + npc("Who knows? If it was the death curse of the Empty Lord, there may be no way to break it.") + npc("If it was not his death curse, and he is still alive but not on this world, then the only way to break it may be to bring him back here;") + npc("But I would rather stay cursed than suffer under his rule again...") + player("But WHO was this 'Empty Lord'? WHAT was his NAME?") + npc("You do not know? You have not guessed yet?") + npc("He was Zaros.") + + label("subsequenttime") + player("Hello.") + npc("Hello yourself.") + player("I really liked your little story. Can you tell me another one?") + player("Preferably something with a big fight and lots of explosions!") + npc("...You are a very strange young @g[man/woman].") + + label("lostghostlything") + player(ChatAnim.SAD, "Can I have that cloak back?") + exec { player, npc -> + addItemOrDrop(player, Items.GHOSTLY_CLOAK_6111) + } + npc("Hmph.", "I suppose.") + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/ArchaeologistDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/ArchaeologistDialogue.kt index 79a033158..397cd052f 100644 --- a/Server/src/main/content/region/desert/quest/deserttreasure/ArchaeologistDialogue.kt +++ b/Server/src/main/content/region/desert/quest/deserttreasure/ArchaeologistDialogue.kt @@ -1,102 +1,211 @@ -package rs09.game.content.dialogue.region.lletya +package content.region.desert.quest.deserttreasure -import core.api.setQuestStage +import content.global.handlers.iface.BookInterface +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills import core.plugin.Initializable +import org.rs09.consts.Items import org.rs09.consts.NPCs -/** - * @author qmqz - */ - @Initializable +/** Known in RS3 as Asgarnia Smith */ class ArchaeologistDialogue(player: Player? = null) : DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - if (!player.questRepository.hasStarted("Desert Treasure")) { - if (player.questRepository.isComplete("The Digsite Quest") && - player.questRepository.isComplete("The Tourist Trap") && - player.questRepository.isComplete("The Temple of Ikov") && - player.questRepository.isComplete("Priest In Peril") && - player.questRepository.isComplete("Waterfall Quest") && - player.questRepository.isComplete("Troll Stronghold") && - player.skills.getStaticLevel(Skills.SLAYER) >= 10 && - player.skills.getStaticLevel(Skills.FIREMAKING) >= 50 && - player.skills.getStaticLevel(Skills.MAGIC) >= 50 && - player.skills.getStaticLevel(Skills.THIEVING) >= 53 ) { - player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } - } else { - player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 999 } - } - - } else { - - } - - return true - } - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - - 999 -> sendDialogue("He seems to be lost in his own thoughts...").also { stage = 99 } - - 0 -> npc(FacialExpression.FRIENDLY, "Howdy stranger. What brings you out to these parts?").also { stage++ } - 1 -> options("What are you doing here?", "Do you have any quests?", "Who are you?", "Nothing really.").also { stage++ } - 2 -> when (buttonId) { - //1 -> todo - 2 -> player(FacialExpression.FRIENDLY, "Do you have any quests?", "Call it a hunch, but you look like the type of man who", "might...").also { stage = 20 } - //3 -> todo - //4 -> todo - } - - 20 -> npc(FacialExpression.HALF_THINKING, "Well, it's funny you should say that.", - "I'm not sure if I would really call it a quest as such,", - "but I found this ancient stone tablet in one of my", - "excavations, and it would really help me out if you").also { stage++ } - 21 -> npc(FacialExpression.FRIENDLY, "could go and take it back to the digsite for me and get", - "it examined.").also { stage++ } - 22 -> npc(FacialExpression.NEUTRAL, "It's very old, and I don't recognise and of the", - "inscriptions on it.").also { stage++ } - 23 -> options("Yes, I'll help you.", "No thanks, I don't want to help.").also { stage++ } - 24 -> when(buttonId) { - 1 -> player(FacialExpression.FRIENDLY, "Sure, I was heading that way anyways.", - "Any particular person at the digsite you want me to", - "talk to?").also { stage = 30 } - // 2 -> todo - } - - 30 -> npc(FacialExpression.NEUTRAL, "His name's Terry Balando. Give it to nobody but him.", - "I'm sorry, I can't entrust you with the actual tablet I", - "found, but it is far too valuable to give away, but I took", - "these etchings - they should be enough for him to make").also { stage++ } - 31 -> npc(FacialExpression.NEUTRAL, "a preliminary translation on.", - "Come back and let me know what he says, I would hate", - "to waste my time excavating anything that isn't worth", - "my time as a world famous archaeologist!").also { - player.questRepository.getQuest("Desert Treasure").start(player) - setQuestStage(player, "Desert Treasure", 1) - stage = 99 - } - - - - - 99 -> end() - } - return true + openDialogue(player!!, ArchaeologistDialogueFile(), npc) + return false } - override fun newInstance(player: Player?): DialoguePlugin { return ArchaeologistDialogue(player) } - override fun getIds(): IntArray { return intArrayOf(NPCs.ARCHAEOLOGIST_1918) } } + +class ArchaeologistDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0) + .player(FacialExpression.FRIENDLY,"Hello there.") + .branch { player -> + return@branch if (DesertTreasure.hasRequirements(player)) { 1 } else { 0 } + }.let{ branch -> + // Failure branch + branch.onValue(0) + .line("He seems to be lost in his own thoughts...") + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) // Success branch + .npc(FacialExpression.FRIENDLY, "Howdy stranger. What brings you out to these parts?") + .let { path -> + val originalPath = b.placeholder() + path.goto(originalPath) + originalPath.builder().options().let { optionBuilder -> + optionBuilder.option("What are you doing here?") + .player("Nothing much - What are you doing here?", "It doesn't seem like there's much to do out here in the", "desert!") + .npc("Well, that's where you'd be wrong.", "I work for the Archaeological Society of Varrock, and", "have been excavating around here recently.") + .npcl("Was there something you specifically wanted?") + .goto(originalPath) + optionBuilder.option("Do you have any quests?") + .player(FacialExpression.FRIENDLY, "Do you have any quests?", "Call it a hunch, but you look like the type of man who", "might...") + .npc(FacialExpression.HALF_THINKING, "Well, it's funny you should say that.", "I'm not sure if I would really call it a quest as such,", "but I found this ancient stone tablet in one of my", "excavations, and it would really help me out if you") + .npc(FacialExpression.FRIENDLY, "could go and take it back to the digsite for me and get", "it examined.") + .npc(FacialExpression.NEUTRAL, "It's very old, and I don't recognise and of the", "inscriptions on it.") + .options().let { optionBuilder2 -> + optionBuilder2.option("Yes, I'll help you.") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.ETCHINGS_4654) + } + .player("Sure, I was heading that way anyway.", "Any particular person at the digsite you want me to", "talk to?") + .npc(FacialExpression.NEUTRAL, "His name's Terry Balando. Give it to nobody but him.", "I'm sorry, I can't entrust you with the actual tablet I", "found, but it is far too valuable to give away, but I took", "these etchings - they should be enough for him to make") + .npc(FacialExpression.NEUTRAL, "a preliminary translation on.", "Come back and let me know what he says, I would hate", "to waste my time excavating anything that isn't worth", "my time as a world famous archaeologist!") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 0) { + setQuestStage(player, DesertTreasure.questName, 1) + } + } + optionBuilder2.option("No thanks, I don't want to help.") + .playerl("No thanks. Playing delivery boy doesn't really sound like much fun to me. ") + .npcl(" Well, okay. Can't say as I blame you for thinking that. I'll go take it there myself later I guess.") + .end() + } + optionBuilder.option("Who are you?") + .playerl(FacialExpression.THINKING, "Who are you, anyway?") + .npc(FacialExpression.EXTREMELY_SHOCKED, "You don't recognize me???", "I am the world famous Asgarnia Smith, Archaeologist", "extraordinaire!") + .npc(FacialExpression.HALF_THINKING, "I am the one who discovered the long forgotten", "Temple of Ikov!", "The one who unearthed the strange trap filled arena", "in Brimhaven!") + .npc(FacialExpression.HALF_THINKING, "I'm the leading archaeological expert of our time!", "I was voted archaeologist of the year four years", "running by the Varrock Herald! Are you REALLY", "sure you've never heard of me?") + .playerl(FacialExpression.THINKING, "No, I really haven't.") + .npc(FacialExpression.EXTREMELY_SHOCKED, "Well then, I'm confused.", "Why did you come over and speak to me if you don't", "know who I am? What do you want?") + .goto(originalPath) + optionBuilder.option("Nothing really.") + .playerl("Nothing really. I'm just wandering around for no good reason.") + .npcl("Uh-huh. Well, if you'll excuse me, I have a lot more work to do before I can bed down for the night.") + .end() + } + } + + + b.onQuestStages(DesertTreasure.questName, 1,2) + .npcl("So what did Terry Balando say about those etchings? Did he give you a translation for me?") + .playerl("Um...yeah...about that... I kind of didn't go and speak to him yet...") + .npcl("Well what are you waiting for? A written invitation? All I want you to do is take those etchings up to the digsite for me!") + .playerl("Okay, I'll do that then.") + .end() + + b.onQuestStages(DesertTreasure.questName, 3) + .playerl("Hello there.") + .npcl("So what did Terry Balando say about those etchings? Did he give you a translation for me?") + .branch { player -> + return@branch if (inInventory(player, Items.TRANSLATION_4655)) { 1 } else { 0 } + }.let{ branch -> + // Failure branch + branch.onValue(0) + .playerl("Yeah, he did. But I don't have it with me.") + .npcl("...") + .npcl("I see.") + .npcl("Well could you go and get me that translation? It could be vital!") + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) // Success branch + .playerl("Yeah, he did. I have it here in this book.") + .npcl("Did you take a read of this? It will do you good to understand how this profession works. Here, have a read.") + .options().let { optionBuilder -> + optionBuilder.option("Read book") + .endWith { _, player -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, TranslationBook.Companion::display) + } + return@let optionBuilder + } + .option("Don't read book") + .playerl("Yeah, I did. Kind of boring really.") + .npcl("Excellent. Just give me a moment to read this, and talk to me again in a second.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 3) { + removeItem(player, Items.TRANSLATION_4655) // remove if exists + setQuestStage(player, DesertTreasure.questName, 4) + } + } + + b.onQuestStages(DesertTreasure.questName, 4) + .playerl("Hello there.") + .npcl("Hmmm. Interesting. It seems to me like there's some kind of treasure hidden out in the desert.") + .npcl("So what do you say? Fancy being a treasure hunter like me?") + .playerl("Uh... don't you mean archaeologist?") + .npcl("Yeeees... An archaeologist...") + .npcl("It's all this hot sun getting to me I think.") + .npcl("Well anyway, let's just assume there is a treasure hidden in the desert somewhere, and let's just say for the sake of argument that maybe if I found a big stash of gold and treasure I wouldn't necessarily just hand it") + .npcl("all over to the Museum of Varrock.") + .npcl("Let's also say, purely hypothetically, that if there were such a big stash of treasure and someone were to help me find it, then that hypothetical personal might be entitled to, oh, let's say a purely for the sake of") + .npcl("argument thirty percent split...") + .playerl("Fifty percent.") + .npcl("That's right!") + .npcl("A purely for the sake of argument fifty-fifty split on this hypothetical treasure, should it exist...") + .npcl("Do you see where I'm going with this?") + .playerl("You want me to help you find some treasure for a fifty percent share, as long as I don't tell anybody, and your reputation as an esteemed archaeologist with the Museum of Varrock remains intact, and nobody") + .playerl("discovers you're actually just a treasure hunter?") + .npcl("Uh... yes, but the way you said it makes it sound like I'm doing something wrong...") + .npcl("So what do you say? Partners?") + .options() + .let { optionBuilder -> + optionBuilder.option("Help him") + .playerl("Well... I guess nobody is really going to lose out on anything, and we don't even know if there is any treasure...") + .playerl("Aw, go on then. Count me in.") + .npcl("Good @g[lad,lass]! Well, if we split up we'll be able to find treasure quicker.") + .npcl("I'll continue searching around here in this Bedabin Camp, you head due South to the Bandit Village. If either of us find anything, we'll come find each other and say what, okay?") + .npcl("You head due South, see what you can find out about this tablet.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 4) { + setQuestStage(player, DesertTreasure.questName, 5) + } + } + optionBuilder.option("Don't help him") + .playerl("This all sounds very immoral, and I don't want any part of your seedy little schemes.") + .npcl("Aw, c'mon, cut me a break here man! Look, nobody is really getting hurt, right? This treasure is either going to lie around in some underground ruin, or lie around in some museum for") + .npcl("people to look at!") + .npcl("It makes sense for the people to go to all the risk to get a little payback for it, right? Besides, anything of real historical value we can hand over to the museum - we'll just keep the cash for") + .npcl("ourselves!") + .npcl("C'mon... what do you say? Partners?") + .options() + .let { optionBuilder -> + optionBuilder.option("Help him") + .playerl("Well... I guess nobody is really going to lose out on anything, and we don't even know if there is any treasure...") + .playerl("Aw, go on then. Count me in.") + .npcl("Good @g[lad,lass]! Well, if we split up we'll be able to find treasure quicker.") + .npcl("I'll continue searching around here in this Bedabin Camp, you head due South to the Bandit Village. If either of us find anything, we'll come find each other and say what, okay?") + .npcl("You head due South, see what you can find out about this tablet.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 4) { + setQuestStage(player, DesertTreasure.questName, 5) + } + } + optionBuilder.option("Don't help him") + .playerl("I don't trust anything about your little scheme, including you! I want nothing to do with this!") + .npcl("Eh, you'll come running back when you realize just how much money we could be talking here.") + .npcl("I'll see you again when you come crawling back, begging to be cut in on the deal.") + .end() + } + } + + b.onQuestStages(DesertTreasure.questName, 5) + .playerl("Hello there.") + .npcl("Hello again. You find any signs of that treasure yet?") + .playerl("Not yet...") + .npcl("Well, head south to that bandit camp. I bet if there's anything out here, they'll know about it. Don't let on it might be valuable though! Those thieves will steal it as soon as look at you!") + + b.onQuestStages(DesertTreasure.questName, 6,7,8) + .playerl("Hello there.") + .npcl("Hello again. You find any signs of that treasure yet?") + .playerl("Not as such... Although I think I'm onto something of a promising lead...") + .playerl("Did you ever hear of something called the Diamonds of Azzanadra?") + .npcl("Diamonds of Azzanadra? That sounds very promising indeed!") + .playerl("So you have heard of them?") + .npcl("Nope, never heard of them before, but you said diamonds! Diamonds are always promising!") + + + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/AzzanadraDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/AzzanadraDialogue.kt new file mode 100644 index 000000000..c8401ac87 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/AzzanadraDialogue.kt @@ -0,0 +1,59 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.SpellBookManager +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class AzzanadraDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, AzzanadraDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return AzzanadraDialogue(player) + } + override fun getIds(): IntArray { + /* This is wrong SCARABS_1970 should be AZZANADRA_1970 */ + return intArrayOf(NPCs.SCARABS_1970, NPCs.AZZANADRA_1971) + } +} + +class AzzanadraDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 10) + .npcl(FacialExpression.OLD_DEFAULT, "I knew they could not trap me here for long!") + .npcl(FacialExpression.OLD_DEFAULT, "Well done, soldier, tell me, how goes the battle?") + .playerl(FacialExpression.THINKING, "Battle?") + .npcl(FacialExpression.OLD_DEFAULT, "You do not know of the battle?") + .npcl(FacialExpression.OLD_DEFAULT, "More time must have passed than I had thought...") + .npcl(FacialExpression.OLD_DEFAULT, "Tell me, what news of great Paddewwa? Do the shining spires of Lassar still stand? And what of glorious Annakarl? The fortress is still intact?") + .player("Uh...", "Sorry, I've never heard of them...") + .npcl(FacialExpression.OLD_SAD, "No!") + .npcl(FacialExpression.OLD_SAD, "My lord... What has become of you? I cannot hear your voice in my mind anymore!") + .npcl(FacialExpression.OLD_DEFAULT, "My thanks to you brave warrior for your help in freeing me from this accursed tomb, but it seems I have much to do to make amends.") + .npcl(FacialExpression.OLD_DEFAULT, "If the shining cities no longer stand, then it means that we must have failed my lord...") + .npcl(FacialExpression.OLD_DEFAULT, "How long have I been trapped here? Master... Have you truly been dispatched from this world?") + .npcl(FacialExpression.OLD_DEFAULT, "Warrior, for your efforts in freeing me, I offer you the gift of knowledge.") + .npcl(FacialExpression.OLD_DEFAULT, "I bestow upon you the ancient magicks, taught me by my Lord before his disappearance, may you use them well in battle for our people!") + .npcl(FacialExpression.OLD_DEFAULT, "They will replace the knowledge you previously had, but you may switch between them by praying at the altar in this room at any time.") + .npcl(FacialExpression.OLD_DEFAULT, "I trust that we shall meet again adventurer, I offer you the blessings of myself and my master in all of your endeavours!") + .npcl(FacialExpression.OLD_DEFAULT, "Now, I must leave you, for there must be some trace of my master's power left somewhere. Feel free to use the portal I shall create to return here easily in the future!") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 10) { + sendMessage(player,"A strange wisdom has filled your mind...") + finishQuest(player, DesertTreasure.questName) + player.spellBookManager.setSpellBook(SpellBookManager.SpellBook.ANCIENT) + player.spellBookManager.update(player) + } + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/BartenderDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/BartenderDialogue.kt new file mode 100644 index 000000000..2e3e78954 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/BartenderDialogue.kt @@ -0,0 +1,155 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class BartenderDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, BartenderDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return BartenderDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.BARTENDER_1921) + } +} +class BartenderDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4) + .npcl(FacialExpression.ANNOYED, "Get out of here. I have nothing to say to the likes of you.") + .end() + + b.onQuestStages(DesertTreasure.questName, 5) + .branch { player -> + return@branch if (getAttribute(player, DesertTreasure.attributeBoughtBeer, false)) { 1 } else { 0 } + }.let{ branch -> + branch.onValue(1) + .npcl("You've had your drink, now get out of here. I have nothing to say to the-") + .playerl("No, Wait! Look, I'm here on an archaeological expedition for the Museum of Varrock.") + .playerl("I am only here looking for artefacts...") + .npcl("Oh really? Our inheritance is only sand and death. What do you expect to find out here in this desert forsaken by the gods?") + .options() + .let { optionBuilder -> + optionBuilder.option("I heard about treasure...") + .playerl("As I understand it there's some kind of hidden treasure in these parts...") + .npcl("Look around @g[pal,lady]. Does it looks like there's any treasure near here?") + .npcl("If I were you I'd get lost before someone takes a dislike to your face and removes it for you.") + .end() + + optionBuilder.option("I heard about four diamonds...") + .playerl("I heard a rumour about four diamonds or crystals...") + .npcl("The four diamonds of Azzanadra??? How came you to know of this?") + .playerl("You've heard of them then?") + .npcl("It's just a fairy tale for children. Maybe one of the village elders might know more, but it's not really something I care about.") + .npcl("Now get out of here, your sort isn't welcome in my bar.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 5) { + setQuestStage(player, DesertTreasure.questName, 6) + } + } + + optionBuilder.option("I heard about a fortress...") + .playerl("Certain things have led me to believe there may be some kind of ruined fortress around here...") + .npcl("Doubt it. What in the world would someone need to guard against in the middle of this desert?") + .npcl("A bad attack of sand? I think you're on the wrong track, mister so-called archaeologist.") + .end() + } + + branch.onValue(0) + .npcl("If you're not buying anything, I have nothing to say to you.") + .options() + .let { optionBuilder -> + optionBuilder.option("Ask about Desert Treasure") + .playerl("As I understand it there's some kind of hidden treasure in these parts...") + .npcl("Look around @g[pal,lady]. Does it looks like there's any treasure near here?") + .npcl("If I were you I'd get lost before someone takes a dislike to your face and removes it for you.") + .end() + optionBuilder.option("Buy a drink") + .branch { player -> + return@branch if (inInventory(player, Items.COINS_995, 650)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("What's that? You wanna buy a beer? It'll cost ya 650 coins.") + .options() + .let { optionBuilder -> + optionBuilder.option("Buy a beer") + .betweenStage { _, player, _, _ -> + if (removeItem(player, Item(Items.COINS_995, 650))) { + addItemOrDrop(player, Items.BANDITS_BREW_4627) + setAttribute(player, DesertTreasure.attributeBoughtBeer, true) + } + } + .npcl("There you go. Now get out, we don't like your sort around here.") + .end() + optionBuilder.option("Don't buy anything") + .npcl("Get out of my bar then! We don't like your sort round here!") + .end() + } + + branch.onValue(0) + .npcl("You ain't got the 650 coins it costs to buy it, and I'm glad 'cos I didn't want to serve you anyway.") + .end() + } + + } + } + + + b.onQuestStages(DesertTreasure.questName, 6,7,8,9,10,11) + .branch { player -> + return@branch if (getAttribute(player, DesertTreasure.attributeBoughtBeer, false)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("You've had your drink, now get out of here. I have nothing to say to the-") + .playerl("No, Wait! Look, I'm here on an archaeological expedition for the Museum of Varrock.") + .playerl("I am only here looking for artefacts...") + .npcl("Oh really? Our inheritance is only sand and death. What do you expect to find out here in this desert forsaken by the gods?") + .options() + .let { optionBuilder -> + optionBuilder.option("I heard about treasure...") + .playerl("As I understand it there's some kind of hidden treasure in these parts...") + .npcl("Look around @g[pal,lady]. Does it looks like there's any treasure near here?") + .npcl("If I were you I'd get lost before someone takes a dislike to your face and removes it for you.") + .end() + + optionBuilder.option("I heard about four diamonds...") + .playerl("I heard a rumour about four diamonds or crystals...") + .npcl("The four diamonds of Azzanadra??? How came you to know of this?") + .playerl("You've heard of them then?") + .npcl("It's just a fairy tale for children. Maybe one of the village elders might know more, but it's not really something I care about.") + .npcl("Now get out of here, your sort isn't welcome in my bar.") + .end() + + optionBuilder.option("I heard about a fortress...") + .playerl("Certain things have led me to believe there may be some kind of ruined fortress around here...") + .npcl("Doubt it. What in the world would someone need to guard against in the middle of this desert?") + .npcl("A bad attack of sand? I think you're on the wrong track, mister so-called archaeologist.") + .end() + + optionBuilder.option("I heard of the Diamonds of Azzanadra.") + .playerl("Tell me all you know about the Diamonds of Azzanadra.") + .npcl("Not that I think it's any of your business, but when I was a child I remember hearing the legend.") + .npcl("I don't recall it particularly well, other than they are said to contain an incredible power.") + .npcl("If you really want to hear more about it you'd be best off finding someone who cares about the past, and the history of this area, and stop bothering me.") + .end() + } + } + + b.onQuestStages(DesertTreasure.questName, 100) + .npcl("So you're the @g[fella,lass] that freed Azzanadra, huh? Fair play to ya. What will you have?") + .end() + + } +} diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DamisBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DamisBehavior.kt new file mode 100644 index 000000000..1b996dafc --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DamisBehavior.kt @@ -0,0 +1,81 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.interaction.QueueStrength +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.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class DamisBehavior : NPCBehavior(NPCs.DAMIS_1974, NPCs.DAMIS_1975) { + private var disappearing = false + + override fun tick(self: NPC): Boolean { + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, self.walkRadius)) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Damis has vanished once more into the shadows...") + removeAttribute(player, DesertTreasure.attributeDamisInstance) + } + poofClear(self) + } + return true + } + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (state.estimatedHit + Integer.max(state.secondaryHit, 0) >= self.skills.lifepoints && self.id == NPCs.DAMIS_1974) { + state.estimatedHit = self.skills.lifepoints + 1 + state.secondaryHit = -1 + + transformNpc(self, NPCs.DAMIS_1975, 500) + self.skills.lifepoints = self.skills.maximumLifepoints + sendChat(self, "Armour... is for restraint, not... protection...") + queueScript(self, 2, QueueStrength.NORMAL) { stage: Int -> + sendChat(self, "Now I show... you... my true power!") + self.properties.attackSpeed = 3 + return@queueScript stopExecuting(self) + } + } + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + // In second form, drain prayer by 5. + if (self.id == NPCs.DAMIS_1975) { + victim.skills.decrementPrayerPoints(5.0) + } + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + if (self.id == NPCs.DAMIS_1975) { + if (DesertTreasure.getSubStage(killer, DesertTreasure.attributeShadowStage) == 3) { + GroundItemManager.create(Item(Items.SHADOW_DIAMOND_4673), self.location, killer) + DesertTreasure.setSubStage(killer, DesertTreasure.attributeShadowStage, 100) + removeAttribute(killer, DesertTreasure.attributeFareedInstance) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt index f42498ac6..f23f6e024 100644 --- a/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt @@ -1,103 +1,544 @@ -//package rs09.game.content.quest.members.deserttreasure +package content.region.desert.quest.deserttreasure -//import core.game.node.entity.player.Player -//import core.game.node.entity.player.link.quest.Quest -//import core.game.node.entity.skill.Skills -//import core.plugin.Initializable +import content.data.Quests +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items /** - * @author qmqz + * Desert Treasure Quest + * + * https://www.youtube.com/watch?v=INaSdOAHdT8: Jesus fucking devil best shit on planet fuckall 31:57 + * https://www.youtube.com/watch?v=LAoOISdsX4w: This guy did the best initial quest journal and then ignored the rest. + * https://www.youtube.com/watch?v=3cyU36qNBpI: one impt quest log part in the front. + * https://www.youtube.com/watch?v=pY-Czja2lE4: Good shit part 2 6:24 + * https://www.youtube.com/watch?v=NB1-m_hoTPk: Blur ass quest log. + * https://www.youtube.com/watch?v=bcy54b6NnJs: Blur ass quest log. + * https://www.youtube.com/watch?v=J_dfMXFz3WQ: Blur ass quest log. + * https://www.youtube.com/watch?v=7n0OcHUNFHc: Blur ass quest log 1:07. + * https://www.youtube.com/@Gunz4Range/videos + * https://www.youtube.com/watch?v=baQ6oJOk7Gc + * https://www.youtube.com/watch?v=u0C2zW6mrcc: Blur ass ending quest log 3:23 + * https://www.youtube.com/watch?v=SUzM0WKY6jk: 1:03 + * https://www.youtube.com/watch?v=3AA5fkkBW-I: 2:05 + * + * https://www.youtube.com/watch?v=ofRV2-h3Dug: 17:21 27:45 + * + * if (VARPBIT[358] == 15) return 2; if (VARPBIT[358] == 0) return 0; return 1; }; if (arg0 == 13) */ +@Initializable +class DesertTreasure : Quest(Quests.DESERT_TREASURE,45, 44, 3, 440, 358, 0, 1, 15){ -//@Initializable -//class DesertTreasure : Quest("Desert Treasure",15, 44, 3, 440, 0, 1, 15){ + companion object { + val questName = Quests.DESERT_TREASURE + /** This is an important varbit as it is literally controlling the Quest varbit (440 Varp 0-14 bits -> 358 Varbit )*/ + const val varbitDesertTreasure = 358 // 10-15-Eblis 1924 shows up other values Eblis disappears. This is tied to the quest. - //override fun drawJournal(player: Player?, stage: Int) { - // super.drawJournal(player, stage) - // var line = 12 + // Desert Treasure Start + const val attributeBoughtBeer = "/save:quest:deserttreasure-boughtbeer" + const val attributeCountMagicLogs = "/save:quest:deserttreasure-countmagiclogs" + const val attributeCountSteelBars = "/save:quest:deserttreasure-countsteelbars" + const val attributeCountMoltenGlass = "/save:quest:deserttreasure-countmoltenglass" + const val attributeCountBones = "/save:quest:deserttreasure-countbones" + const val attributeCountAshes = "/save:quest:deserttreasure-countashes" + const val attributeCountCharcoal = "/save:quest:deserttreasure-countcharocal" + const val attributeCountBloodRune = "/save:quest:deserttreasure-countbloodrune" + const val varbitMirrors = 392 // Set to 1 to show mirrors, they are cactuses before desert treasure. - //val stage = player?.questRepository?.getStage("Desert Treasure")!! + // Blood Diamond + const val attributeBloodStage = "/save:quest:deserttreasure-bloodstage" + const val attributeDessousInstance = "quest:deserttreasure-dessousinstance" - //when (stage) { - // 0 -> { - // line(player,"I can start this quest by speaking to !!The Archaeologist??", line++) - // line(player,"who is exploring the !!Bedabin Camp?? South West of the", line++) - // line(player,"!!Shantay Pass.??", line++) - // line(player,"To complete this quest I will need:", line++) - // if (player.skills.getStaticLevel(Skills.SLAYER) < 10) { - // line(player,"Level 10 Slayer", line++) - // } else { - // line(player,"---Level 10 Slayer/--", line++) - // } - // if (player.skills.getStaticLevel(Skills.SLAYER) < 10) { - // line(player,"Level 50 Firemaking", line++) - // } - // if (player.skills.getStaticLevel(Skills.SLAYER) < 10) { - // line(player,"Level 50 Magic", line++) - // } - // if (player.skills.getStaticLevel(Skills.SLAYER) < 10) { - // line(player,"Level 53 Thieving", line++) - // } - // line(player,"I must have completed the following quests:", line++) + // Smoke Diamond + const val attributeSmokeStage = "/save:quest:deserttreasure-smokestage" + const val attributeUnlockedGate = "/save:quest:deserttreasure-unlockedgate" + const val attributeFareedInstance = "quest:deserttreasure-fareedinstance" + const val varbitStandingTorchNorthEast = 360 // North East standing torch 6406 + const val varbitStandingTorchSouthEast = 361 // South East standing torch 6408 + const val varbitStandingTorchSouthWest = 362 // South West standing torch 6410 + const val varbitStandingTorchNorthWest = 363 // North West standing torch 6412 - // if (player.questRepository.isComplete("The Digsite Quest")) { - // line(player,"---!!The digsite Quest??/--", line++) - // } else { - // line(player,"!!The digsite Quest??", line++) - // } + // Ice Diamond + const val attributeIceStage = "/save:quest:deserttreasure-icestage" + const val attributeTrollKillCount = "/save:quest:deserttreasure-iciclecount" + const val attributeKamilInstance = "quest:deserttreasure-kamilinstance" + const val varbitFrozenFather = 380 // 0-frozen 1-defrosted | Base: 1943 Iced: 1944 Broke: 1948 Reunion: 1947 + const val varbitFrozenMother = 381 // 0-frozen 1-defrosted | Base: 1945 Iced: 1946 Broke: 1950 Reunion: 1949 + const val varbitChildReunite = 382 // 0-frozen 4-reunited 5-reunited 6-varbitfails/ends (This varbit seems to manage ice stages...) + // 0 - start, 1 - feed the crying child, 2 - fought kamil, 3 - unfreeze dad/mum, 4 - reunite, 5 - all chat complete. + const val varbitCaveEntrance = 378 // 6446 0 - 6 6441 - // if (player.questRepository.isComplete("The Tourist Trap")) { - // line(player,"---!!The Tourist Trap??/--", line++) - // } else { - // line(player,"!!The Tourist Trap??", line++) - // } + // Shadow Diamond + const val attributeShadowStage = "/save:quest:deserttreasure-shadowstage" + const val attributeDamisWarning = "quest:deserttreasure-damiswarning" + const val attributeDamisInstance = "quest:deserttreasure-damisinstance" + const val varbitRingOfVisibility = 393 // Set to 1 to show ladder and other cool stuff when wearing ring of visibility. - // if (player.questRepository.isComplete("The Temple of Ikov")) { - // line(player,"---!!The Temple of Ikov??/--", line++) - // } else { - // line(player,"!!The Temple of Ikov??", line++) - // } + // Desert Treasure End + const val attributeBloodDiamondInserted = "/save:quest:deserttreasure-blooddiamondinserted" + const val attributeSmokeDiamondInserted = "/save:quest:deserttreasure-smokediamondinserted" + const val attributeIceDiamondInserted = "/save:quest:deserttreasure-icediamondinserted" + const val attributeShadowDiamondInserted = "/save:quest:deserttreasure-shadowdiamondinserted" + const val varbitBloodObelisk = 390 + const val varbitSmokeObelisk = 387 + const val varbitIceObelisk = 389 + const val varbitShadowObelisk = 388 - // if (player.questRepository.isComplete("Priest In Peril")) { - // line(player,"---!!Priest In Peril??/--", line++) - // } else { - // line(player,"!!Priest In Peril??", line++) - // } + fun completedAllSubstages(player: Player): Boolean { + return getSubStage(player, attributeBloodStage) == 100 && + getSubStage(player, attributeSmokeStage) == 100 && + getSubStage(player, attributeIceStage) == 100 && + getSubStage(player, attributeShadowStage) == 100 + } - // if (player.questRepository.isComplete("Waterfall Quest")) { - // line(player,"---!!Waterfall Quest??/--", line++) - // } else { - // line(player,"!!Waterfall Quest??", line++) - // } + fun getSubStage(player: Player, attributeName: String): Int { + return getAttribute(player, attributeName, 0) + } - // if (player.questRepository.isComplete("Troll Stronghold")) { - // line(player,"---!!Troll Stronghold??/--", line++) - // } else { - // line(player,"!!Troll Stronghold??", line++) - // } + fun setSubStage(player: Player, attributeName: String, value: Int) { + return setAttribute(player, attributeName, value) + } + + fun hasRequirements(player: Player): Boolean { + return arrayOf( + hasLevelStat(player, Skills.SLAYER, 10), + hasLevelStat(player, Skills.FIREMAKING, 50), + hasLevelStat(player, Skills.MAGIC, 50), + hasLevelStat(player, Skills.THIEVING, 53), + isQuestComplete(player, Quests.THE_DIG_SITE), + isQuestComplete(player, Quests.THE_TOURIST_TRAP), + isQuestComplete(player, Quests.TEMPLE_OF_IKOV), + isQuestComplete(player, Quests.PRIEST_IN_PERIL), + isQuestComplete(player, Quests.WATERFALL_QUEST), + isQuestComplete(player, Quests.TROLL_STRONGHOLD), + ).all { it } + } + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, questName) > 0 + + if(!started){ + line(player,"I can start this quest by speaking to !!The Archaeologist??", line++) + line(player,"who is exploring the !!Bedabin Camp?? South West of the", line++) + line(player,"!!Shantay Pass??.", line++) + line(player,"To complete this quest I will need:", line++) + line(player, "Level 10 Slayer", line++, hasLevelStat(player, Skills.SLAYER, 10)) + line(player, "Level 50 Firemaking", line++, hasLevelStat(player, Skills.FIREMAKING, 50)) + line(player, "Level 50 Magic", line++, hasLevelStat(player, Skills.MAGIC, 50)) + line(player, "Level 53 Thieving", line++, hasLevelStat(player, Skills.THIEVING, 53)) + line(player,"I must have completed the following quests:", line++) // After - I have completed all of the required quests: + line(player, "The Digsite Quest", line++, isQuestComplete(player, Quests.THE_DIG_SITE)) + line(player, "The Tourist Trap", line++, isQuestComplete(player, Quests.THE_TOURIST_TRAP)) + line(player, "The Temple of Ikov", line++, isQuestComplete(player, Quests.TEMPLE_OF_IKOV)) + line(player, "Priest In Peril", line++, isQuestComplete(player, Quests.PRIEST_IN_PERIL)) + line(player, "Waterfall Quest", line++, isQuestComplete(player, Quests.WATERFALL_QUEST)) + line(player, "Troll Stronghold", line++, isQuestComplete(player, Quests.TROLL_STRONGHOLD)) + } else { + + if (stage >= 2) { + line(player, "I took some etchings of a stone tablet discovered by the", line++, true) + line(player, "archaeologist in the desert to Terry Balando at the", line++, true) + line(player, "Varrock digsite.", line++, true) + } else if (stage >= 1) { + line(player, "The !!archaeologist?? has given me some !!etchings from a??", line++, false) + line(player, "!!stone tablet?? that he discovered in the desert somewhere,", line++, false) + line(player, "and asked me to take it to an archaeological expert called", line++, false) + line(player, "!!Terry Balando?? at the Varrock !!digsite??.", line++, false) + } + + if (stage >= 4) { + line(player, "He made a rough translation, which I returned to the", line++, true) + line(player, "archaeologist.", line++, true) + } else if (stage == 3) { + line(player, "Terry Balando made some quick !!translation notes?? of the", line++, false) + line(player, "tablet and asked me to return them to the !!archaeologist?? in", line++, false) + line(player, "the !!Bedabin Camp??.", line++, false) + } else if (stage == 2) { + line(player, "I should get the !!translation notes?? from !!Terry Balando??", line++, false) + } + + if(stage >= 5) { + line(player, "The archaeologist I met in the desert seemed to think that", line++, true) + line(player, "there was some hidden treasure in the desert somewhere,", line++, true) + line(player, "I agreed to help him find it in return for a fifty percent", line++, true) + line(player, "share of whatever we found.", line++, true) + } else if (stage == 4) { + line(player, "I should find out what the !!archaeologist?? has to say about", line++, false) + line(player, "the !!translation notes??.", line++, false) + } + + if (stage >= 6) { + line(player, "I headed South and found a Bandit Camp.", line++, true) + } else if (stage >= 5) { + line(player, "I should head South to the !!Bandit Camp?? and try to find out", line++, false) + line(player, "more about this !!treasure??.", line++, false) + } + + if (stage >= 7) { + line(player, "I asked around the Bandit Camp and discovered someone", line++, true) + line(player, "who was willing to help me find the diamonds, by using a", line++, true) + line(player, "magic spell and some scrying glasses.", line++, true) + } else if (stage >= 6) { + line(player, "After some searching, the barman let slip some", line++, true) + line(player, "information about the 'Four Diamonds of Azzanadra'.", line++, true) + line(player, "I should ask around the !!Bandit Camp?? and see if anyone", line++, false) + line(player, "else has any information about the !!Four Diamonds of??", line++, false) + line(player, "!!Azzanadra??", line++, false) + } + + if (stage >= 10 || (stage >= 9 && completedAllSubstages(player))) { + // This disappears after you find all the diamonds. + } else if (stage >= 8) { + line(player, "I brought Eblis the ingredients so that he could cast the", line++, true) + line(player, "spell to see the places touched by the magic of the", line++, true) + line(player, "diamonds.", line++, true) + } else if (stage >= 7) { + // Crosses out gradually as you SUBMIT them. + line(player, "To make the scrying glasses I need to bring the following", line++, false) + // Crosses out with "I have brought him all xxx(6 Steel bars)" + line(player, "items to Eblis:", line++, false) + line(player, "!!12 Magic logs??", line++, false) + line(player, "!!6 Steel bars??", line++, false) + line(player, "!!6 Molten glass??", line++, false) + line(player, "To cast the spell I will need to bring Eblis:", line++, false) + // Crosses out with "I have brought him xxx(some bones)" + line(player, "!!Some bones??", line++, false) + line(player, "!!Some ash??", line++, false) + line(player, "!!Some charcoal??", line++, false) + line(player, "!!A blood rune??", line++, false) + } + + if (stage >= 10 || (stage >= 9 && completedAllSubstages(player))) { + // This disappears after you find all the diamonds. + } else if (stage >= 9 && !completedAllSubstages(player)) { + // This was supposed to disappear... youtu.be/J4cQMY66MI4 is old but disagrees + line(player, "I headed East into the desert and used the scrying", line++, true) + line(player, "glasses set up for me there by Eblis to try and find the", line++, true) + line(player, "Four Diamonds of Azzanadra.", line++, true) + line++ + } else if (stage >= 8) { + line(player, "I should head !!East into the desert?? and meet Eblis where he", line++, false) + line(player, "has set up the scrying glasses, and use them to try and", line++, false) + line(player, "track down the !!Four Diamonds of Azzanadra??.", line++, false) + line++ + } - // line(player,"", line++) - //} - // } - //} + // The huge one with sub quests. + if (stage >= 9 && completedAllSubstages(player)) { + line(player, "I found all four diamonds using this spell.", line++, true) + } else if (stage == 9) { + line++ + // DIAMOND OF BLOOD (DESSOUS) + // Dessous returns to his grave, bored of toying with you. // https://youtu.be/7CvfS7pypso + if (getSubStage(player, attributeBloodStage) == 100) { + line(player, "I defeated a vampire named Dessous to claim the Diamond", line++, true) + line(player, "of Blood.", line++, true) + } else if (getSubStage(player, attributeBloodStage) >= 1) { + // ZSOCwWEwimM 4:25 helped the first line. + line(player, "I discovered that the location of the Diamond of Blood was", line++, true) + line(player, "somewhere in Morytania, and in the possession of a", line++, true) + line(player, "vampire warrior named Dessous.", line++, true) - // override fun finish(player: Player) { - // var ln = 10 - // super.finish(player) - // player.packetDispatch.sendString("You have completed Desert Treasure!", 277, 4) - // player.packetDispatch.sendItemZoomOnInterface(1891, 240, 277, 5) -// - // drawReward(player,"3 Quest Points", ln++) - // drawReward(player,"20,000 Magic XP", ln++) - // drawReward(player,"Ability to use", ln++) - // drawReward(player,"Ancient Magicks", ln) + if (getSubStage(player, attributeBloodStage) >= 3) { + line(player, "I don't fully trust Malek, but he has agreed to help me kill", line++, true) + line(player, "Dessous.", line++, true) - // player.skills.addExperience(Skills.MAGIC, 20000.0) + line(player, "I made a special blessed pot, filled with blood, garlic and", line++, true) + line(player, "spices, which I used to lure Dessous from his tomb.", line++, true) + line(player, "I managed to defeat the vampire warrior Dessous, but", line++, true) + line(player, "there was no sign of the Diamond of Blood anywhere.", line++, true) + line(player, "I should find out what game !!Malek?? has been playing with", line++, false) + line(player, "me, and where I can actually find the !!Diamond of Blood??.", line++, false) + } else if (getSubStage(player, attributeBloodStage) >= 2) { + line(player, "I don't fully trust Malek, but he has agreed to help me kill", line++, true) + line(player, "Dessous.", line++, true) - // } + // None of these lines cross out when you do it. So it remains like this. + line(player, "Apparently I can find an old !!assistant of Count Draynor?? in", line++, false) + line(player, "the !!sewers of Draynor Village??, who will be able to help me", line++, false) + line(player, "make a !!sacrificial pot??", line++, false) + line(player, "I then need to take that !!sacrificial pot?? to !!Entrana?? and get", line++, false) + line(player, "it blessed by the !!head priest??", line++, false) + line(player, "When I have done that, I should return to !!Malak??, and he will", line++, false) + line(player, "provide me with some !!fresh blood??", line++, false) + line(player, "I then need to add !!garlic?? and !!spices?? to the pot in order to", line++, false) + line(player, "lure Dessous from his tomb.", line++, false) + line(player, "When I have done all of this, I must !!kill Dessous!??", line++, false) + } else if (getSubStage(player, attributeBloodStage) >= 1) { + line(player, "I should speak to !!Malek?? again and find out how exactly I", line++, false) + line(player, "can kill !!Dessous??.", line++, false) // This doesn't stay + } + } else if (getSubStage(player, attributeBloodStage) == 0) { + line(player, "I can use the !!scrying glasses?? to help find the", line++, false) + line(player, "!!Diamond of Blood??.", line++, false) + } - // override fun newInstance(`object`: Any?): Quest { - // return this - // } -//} \ No newline at end of file + line++ + // DIAMOND OF SMOKE (FAREED) + // Fareed has lost interest in you, and returned to his flames. + if (getSubStage(player, attributeSmokeStage) == 100) { + line(player, "I defeated a fire warrior, and now have the Diamond of", line++, true) + line(player, "Smoke.", line++, true) + } else if (getSubStage(player, attributeSmokeStage) >= 1) { + line(player, "I entered a smokey well and lit up the path. I found a", line++, true) // Derived. + line(player, "key in a chest.", line++, true) // Derived. + line(player, "I should find out what the !!key?? unlocks.", line++, false) // Derived. + } else if (getSubStage(player, attributeSmokeStage) == 0) { + // This doesn't change to stage 1 even when you are lighting the fires... + line(player, "I can use the !!scrying glasses?? to help find the", line++, false) + line(player, "!!Diamond of Smoke??.", line++, false) + } + + line++ + // DIAMOND OF ICE (KAMIL) + // Kamil vanishes on an icy wind... + if (getSubStage(player, attributeIceStage) == 100) { + line(player, "I defeated a warrior named Kamil, and now have the", line++, true) + line(player, "Diamond of Ice.", line++, true) + } else if (getSubStage(player, attributeIceStage) >= 1) { + // https://www.youtube.com/watch?v=F5F6Ds-T1P8 28:52 + line(player, "I met a crying ice troll child to the North of Trollheim.", line++, true) + if (getSubStage(player, attributeIceStage) >= 3) { + line(player, "I managed to cheer him up slightly with a sweet treat.", line++, true) + line(player, "After speaking with him, I discovered that his parents had", line++, true) + line(player, "been hurt by a 'bad man' who had the Diamond of Ice, and I", line++, true) + line(player, "agreed to help him rescue them.", line++, true) + line(player, "While heading through the icy area, I was attacked by an", line++, true) + line(player, "ice warrior named Kamil, and managed to defeat him.", line++, true) + line(player, "Was this the 'bad man' the troll child has spoken of?", line++, true) + line(player, "I should head further into the icy area to try and find", line++, false) + line(player, "them.", line++, false) + } else if (getSubStage(player, attributeIceStage) >= 2) { + line(player, "I managed to cheer him up slightly with a sweet treat.", line++, true) + line(player, "After speaking with him, I discovered that his parents had", line++, true) + line(player, "been hurt by a 'bad man' who had the Diamond of Ice, and I", line++, true) + line(player, "agreed to help him rescue them.", line++, true) + line(player, "I should head further into the icy area to try and find", line++, false) + line(player, "them.", line++, false) + } else if (getSubStage(player, attributeIceStage) >= 1) { + line(player, "I should cheer him up with something sweet.", line++, false) // Derived + } + } else if (getSubStage(player, attributeIceStage) == 0) { + line(player, "I can use the !!scrying glasses?? to help find the", line++, false) + line(player, "!!Diamond of Ice??.", line++, false) + } + + line++ + // DIAMOND OF SHADOW (DAMIS) + // Damis has vanished once more into the shadows... + if (getSubStage(player, attributeShadowStage) == 100) { + line(player, "I defeated a warrior named Damis, and now have the", line++, true) + line(player, "Diamond of Shadow.", line++, true) + } else if (getSubStage(player, attributeShadowStage) >= 1) { + line(player, "A travelling merchant named Rasolo had some information", line++, true) + line(player, "about the Diamond of Shadow.", line++, true) + line(player, "Apparently it was owned by an invisible warrior, who I", line++, true) + line(player, "needed a special ring to see.", line++, true) + line(player, "Rasolo owned such a ring, but would only trade it in return", line++, true) + line(player, "for a gilded cross stolen from him by a bandit named.", line++, true) + line(player, "Laheeb.", line++, true) + if (getSubStage(player, attributeShadowStage) >= 3) { + line(player, "I found Laheeb's treasure chest, and managed to bypass", line++, true) + line(player, "the traps on it to take the gilded cross, which I returned to", line++, true) + line(player, "Rasolo.", line++, true) + line(player, "In return, he gave me the Ring of Visibility.", line++, true) + line(player, "I should put the !!Ring of Visibility?? on and try and find the", line++, false) + line(player, "hidden home of !!Damis?? - Rasolo suggested it was very", line++, false) + line(player, "close by to where he is...", line++, false) + } else if (getSubStage(player, attributeShadowStage) >= 2) { + line(player, "I found Laheeb's treasure chest, and managed to bypass", line++, true) + line(player, "the traps on it to take the gilded cross.", line++, true) + line(player, "I need to return the !!gilded cross?? to !!Rasolo??.", line++, false) // Derived + } else if (getSubStage(player, attributeShadowStage) >= 1) { + line(player, "I need to find !!Laheeb's loot?? and retrieve the stolen !!gilded??", line++, false) + line(player, "!!cross??.", line++, false) + } + } else if (getSubStage(player, attributeShadowStage) == 0) { + line(player, "I can use the !!scrying glasses?? to help find the", line++, false) + line(player, "!!Diamond of Shadow??.", line++, false) + } + } + + // If you hold a diamond too long a stranger will try to kill you. + // https://oldschool.runescape.wiki/w/Transcript:Stranger + // After defeating the stranger + // player(THINKING, "I wonder what that was all about?") + + // After all 4 diamonds + // Player dialogue("I should make sure I have all four diamonds with me", "before speaking to Eblis again.") + + if (stage >= 10) { + // This disappears after you place all the diamonds. + } else if (stage >= 9 && completedAllSubstages(player)) { + line(player, "Now that I have recovered all of the !!Diamonds of??", line++, false) + line(player, "!!Azzanadra?? I should take them all to !!Eblis?? and find out what.", line++, false) + line(player, "is so special about them.", line++, false) + // This is the current message even AFTER you speak to Eblis... baQ6oJOk7Gc + } + + if (stage >= 11) { + // This disappears at the end of this quest. + } else if (stage >= 10) { + line(player, "I should explore the !!pyramid?? and see what !!treasure?? awaits", line++, false) + line(player, "me!", line++, false) + } + + if (stage >= 100) { + line(player, "At the heart of the pyramid I found a strange being, who", line++, true) + line(player, "gave me some powerful new magic spells.", line++, true) + line(player, "I can switch between my old spells and my new spells any", line++, true) + line(player, "time by using the altar there, and can avoid the traps by", line++, true) + line(player, "using the secret passage.", line++, true) + line++ + line++ + line(player,"QUEST COMPLETE!", line) + } + } + } + + override fun reset(player: Player) { + setVarbit(player, varbitChildReunite, 0, true) + + removeAttribute(player, attributeBoughtBeer) + removeAttribute(player, attributeCountMagicLogs) + removeAttribute(player, attributeCountSteelBars) + removeAttribute(player, attributeCountMoltenGlass) + removeAttribute(player, attributeCountBones) + removeAttribute(player, attributeCountAshes) + removeAttribute(player, attributeCountCharcoal) + removeAttribute(player, attributeCountBloodRune) + setVarbit(player, varbitMirrors, 0, true) + + removeAttribute(player, attributeBloodStage) + removeAttribute(player, attributeDessousInstance) + + removeAttribute(player, attributeSmokeStage) + removeAttribute(player, attributeFareedInstance) + removeAttribute(player, attributeUnlockedGate) + setVarbit(player, varbitStandingTorchNorthEast, 0, true) + setVarbit(player, varbitStandingTorchSouthEast, 0, true) + setVarbit(player, varbitStandingTorchSouthWest, 0, true) + setVarbit(player, varbitStandingTorchNorthWest, 0, true) + + removeAttribute(player, attributeIceStage) + removeAttribute(player, attributeKamilInstance) + removeAttribute(player, attributeTrollKillCount) + setVarbit(player, varbitFrozenFather, 0, true) // 0-frozen 1-defrosted + setVarbit(player, varbitFrozenMother, 0, true) // 0-frozen 1-defrosted + setVarbit(player, varbitChildReunite, 0, true) // 0-frozen 4-reunited 5-reunited 6-varbitfails/ends (This varbit seems to manage ice stages...) + setVarbit(player, varbitCaveEntrance, 0, true) // 6446 0 - 6 6441 + + removeAttribute(player, attributeShadowStage) + removeAttribute(player, attributeDamisInstance) + removeAttribute(player, attributeDamisWarning) + setVarbit(player, varbitRingOfVisibility, 0, true) // Set to 1 to show ladder when wearing ring of visibility. + + removeAttribute(player, attributeBloodDiamondInserted) + removeAttribute(player, attributeSmokeDiamondInserted) + removeAttribute(player, attributeIceDiamondInserted) + removeAttribute(player, attributeShadowDiamondInserted) + setVarbit(player, varbitBloodObelisk, 0, true) + setVarbit(player, varbitSmokeObelisk, 0, true) + setVarbit(player, varbitIceObelisk, 0, true) + setVarbit(player, varbitShadowObelisk, 0, true) + } + + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Desert Treasure Quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.ANCIENT_STAFF_4675, 240, 277, 5) + + drawReward(player,"3 Quest Points", ln++) + drawReward(player,"20,000 Magic XP", ln++) + drawReward(player,"Ancient Magicks", ln) + + player.skills.addExperience(Skills.MAGIC, 20000.0) + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + this.updateVarps(player) + } + + override fun updateVarps(player: Player) { + + // Ice: Cave Entrance Varbit + setVarbit(player, varbitCaveEntrance, getAttribute(player,attributeTrollKillCount, 0)) + + // Shadow: Ring of Visibility Ladder Varbit + if (inEquipment(player, Items.RING_OF_VISIBILITY_4657)) { + setVarbit(player, varbitRingOfVisibility, 1) + } else { + setVarbit(player, varbitRingOfVisibility, 0) + } + + // Obelisks Varbits + if (getAttribute(player, attributeBloodDiamondInserted, 0) == 1) { + setVarbit(player, varbitBloodObelisk, 1) + } else { + setVarbit(player, varbitBloodObelisk, 0) + } + + if (getAttribute(player, attributeSmokeDiamondInserted, 0) == 1) { + setVarbit(player, varbitSmokeObelisk, 1) + } else { + setVarbit(player, varbitSmokeObelisk, 0) + } + + if (getAttribute(player, attributeIceDiamondInserted, 0) == 1) { + setVarbit(player, varbitIceObelisk, 1) + } else { + setVarbit(player, varbitIceObelisk, 0) + } + + if (getAttribute(player, attributeShadowDiamondInserted, 0) == 1) { + setVarbit(player, varbitShadowObelisk, 1) + } else { + setVarbit(player, varbitShadowObelisk, 0) + } + + // Special Varbit for Ice Stage + if (getAttribute(player, attributeIceStage, 0) > 5) { + setVarbit(player, varbitChildReunite, 5) + } else { + setVarbit(player, varbitChildReunite, 0) + } + + // Stage Varbits + if(getQuestStage(player, questName) == 0) { + setVarbit(player, varbitDesertTreasure, 0, true) + setVarbit(player, varbitMirrors, 0, true) + } + if(getQuestStage(player, questName) in 1..7) { + setVarbit(player, varbitDesertTreasure, 1, true) + setVarbit(player, varbitMirrors, 0, true) + } + if(getQuestStage(player, questName) in 8..9) { + setVarbit(player, varbitDesertTreasure, 10, true) + setVarbit(player, varbitMirrors, 1, true) + } + if(getQuestStage(player, questName) == 10) { + setVarbit(player, varbitDesertTreasure, 13, true) + setVarbit(player, varbitMirrors, 1, true) + } + if(getQuestStage(player, questName) >= 100) { + setVarbit(player, varbitDesertTreasure, 15, true) + setVarbit(player, varbitMirrors, 1, true) + } + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasureListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasureListeners.kt new file mode 100644 index 000000000..3115efd3f --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasureListeners.kt @@ -0,0 +1,464 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.activity.Cutscene +import core.game.global.action.DoorActionHandler +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.world.map.Location +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class DesertTreasureListeners : InteractionListener { + + companion object { + fun allDiamondsInserted(player: Player): Boolean{ + return getAttribute(player, DesertTreasure.attributeBloodDiamondInserted, 0) == 1 && + getAttribute(player, DesertTreasure.attributeSmokeDiamondInserted, 0) == 1 && + getAttribute(player, DesertTreasure.attributeIceDiamondInserted, 0) == 1 && + getAttribute(player, DesertTreasure.attributeShadowDiamondInserted, 0) == 1 + } + } + var temp = 6517 + + override fun defineListeners() { + + // THE MIRRORS + on(Scenery.MYSTICAL_MIRROR_6423, SCENERY, "look-into") { player, node -> + SouthMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6425, SCENERY, "look-into") { player, node -> + SouthWestMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6427, SCENERY, "look-into") { player, node -> + NorthWestMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6429, SCENERY, "look-into") { player, node -> + NorthMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6431, SCENERY, "look-into") { player, node -> + NorthEastMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6433, SCENERY, "look-into") { player, node -> + SouthEastMirrorLookCutscene(player).start() + return@on true + } + + + // THE OBELISKS + onUseWith(IntType.SCENERY, intArrayOf(Items.ICE_DIAMOND_4671, Items.SMOKE_DIAMOND_4672, Items.SHADOW_DIAMOND_4673), Scenery.OBELISK_6483) { player, used, with -> + sendMessage(player, "That doesn't appear to be the correct diamond...") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.BLOOD_DIAMOND_4670, Scenery.OBELISK_6483) { player, used, with -> + if (getDynLevel(player, Skills.MAGIC) > 50) { + if (removeItem(player, used)) { + sendMessage(player, "The diamond is absorbed into the pillar.") + setVarbit(player, DesertTreasure.varbitBloodObelisk, 1) + setAttribute(player, DesertTreasure.attributeBloodDiamondInserted, 1) + if (allDiamondsInserted(player)) { + sendMessage(player, "The force preventing access to the Pyramid has now vanished.") + if (getQuestStage(player, DesertTreasure.questName) == 9) { + setQuestStage(player, DesertTreasure.questName, 10) + } + } + } + } else { + sendMessage(player, "You are not a powerful enough mage to breach the protective aura.") + sendMessage(player, "You need a magic level of at least 50 to enter the Pyramid.") + } + return@onUseWith true + } + onUseWith(IntType.SCENERY, intArrayOf(Items.BLOOD_DIAMOND_4670, Items.ICE_DIAMOND_4671, Items.SHADOW_DIAMOND_4673), Scenery.OBELISK_6486) { player, used, with -> + sendMessage(player, "That doesn't appear to be the correct diamond...") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.SMOKE_DIAMOND_4672, Scenery.OBELISK_6486) { player, used, with -> + if (getDynLevel(player, Skills.MAGIC) > 50) { + if (removeItem(player, used)) { + sendMessage(player, "The diamond is absorbed into the pillar.") + setVarbit(player, DesertTreasure.varbitSmokeObelisk, 1) + setAttribute(player, DesertTreasure.attributeSmokeDiamondInserted, 1) + if (allDiamondsInserted(player)) { + sendMessage(player, "The force preventing access to the Pyramid has now vanished.") + if (getQuestStage(player, DesertTreasure.questName) == 9) { + setQuestStage(player, DesertTreasure.questName, 10) + } + } + } + } else { + sendMessage(player, "You are not a powerful enough mage to breach the protective aura.") + sendMessage(player, "You need a magic level of at least 50 to enter the Pyramid.") + } + return@onUseWith true + } + onUseWith(IntType.SCENERY, intArrayOf(Items.BLOOD_DIAMOND_4670, Items.SMOKE_DIAMOND_4672, Items.SHADOW_DIAMOND_4673), Scenery.OBELISK_6489) { player, used, with -> + sendMessage(player, "That doesn't appear to be the correct diamond...") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.ICE_DIAMOND_4671, Scenery.OBELISK_6489) { player, used, with -> + if (getDynLevel(player, Skills.MAGIC) > 50) { + if (removeItem(player, used)) { + sendMessage(player, "The diamond is absorbed into the pillar.") + setVarbit(player, DesertTreasure.varbitIceObelisk, 1) + setAttribute(player, DesertTreasure.attributeIceDiamondInserted, 1) + if (allDiamondsInserted(player)) { + sendMessage(player, "The force preventing access to the Pyramid has now vanished.") + if (getQuestStage(player, DesertTreasure.questName) == 9) { + setQuestStage(player, DesertTreasure.questName, 10) + } + } + } + } else { + sendMessage(player, "You are not a powerful enough mage to breach the protective aura.") + sendMessage(player, "You need a magic level of at least 50 to enter the Pyramid.") + } + return@onUseWith true + } + onUseWith(IntType.SCENERY, intArrayOf(Items.BLOOD_DIAMOND_4670, Items.ICE_DIAMOND_4671, Items.SMOKE_DIAMOND_4672), Scenery.OBELISK_6492) { player, used, with -> + sendMessage(player, "That doesn't appear to be the correct diamond...") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.SHADOW_DIAMOND_4673, Scenery.OBELISK_6492) { player, used, with -> + if (getDynLevel(player, Skills.MAGIC) > 50) { + if (removeItem(player, used)) { + sendMessage(player, "The diamond is absorbed into the pillar.") + setVarbit(player, DesertTreasure.varbitShadowObelisk, 1) + setAttribute(player, DesertTreasure.attributeShadowDiamondInserted, 1) + if (allDiamondsInserted(player)) { + sendMessage(player, "The force preventing access to the Pyramid has now vanished.") + if (getQuestStage(player, DesertTreasure.questName) == 9) { + setQuestStage(player, DesertTreasure.questName, 10) + } + } + } + } else { + sendMessage(player, "You are not a powerful enough mage to breach the protective aura.") + sendMessage(player, "You need a magic level of at least 50 to enter the Pyramid.") + } + return@onUseWith true + } + + // THE DOOR + on(intArrayOf(Scenery.PYRAMID_ENTRANCE_6545, Scenery.PYRAMID_ENTRANCE_6547), SCENERY, "open") { player, node -> + if (allDiamondsInserted(player)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "A mystical power has sealed this door...") + } + return@on true + } + + // THE LADDERS + on(Scenery.LADDER_6497, SCENERY, "climb-down") { player, node -> + teleport(player, Location(2913, 4954, 3)) + return@on true + } + on(Scenery.LADDER_6504, SCENERY, "climb-up") { player, node -> + teleport(player, Location(3233, 2898, 0)) + return@on true + } + + on(Scenery.LADDER_6498, SCENERY, "climb-down") { player, node -> + teleport(player, Location(2846, 4964, 2)) + return@on true + } + on(Scenery.LADDER_6503, SCENERY, "climb-up") { player, node -> + teleport(player, Location(2909, 4963, 3)) + return@on true + } + + on(Scenery.LADDER_6499, SCENERY, "climb-down") { player, node -> + teleport(player, Location(2782, 4972, 1)) + return@on true + } + on(Scenery.LADDER_6502, SCENERY, "climb-up") { player, node -> + teleport(player, Location(2845, 4973, 2)) + return@on true + } + + on(Scenery.LADDER_6500, SCENERY, "climb-down") { player, node -> + teleport(player, Location(3233, 9293, 0)) + return@on true + } + on(Scenery.LADDER_6501, SCENERY, "climb-up") { player, node -> + teleport(player, Location(2783, 4941, 1)) + return@on true + } + + on((6512..6517).toIntArray(), SCENERY, "search") { player, node -> + // Technically there is loot, but I'm lazy as hell. + sendMessage(player, "You don't find anything interesting.") + return@on true + } + + // After Quest + + // Backdoor + on(Scenery.TUNNEL_6481, SCENERY, "enter") { player, node -> + if (isQuestComplete(player, DesertTreasure.questName)){ + teleport(player, Location(3233, 9313, 0)) + } else { + sendMessage(player, "This passage does not seem to lead anywhere...") // https://youtu.be/uNkBucGaqac + } + return@on true + } + + // Portal, which only appears after DT + on(Scenery.PORTAL_6551, SCENERY, "use") { player, node -> + teleport(player, Location(3233, 2887, 0)) + return@on true + } + + } +} + + +// https://www.youtube.com/watch?v=yMwp78OI2y8 + +// Ice Troll +class NorthMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(11322) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 5, 27) + moveCamera(5, 27, 1000) + rotateCamera(6, 27, 1000) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Canifis +class NorthEastMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13878) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 47, 31) + moveCamera(47, 31, 1000) + rotateCamera(43, 31, 1000) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Smoke Dungeon +class SouthEastMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13102) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 36, 20) + moveCamera(36, 20, 1000) + rotateCamera(44, 20, 1000) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Pyramid +class SouthMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(12845) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 33, 41) + moveCamera(33, 41, 1000) + rotateCamera(33, 39, 990) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Bedabin +class SouthWestMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(12590) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 40, 39) + moveCamera(40, 39, 1200) + rotateCamera(10, 39, 1200) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Rasolo +class NorthWestMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(10037) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 56, 40) + moveCamera(56, 40, 1400) + rotateCamera(56, 35, 1000) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DessousBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DessousBehavior.kt new file mode 100644 index 000000000..ec4e8a142 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DessousBehavior.kt @@ -0,0 +1,144 @@ +package content.region.desert.quest.deserttreasure + +import content.region.kandarin.quest.templeofikov.TempleOfIkov +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.* +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.impl.Projectile +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.prayer.PrayerType +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +class DessousMeleeBehavior : NPCBehavior(NPCs.DESSOUS_1914, NPCs.DESSOUS_1915) { + private var disappearing = false; + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun tick(self: NPC): Boolean{ + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, self.walkRadius)) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Dessous returns to his grave, bored of toying with you.") + removeAttribute(player, DesertTreasure.attributeDessousInstance) + } + poofClear(self) + } + + // Dessous just continually hisses independently of projectile fires. + if (self.id == NPCs.DESSOUS_1915 && self.properties.combatPulse.isInCombat) { + animate(self, Animation(1914)) + } + // This is probably the prayer flicking nonsense. + if (self.id == NPCs.DESSOUS_1914 && player != null && player.prayer.get(PrayerType.PROTECT_FROM_MELEE)) { + self.transform(NPCs.DESSOUS_1915) + Graphics.send(Graphics(86), self.location) + } else if (self.id == NPCs.DESSOUS_1915 && player != null && (player.prayer.get(PrayerType.PROTECT_FROM_MAGIC) || player.prayer.get(PrayerType.PROTECT_FROM_MISSILES))) { + self.transform(NPCs.DESSOUS_1914) + Graphics.send(Graphics(86), self.location) + } + return true + } + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + if (self.id == NPCs.DESSOUS_1915) { + // 2 x 5HP (One Magic, One Ranged) + return CombatHandler() + } else { + // Fast melee attack 3 ticks up to 19HP + return original + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + // Teleport nearer, if too far. + if (victim is Player) { + if (victim.location.getDistance(self.location) >= 5) { + Graphics.send(Graphics(86), self.location) + self.properties.teleportLocation = victim.location + Graphics.send(Graphics(86), self.location) + } + } + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + val player = killer + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 2) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeBloodStage, 3) + } + removeAttribute(player, DesertTreasure.attributeDessousInstance) + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> player(FacialExpression.ANGRY, "Well that's Dessous dead, but where is the Diamond he", "was supposed to have?").also { stage++ } + 1 -> playerl(FacialExpression.ANGRY, "If Malak lied to me about it, he is going to pay!").also { + stage = END_DIALOGUE + } + } + } + }) + } + } + + /** Handler for ranged. */ + class CombatHandler : MultiSwingHandler( + SwitchAttack(CombatStyle.MAGIC.swingHandler, null), + SwitchAttack(CombatStyle.RANGE.swingHandler, null) + ) { + override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { + if (entity is NPC && victim is Player) { + val projectile = Projectile.create( + victim.location.transform(Location(intArrayOf(3, -3).random(),intArrayOf(3, -3).random())), + victim.location, + 350, + 0, + 0, + 0, + 60, + 0, + 255 + ) + // 2 x 5HP (One Magic, One Ranged) + state!!.estimatedHit = 5 + state.secondaryHit = 5 // I have no idea what I'm doing + queueScript(entity, 0, QueueStrength.STRONG) { stage: Int -> + when (stage) { + 0 -> { + sendChat(entity, "Hssssssssssss") + projectile.send() + return@queueScript delayScript(entity, entity.location.getDistance(victim.location).toInt()) + } + 1 -> { + return@queueScript stopExecuting(entity) + } + else -> return@queueScript stopExecuting(entity) + } + } + + } + return super.swing(entity, victim, state) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfBloodListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfBloodListeners.kt new file mode 100644 index 000000000..8105aebf7 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfBloodListeners.kt @@ -0,0 +1,134 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class DiamondOfBloodListeners : InteractionListener { + override fun defineListeners() { + + // Silver pot conversions + onUseWith(ITEM, Items.SILVER_POT_4660, Items.SPICE_2007) { player, used, with -> + sendMessage(player, "You add some spices to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.SILVER_POT_4664) + } + return@onUseWith true + } + onUseWith(ITEM, Items.SILVER_POT_4660, Items.GARLIC_POWDER_4668) { player, used, with -> + sendMessage(player, "You add some crushed garlic to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.SILVER_POT_4662) + } + return@onUseWith true + } + onUseWith(ITEM, Items.SILVER_POT_4662, Items.SPICE_2007) { player, used, with -> + sendMessage(player, "You add some spices to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.SILVER_POT_4666) + } + return@onUseWith true + } + onUseWith(ITEM, Items.SILVER_POT_4664, Items.GARLIC_POWDER_4668) { player, used, with -> + sendMessage(player, "You add some crushed garlic to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.SILVER_POT_4666) + } + return@onUseWith true + } + // Blessed pot conversions + onUseWith(ITEM, Items.BLESSED_POT_4661, Items.SPICE_2007) { player, used, with -> + sendMessage(player, "You add some spices to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.BLESSED_POT_4665) + } + return@onUseWith true + } + onUseWith(ITEM, Items.BLESSED_POT_4661, Items.GARLIC_POWDER_4668) { player, used, with -> + sendMessage(player, "You add some crushed garlic to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.BLESSED_POT_4663) + } + return@onUseWith true + } + onUseWith(ITEM, Items.BLESSED_POT_4663, Items.SPICE_2007) { player, used, with -> + sendMessage(player, "You add some spices to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.BLESSED_POT_4667) + } + return@onUseWith true + } + onUseWith(ITEM, Items.BLESSED_POT_4665, Items.GARLIC_POWDER_4668) { player, used, with -> + sendMessage(player, "You add some crushed garlic to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.BLESSED_POT_4667) + } + return@onUseWith true + } + + // You need to crush the garlic. + onUseWith(ITEM, intArrayOf(Items.SILVER_POT_4660, Items.BLESSED_POT_4661, Items.SILVER_POT_4662, Items.BLESSED_POT_4663, Items.SILVER_POT_4664, Items.BLESSED_POT_4665, Items.SILVER_POT_4666, Items.BLESSED_POT_4667 ), Items.GARLIC_1550) { player, used, with -> + sendMessage(player, "You need to crush the garlic before adding it to the pot.") + return@onUseWith true + } + + // Dessous jumps out. + onUseWith(SCENERY, Items.BLESSED_POT_4667, Scenery.VAMPIRE_TOMB_6437) { player, used, with -> + val prevNpc = getAttribute(player, DesertTreasure.attributeDessousInstance, null) + if (prevNpc != null) { + prevNpc.clear() + } + sendMessage(player, "You pour the blood from the pot onto the tomb.") + removeItem(player, used) + val scenery = with.asScenery() + // Swap to a splittable vampire tomb scenery. + replaceScenery(scenery, Scenery.VAMPIRE_TOMB_6438, Animation(1915).duration) + // Vampire Tomb breaks open. + animateScenery(player, scenery, 1915) + // 8 Bat projectiles + spawnProjectile(Location(3570, 3402), Location(3570, 3404), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3570, 3400), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3568, 3402), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3572, 3402), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3568, 3404), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3572, 3404), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3568, 3400), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3572, 3400), 350, 0, 0, 0, 60, 0) + val npc = NPC(NPCs.DESSOUS_1914) + queueScript(player, 1, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + // Projectile gfx for Dessous to jump out. + spawnProjectile(Location(3570, 3402), Location(3570, 3405), 351, 0, 0, 0, 40, 0) + return@queueScript delayScript(player, 1) + } + 1 -> { + npc.isRespawn = false + npc.isWalks = false + npc.location = Location(3570, 3405, 0) + npc.direction = Direction.NORTH + setAttribute(player, DesertTreasure.attributeDessousInstance, npc) + setAttribute(npc, "target", player) + + npc.init() + npc.attack(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + + //sendGraphics(350, Location(3570, 3402)) + //sendGraphics(351, Location(3570, 3402)) + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfIceListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfIceListeners.kt new file mode 100644 index 000000000..9ee2c5ba9 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfIceListeners.kt @@ -0,0 +1,168 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class DiamondOfIceListeners : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.NPC, Items.CHOCOLATE_CAKE_1897, NPCs.BANDIT_1932 /* should be NPCs.TROLL_CHILD_1932 */) { player, used, with -> + if (removeItem(player, used)) { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 0) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeIceStage, 1) + } + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> player("Hey there little troll...", "Take this and dry those tears...").also { stage++ } + 1 -> npc(FacialExpression.OLD_NEARLY_CRYING, "(sniff)").also { + stage = END_DIALOGUE + } + } + } + }, with as NPC) + } + return@onUseWith true + } + + on(intArrayOf(Scenery.ICE_GATE_5043, Scenery.ICE_GATE_5044), SCENERY, "go-through") { player, node -> + + if ((getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) > 1) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + sendMessage(player, "You squeeze through the large icy bars of the gate.") + // Anim 3272 to squeeze through? + if(player.location.x > 2838) { + teleport(player, Location(2837, 3739, 0)) + } else { + teleport(player, Location(2839, 3739, 0)) + } + } else { + // j_SdwOX1JWg + sendDialogueLines(player, "The bars are frozen tightly shut and a sturdy layer of ice prevents", "you from slipping through.") + } + return@on true + } + + on(Scenery.CAVE_ENTRANCE_6441, SCENERY, "enter") { player, node -> + lock(player, 3) + animate(player, 2796) // Crawling + queueScript(player, 3, QueueStrength.SOFT) { + teleport(player, Location(2874, 3720, 0)) + return@queueScript stopExecuting(player) + } + return@on true + } + + on(Scenery.CAVE_ENTRANCE_6446, SCENERY, "enter") { player, node -> + sendMessage(player, "The entrance to the cave is covered in too much ice to get through.") + return@on true + } + + on(Scenery.CAVE_EXIT_6447, SCENERY, "enter") { player, node -> + lock(player, 3) + animate(player, 2796) // Crawling + queueScript(player, 3, QueueStrength.SOFT) { + teleport(player, Location(2867, 3719, 0)) + return@queueScript stopExecuting(player) + } + return@on true + } + + on(Scenery.ICE_LEDGE_6455, SCENERY, "use") { player, node -> + + if ((getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) >= 3) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + if (inEquipment(player, Items.SPIKED_BOOTS_3107)) { + teleport(player, Location(2838, 3803, 1)) + } else { + sendPlayerDialogue(player, "I don't think I'll make much headway along that icy slope without some spiked boots...") + } + } else { + sendMessage(player, "You have not defeated Kamil yet.") + } + return@on true + } + + on(intArrayOf(Scenery.ICE_GATE_6461, Scenery.ICE_GATE_6462), SCENERY, "go-through") { player, node -> + + teleport(player, Location(2852, 3810, 2)) + return@on true + } + + // This is 1943 as base + on(NPCs.ICE_BLOCK_1944, NPC, "talk-to") { player, node -> + sendDialogueLines(player, "There is a thick layer of ice covering this troll.", "You will have to find some way of shattering it.") + return@on true + } + on(NPCs.ICE_BLOCK_1944, NPC, "smash-ice") { player, node -> + player.attack(node) + return@on true + } + + // This is 1945 as base + on(NPCs.ICE_BLOCK_1946, NPC, "talk-to") { player, node -> + sendDialogueLines(player, "There is a thick layer of ice covering this troll.", "You will have to find some way of shattering it.") + return@on true + } + on(NPCs.ICE_BLOCK_1946, NPC, "smash-ice") { player, node -> + player.attack(node) + return@on true + } + } +} + +/** This is while you are walking up the ice path. **/ +class ComicalTrippingIceArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf( + ZoneBorders(2815, 3775, 2880, 3839, 1), + ZoneBorders(2815, 3775, 2880, 3839, 2) + ) + } + + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + if ((1..10).random() == 1) { + lock(entity, 2) + stopWalk(entity) + animate(entity, 767) + } + } + } +} + +class IceAreaAttack : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2850, 3750, 2880, 3770)) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player && + getQuestStage(entity, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(entity, DesertTreasure.attributeIceStage) == 2 && + getAttribute(entity, DesertTreasure.attributeKamilInstance, null) == null + ) { + sendMessage(entity, "You can feel an evil presence nearby...") + val npc = NPC.create(NPCs.KAMIL_1913, Location(2857, 3754, 0)) + setAttribute(entity, DesertTreasure.attributeKamilInstance, npc) + setAttribute(npc, "target", entity) + npc.isRespawn = false + npc.init() + npc.attack(entity) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfShadowListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfShadowListeners.kt new file mode 100644 index 000000000..3ca6e6a60 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfShadowListeners.kt @@ -0,0 +1,264 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class DiamondOfShadowListeners : InteractionListener { + + companion object { + + fun roll(player: Player): Boolean { + + val chance = RandomFunction.randomDouble(1.0, 100.0) + val successChance = RandomFunction.getSkillSuccessChance(52.0, 128.0, getDynLevel(player, Skills.THIEVING)) + + return chance < successChance + } + + fun pickAttempt(player: Player, picklockItem: Item?) { + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "You attempt to pick the first lock...") + return@queueScript delayScript(player, 1) + } + + 1 -> { + if (roll(player)) { + sendMessage(player, "...You successfully picked the first lock!") + return@queueScript delayScript(player, 1) + } else { + if(picklockItem != null) { + removeItem(player, picklockItem) + } else { + removeItem(player, Items.LOCKPICK_1523) + } + sendMessage(player, "...and fail. The locking mechanism has reset itself.") + sendMessage(player, "Your lock pick snapped while attempting to pick the lock.") + impact(player, 3) + applyPoison(player, player, 6) + return@queueScript stopExecuting(player) + } + } + + 2 -> { + sendMessage(player, "You attempt to pick the second lock...") + return@queueScript delayScript(player, 1) + } + + 3 -> { + if (roll(player)) { + sendMessage(player, "...You successfully picked the second lock!") + return@queueScript delayScript(player, 1) + } else { + if(picklockItem != null) { + removeItem(player, picklockItem) + } else { + removeItem(player, Items.LOCKPICK_1523) + } + sendMessage(player, "...and fail. The locking mechanism has reset itself.") + sendMessage(player, "Your lock pick snapped while attempting to pick the lock.") + impact(player, 3) + applyPoison(player, player, 6) + return@queueScript stopExecuting(player) + } + } + + 4 -> { + sendMessage(player, "You attempt to pick the final lock...") + return@queueScript delayScript(player, 1) + } + + 5 -> { + if (roll(player)) { + sendMessage(player, "You managed to pick the final lock.") + return@queueScript delayScript(player, 1) + } else { + if(picklockItem != null) { + removeItem(player, picklockItem) + } else { + removeItem(player, Items.LOCKPICK_1523) + } + sendMessage(player, "...and fail. The locking mechanism has reset itself.") + sendMessage(player, "Your lock pick snapped while attempting to pick the lock.") + impact(player, 3) + applyPoison(player, player, 6) + return@queueScript stopExecuting(player) + } + } + + 6 -> { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 1) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeShadowStage, 2) + } + return@queueScript stopExecuting(player) + } + + else -> return@queueScript stopExecuting(player) + } + } + } + } + + override fun defineListeners() { + + // Hidden entrance to the shadow dungeon. + on(Scenery.LADDER_6561, SCENERY, "climb-down") { player, node -> + teleport(player, Location(2630, 5072)) + return@on true + } + + /** This will open up a lot of other places. Maybe have this in a general file? */ + onEquip(Items.RING_OF_VISIBILITY_4657) { player, _ -> + + if((DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) >= 3 && + getQuestStage(player, DesertTreasure.questName) >= 9) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + + setVarbit(player, DesertTreasure.varbitRingOfVisibility, 1) + return@onEquip true + } + sendMessage(player, "You need to complete part of Desert Treasure to equip this.") + return@onEquip false + } + + onUnequip(Items.RING_OF_VISIBILITY_4657) { player, _ -> + setVarbit(player, DesertTreasure.varbitRingOfVisibility, 0) + return@onUnequip true + } + + + + on(Scenery.SECURE_CHEST_6448, SCENERY, "open") { player, node -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 1) { + if (inInventory(player, Items.LOCKPICK_1523)) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> dialogue("Your skill as a thief allows you to see some kind of elaborate booby", "trapped locking mechanism on this chest.").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes", 2, true), + Topic(FacialExpression.NEUTRAL, "No", END_DIALOGUE, true), + title = "Try to open the chest?" + ) + 2 -> end().also { + if (inInventory(player, Items.LOCKPICK_1523)) { + pickAttempt(player, null) + } else { + sendMessage(player, "You need a lockpick in order to attempt this.") + } + } + } + } + }) + } else { + sendMessage(player, "You need a lockpick in order to attempt this.") + } + } else if ((DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) >= 2 && + getQuestStage(player, DesertTreasure.questName) >= 9) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + if (inInventory(player, Items.GILDED_CROSS_4674)) { + sendMessage(player, "The chest is empty.") + } else { + replaceScenery(node as core.game.node.scenery.Scenery, 6449, 2) + sendMessage(player, "Inside the chest, hidden under some rags, you find a Gilded Cross.") + addItemOrDrop(player, Items.GILDED_CROSS_4674) + } + } else { + sendPlayerDialogue(player, "These bandits are hostile enough without me trying to rob them!") + } + return@on true + } + + onUseWith(SCENERY, Items.LOCKPICK_1523, Scenery.SECURE_CHEST_6448) { player, used, with -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 1) { + pickAttempt(player, used as Item) + } else if((DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) >= 2 && + getQuestStage(player, DesertTreasure.questName) >= 9) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + sendMessage(player, "The chest is unlocked.") + } else { + sendPlayerDialogue(player, "These bandits are hostile enough without me trying to rob them!") + } + return@onUseWith true + } + + } +} + +class ShadowDungeonWarning : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2726, 5072, 2728, 5072)) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + if ( + getQuestStage(entity, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(entity, DesertTreasure.attributeShadowStage) == 3 && + getAttribute(entity, DesertTreasure.attributeDamisWarning, false) + ) { + sendMessage(entity, "A voice seems to come from the walls around you;") + sendMessage(entity, "'You... do not be... long in this... place") + sendMessage(entity, "Turn... back now, or... prepare... to meet your... doom'") + getAttribute(entity, DesertTreasure.attributeDamisWarning, true) + } else if ( + getQuestStage(entity, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(entity, DesertTreasure.attributeShadowStage) == 100 + ) { + if (!inInventory(entity, Items.SHADOW_DIAMOND_4673) && !inBank(entity, Items.SHADOW_DIAMOND_4673)) { + sendMessage(entity, "The Diamond of Shadow seems to have mystically found its way back here...") + GroundItemManager.create(Item(Items.SHADOW_DIAMOND_4673), Location(2739, 5088, 0), entity) + } + } + } + } +} + +class ShadowDungeonAttack : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2731, 5085, 2748, 5097)) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + if (getQuestStage(entity, DesertTreasure.questName) == 9) { + if (DesertTreasure.getSubStage(entity, DesertTreasure.attributeShadowStage) == 3 && + getAttribute(entity, DesertTreasure.attributeDamisInstance, null) == null + ) { + val npc = NPC.create(NPCs.DAMIS_1974, Location(2739, 5088, 0)) + setAttribute(entity, DesertTreasure.attributeDamisInstance, npc) + setAttribute(npc, "target", entity) + npc.isRespawn = false + npc.walkRadius = 30 + npc.init() + npc.attack(entity) + sendChat(npc, "You should have listened to me!") + } + } else if (DesertTreasure.getSubStage(entity, DesertTreasure.attributeShadowStage) >= 100) { + if (!inInventory(entity, Items.SHADOW_DIAMOND_4673) && !inBank(entity, Items.SHADOW_DIAMOND_4673)) { + sendMessage(entity, "The Diamond of Shadow seems to have mystically found its way back here...") + GroundItemManager.create(Item(Items.SHADOW_DIAMOND_4673), Location(2739, 5088, 0), entity) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfSmokeListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfSmokeListeners.kt new file mode 100644 index 000000000..279b6d17d --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfSmokeListeners.kt @@ -0,0 +1,180 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.system.timer.RSTimer +import core.game.world.map.Location +import core.tools.secondsToTicks +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class DiamondOfSmokeListeners : InteractionListener { + companion object { + const val timerIdentifierTorchNE = "deserttreasureNEtorch" + const val timerIdentifierTorchSE = "deserttreasureSEtorch" + const val timerIdentifierTorchSW = "deserttreasureSWtorch" + const val timerIdentifierTorchNW = "deserttreasureNWtorch" + + fun checkAllTorchesLit(player: Player) : Boolean { + return getVarbit(player, DesertTreasure.varbitStandingTorchNorthEast) == 1 && + getVarbit(player, DesertTreasure.varbitStandingTorchSouthEast) == 1 && + getVarbit(player, DesertTreasure.varbitStandingTorchSouthWest) == 1 && + getVarbit(player, DesertTreasure.varbitStandingTorchNorthWest) == 1 + } + } + + override fun defineListeners() { + on(Scenery.BURNT_CHEST_6420, SCENERY, "open") { player, node -> + if (checkAllTorchesLit(player)) { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeSmokeStage) == 0) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeSmokeStage, 1) + } + } + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeSmokeStage) >= 1) { + replaceScenery(node as core.game.node.scenery.Scenery, 6421, 2) + addItemOrDrop(player, Items.WARM_KEY_4656) + sendMessage(player, "You open the chest and take a key.") + } else { + sendDialogueLines(player, "There seems to be no way to open this chest. Engraved where the", "keyhole should be is a message:", "'Light the path, and find the key...'") + } + return@on true + } + + on(intArrayOf(Scenery.GATE_6451, Scenery.GATE_6452), SCENERY, "open") { player, node -> + if (getQuestStage(player, DesertTreasure.questName) == 9) { + // Set attributeUnlockedGate to true if warm key and first time unlocking the gate. + if (!getAttribute(player, DesertTreasure.attributeUnlockedGate, false) && inInventory(player, Items.WARM_KEY_4656)) { + if(removeItem(player, Items.WARM_KEY_4656)) { + sendMessage(player, "You unlock the gate and enter the room.") + setAttribute(player, DesertTreasure.attributeUnlockedGate, true) + } + } + // Fight if unlocked gate, drop recovery diamond if done. + if (getAttribute(player, DesertTreasure.attributeUnlockedGate, false)) { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeSmokeStage) == 1 && + getAttribute(player, DesertTreasure.attributeFareedInstance, null) == null) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + val npc = core.game.node.entity.npc.NPC.create(NPCs.FAREED_1977, Location(3315, 9376, 0)) + setAttribute(player, DesertTreasure.attributeFareedInstance, npc) + setAttribute(npc, "target", player) + npc.isRespawn = false + npc.init() + npc.attack(player) + sendChat(npc, "You dare trespass in my realm?") + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeSmokeStage) >= 100) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + if (!inInventory(player, Items.SMOKE_DIAMOND_4672) && !inBank(player, Items.SMOKE_DIAMOND_4672)) { + sendMessage(player, "The Diamond of Smoke seems to have mystically found its way back here...") + GroundItemManager.create(Item(Items.SMOKE_DIAMOND_4672), Location(3315, 9376, 0), player) + } + } + } else { + sendMessage(player, "The gate is locked.") + } + + } else if (getQuestStage(player, DesertTreasure.questName) in 9..100) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "The gate is locked.") + } + return@on true + } + + + on(Scenery.A_DARK_HOLE_31367, SCENERY, "enter") { player, node -> + // You have to come from the Pollnivneach Slayer Dungeon, then this will tele you back. + sendDialogueLines(player, "This hole leads to a maze of other passages. Without knowing where", "you are headed, there's no chance of reaching anywhere interesting.") + return@on true + } + + // NE Standing Torch + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.STANDING_TORCH_6406, Scenery.STANDING_TORCH_6413) { player, used, with -> + if (getDynLevel(player, Skills.FIREMAKING) < 50) { + sendMessage(player, "You need a firemaking level of 50 to light this torch.") + return@onUseWith true + } + setVarbit(player, DesertTreasure.varbitStandingTorchNorthEast, 1) + sendMessage(player, "You light the torch.") + if (checkAllTorchesLit(player)) { + sendMessage(player, "The path is lit, now claim the key...") + } + removeTimer(player, timerIdentifierTorchNE) + registerTimer(player, StandingTorchTimer(timerIdentifierTorchNE, DesertTreasure.varbitStandingTorchNorthEast)) + return@onUseWith true + } + + // SE Standing Torch + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.STANDING_TORCH_6408, Scenery.STANDING_TORCH_6414) { player, used, with -> + if (getDynLevel(player, Skills.FIREMAKING) < 50) { + sendMessage(player, "You need a firemaking level of 50 to light this torch.") + return@onUseWith true + } + setVarbit(player, DesertTreasure.varbitStandingTorchSouthEast, 1) + sendMessage(player, "You light the torch.") + if (checkAllTorchesLit(player)) { + sendMessage(player, "The path is lit, now claim the key...") + } + removeTimer(player, timerIdentifierTorchSE) + registerTimer(player, StandingTorchTimer(timerIdentifierTorchSE, DesertTreasure.varbitStandingTorchSouthEast)) + return@onUseWith true + } + + // SW Standing Torch + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.STANDING_TORCH_6410, Scenery.STANDING_TORCH_6415) { player, used, with -> + if (getDynLevel(player, Skills.FIREMAKING) < 50) { + sendMessage(player, "You need a firemaking level of 50 to light this torch.") + return@onUseWith true + } + setVarbit(player, DesertTreasure.varbitStandingTorchSouthWest, 1) + sendMessage(player, "You light the torch.") + if (checkAllTorchesLit(player)) { + sendMessage(player, "The path is lit, now claim the key...") + } + removeTimer(player, timerIdentifierTorchSW) + registerTimer(player, StandingTorchTimer(timerIdentifierTorchSW, DesertTreasure.varbitStandingTorchSouthWest)) + return@onUseWith true + } + + // NW Standing Torch + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.STANDING_TORCH_6412, Scenery.STANDING_TORCH_6416) { player, used, with -> + if (getDynLevel(player, Skills.FIREMAKING) < 50) { + sendMessage(player, "You need a firemaking level of 50 to light this torch.") + return@onUseWith true + } + setVarbit(player, DesertTreasure.varbitStandingTorchNorthWest, 1) + sendMessage(player, "You light the torch.") + if (checkAllTorchesLit(player)) { + sendMessage(player, "The path is lit, now claim the key...") + } + removeTimer(player, timerIdentifierTorchNW) + registerTimer(player, StandingTorchTimer(timerIdentifierTorchNW, DesertTreasure.varbitStandingTorchNorthWest)) + return@onUseWith true + } + } +} + +class StandingTorchTimer(private val timerIdentifier: String = "deserttreasureunknowntimer", private val torchVarbit: Int = 0) : RSTimer(secondsToTicks(150), timerIdentifier) { + override fun run(entity: Entity): Boolean { + if (entity is Player) { + when(timerIdentifier) { + DiamondOfSmokeListeners.timerIdentifierTorchNE -> sendMessage(entity, "The North-east torch burns out...") + DiamondOfSmokeListeners.timerIdentifierTorchSE -> sendMessage(entity, "The South-east torch burns out...") + DiamondOfSmokeListeners.timerIdentifierTorchSW -> sendMessage(entity, "The South-west torch burns out...") + DiamondOfSmokeListeners.timerIdentifierTorchNW -> sendMessage(entity, "The North-west torch burns out...") + else -> sendMessage(entity, "The torch burns out...") + } + setVarbit(entity, torchVarbit, 0) + } + entity.timers.removeTimer(this) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/EblisDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/EblisDialogue.kt new file mode 100644 index 000000000..11608e56c --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/EblisDialogue.kt @@ -0,0 +1,328 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class EblisDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, EblisDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return EblisDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.EBLIS_1923) + } + +} +class EblisDialogueFile : DialogueBuilderFile() { + + companion object { + fun checkAllGiven(player: Player): Boolean { + return (getAttribute(player, DesertTreasure.attributeCountMagicLogs, 0) >= 12 && + getAttribute(player, DesertTreasure.attributeCountSteelBars, 0) >= 6 && + getAttribute(player, DesertTreasure.attributeCountMoltenGlass, 0) >= 6 && + getAttribute(player, DesertTreasure.attributeCountBones, 0) >= 1 && + getAttribute(player, DesertTreasure.attributeCountAshes, 0) >= 1 && + getAttribute(player, DesertTreasure.attributeCountCharcoal, 0) >= 1 && + getAttribute(player, DesertTreasure.attributeCountBloodRune, 0) >= 1) + } + } + + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4) + .npcl("Leave us to our fate. We care nothing for the world that betrayed us, or those that come from it.") + .end() + + b.onQuestStages(DesertTreasure.questName, 5,6) + .playerl("Hello. I represent the Museum of Varrock, and I have reason to believe there may be some kinds of artefacts of historical significance in the nearby area...") + .npcl("Ah yes. The only time people care about our existence is when they think they have something to gain from us.") + .npcl("I have nothing to say to you. You and your kind are not welcome here.") + .playerl("Please, if I can just have a few minutes of your time to ask some questions...?") + .npcl("(sigh) I suppose I can spare you that. What do you wish to know about?") + .options() + .let { optionBuilder -> + optionBuilder.option("Why is this village so hostile?") + .playerl("Why are all of the people here so hostile? You would think I was asking you for money instead of just for answers to a few questions...") + .npcl("It is a long story, and I doubt you have much interest in hearing it. Your sort never are, you just take what you can of ours, and then abandon us once more to the desert.") + .options() + .let { optionBuilder2 -> + optionBuilder2.option("No, I want to hear this story.") + .playerl("Actually, I'd be quite interested to hear what it is you have to say to excuse the attitude everybody in this village seems to have.") + .npcl("Ah, it all begun many generations ago, when our ancestors were the proud rulers of these lands...") + .npcl("My ancestors lived far to the North of here, and our lands stretched from the sea in the East to the river Lum, and the mountain of ice. From coast to coast, North to South, our domain was absolute.") + .npcl("Our god was kind to us, and blessed us with prosperity and happiness, and in return we were merciless to his enemies wherever we found them.") + .npcl("Then came the betrayal.") + .npcl("Our god was banished, leaving us helpless to our fates.") + .npcl("Without his protection, we were forced to fend for ourselves once more, against the enemies that sought to destroy us through their petty jealousies.") + .npcl("But we did not succumb without fighting! The spiteful Saradomin and pathetic Zamorak warred with each other, but the hatred they had for each other was as nothing to the hatred they held towards us!") + .npcl("With each battle they waged, we lost more and more land, unable to fight on all fronts, and were pushed further and further South into this gods-forsaken desert.") + .npcl("Our greatest hero, Azzanadra, was finally trapped in a strange stone structure to the South of here, and bound within by terrible powers...") + .npcl("And with that our lands, our homes, our very lives were stolen from us! Too weak to reclaim what was rightfully ours, we made our homes here, knowing that someday Azzanadra will") + .npcl("return with his magnificent power, and bring us back to our former glory...") + .playerl("So you're upset because of something that happened hundreds of years ago?") + .playerl("Seems to me like maybe you should find some closure, and let the past go...") + .npcl("The insults heaped upon my race will never be forgotten, will never be forgiven and will never again be overlooked.") + .npcl("Someday, a harsh wind will blow upon this land, uncovering the wrongs of the past, and we will get back what is rightfully ours. Until such a day we will bide our time here, and will") + .npcl("always be ready with our blades for our righteous vengeance.") + .end() + + optionBuilder2.option("I don't care about your story.") + .playerl("I don't really care what your story is to be honest, there is no excuse for such rudeness or hostility.") + .playerl("I have done nothing wrong to you, but everybody here treats me like I have committed some great crime against the village.") + .npcl("That is because, from our point of view, you have.") + .playerl("What? Just because I entered your village?") + .npcl("You have no right to be here! You have no right to the life you have, for it was taken at our expense!") + .playerl("Whatever... No wonder all you loonies live out here in the desert by yourselves.") + .end() + } + + optionBuilder.option("Do you know anything about treasure near here?") + .playerl("I was wondering if you knew anything about some treasure somewhere around here?") + .playerl("I have some evidence that there might be some kind of treasure hidden very close to this village...") + .npcl("If I knew of any treasure I would not choose to spend my life in this gods-forsaken desert.") + .end() + + optionBuilder.option("Do you know anything about a fortress near here?") + .playerl("Do you know anything about some kind of fortress nearby? I have reason to believe there is, or at least used to be, some kind of fortress very close to here...") + .npcl("Nobody would build anything in this wasteland unless they were forced to, to survive.") + .npcl("I know of no fortress, I know of no reason why anyone would ever bother doing anything out here in the desert.") + .end() + + + // This will only show up after you've talked to the bartender. + optionBuilder.optionIf("Tell me of the four diamonds of Azzanadra.") { player -> + return@optionIf getQuestStage(player, DesertTreasure.questName) == 6 + } + .playerl("So tell me... Did you ever hear of something called the Diamonds of Azzanadra?") + .npcl("This is the treasure which you seek???") + .npcl("Please accept my apologies noble @g[sir,madam]! I thought you were but some opportunistic thief, looking to steal what heritage we have left! Now I see that you are in fact a brave adventurer,") + .npcl("looking to restore our glories back upon us!") + .playerl("Uh... yeah... So anyway, you have heard of them?") + .npcl("Heard of them? Of course I have heard of them! They are the legacy of the great Mahjarrat hero, Azzanadra!") + .playerl("So... do you have any idea where they might be? I have a feeling they will be very valuable.") + .playerl("Uh, valuable as historical artefacts I mean, obviously.") + .npcl("They were stolen by warriors of the false god Zamorak generations ago. When you find the warriors, you will find the diamonds.") + .npcl("I suspect they will not willingly part with such objects of power however.") + .npc("Beware too, for these warriors are very powerful;", "they have taken the powers of the diamonds into themselves!") + .playerl("How do you mean?") + .npcl("Each diamond has an elemental quality...") + .npcl("There is the Diamond of Blood, the Diamond of Ice, the Diamond of Smoke and the Diamond of Shadow.") + .npcl("You should expect the warriors to have taken some aspect of these diamonds as their own...") + .playerl("Do you have any idea how I could track down these warriors somehow, then?") + .npcl("There is an ancient spell I know of that may spy upon such power... But it will require a few ingredients for it to work.") + .npcl("Should you be willing to get these ingredients for me, I will be able to locate the rough area where each of these warriors has taken refuge. The spell is imprecise, but it should help you get on the") + .npcl("right track in your search.") + .npcl("Is your desire for our freedom strong enough? Will you gather the ingredients for this spell for me?") + .options() + .let { optionBuilder2 -> + + optionBuilder2.option("Yes") + // The quest should jump to the next stage here, but I didn't want to write some weird if else here. + .playerl("Sure, what do you need?") + .npcl("For this spell, I will need to make some scrying glasses. I will need enough so that we can view the realm in its entirety.") + .npcl("When enchanted, the scrying glass will be able to let us view any area that has been influenced by the presence of the Diamonds of Azzanadra.") + .playerl("Okay, but what exactly do you need for this spell?") + .npcl("Well, six scrying glasses should be sufficient. For each scrying glass, I will need two magic logs, a steel bar and some molten glass. This makes a total of 12 magic logs, 6 pieces of molten") + .npcl("glass, and 6 steel bars.") + .npcl("In addition, for the actual spell to enchant the glasses, I will require one set of normal bones, some ash, some charcoal and a single blood rune.") + .npcl("Do you understand me, adventurer?") + .playerl("Quick question; what kind of bones do you need?") + .npcl("Standard bones. Other types of bones are of no use to me in this spell.") + .options() + .let { optionBuilder3 -> + optionBuilder3.option("Yes, I will go get those for you.") + .playerl("It's a slightly odd collection of ingredients, but I shouldn't have too much trouble getting those for you.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 6) { + setQuestStage(player, DesertTreasure.questName, 7) + } + } + + optionBuilder3.option("No, please repeat those ingredients.") + .npcl("Before I can complete the spell I will still need the following items;") + .npc("12 magic logs", "6 steel bars", "6 molten glass") + .npc("1 bones,", "1 ashes,", "1 charcoal", "and 1 blood rune.") // This is sic authentic trash dialogue. + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 6) { + setQuestStage(player, DesertTreasure.questName, 7) + } + } + + } + + optionBuilder2.option("No") + .playerl("Actually I don't feel like going on a shopping trip for you right now.") + .npcl("As you wish. I should have known not to get my hopes up that our long cursed life may soon be at an end...") + .end() + } + + optionBuilder.option("Nothing thanks.") + .playerl("Actually, there was nothing I really wanted to ask you about.") + .npcl("Yes, it is exactly like your sort to waste my time in such a way.") + .end() + } + + b.onQuestStages(DesertTreasure.questName, 7) + // Branch to check + + .branch { player -> + return@branch if (checkAllGiven(player)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("Excellent! Those are all the ingredients I need to create the scrying glasses.") + .npcl("I will find a suitable spot in the desert to the East of here, and set them up. When you are ready to begin your search, please come and find me there, I will show you how to utilise the") + .npcl("mirrors to find the diamonds.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 7) { + setQuestStage(player, DesertTreasure.questName, 8) + } + } + + branch.onValue(0) + .npcl("Before I can complete the spell I will still need the following items;") + .manualStage { df, player, _, _ -> + df.interpreter!!.sendDialogues(npc!!.id, FacialExpression.NEUTRAL, + "" + (12 - getAttribute(player, DesertTreasure.attributeCountMagicLogs, 0)) + " magic logs", + "" + (6 - getAttribute(player, DesertTreasure.attributeCountSteelBars, 0)) + " steel bars", + "" + (6 - getAttribute(player, DesertTreasure.attributeCountMoltenGlass, 0)) + " molten glass") + } + .manualStage { df, player, _, _ -> + df.interpreter!!.sendDialogues(npc!!.id, FacialExpression.NEUTRAL, + "" + (1 - getAttribute(player, DesertTreasure.attributeCountBones, 0)) + " bones,", + "" + (1 - getAttribute(player, DesertTreasure.attributeCountAshes, 0)) + " ashes,", + "" + (1 - getAttribute(player, DesertTreasure.attributeCountCharcoal, 0)) + " charcoal", + "and " + (1 - getAttribute(player, DesertTreasure.attributeCountBloodRune, 0)) + " blood rune.") // This is sic authentic trash dialogue. + } + .end() + } + + b.onQuestStages(DesertTreasure.questName, 8,9,10) + .npcl("Meet me again in the desert East of here, I will use these ingredients to create a scrying glass for you.") + .end() + + b.onQuestStages(DesertTreasure.questName, 100) + .npcl("Meet me again in the desert East of here.") + .end() + + } +} + +class EblisCollectionsListeners : InteractionListener { + override fun defineListeners() { + + onUseWith(IntType.NPC, intArrayOf(Items.MAGIC_LOGS_1513, Items.MAGIC_LOGS_1514), NPCs.EBLIS_1923) { player, used, with -> + for(i in 0..11) { + if (inInventory(player, used.id)) { + if (getAttribute(player, DesertTreasure.attributeCountMagicLogs, 0) < 12) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountMagicLogs, + getAttribute(player, DesertTreasure.attributeCountMagicLogs, 0) + 1) + sendMessage(player, "You hand over a magic log.") + } + } else { + break + } + } else { + break + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.STEEL_BAR_2353, Items.STEEL_BAR_2354), NPCs.EBLIS_1923) { player, used, with -> + for(i in 0..5) { + if (inInventory(player, used.id)) { + if (getAttribute(player, DesertTreasure.attributeCountSteelBars, 0) < 6) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountSteelBars, + getAttribute(player, DesertTreasure.attributeCountSteelBars, 0) + 1) + sendMessage(player, "You hand over a steel bar.") + } + } else { + break + } + } else { + break + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.MOLTEN_GLASS_1775, Items.MOLTEN_GLASS_1776), NPCs.EBLIS_1923) { player, used, with -> + for(i in 0..5) { + if (inInventory(player, used.id)) { + if (getAttribute(player, DesertTreasure.attributeCountMoltenGlass, 0) < 6) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountMoltenGlass, + getAttribute(player, DesertTreasure.attributeCountMoltenGlass, 0) + 1) + sendMessage(player, "You hand over some molten glass.") + } + } else { + break + } + } else { + break + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.BONES_526, Items.BONES_527), NPCs.EBLIS_1923) { player, used, with -> + if (getAttribute(player, DesertTreasure.attributeCountBones, 0) < 1) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountBones, + getAttribute(player, DesertTreasure.attributeCountBones, 0) + 1) + sendNPCDialogue(player, NPCs.EBLIS_1923, "Thank you, those are enough bones for the spell.") + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.ASHES_592, Items.ASHES_593), NPCs.EBLIS_1923) { player, used, with -> + if (getAttribute(player, DesertTreasure.attributeCountAshes, 0) < 1) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountAshes, + getAttribute(player, DesertTreasure.attributeCountAshes, 0) + 1) + sendNPCDialogue(player, NPCs.EBLIS_1923, "Thank you, that is enough ash for the spell.") + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.CHARCOAL_973, Items.CHARCOAL_974), NPCs.EBLIS_1923) { player, used, with -> + if (getAttribute(player, DesertTreasure.attributeCountCharcoal, 0) < 1) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountCharcoal, + getAttribute(player, DesertTreasure.attributeCountCharcoal, 0) + 1) + sendNPCDialogue(player, NPCs.EBLIS_1923, "Thank you, that is enough charcoal for the spell.") + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.BLOOD_RUNE_565), NPCs.EBLIS_1923) { player, used, with -> + if (getAttribute(player, DesertTreasure.attributeCountBloodRune, 0) < 1) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountBloodRune, + getAttribute(player, DesertTreasure.attributeCountBloodRune, 0) + 1) + sendNPCDialogue(player, NPCs.EBLIS_1923, "Thank you, that blood rune should be sufficient for the spell.") + } + } + return@onUseWith true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/EblisMirrorsDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/EblisMirrorsDialogue.kt new file mode 100644 index 000000000..43bec25f1 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/EblisMirrorsDialogue.kt @@ -0,0 +1,127 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class EblisMirrorsDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, EblisMirrorsDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return EblisMirrorsDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.EBLIS_1924, NPCs.EBLIS_1925) + } +} +class EblisMirrorsDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 8) + .npcl("Ah, so you got here at last.") + .npc("As you may noticed, I have made the mirrors for", "the spell, and cast the enchantment upon them.") + .npc("By simply looking into each mirror, you will be able to", "see the area where the trace magics from the Diamonds", "of Azzanadra are emanating from.") + .npc("Unfortunately, I cannot narrow the search closer with", "this kind of spell, but if you search the areas shown to", "you, you may be able to find some clues leading you to", "the evil warriors of Zamorak who stole the diamonds in") + .npc("the first place.") + .player(FacialExpression.THINKING, "So you can't be anymore specific about where to look", "for these warriors and their diamonds?") + .npc("I'm afraid not, other than the direction that the mirror", "is facing will be approximately the direction you will", "need to head in.") + .npc("Make sure to come and speak to me when you have", "retrieved all four diamonds.") + .endWith { _, player -> + if (getQuestStage(player, DesertTreasure.questName) == 8) { + setQuestStage(player, DesertTreasure.questName, 9) + } + } + + b.onQuestStages(DesertTreasure.questName, 9, 10) + .branch { player -> + return@branch if (DesertTreasure.completedAllSubstages(player)) { + 1 + } else { + 0 + } + }.let { branch -> + branch.onValue(1) + .player(FacialExpression.THINKING, "So I have all four of these Diamonds of Azzanadra, now", "what?") + .npc("Azzanadra was our greatest ever hero.", "He was unkillable, and the cowardly traitors who stole", "our lands did not know what to do with him, for his", "hatred for them was as strong as his magics.") + .npc(FacialExpression.SAD, "In the end, they cast a spell upon him, to trap him in", "the stone structure to the South of here.") + .npc(FacialExpression.ANNOYED, "They stole his very life force, the essence of his power,", "and trapped it within four crystals - the very same", "Four Diamonds which you have now recovered from", "the brigands who stole from us.") + .npc(FacialExpression.ANNOYED, "The four pillars surrounding the structure are keeping", "the containment spell intact.", "By placing a diamond into each, you will breach the", "magical defenses and begin to restore Azzanadra's") + .npcl(FacialExpression.ANNOYED, "power, and be able to enter the structure.") + .npcl("Go, place the diamonds, and free my lord Azzanadra!") + .npc(FacialExpression.FRIENDLY, "The path will be hard, for his prison is full of traps", "and danger to prevent his rescue, but he will reward you", "beyond your wildest dreams when freed!") + .npc("Quickly...", "After all these centuries, Lord Azzanadra is nearly free!", "You must spare no time, place the Diamonds upon the", "pillars and enter the pyramid so that you may free him!") + .end() + + branch.onValue(0) + .playerl("So can you give me any help on where to find these warriors and their diamonds?") + .npcl("No, the magic used in this spell is powerful, but inaccurate. The direction the scrying glass faces is roughly the direction you will find the warrior, but I'm afraid I") + .npcl("can't be any more help than that.") + .playerl("I don't understand why there are six mirrors when there are only four diamonds...") + .npcl("As I say, the enchantment is very inaccurate.") + .npcl("I can only focus upon the aura the diamonds have left behind them, so any place where the Diamonds were present for a significant period of time will still be shown - such as the Bandit Camp where I make my home.") + .npcl("My apologies, but magic is an inaccurate art in many respects.") + .npcl("Don't forget to come back here when you have collected all four diamonds.") + .end() + } + + b.onQuestStages(DesertTreasure.questName, 100) + .branch { player -> + if (inInventory(player, Items.ANCIENT_STAFF_4675)) { + 1 + } else { + 0 + } + }.let { branch -> + branch.onValue(1) + .playerl("Hello again.") + .npcl("Greetings. I await the return of my Lord Azzanadra and of our god. I do not know why, but I feel this spot has some significance...") + .end() + + branch.onValue(0) + .npcl("So have you spoken to my Lord Azzanadra yet?") + .playerl("Yes I have.") + .npcl("And what words did he have for his followers?") + .playerl("Er... He didn't really mention you at all, but he did teach me some cool new magic spells.") + .npcl("It is understandable perhaps... His poor mind must be addled after all of those years of confinement, he would not willingly ignore his followers...") + .npcl("Anyway, if he has taught you our ancient magics, you may be interested in purchasing an ancient heirloom that was passed down to me. My ancestor fought in the ancient battles using the") + .npcl("magic of our god. This heirloom will help you with the speed of your spell-casting.") + .npcl("Normally I could not bear to part with such a priceless relic, but for your help in freeing my Lord Azzanadra, I will be prepared to sell it to you for a mere 80,000 gold.") + .npcl("Are you interested?") + .options() + .let { optionBuilder -> + optionBuilder.option("Yes please") + .branch { player -> + if (inInventory(player, Items.COINS_995, 80000)) { 1 } else { 0 } + } + .let { branch2 -> + branch2.onValue(1) + .npcl("Take care of it, it is the only heirloom from those times I possess, although rumour has it many of our ancient warriors were buried with identical weapons so that they could continue to fight for my Lord in their deaths.") + .endWith { _, player -> + if (removeItem(player, Item(Items.COINS_995, 80000))) { + addItemOrDrop(player, Items.ANCIENT_STAFF_4675) + } + } + + branch2.onValue(0) + .linel("You don't have enough money to buy that.") + .end() + } + + optionBuilder.option("No thanks") + .playerl("No, not really.") + .npcl("As you wish. Bear my offer in mind should you ever change your decision, I will remain here.") + .end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/FareedBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/FareedBehavior.kt new file mode 100644 index 000000000..71db43e71 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/FareedBehavior.kt @@ -0,0 +1,77 @@ +package content.region.desert.quest.deserttreasure + +import content.global.skill.magic.modern.WaterSpell +import core.api.* +import core.game.container.impl.EquipmentContainer +import core.game.global.action.EquipHandler +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.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class FareedBehavior : NPCBehavior(NPCs.FAREED_1977) { + private var disappearing = false + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun tick(self: NPC): Boolean { + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, self.walkRadius)) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Fareed has lost interest in you, and returned to his flames.") + removeAttribute(player, DesertTreasure.attributeFareedInstance) + } + poofClear(self) + } + return true + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (state.style == CombatStyle.MAGIC && state.spell !is WaterSpell) { + state.neutralizeHits() + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + if (victim is Player) { + if (!inEquipment(victim, Items.ICE_GLOVES_1580)) { + val weapon = getItemFromEquipment(victim, EquipmentSlot.WEAPON) + if (weapon != null) { + EquipHandler.unequip(victim, EquipmentContainer.SLOT_WEAPON, weapon.id) + sendMessage(victim, "The heat from the warrior causes you to drop your weapon.") + } +// val weapon = getItemFromEquipment(victim, EquipmentSlot.WEAPON) +// if(weapon != null && removeItem(victim, weapon.id, Container.EQUIPMENT)) { +// addItemOrDrop(victim, weapon.id) +// } + } + } + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + addItemOrDrop(killer, Items.SMOKE_DIAMOND_4672) + sendMessage(killer, "You take the Diamond of Smoke from the ashes of the warrior.") + if (DesertTreasure.getSubStage(killer, DesertTreasure.attributeSmokeStage) == 1) { + DesertTreasure.setSubStage(killer, DesertTreasure.attributeSmokeStage, 100) + removeAttribute(killer, DesertTreasure.attributeFareedInstance) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollBehavior.kt new file mode 100644 index 000000000..5b4eeed6a --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollBehavior.kt @@ -0,0 +1,75 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +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.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import org.rs09.consts.NPCs + +class FatherTrollBehavior : NPCBehavior(NPCs.ICE_TROLL_1943 /** WRONG NAME ITS ICE_BLOCK */) { + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + return attacker is Player + } + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + self.properties.combatPulse.stop() + attacker.properties.combatPulse.stop() + if (state.estimatedHit + Integer.max(state.secondaryHit, 0) >= self.skills.lifepoints) { + state.estimatedHit = self.skills.lifepoints + 1 + state.secondaryHit = -1 + self.skills.lifepoints = self.skills.maximumLifepoints // Reset life of ice block + + setVarbit(attacker, DesertTreasure.varbitFrozenFather, 1) + if (getVarbit(attacker, DesertTreasure.varbitFrozenMother) == 1) { + setVarbit(attacker, DesertTreasure.varbitChildReunite, 4) + queueScript(self, 1, QueueStrength.NORMAL) { stage: Int -> + openDialogue(attacker, ChatFatherAndMotherTrollDialogueFile()) + return@queueScript stopExecuting(self) + } + } else { + queueScript(self, 1, QueueStrength.NORMAL) { stage: Int -> + sendNPCDialogue(attacker, NPCs.TROLL_FATHER_1948, "Oh thank you! It was really cold in there! But please, you must free my wife as well! Our son is depending on us!", FacialExpression.OLD_CALM_TALK2) + return@queueScript stopExecuting(self) + } + } + } + } + } +} + +class MotherTrollBehavior : NPCBehavior(NPCs.ICE_BLOCK_1945) { + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + return attacker is Player + } + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + self.properties.combatPulse.stop() + attacker.properties.combatPulse.stop() + if (state.estimatedHit + Integer.max(state.secondaryHit, 0) >= self.skills.lifepoints) { + state.estimatedHit = self.skills.lifepoints + 1 + state.secondaryHit = -1 + self.skills.lifepoints = self.skills.maximumLifepoints // Reset life of ice block + + setVarbit(attacker, DesertTreasure.varbitFrozenMother, 1) + if (getVarbit(attacker, DesertTreasure.varbitFrozenFather) == 1) { + setVarbit(attacker, DesertTreasure.varbitChildReunite, 4) + queueScript(self, 1, QueueStrength.NORMAL) { stage: Int -> + openDialogue(state.attacker!!.asPlayer(), ChatFatherAndMotherTrollDialogueFile()) + return@queueScript stopExecuting(self) + } + } else { + queueScript(self, 1, QueueStrength.NORMAL) { stage: Int -> + sendNPCDialogue(attacker, NPCs.TROLL_MOTHER_1950, "Wow, thanks for breaking me out of that ice! But please, my husband is still trapped in there!", FacialExpression.OLD_CALM_TALK2) + return@queueScript stopExecuting(self) + } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollDialogue.kt new file mode 100644 index 000000000..a83ededd6 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollDialogue.kt @@ -0,0 +1,170 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.* +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +// Base: 1943 Iced: 1944 Broke: 1948 Reunion: 1947 +// Base: 1945 Iced: 1946 Broke: 1950 Reunion: 1949 + +@Initializable +class FatherTrollDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 3 && + getVarbit(player, DesertTreasure.varbitFrozenFather) == 1 && + getVarbit(player, DesertTreasure.varbitFrozenMother) == 1) { + openDialogue(player!!, ChatFatherAndMotherTrollDialogueFile(), npc) + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 3) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_CALM_TALK2, "Oh thank you! It was really cold in there! But please, you must free my wife as well! Our son is depending on us!").also { stage = END_DIALOGUE } + } + } + }, npc) + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 4) { + openDialogue(player!!, ChatFatherAndMotherTrollAfterDialogueFile(), npc) + } else if ((getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) >= 5) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_CALM_TALK2, "Thanks again for freeing me from that ice block! I might be a troll, but it was real uncomfortable in there!").also { stage = END_DIALOGUE } + } + } + }, npc) + } + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return FatherTrollDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(1943) + } +} + +@Initializable +class MotherTrollDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + println(getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) >= 5) + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 3 && + getVarbit(player, DesertTreasure.varbitFrozenFather) == 1 && + getVarbit(player, DesertTreasure.varbitFrozenMother) == 1) { + openDialogue(player!!, ChatFatherAndMotherTrollDialogueFile(), npc) + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 3) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_CALM_TALK2, "Wow, thanks for breaking me out of that ice! But please, my husband is still trapped in there!").also { stage = END_DIALOGUE } + } + } + }, npc) + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 4) { + openDialogue(player!!, ChatFatherAndMotherTrollAfterDialogueFile(), npc) + } else if ((getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) >= 5) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_CALM_TALK2, "Thanks again for freeing me from that ice block! I don't know what my little snookums would have done without us!").also { stage = END_DIALOGUE } + } + } + }, npc) + } + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return MotherTrollDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.TROLL_MOTHER_1950) + } +} + +class ChatFatherAndMotherTrollDialogueFile : DialogueFile() { + // We gon do like this as the old way allows to easily jump between npcs. + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "Phew! Am I glad to be out of that big ice cube! Are you okay too darling?").also { stage++ } + 1 -> npcl(NPCs.TROLL_MOTHER_1950, FacialExpression.OLD_CALM_TALK2, "Yes, I thought we were done for! Why ever did that nasty Kamil freeze us up there anyway?").also { stage++ } + 2 -> playerl("He must have been trying to protect his Diamond...").also { stage++ } + 3 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "You mean that diamond I found the other day belonged to him? But why didn't he just ask for it back? It's not like I really want it or anything!").also { stage++ } + 4 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "And how did you know we had that diamond anyway, fleshy?").also { stage++ } + 5 -> playerl("Your son told me. That's why I rescued you, it is very important that I have that diamond...").also { stage++ } + 6 -> npcl(NPCs.TROLL_MOTHER_1950, FacialExpression.OLD_CALM_TALK2, "Ooohhhhh, my poor baby! He must have been so worried about us...").also { stage++ } + 7 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "Yes, but he certainly inherited his Dad's smarts!").also { stage++ } + 8 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "If he'd told this fleshy that he had the Diamond and not us, we might never have been rescued!").also { stage++ } + 9 -> playerl("Wait... what? That stupid little troll kid had the diamond all along?").also { stage++ } + 10 -> npcl(NPCs.TROLL_MOTHER_1950, FacialExpression.OLD_CALM_TALK2, "Don't you talk about my baby like that!").also { stage++ } + 11 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "Now, now dear, all's well that ends well. We've been freed and this fleshy has certainly earned himself that diamond.").also { stage++ } + 12 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "Let's get out of this terrible place and see our son!").also { + stage++ + if (DesertTreasure.getSubStage(player!!, DesertTreasure.attributeIceStage) == 3) { + DesertTreasure.setSubStage(player!!, DesertTreasure.attributeIceStage, 4) + } + } + + 13 -> { + queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + closeOverlay(player!!) + openOverlay(player!!, Components.FADE_TO_BLACK_120) + return@queueScript delayScript(player!!, 6) + } + 1 -> { + teleport(player!!, Location(2836, 3739, 0)) + return@queueScript delayScript(player!!, 1) + } + 2 -> { + openOverlay(player!!, Components.FADE_FROM_BLACK_170) + return@queueScript delayScript(player!!, 6) + } + 3 -> { + closeOverlay(player!!) + openDialogue(player!!, ChatFatherAndMotherTrollAfterDialogueFile()) + return@queueScript stopExecuting(player!!) + } + else -> return@queueScript stopExecuting(player!!) + } + } + end() + } + } + } +} + +class ChatFatherAndMotherTrollAfterDialogueFile : DialogueFile() { + // We gon do like this as the old way allows to easily jump between npcs. + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(NPCs.TROLL_CHILD_1933, FacialExpression.OLD_CALM_TALK2, "Mommy! Daddy! You're free!").also { stage++ } + 1 -> npc(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK1, "That's right son, and it's all thanks to this brave", "adventurer here.", "Now, make sure you hand over that diamond he was", "looking for.").also { stage++ } + 2 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK1, "It has been nothing but trouble for us, let's just get back to our cave and have dinner.").also { stage++ } + 3 -> npcl(NPCs.TROLL_MOTHER_1950, FacialExpression.OLD_CALM_TALK2, "That's right son, it's your favorite tonight too! A big plate of raw mackerel!").also { stage++ } + 4 -> npcl(NPCs.TROLL_CHILD_1933, FacialExpression.OLD_CALM_TALK2, "RAW MACKEREL! YUMMY!").also { stage++ } + 5 -> npcl(NPCs.TROLL_CHILD_1933, FacialExpression.OLD_CALM_TALK1, "Here ya go mister! Thanks for getting my mom and dad away from the bad man!").also { + stage++ + if (DesertTreasure.getSubStage(player!!, DesertTreasure.attributeIceStage) in 3 .. 4) { + addItemOrDrop(player!!, Items.ICE_DIAMOND_4671) + DesertTreasure.setSubStage(player!!, DesertTreasure.attributeIceStage, 100) + } + } + 6 -> playerl("Don't worry about it, just as long as I don't have to go back into that blizzard.").also { + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollBehavior.kt new file mode 100644 index 000000000..1146559a4 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollBehavior.kt @@ -0,0 +1,35 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import org.rs09.consts.NPCs + +class IceTrollBehavior : NPCBehavior(*iceTrollIds) { + companion object { + private val iceTrollIds = intArrayOf( + NPCs.ICE_TROLL_1936, + NPCs.ICE_TROLL_1937, + NPCs.ICE_TROLL_1938, + NPCs.ICE_TROLL_1939, + NPCs.ICE_TROLL_1940, + NPCs.ICE_TROLL_1941, + NPCs.ICE_TROLL_1942, + ) + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + if (getQuestStage(killer, DesertTreasure.questName) >= 9) { + val currentIceTrollKill = getAttribute(killer, DesertTreasure.attributeTrollKillCount, 0) + if (currentIceTrollKill < 5) { + setAttribute(killer, DesertTreasure.attributeTrollKillCount, currentIceTrollKill + 1) + setVarbit(killer, DesertTreasure.varbitCaveEntrance, getAttribute(killer, DesertTreasure.attributeTrollKillCount, 0)) + sendMessage(killer, "A chunk of ice falls away from the cave entrance...") + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollDialogue.kt new file mode 100644 index 000000000..ac37cb4dd --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollDialogue.kt @@ -0,0 +1,35 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class IceTrollDialogue (player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, IceTrollDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return IceTrollDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ICE_TROLL_1935) + } + +} +class IceTrollDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onPredicate { _ -> true } + .npc(FacialExpression.OLD_LAUGH1, "Hur hur hur!", "Well look here, a puny fleshy human!") + .npc(FacialExpression.OLD_LAUGH1, "You should beware of the icy wind that runs through", "this valley, it will bring a fleshy like you to a cold end", "indeed!") + .end() + } + +} diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/KamilBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/KamilBehavior.kt new file mode 100644 index 000000000..c5de39024 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/KamilBehavior.kt @@ -0,0 +1,84 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.* +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +// https://www.youtube.com/watch?v=xeu6Ncmt1fY + +class KamilBehavior : NPCBehavior(NPCs.KAMIL_1913) { + private var disappearing = false + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun tick(self: NPC): Boolean { + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, (self.walkRadius*1.5).toInt())) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Kamil vanishes on an icy wind...") + removeAttribute(player, DesertTreasure.attributeKamilInstance) + } + poofClear(self) + } + return true + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + if (DesertTreasure.getSubStage(killer, DesertTreasure.attributeIceStage) == 2) { + DesertTreasure.setSubStage(killer, DesertTreasure.attributeIceStage, 3) + removeAttribute(killer, DesertTreasure.attributeKamilInstance) + sendPlayerDialogue(killer, "Well, that must have been the 'bad man' that the troll kid was on about... His parents must be up ahead somewhere.") + } + } + } + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + return KamilCombatHandler() + } +} + +// All these combat shit is the most trash level thing to use or decipher. +class KamilCombatHandler: MultiSwingHandler( + SwitchAttack(CombatStyle.MELEE.swingHandler, null), +) { + override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { + if (victim is Player) { + // This is following RevenantCombatHandler.java, no idea if this is good. + // I can't be bothered to fix fucking frozen. The player can hit through frozen. What the fuck is frozen for then, to glue his fucking legs??? + if (RandomFunction.roll(3) && !hasTimerActive(victim, "frozen") && !hasTimerActive(victim, "frozen:immunity")) { + sendChat(entity as NPC, "Sallamakar Ro!") // Salad maker roll. + impact(victim, 5) + impact(victim, 5) + registerTimer(victim, spawnTimer("frozen", 7, true)) + sendMessage(victim, "You've been frozen!") + // FIXME: before the below vfx hits, there should be another one that looks kinda like a wind wave exploding at the player's feet. Hope somebody finds the id. + sendGraphics(539, victim.location) + victim.properties.combatPulse.stop() // Force the victim to stop fighting. Whatever. + // FIXME: sfx + }else { + animate(entity!!, Animation(440)) + } + } + super.impact(entity, victim, state) + } +} diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/MalakDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/MalakDialogue.kt new file mode 100644 index 000000000..37ccfdc53 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/MalakDialogue.kt @@ -0,0 +1,262 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.combat.ImpactHandler.HitsplatType +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class MalakDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, MalakDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return MalakDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.MALAK_1920) + } +} + +class MalakDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4,5,6,7,8) + .npc("Away from me, dog.", "I have business to discuss with the barkeeper.") + .end() + + + b.onQuestStages(DesertTreasure.questName, 9) + .branch { player -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 1) { + return@branch 0 // Same branch as 0 + } + return@branch DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) + }.let { branch -> + branch.onValue(0) + .npcl("A human, eh? Give me one good reason why I should not just take you to Lord Drakan now.") + // There's a ring of charos dialogue here, but its one line and doesn't change anything... + .npcl("You had better make it a good one too, or you will not survive the night, I'll wager.") + .let { builder -> + val returnJoin = b.placeholder() + builder.goto(returnJoin) + return@let returnJoin.builder() + .options() + .let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option("I am here to worship Zamorak") + .playerl("I am here to worship the almighty Zamorak! Yay! Go Zamorak!") + .npcl("I see. You are a moron. You have probably settled right in with the rest of the idiots in this pathetic excuse for a village.") + .npcl("Unfortunately for you, that is not a good enough reason to explain your presence here.") + .npcl("Now tell me the reason for your coming here, or I will ensure you suffer a horrible fate indeed.") + .goto(returnJoin) + + optionBuilder.option("I am here to praise Lord Drakan") + .playerl("I am here only to serve the mighty Drakan. Yup, Drakan, he's the man.") + .npcl("I see. I would perhaps be more inclined to believe you if I could not smell the death blood of his brother Draynor upon you.") + .npcl("What are you real intentions here?") + // "If the player has not completed Vampyre Slayer" but I'm lazy again + // .npcl("Really? That is interesting, that you would want to give your life by tresspassing in this land for such an unbelievable reason.") + // .npcl("Now speak, what is your purpose in coming here?") + .goto(returnJoin) + + optionBuilder.option("I am here to worship you, oh mighty Malak") + .playerl("I am here only to serve the mighty Drakan.") + .playerl("I came here looking for you, oh mighty Malak, so that I might serve your glory.") + .npcl("Please. Do not think that I am so vain and foolish as to allow you to avoid my question with such obvious sycophancy.") + .npcl("Now tell me the reason behind your being here, or I will ensure that you suffer.") + .goto(returnJoin) + + optionBuilder.option("I am here to kill Lord Drakan") + .playerl("I am here to kill Lord Drakan, and every stinking one of his vampyre brood!") + .npcl("Hah! Most entertaining, human!") + .npcl("Now tell me the reason you are here, or we shall soon see who will be killing whom.") + .goto(returnJoin) + + optionBuilder.option("I am looking for a special Diamond...") + .playerl("I am here looking for a special diamond... I have reason to believe it is somewhere in this vicinity, and it is probably in the possession of a warrior of Zamorak.") + .playerl("I'm fairly sure it will have some kind of magical aura or something too. I don't suppose you've seen it, or know where it might be?") + .npcl("Interesting... Well perhaps we can come to a little... arrangement, human.") + .npcl("I may have information that may assist you, but you in turn will have to do something for me. What do you say? Do you think we could come to some form of") + .npcl("agreement?") + .playerl("Well, what kind of something? No offence, but you're not exactly the trustworthy type...") + .npcl("Ah, you have a healthy sense of paranoia, I see. It is not a particularly unfair request on my part...") + .npcl("All I ask is that you ensure that the current owner of the diamond is killed. For my part, I will let you know his whereabouts, and how exactly to kill him.") + .npcl("When he is dead, you may take the diamond from his corpse and do with it what you will. I have no interest in such baubles.") + .npcl("So what say you? A life for a diamond. As a mark of good faith, I will give you some information free:") + .npcl("The current owner of this diamond is named Dessous.") + .betweenStage { df, player, _, _ -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 0) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeBloodStage, 1) + } + } + .options("Agree to this arrangement?") + .let { optionBuilder2 -> + optionBuilder2.option("Yes") + .betweenStage { df, player, _, _ -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 1) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeBloodStage, 2) + } + } + .playerl("Well... I can't see any drawback. Okay, I accept your offer. Now tell me what you know.") + .goto(continuePath) + + optionBuilder2.option("No") + .playerl("I don't trust you, or anything you say. I'm afraid I have to decline.") + .npcl("As you wish human. I doubt anybody near here knows of the diamond you seek, however.") + .npcl("If you wish to claim it as your own, you have little choice but to accept my bargain. I will wait here until such time as you change your mind.") + .end() + } + return@let continuePath.builder() + } + } + .npcl("What I know? Hah! After you have been alive for as long as I, the things I know are enough to fill a library.") + .npcl("I'm afraid you will need to be a little more specific.") + .let { builder -> + val returnJoin = b.placeholder() + builder.goto(returnJoin) + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option("Why do you want Dessous dead?") + .playerl("I don't see exactly how this bargain benefits you... Why exactly do you want Dessous killed anyway?") + .npcl("That is an impertinent question to demand from myself.") + .npcl("However, if it will help seal our deal, I will let you know some of the details.") + .npcl("As you may or may not know, myself and my blood kin are the rulers of this land, all serving under Lord Drakan. However, we tend not to die of old age or similar") + .npcl("natural causes, which means that to gain another Lords tithe or land, there often need to be...") + .npcl("Unnatural causes of death involved. Let us just say, that Dessous is in control of some land that I myself would like some say in, and that it is not in my interest to be seen to be responsible for the") + .npcl("death of a fellow Lord.") + .npcl("It would however be extremely advantageous to myself should some random human adventurer take it upon themself to remove this rival for me... Do you understand?") + .playerl("Yes... I think so...") + .npcl("Good, we understand each other then. We will both benefit from the death of Dessous.") + .goto(returnJoin) + + optionBuilder.option("Where can I find Dessous?") + .playerl("Where can I find this Dessous?") + .npcl("He currently resides in a graveyard to the South-East of here. You will not be able to move the gravestone which he lies beneath however, you will need to find some way to") + .npcl("lure him out from his tomb.") + .playerl("And how exactly would I go about doing that?") + .npcl("Well, he is a vampyre, so fresh blood would almost certainly entice him out.") + .npcl("However, even though he is a frail and decrepit example of our species, he will be able to kill a weakling human such as yourself extremely easily. Having him in a bloodlust as he does so, will not make") + .npcl("your job any easier.") + .goto(returnJoin) + + optionBuilder.option("How can I kill Dessous?") + .playerl("So what advice can you give me on killing Dessous?") + .npcl("As ancient and weak as Dessous is, he is still more than a match for the likes of you.") + .npcl("That is, assuming you were to fight him fairly.") + .npcl("My proposal would be for you to even the odds up a little bit...") + .playerl("How would I go about doing that?") + .npcl("Well, my plan would be as follows. First, take a silver bar to the man living in the sewers in Draynor. He was an assistant to Count Draynor in some of his...") + .npcl("more interesting experiments many years past. Tell him you need a sacrificial offering pot. He will know what you speak of, it is a unique type of container used in various ancient vampyric ceremonies.") + .npcl("Then take the pot to Entrana and get it blessed by the Head Monk. This will lend the pot some holy power.") + .npcl("If you then bring that silver pot back to me, I will provide you with some fresh blood, to put into it. To that pot of blood, you will add some crushed garlic, and some spice to disguise the smell.") + .npcl("Use that pot of blood upon Dessous' tomb, and he will be unable to resist rising and drinking from it.") + .npcl("The combination of garlic, silver, and blessings from Saradomin will act upon him as a poison, and allow you to kill him.") + .npcl("This is just my suggestion of course, you may ignore it if you wish, although I offer no guarantees of your ability to defeat him otherwise.") + .npcl("Was there anything else you wanted?") + .goto(returnJoin) + + optionBuilder.option("Actually, I don't need to know anything.") + .playerl("Never mind, I will figure out all I need to know by myself.") + .npcl("As you wish. Come and see me when you have managed to kill Dessous.") + .end() + } + } + + branch.onValue(2) + .branch { player -> + return@branch if(inInventory(player, Items.BLESSED_POT_4659) || inInventory(player, Items.SILVER_POT_4658)) { 1 } else { 0} + }.let { branch2 -> + branch2.onValue(1) + .player("I found Ruantun in Draynors sewers.", "He made me this pot, now where can I get some fresh", "blood to fill it with?") + .betweenStage { df, player, _, _ -> + sendMessage(player, "Malak cuts you and pours some of your blood into the pot.") + if (removeItem(player, Items.SILVER_POT_4658)) { + addItemOrDrop(player, Items.SILVER_POT_4660) + } else if (removeItem(player, Items.BLESSED_POT_4659)) { + addItemOrDrop(player, Items.BLESSED_POT_4661) + } + animate(npc!!, 1264) + player.impactHandler.manualHit(player, 5, HitsplatType.NORMAL) + } + .linel("Malak cuts you and pours some of your blood into the pot.") // Supposed to be a sendMessage not a dialogue, but why... + .playerl("Ow!") + .npcl("There you go. As fresh as it gets.") + .playerl("Thanks for nothing.") + .npcl("Come and speak to me again when you have managed to kill Dessous.") + .end() + + branch2.onValue(0) + .npcl("Why are you still here? I notice Dessous still lives.") + .options() + .let { optionBuilder -> + optionBuilder.option("Where can I find Dessous?") + .playerl("Where can I find this Dessous?") + .npcl("He currently resides in a graveyard to the South-East of here. You will not be able to move the gravestone which he lies beneath however, you will need to find some way to") + .npcl("lure him out from his tomb.") + .playerl("And how exactly would I go about doing that?") + .npcl("Come and see my when you have prepared a silver ritual pot in the manner I have told you.") + .npcl("I will ensure that you get some fresh blood that you may taint with garlic and spices, to lure our Dessous.") + .end() + + optionBuilder.option("How do I kill Dessous again?") + .playerl("How am I supposed to kill Dessous again?") + .npcl("Take a silver bar to the man in the Draynor sewers. He will fashion a ritualistic pot for you, which you should then take to Entrana and get blessed.") + .npcl("When you have done that, come back here and speak to me, I will provide you with some fresh blood which you will then crush some garlic into, and then add some spices to hide the garlic.") + .npcl("In this way, you will be able to lure him from his tomb and he should be sufficiently weakened to be vulnerable to your attacks.") + .end() + + optionBuilder.option("Actually, I don't need to know anything.") + .playerl("Never mind, I will figure out all I need to know by myself.") + .npcl("As you wish. Come and see me when you have managed to kill Dessous.") + .end() + } + } + + branch.onValue(3) + .npcl("Ah, the wandering hero returns! I take it you have dispatched poor old Dessous for me?") + .playerl("Quit playing games with me, Malak. I want that diamond, and I want it now!") + .betweenStage { df, player, _, _ -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 3) { + addItemOrDrop(player, Items.BLOOD_DIAMOND_4670) + DesertTreasure.setSubStage(player, DesertTreasure.attributeBloodStage, 100) + } + } + .npcl("Do not take that tone of voice with me, meat. You should be thankful I have allowed you your life.") + .npcl("Here, take your precious little bauble.") + .npcl("I will take that silver pot from you as well, humans are not meant to possess such artefacts. Now get out of my sight, our deal is complete, and if I see you here again I will not hesistate to take you to") + .npcl("Lord Drakan.") + .npcl("He will be extremely pleased to meet the murderer of poor Dessous, I suspect.") + .end() + + branch.onValue(100) + .branch { player -> + return@branch if(!inInventory(player, Items.BLOOD_DIAMOND_4670)) { 1 } else { 0 } + }.let { branch2 -> + branch2.onValue(1) + .playerl("Where is the Diamond of Blood? I know you have it!") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.BLOOD_DIAMOND_4670) + } + .npcl("Do not take that tone of voice with me, meat. Here, take your bauble. I have no use for it.") + .end() + + branch2.onValue(0) + .npcl("Be lucky I have let you live, meat. Our deal is done, I wish no further dealing with you.") + .end() + + } + } + + b.onQuestStages(DesertTreasure.questName, 10,11,12,13,100) + .npcl("Be lucky I have let you live, meat. Our deal is done, I wish no further dealing with you.") + .end() + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/MummyNPC.kt b/Server/src/main/content/region/desert/quest/deserttreasure/MummyNPC.kt new file mode 100644 index 000000000..d5fed9d6e --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/MummyNPC.kt @@ -0,0 +1,18 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import org.rs09.consts.NPCs + +class MummyNPC : NPCBehavior(NPCs.MUMMY_1958) { + var clearTime = 0 + + override fun tick(self: NPC): Boolean { + if (clearTime++ > 100) { + clearTime = 0 + poofClear(self) + } + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/PyramidArea.kt b/Server/src/main/content/region/desert/quest/deserttreasure/PyramidArea.kt new file mode 100644 index 000000000..2a28610b2 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/PyramidArea.kt @@ -0,0 +1,319 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.tools.RandomFunction +import org.rs09.consts.Components +import org.rs09.consts.NPCs + +class PyramidArea { + companion object { + /** All The Sarcophagus Locations */ + val sarcophagusList = arrayOf( + // Level 1 + Location(2901, 4946, 3), + Location(2902, 4969, 3), + Location(2903, 4961, 3), + Location(2909, 4954, 3), + Location(2913, 4950, 3), + Location(2916, 4954, 3), + Location(2917, 4951, 3), + Location(2917, 4959, 3), + Location(2918, 4963, 3), + // Level 2 + Location(2831, 4962, 2), + Location(2837, 4970, 2), + Location(2843, 4964, 2), + Location(2847, 4947, 2), + Location(2858, 4956, 2), + Location(2858, 4973, 2), + Location(2864, 4942, 2), + Location(2868, 4948, 2), + Location(2869, 4968, 2), + // Level 3 + Location(2759, 4962, 1), + Location(2760, 4956, 1), + Location(2763, 4966, 1), + Location(2764, 4941, 1), + Location(2765, 4936, 1), + Location(2765, 4940, 1), + Location(2767, 4945, 1), + Location(2768, 4947, 1), + Location(2771, 4944, 1), + Location(2774, 4947, 1), + Location(2776, 4941, 1), + Location(2786, 4974, 1), + Location(2787, 4964, 1), + Location(2790, 4968, 1), + Location(2791, 4977, 1), + Location(2798, 4947, 1), + Location(2798, 4952, 1), + Location(2800, 4960, 1), + Location(2802, 4940, 1), + Location(2806, 4936, 1), + Location(2806, 4942, 1), + Location(2810, 4968, 1), + Location(2810, 4975, 1), + // Level 4 + Location(3208, 9315, 0), + Location(3211, 9330, 0), + Location(3217, 9295, 0), + Location(3218, 9281, 0), + Location(3221, 9313, 0), + Location(3221, 9320, 0), + Location(3221, 9324, 0), + Location(3222, 9281, 0), + Location(3225, 9308, 0), + Location(3225, 9310, 0), + Location(3226, 9312, 0), + Location(3226, 9318, 0), + Location(3227, 9288, 0), + Location(3229, 9309, 0), + Location(3231, 9297, 0), + Location(3233, 9309, 0), + Location(3234, 9297, 0), + Location(3234, 9330, 0), + Location(3236, 9302, 0), + Location(3237, 9309, 0), + Location(3240, 9312, 0), + Location(3240, 9318, 0), + Location(3241, 9282, 0), + Location(3242, 9302, 0), + Location(3246, 9282, 0), + Location(3246, 9308, 0), + Location(3246, 9324, 0), + Location(3247, 9323, 0), + Location(3249, 9293, 0), + Location(3250, 9324, 0), + Location(3251, 9323, 0), + Location(3251, 9330, 0), + Location(3251, 9337, 0), + Location(3252, 9330, 0), + Location(3252, 9337, 0), + Location(3253, 9301, 0), + Location(3254, 9324, 0), + Location(3255, 9323, 0), + Location(3255, 9330, 0), + Location(3255, 9337, 0), + Location(3256, 9330, 0), + Location(3256, 9337, 0), + Location(3257, 9289, 0), + Location(3259, 9310, 0), + Location(3259, 9313, 0), + ) + + val safeZone = ZoneBorders(3227, 9310, 3239, 9320) + + // Direction.NORTH - LEFT + // rot 0 - LEFT + // rot 1 - NORTH + // rot 2 - RIGHT + // rot 3 - SOUTH + /** Sarcophagus Opening Location (Note, they are all wrongly mapped due to Scenery) */ + fun getNewLocation(direction: Direction): Location { + return when (direction) { + Direction.NORTH -> Location(-1, 0) + Direction.WEST -> Location(0, -1) + Direction.EAST -> Location(0, 1) + Direction.SOUTH -> Location(1, 0) + else -> Location(0, 0) + } + } + + /** Sarcophagus Opening Facing (Note, they are all wrongly mapped due to Scenery) */ + fun getNewFacing(direction: Direction): Direction { + return when (direction) { + Direction.NORTH -> Direction.NORTH + Direction.WEST -> Direction.NORTH_WEST + Direction.SOUTH -> Direction.WEST + Direction.EAST -> Direction.NORTH_EAST + else -> direction + } + } + + fun nearSarcophagus(loc: Location): Location? { + for (sarcoph in sarcophagusList) { + if(loc.withinDistance(sarcoph, 3)) { + return sarcoph + } + } + return null + } + + /** Trapdoor randomly throws you out of the Pyramid. */ + fun trapdoorTrap(player: Player) { + stopWalk(player) + player.walkingQueue.reset() + forceWalk(player, Location(3233, 2887, 0), "") + lock(player, 8) + sendMessage(player, "You accidentally trigger a trap...") + // addScenery(6521, location) -> animateScenery(scenery, 1939), but 6522 does it for you. + val pitfallScenery = addScenery(6522, player.location) // Scenery - Trapdoor Scenery + animate(player, 1950) // Anim - Player Falling Animation + queueScript(player, 4, QueueStrength.SOFT) { stage -> + player.walkingQueue.reset() + when (stage) { + 0 -> { + sendGraphics(354, player.location) // Gfx - Puff of Smoke + closeOverlay(player) + openOverlay(player, Components.FADE_TO_BLACK_120) + return@queueScript delayScript(player, 2) + } + 1 -> { + removeScenery(pitfallScenery) + teleport(player, Location(3233, 2887, 0)) + sendMessage(player, "...and tumble unharmed outside the pyramid.") + closeOverlay(player) + openOverlay(player, Components.FADE_FROM_BLACK_170) + stopWalk(player) + player.walkingQueue.reset() + // animate(player, ??) // Anim - Player Getting Up https://www.youtube.com/watch?v=95OvIPFYCwg + return@queueScript delayScript(player, 3) + } + 2 -> { + player.walkingQueue.reset() + forceWalk(player, Location(3233, 2887, 0), "") + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + /** Mummies randomly spawns out of a sarcophagus. */ + fun spawnMummy(player: Player, sarcophagusLocation: Location) { + val sarcophagusScenery = getScenery(sarcophagusLocation) ?: return + val locationInFront = sarcophagusScenery.location.transform(getNewLocation(sarcophagusScenery.direction)) + // There are 6 sarcophagus, 6512 - 6517 with different door designs. + // They map nicely to 6506 - 6511 and act like a door. + // 6505 is the underlying hole scenery after the door opens, but it is done for you. + replaceScenery( + sarcophagusScenery, + sarcophagusScenery.id - 6, + 5, + getNewFacing(sarcophagusScenery.direction), + locationInFront + ) + val mummyNpc = NPC(NPCs.MUMMY_1958) + mummyNpc.isRespawn = false + mummyNpc.isWalks = false + mummyNpc.isAggressive = true + mummyNpc.location = sarcophagusScenery.location + mummyNpc.init() + mummyNpc.walkingQueue.addPath(locationInFront.x, locationInFront.y) + sendChat(mummyNpc, "Rawr!") + lock(player, 1) + stopWalk(player) + queueScript(player, 2, QueueStrength.SOFT) { stage -> + stopWalk(player) + mummyNpc.walkingQueue.addPath(locationInFront.x, locationInFront.y) + mummyNpc.isWalks = true + mummyNpc.isAggressive = true + mummyNpc.attack(player) + return@queueScript stopExecuting(player) + } + + } + + /** Scarabs randomly spawns somewhere near you. */ + fun spawnScarabs(player: Player) { + stopWalk(player) + lock(player, 3) + val scarabsLocation = Location.getRandomLocation(player.location, 3, true) + sendGraphics(356, scarabsLocation) // Gfx - Cracks before scarab appears. + val scarabNpc = NPC(NPCs.SCARABS_1969) + stopWalk(player) + queueScript(player, 2, QueueStrength.STRONG) { stage -> + stopWalk(player) + when (stage) { + 0 -> { + scarabNpc.isRespawn = false + scarabNpc.isWalks = false + scarabNpc.location = scarabsLocation + scarabNpc.init() + animate(scarabNpc, 1949) // Anim - Scarabs appearing from ground. + return@queueScript delayScript(player, 3) + } + 1 -> { + scarabNpc.isWalks = true + scarabNpc.isAggressive = true + scarabNpc.attack(player) + return@queueScript stopExecuting(player) + } + } + return@queueScript stopExecuting(player) + } + } + } +} + +class PyramidAreaFirstThree: MapArea { + + override fun defineAreaBorders(): Array { + return arrayOf( + getRegionBorders(11597), + getRegionBorders(11341), + getRegionBorders(11085), + ) + } + + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + val averageLevel = ( + getDynLevel(entity, Skills.AGILITY) + + getDynLevel(entity, Skills.THIEVING) + ) / 2.0 + val randomValue = RandomFunction.randomDouble(99.5) + + if ((1..20).random() == 1) { + // A mummy would jump out if you walk near a sarcophagus of 2 radius. + val sarcoph = PyramidArea.nearSarcophagus(entity.location) + if (sarcoph != null) { + PyramidArea.spawnMummy(entity, sarcoph) + } + } + + if ((1..80).random() == 2) { + PyramidArea.spawnScarabs(entity) + } + if (randomValue > averageLevel && (1..128).random() == 1) { + PyramidArea.trapdoorTrap(entity) + } + } + } +} + +class PyramidAreaFinal: MapArea { + + override fun defineAreaBorders(): Array { + return arrayOf( + getRegionBorders(12945), + ) + } + + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + if (!PyramidArea.safeZone.insideBorder(entity.location)) { // Safezone is talking to Azzanadra. + if ((1..20).random() == 1) { + // A mummy would jump out if you walk near a sarcophagus of 2 radius. + val sarcoph = PyramidArea.nearSarcophagus(entity.location) + if (sarcoph != null) { + PyramidArea.spawnMummy(entity, sarcoph) + } + } + if ((1..80).random() == 2) { + PyramidArea.spawnScarabs(entity) + } + } + + // No Trapdoor For The Final Level + } + } +} diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/RasoloDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/RasoloDialogue.kt new file mode 100644 index 000000000..273dd5d34 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/RasoloDialogue.kt @@ -0,0 +1,217 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +@Initializable +class RasoloDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, RasoloDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return RasoloDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.RASOLO_1972) + } +} + +class RasoloDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4,5,6,7,8) + .npc("Greetings friend.", "I am Rasolo, the famous merchant.", "Would you care to see my wares?") + .options() + .let { optionBuilder -> + optionBuilder.option("Yes") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + optionBuilder.option("No") + .playerl("No, not really.") + .npcl("As you wish. I will travel wherever the business takes me.") + .end() + } + + b.onPredicate { player -> + DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 0 && + getQuestStage(player, DesertTreasure.questName) >= 9 + } + .npc("Greetings friend.", "I am Rasolo, the famous merchant.", "Would you care to see my wares?") + .options() + .let { optionBuilder -> + optionBuilder.option("Yes") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + optionBuilder.option("No") + .playerl("No, not really.") + .npcl("As you wish. I will travel wherever the business takes me.") + .end() + + optionBuilder.option("Ask about the Diamonds of Azzanadra") + .playerl("No, actually I was looking for something specific...") + .npcl("Hmmmm? And what would that be?") + .playerl("I am looking for one of the Diamonds of Azzanadra. I have reason to believe it may be somewhere around here...") + .npcl("Ahhh... The Shadow Diamond...") + .npcl("I know the object of which you speak. It is guarded by a fearsome warrior known as Damis, they say, who lives in the shadows, invisible to prying eyes...") + .playerl("How can I find this 'Damis' then?") + .npcl("Well now... perhaps we can help each other out here.") + .npcl("I have in my possession a small trinket, a ring, that allows its wearer to see the unseen...") + .playerl("How much do you want for it?") + .npcl("Ah, but it is not for sale... As such...") + .npcl("I am offering to trade it for an item that was rightfully mine, but that was stolen by a bandit named Laheeb.") + .npcl("The item is question is a gilded cross, that has some sentimental value to myself. I wish for you to recover this item for me, and I will happily let you have my ring of visibility.") + .playerl("Where can I find this Laheeb?") + .npcl("Well, as a travelling merchant I have roamed these lands for many years...") + .npcl("To the far east of here there is an area that is dry and barren like the desert... it is called...") + .playerl("Al Kharid?") + .npcl("...Yes, Al Kharid. Now to the south of Al Kharid there is a passageway, it is called the...") + .playerl("Shantay Pass.") + .npcl("...Yes. I didn't realise you had travelled there yourself.") + .npcl("Anyway, when you have gone through the Shantay Pass, you will find yourself in a hostile desert... You will need to bring water with you to keep your life. Now, to the south-west of this pass, you will find a small") + .npcl("village...") + .playerl("The Bedabin camp.") + .npcl(FacialExpression.ANNOYED, "If you know where Laheeb lives, why did you ask me?") + .playerl("I don't. Sorry, please continue.") + .npcl("Well, okay then. Anyway, south of this encampment there is an area where few have ever been... It is a village of murderous bandits, and treacherous") + .npcl("thieves... This is where Laheeb makes his home.") + .npcl("He will have hidden his stolen treasure somewhere in that village, I am sure of it. When you find his loot, you will find my gilded cross. Return it to me, and I will reward you with my ring") + .npcl("of visibility, so that you may find Damis. Does this seem fair to you?") + .options() + .let { optionBuilder2 -> + optionBuilder2.option("Yes") + .playerl("Not a problem. I'll be back with your cross before you know it.") + .endWith { _, player -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 0) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeShadowStage, 1) + } + } + optionBuilder2.option("No") + .playerl("Sounds like too much effort to me. I'll find this Damis by myself.") + .npcl("As you wish.") + .end() + } + } + + b.onPredicate { player -> + DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) in 1 .. 2 && + getQuestStage(player, DesertTreasure.questName) >= 9 + } + .npcl("Have you retrieved my gilded cross for me yet?") + .branch { player -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 2 && inInventory(player, Items.GILDED_CROSS_4674)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .playerl("No, not yet...") + .npcl("Well what seems to be the problem?") + .options() + .let { optionBuilder -> + optionBuilder.option("Where can I find Laheeb?") + .playerl("Where can I find this Laheeb?") + .npcl("Well, as a travelling merchant I have roamed these lands for many years...") + .npcl("To the far east of here there is an area that is dry and barren like the desert... it is called...") + .playerl("Al Kharid?") + .npcl("...Yes, Al Kharid. Now to the south of Al Kharid there is a passageway, it is called the...") + .playerl("Shantay Pass.") + .npcl("...Yes. I didn't realise you had travelled there yourself.") + .npcl("Anyway, when you have gone through the Shantay Pass, you will find yourself in a hostile desert... You will need to bring water with you to keep your life. Now, to the south-west of this pass, you will find a small") + .npcl("village...") + .playerl("The Bedabin camp.") + .npcl("If you know where Laheeb lives, why did you ask me?") + .playerl("I don't. Sorry, please continue.") + .npcl("Well, okay then. Anyway, south of this encampment there is an area where few have ever been... It is a village of murderous bandits, and treacherous") + .npcl("thieves... This is where Laheeb makes his home.") + .end() + optionBuilder.option("Can't I just buy your ring?") + .playerl("Can't I just buy your ring?") + .npcl("No, it is not for sale. Some things are more important than money, and the return of my gilded cross is one of them.") + .end() + optionBuilder.option("Is Damis near here, then?") + .playerl("Is Damis near here, then?") + .npcl("You would be surprised to know just how close he is...") + .end() + } + branch.onValue(1) + .playerl("Yes I have!") + .npcl("Excellent, excellent. Here, take this ring. While you wear it, you will be able to see the things that live in shadows...") + .npcl("And you will be able to find the entrance to Damis' lair.") + .endWith { _, player -> + if (removeItem(player, Items.GILDED_CROSS_4674)) { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 2) { + addItemOrDrop(player, Items.RING_OF_VISIBILITY_4657) + DesertTreasure.setSubStage(player, DesertTreasure.attributeShadowStage, 3) + } + } + } + } + + b.onPredicate { player -> + DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 3 && + getQuestStage(player, DesertTreasure.questName) >= 9 + } + .npcl("So how goes your quest? Did you managed to find the Diamond you were looking for yet?") + .branch { player -> + if (inInventory(player, Items.RING_OF_VISIBILITY_4657)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("Not yet...") + .npcl("Well, his lair is very close to here. I suggest you look around for it.") + .end() + + branch.onValue(0) + .playerl("I lost that ring you gave me...") + .npcl("Then by all means, take another. Only a foolish merchant would give away his only stock!") + .endWith { _, player -> + addItem(player, Items.RING_OF_VISIBILITY_4657) + } + } + + + b.onPredicate { player -> + DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) > 3 && + getQuestStage(player, DesertTreasure.questName) >= 9 || getQuestStage(player, DesertTreasure.questName) >= 10 + } + .npcl("Many thanks for returning my heirloom to me, adventurer. Would you like to buy something?") + .options() + .let { optionBuilder -> + optionBuilder.option("Yes") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + optionBuilder.option("No") + .playerl("No, not really.") + .npcl("As you wish. I will travel wherever the business takes me.") + .end() + + optionBuilder.optionIf("I lost that ring you gave me...") { player -> + return@optionIf !inInventory(player, Items.RING_OF_VISIBILITY_4657) + } + .playerl("I lost that ring you gave me...") + .npcl("Then by all means, take another. Only a foolish merchant would give away his only stock!") + .endWith { _, player -> + addItem(player, Items.RING_OF_VISIBILITY_4657) + } + } + + } +} + +class RasoloTradeListeners : InteractionListener { + override fun defineListeners() { + on(NPCs.RASOLO_1972, NPC, "trade") { player, node -> + openNpcShop(player, node.id) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/RuantunDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/RuantunDialogue.kt new file mode 100644 index 000000000..89fa635f7 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/RuantunDialogue.kt @@ -0,0 +1,83 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class RuantunDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, RuantunDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return RuantunDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.RUANTUN_1916) + } +} + +class RuantunDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4,5,6,7,8) + .playerl("Hello.") + .npcl(FacialExpression.OLD_NORMAL, "You ssshould not be down here...") + .options() + .let { optionBuilder -> + optionBuilder.option("Who are you?") + .playerl("Who are you?") + .npcl(FacialExpression.OLD_NORMAL, "My name isss unimportant... I live only to ssserve my massster.") + .playerl("Um... Okay then.") + .end() + + optionBuilder.option("Why are you down here?") + .playerl("Why are you down here?") + .npcl(FacialExpression.OLD_NORMAL, "Thisss isss where I belong... Beingsss sssuch as myssself cannot abide in the light... It is in the darknesss where we find our homesss...") + .playerl("Uh... Okay then.") + .end() + + optionBuilder.option("Can I use your anvil?") + .playerl("Can I use your anvil?") + .npcl(FacialExpression.OLD_NORMAL, "Of courssse you may... I have very little ussse for it nowadaysss...") + .playerl("Uh... Thanks, I guess.") + .end() + } + + b.onQuestStages(DesertTreasure.questName, 9, 10, 100) + // Technically should happen after talking to Malak, but nah. + .playerl("Hello.") + .npcl(FacialExpression.OLD_NORMAL, "You sshould not be down here...") + .playerl("Are you an assistant to Count Draynor?") + .npc(FacialExpression.OLD_NORMAL, "I usssed to have that honour...", "Why do you ssseek me?") + .branch { player -> + return@branch if (inInventory(player, Items.SILVER_BAR_2355)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("I have a silver bar with me, I was wondering if you could make it into a 'sacrificial offering pot' for me?") + .betweenStage { df, player, _, _ -> + if (removeItem(player, Items.SILVER_BAR_2355)) { + addItemOrDrop(player, Items.SILVER_POT_4658) + } + } + .npc(FacialExpression.OLD_NORMAL, "Yesss, of courssse...", "There you are, put it to good usssse...") + .end() + + branch.onValue(0) + .playerl("I understand that you can make me a 'sacrificial offering pot' if I bring you a bar of silver?") + .npcl(FacialExpression.OLD_NORMAL, "And where did you hear thisss?") + .playerl("It was from Malak in Canifis.") + .npc(FacialExpression.OLD_NORMAL, "Ah, I sssee...", "Yesss, I know how to make sssuch an item...", "It has been many yearsss sssince I have needed to however...") + .npcl(FacialExpression.OLD_NORMAL, "It is not my wisssh to quessstion your desssire for sssuch an item, I wasss merely sssurprisssed that one sssuch as you would make sssuch a requessst...") + .npcl(FacialExpression.OLD_NORMAL, "I will happily make you thisss pot, but you mussst bring me a bar of sssilver... Alasss, I can no longer collect my own ingredientsss, and mussst remain here...") + .end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/ScarabNPC.kt b/Server/src/main/content/region/desert/quest/deserttreasure/ScarabNPC.kt new file mode 100644 index 000000000..93dd60d7b --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/ScarabNPC.kt @@ -0,0 +1,18 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import org.rs09.consts.NPCs + +class ScarabNPC : NPCBehavior(NPCs.SCARABS_1969) { + var clearTime = 0 + + override fun tick(self: NPC): Boolean { + if (clearTime++ > 100) { + clearTime = 0 + poofClear(self) + } + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/TranslationBook.kt b/Server/src/main/content/region/desert/quest/deserttreasure/TranslationBook.kt new file mode 100644 index 000000000..b9ff24e33 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/TranslationBook.kt @@ -0,0 +1,185 @@ +package content.region.desert.quest.deserttreasure + +import content.global.handlers.iface.BookInterface +import content.global.handlers.iface.BookLine +import content.global.handlers.iface.Page +import content.global.handlers.iface.PageSet +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +class TranslationBook : InteractionListener { + + companion object { + private val TITLE = "Translation Primer" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("This is a rough", 55), + BookLine("translation of the stone", 56), + BookLine("tablet brought to me by", 57), + BookLine("courier earlier today.", 58), + BookLine("", 59), + BookLine("", 60), + BookLine("The cuneiforms of this", 61), + BookLine("particular tablet are far", 62), + BookLine("different to anything I", 63), + BookLine("have previously seen in", 64), + BookLine("my career as an", 65) + ), + Page( + BookLine("archaeological expert.", 66), + BookLine("", 67), + BookLine("Where possible I have", 68), + BookLine("given as accurate a", 69), + BookLine("translation as possible, but", 70), + BookLine("some of the words I have", 71), + BookLine("attempted to translate", 72), + BookLine("hold different meanings", 73), + BookLine("depending upon their", 74), + BookLine("intonation and context;", 75), + BookLine("Due to my unfamiliarity", 76) + ) + ), + PageSet( + Page( + BookLine("with this language, I have", 55), + BookLine("given possible translations", 56), + BookLine("for these words wherever", 57), + BookLine("I have encountered them.", 58), + BookLine("Wherever I have a word", 59), + BookLine("n brackets, it is a word", 60), + BookLine("which has many meanings", 61), + BookLine("depending on the context,", 62), + BookLine("although the general", 63), + BookLine("meaning should be clear", 64), + BookLine("to even a casual study.", 65) + ), + Page( + BookLine("", 66), + BookLine("", 67), + BookLine("Hopefully this translation", 68), + BookLine("will help you in your", 69), + BookLine("excavations Asgarnia, and", 70), + BookLine("as usual I look forward", 71), + BookLine("to seeing what relics you", 72), + BookLine("bring back to the", 73), + BookLine("Museum of Varrock this", 74), + BookLine("time!", 75), + BookLine("Your friend, as always,", 76) + ), + ), + PageSet( + Page( + BookLine("Terry Balando", 55), + BookLine("", 56), + BookLine("", 57), + BookLine("", 58), + BookLine("", 59), + BookLine("", 60), + BookLine("", 61), + BookLine("", 62), + BookLine("", 63), + BookLine("", 64), + BookLine("", 65) + ), + Page( + BookLine("Translation follows:", 66), + BookLine("", 67), + BookLine("", 68), + BookLine("(There are some missing", 69), + BookLine("sentence fragments here,", 70), + BookLine("presumably from a stone", 71), + BookLine("tablet preceding this one", 72), + BookLine("which you have not yet", 73), + BookLine("discovered)", 74), + BookLine("", 75), + BookLine("...the permanent", 76) + ) + ), + PageSet( + Page( + BookLine("(exile/journey) of the", 55), + BookLine("people ended.", 56), + BookLine("And so it came to pass,", 57), + BookLine("that deep in the", 58), + BookLine("(fiery/uncomfortable)", 59), + BookLine("desert, the gods", 60), + BookLine("(argued/decided) amongst", 61), + BookLine("themselves that the", 62), + BookLine("(fortress/home) would be", 63), + BookLine("the (selected/chosen) place", 64), + BookLine("that would", 65) + ), + Page( + BookLine("(imprison/conceal) the", 66), + BookLine("(wealth/power).", 67), + BookLine("Thus (guarded/protected)", 68), + BookLine("by the (unusually archaic", 69), + BookLine("word here, I believe it", 70), + BookLine("means either the sick or", 71), + BookLine("the dead depending on", 72), + BookLine("context) and", 73), + BookLine("(defended/trapped) by the", 74), + BookLine("(diamonds/crystals) of", 75), + BookLine("(this word is", 76) + ) + ), + PageSet( + Page( + BookLine("untranslatable). So it was", 55), + BookLine("that the gods left the four", 56), + BookLine("(diamonds/crystals) as the", 57), + BookLine("(key/secret).", 58), + BookLine("(Guarded/Protected) by", 59), + BookLine("the (again, this word has", 60), + BookLine("no modern equivalent)", 61), + BookLine("and held by the", 62), + BookLine("(worthy/strong) so that", 63), + BookLine("the (wealth/power) might", 64), + BookLine("forever be", 65) + ), + Page( + BookLine("(imprisoned/concealed).", 66), + BookLine("", 67), + BookLine("", 68), + BookLine("", 69), + BookLine("", 70), + BookLine("", 71), + BookLine("", 72), + BookLine("", 73), + BookLine("", 74), + BookLine("", 75), + BookLine("", 76) + ) + ), + PageSet( + Page( + BookLine("There seems to be some", 55), + BookLine("further missing", 56), + BookLine("information continued", 57), + BookLine("onto a further tablet, but", 58), + BookLine("from this preliminary", 59), + BookLine("translation I think you", 60), + BookLine("may be onto something", 61), + BookLine("very big indeed!", 62), + BookLine("", 63), + BookLine("", 64), + BookLine("", 65) + ), + ), + ) + fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) + return true + } + } + + override fun defineListeners() { + on(Items.TRANSLATION_4655, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/TrollChildDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/TrollChildDialogue.kt new file mode 100644 index 000000000..24cedf3ef --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/TrollChildDialogue.kt @@ -0,0 +1,142 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class TrollChildDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, TrollChildDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return TrollChildDialogue(player) + } + override fun getIds(): IntArray { + // NPCs.BANDIT_1932 is wrong, should be NPCs.TROLL_CHILD_1932 + // NPCs.TROLL_CHILD_1933, NPCs.TROLL_CHILD_1934 are varbit controlled 1932 instances. + return intArrayOf(NPCs.BANDIT_1932) + } +} +class TrollChildDialogueFile : DialogueBuilderFile() { + + companion object { + fun dialogueBeforeQuestCrying(builder: DialogueBuilder): DialogueBuilder { + // From https://youtu.be/AJaHuCuxfFg 15:19 + return builder + .playerl("Hello there.") + .line("The troll child is crying to itself.", "It is ignoring you completely.") + } + fun dialogueStillCrying(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("Hello there.") + .npcl(FacialExpression.OLD_NEARLY_CRYING,"Waaaaaaa!") + .line("This troll seems very upset about something.", "Maybe some sweet food would take his mind off things?") + } + fun dialogueStoppedCrying(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("Hello there.") + .npc(FacialExpression.OLD_SAD,"-sniff-","H-hello there.") + .playerl("Why so sad, little troll?") + .npc(FacialExpression.OLD_NEARLY_CRYING,"It was the bad man!", "He hurt my mommy and daddy!", "He made them all freezey!") + .playerl("Bad man...?") + .npcl(FacialExpression.OLD_NEARLY_CRYING,"He said it was because they stole his diamond! But they never did! They found it, and didn't know who it belonged to!") + .npcl(FacialExpression.OLD_NEARLY_CRYING,"My mommy always told me stealing is wrong, they would never steal from someone!") + .npcl(FacialExpression.OLD_NEARLY_CRYING,"Then he did some wavey hand thing and my mommy and daddy got frozified!") + .playerl("A diamond you say? Listen, I think I might be able to help your parents, but I need that Diamond in return.") + .npcl(FacialExpression.OLD_NEARLY_CRYING,"-sniff- I don't think they really wanted it anyway, they would have given it back to the bad man if he'd asked before freezifying them...") + .npcl(FacialExpression.OLD_NEARLY_CRYING,"I give you my promise mister that if you unfreeze my mommy and daddy, you can have the stupid diamond.") + .npcl(FacialExpression.OLD_NEARLY_CRYING,"Do we have a deal?") + } + fun dialogueYesToHelp(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("Absolutely. Don't worry kid, I'll get your parents back to you safe and sound.") + } + fun dialogueNoToHelp(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("Sorry, I can't make any promises about that, and I don't think I have the time to waste trying to defrost some stupid ice trolls.") + .npcl(FacialExpression.OLD_NEARLY_CRYING,"Waaaaaaa!") + } + fun dialogueHaveYouFreedThem(builder: DialogueBuilder): DialogueBuilder { + return builder + .npcl(FacialExpression.OLD_SAD,"You didn't free my mommy and daddy yet?") + .player("Not yet...") + .npc(FacialExpression.OLD_SAD,"Please try harder!", "I love my mommy and daddy!") + } + fun dialogueThankYou(builder: DialogueBuilder): DialogueBuilder { + return builder + .npc(FacialExpression.OLD_CALM_TALK1, "Thanks for all of your help!", "I'm surprised you managed to survive the blizzard,", "being a thin skinned fleshy and all!") + .player("What can I say?", "I'm a lot tougher than I look.") + } + fun dialogueLostDiamond(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("I lost that diamond of Ice you gave me...") + .npcl(FacialExpression.OLD_CALM_TALK1, "That's okay, it blew back on an icy wind... It's almost like it wants to stay here! Here, take it back, you've earned it.") + } + } + + override fun create(b: DialogueBuilder) { + // Dialogue Logic + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4,5,6,7,8) + .let{dialogueBeforeQuestCrying(it)} + .end() + + b.onQuestStages(DesertTreasure.questName, 9) + .branch { player -> + return@branch DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) + }.let { branch -> + // Branch on sub-stages. + branch.onValue(0) + .let{dialogueStillCrying(it)} + .end() + + branch.onValue(1) + .let{dialogueStoppedCrying(it)} + .options().let { optionBuilder -> + optionBuilder.option("Yes") + .let{dialogueYesToHelp(it)} + .endWith { _, player -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 1) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeIceStage, 2) + } + } + optionBuilder.option("No") + .let{dialogueNoToHelp(it)} + .end() + } + + branch.onValue(2) + .let{dialogueHaveYouFreedThem(it)} + .end() + + branch.onValue(3) + .let{dialogueHaveYouFreedThem(it)} + .end() + + branch.onValue(4) + .let{dialogueHaveYouFreedThem(it)} + .end() + + branch.onValue(100) + .branch { player -> + return@branch if (!inInventory(player, Items.ICE_DIAMOND_4671)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .let{dialogueLostDiamond(it)} + .endWith { _, player -> + addItemOrDrop(player, Items.ICE_DIAMOND_4671) + } + branch.onValue(0) + .let{dialogueThankYou(it)} + .end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/shadowofthestorm/DarklightListener.kt b/Server/src/main/content/region/desert/quest/shadowofthestorm/DarklightListener.kt index 26e72c57f..87fc7de02 100644 --- a/Server/src/main/content/region/desert/quest/shadowofthestorm/DarklightListener.kt +++ b/Server/src/main/content/region/desert/quest/shadowofthestorm/DarklightListener.kt @@ -6,15 +6,16 @@ import core.api.removeItem import core.game.interaction.IntType import core.game.interaction.InteractionListener import org.rs09.consts.Items +import content.data.Quests class DarklightListener : InteractionListener { override fun defineListeners() { onUseWith(IntType.ITEM, Items.BLACK_MUSHROOM_INK_4622, Items.SILVERLIGHT_2402) { player, used, with -> - if (!hasRequirement(player, "Shadow of the Storm") || (!player.inventory.contains(Items.BLACK_MUSHROOM_INK_4622, 1) && (!player.inventory.contains(Items.SILVERLIGHT_2402, 1)))) + if (!hasRequirement(player, Quests.SHADOW_OF_THE_STORM) || (!player.inventory.contains(Items.BLACK_MUSHROOM_INK_4622, 1) && (!player.inventory.contains(Items.SILVERLIGHT_2402, 1)))) return@onUseWith false if (removeItem(player, used.id) && removeItem(player, with.id)) - addItem(player, Items.DARKLIGHT_6746) - return@onUseWith true + return@onUseWith addItem(player, Items.DARKLIGHT_6746) + return@onUseWith false } } } diff --git a/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt b/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt index b045a1cf1..c1331e614 100644 --- a/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt +++ b/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt @@ -8,6 +8,7 @@ import core.plugin.Initializable import org.rs09.consts.NPCs import core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile +import content.data.Quests @Initializable public final class ClayGolemDialoguePlugin(player: Player? = null) : DialoguePlugin(player) { @@ -29,22 +30,22 @@ public final class ClayGolemDialoguePlugin(player: Player? = null) : DialoguePlu class ClayGolemDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - val opt1 = b.onQuestStages("The Golem", 0) + val opt1 = b.onQuestStages(Quests.THE_GOLEM, 0) .npc("Damage... severe...", "task... incomplete...") .options() opt1 - .optionIf("Shall I try to repair you?") { player -> return@optionIf player.questRepository.getQuest("The Golem").hasRequirements(player) } + .optionIf("Shall I try to repair you?") { player -> return@optionIf player.questRepository.getQuest(Quests.THE_GOLEM).hasRequirements(player) } .playerl("Shall I try to repair you?") .npcl("Repairs... needed...") - .endWith(){ _, player -> if (player.questRepository.getStage("The Golem") < 1 ) { setQuestStage(player, "The Golem", 1) } } + .endWith(){ _, player -> if (player.questRepository.getStage(Quests.THE_GOLEM) < 1 ) { setQuestStage(player, Quests.THE_GOLEM, 1) } } opt1 .option("I'm not going to find a conversation here!") .playerl("I'm not going to find a conversation here!") .end() - b.onQuestStages("The Golem", 1) + b.onQuestStages(Quests.THE_GOLEM, 1) .npcl("Repairs... needed...") .end() - b.onQuestStages("The Golem", 2) + b.onQuestStages(Quests.THE_GOLEM, 2) .npcl("Damage repaired...") .npcl("Thank you. My body and mind are fully healed.") .npcl("Now I must complete my task by defeating the great enemy.") @@ -52,43 +53,43 @@ class ClayGolemDialogueFile : DialogueBuilderFile() { .npcl("A great demon. It broke through from its dimension to attack the city.") .npcl("The golem army was created to fight it. Many were destroyed, but we drove the demon back!") .npcl("The demon is still wounded. You must open the portal so that I can strike the final blow and complete my task.") - .endWith() { _, player -> setQuestStage(player, "The Golem", 3) } - b.onQuestStages("The Golem", 3) + .endWith() { _, player -> setQuestStage(player, Quests.THE_GOLEM, 3) } + b.onQuestStages(Quests.THE_GOLEM, 3) .npcl("The demon is still wounded. You must open the portal so that I can strike the final blow and complete my task.") .end() - b.onQuestStages("The Golem", 4) + b.onQuestStages(Quests.THE_GOLEM, 4) .npcl("My task is incomplete. You must open the portal so I can defeat the great demon.") .playerl("It's ok, the demon is dead!") .npcl("The demon must be defeated...") .playerl("No, you don't understand. I saw the demon's skeleton. It must have died of its wounds.") .npcl("Demon must be defeated! Task incomplete.") - .endWith() { _, player -> setQuestStage(player, "The Golem", 5) } - b.onQuestStages("The Golem", 5) + .endWith() { _, player -> setQuestStage(player, Quests.THE_GOLEM, 5) } + b.onQuestStages(Quests.THE_GOLEM, 5) .npcl("Task incomplete.") .playerl("Oh, how am I going to convince you?") - .endWith() { _, player -> setQuestStage(player, "The Golem", 6) } - b.onQuestStages("The Golem", 6, 7) + .endWith() { _, player -> setQuestStage(player, Quests.THE_GOLEM, 6) } + b.onQuestStages(Quests.THE_GOLEM, 6, 7) .npcl("My task is incomplete. You must open the portal so I can defeat the great demon.") .playerl("I already told you, he's dead!") .npcl("Task incomplete.") .playerl("Oh, how am I going to convince you?") - .endWith() { df, player -> if(player.questRepository.getStage("The Golem") < 7) { setQuestStage(player, "The Golem", 7) } } + .endWith() { df, player -> if(player.questRepository.getStage(Quests.THE_GOLEM) < 7) { setQuestStage(player, Quests.THE_GOLEM, 7) } } } } class ClayGolemProgramDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages("The Golem", 8) + b.onQuestStages(Quests.THE_GOLEM, 8) .npc("New instructions...", "Updating program...") .npcl("Task complete!") .npcl("Thank you. Now my mind is at rest.") - .endWith() { _, player -> finishQuest(player, "The Golem") } + .endWith() { _, player -> finishQuest(player, Quests.THE_GOLEM) } } } class CuratorHaigHalenGolemDialogue : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - val opt1 = b.onQuestStages("The Golem", 3) + val opt1 = b.onQuestStages(Quests.THE_GOLEM, 3) .npcl("Ah yes, a very impressive artefact. The people of that city were excellent sculptors.") .npcl("It's in the display case upstairs.") .playerl("No, I need to take it away with me.") diff --git a/Server/src/main/content/region/desert/quest/thegolem/TheGolemQuest.kt b/Server/src/main/content/region/desert/quest/thegolem/TheGolemQuest.kt index 8ae98312a..89d485f91 100644 --- a/Server/src/main/content/region/desert/quest/thegolem/TheGolemQuest.kt +++ b/Server/src/main/content/region/desert/quest/thegolem/TheGolemQuest.kt @@ -19,9 +19,10 @@ import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.interaction.InterfaceListener import core.game.world.GameWorld +import content.data.Quests @Initializable -class TheGolemQuest : Quest("The Golem", 70, 69, 1, 437, 0, 1, 10) { +class TheGolemQuest : Quest(Quests.THE_GOLEM, 70, 69, 1, 437, 0, 1, 10) { override fun newInstance(`object`: Any?): Quest { return this } @@ -140,7 +141,7 @@ class DisplayCaseListener : InterfaceListener { class TheGolemListeners : InteractionListener { fun repairGolem(player: Player): Boolean { - if(player.questRepository.getStage("The Golem") == 1) { + if(player.questRepository.getStage(Quests.THE_GOLEM) == 1) { var clayUsed = player.getAttribute("the-golem:clay-used", 0) val msg = when(clayUsed) { 0 -> "You apply some clay to the golem's wounds. The clay begins to harden in the hot sun." @@ -157,7 +158,7 @@ class TheGolemListeners : InteractionListener { player.setAttribute("/save:the-golem:clay-used", clayUsed) updateVarps(player) if(clayUsed == 4) { - setQuestStage(player, "The Golem", 2) + setQuestStage(player, Quests.THE_GOLEM, 2) } } } @@ -213,13 +214,13 @@ class TheGolemListeners : InteractionListener { val rotation3 = player.getAttribute("the-golem:statuette-rotation:3", 0) val doorOpen = player.getAttribute("the-golem:door-open", false) var clientStage = 0 - if(player.questRepository.getStage("The Golem") > 0) { + if(player.questRepository.getStage(Quests.THE_GOLEM) > 0) { clientStage = Math.max(clientStage, 1) } if(doorOpen) { clientStage = Math.max(clientStage, 5) } - if(player.questRepository.getStage("The Golem") >= 100) { + if(player.questRepository.getStage(Quests.THE_GOLEM) >= 100) { clientStage = Math.max(clientStage, 10) } setVarbit(player, 346, clientStage) @@ -346,9 +347,9 @@ class TheGolemListeners : InteractionListener { player.sendMessage("You don't know what that would do.") return true } - if(player.questRepository.getStage("The Golem") == 7) { + if(player.questRepository.getStage(Quests.THE_GOLEM) == 7) { player.sendMessage("You insert the key and the golem's skull hinges open.") - setQuestStage(player, "The Golem", 8) + setQuestStage(player, Quests.THE_GOLEM, 8) } return true } @@ -393,7 +394,7 @@ class TheGolemListeners : InteractionListener { if(!player.getAttribute("the-golem:seen-demon", false)) { player.sendMessage("The room is dominated by a colossal horned skeleton!") player.setAttribute("/save:the-golem:seen-demon", true) - setQuestStage(player, "The Golem", 4) + setQuestStage(player, Quests.THE_GOLEM, 4) } teleport(player, Location.create(3552, 4948, 0)) return@on true diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/AlShabimDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/AlShabimDialogue.java index 93e263e25..4afd56007 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/AlShabimDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/AlShabimDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -48,7 +49,7 @@ public final class AlShabimDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (quest.getStage(player)) { default: npc("Hello Effendi!"); diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java index 6f5d0444a..efb949796 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -57,7 +58,7 @@ public final class AnaDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); if ((quest.getStage(player) == 71 || quest.getStage(player) == 72) && args.length > 1) { player.getDialogueInterpreter().sendDialogue("You see a barrel coming to the surface. Before too long you haul it", "onto the side. The barrel seems quite heavy and you hear a muffled", "sound coming from inside."); stage = 400; @@ -277,7 +278,7 @@ public final class AnaDialogue extends DialoguePlugin { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - final Quest quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (quest.getStage(player)) { case 61: player.getDialogueInterpreter().open(822, event.getUsedWith(), true); @@ -320,7 +321,7 @@ public final class AnaDialogue extends DialoguePlugin { @Override public boolean isHidden(final Player player) { - Quest quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + Quest quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); if (quest.getStage(player) > 61) { return true; } diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/BedabinNomadDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/BedabinNomadDialogue.java index 8cdabc88d..420714f24 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/BedabinNomadDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/BedabinNomadDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -46,7 +47,7 @@ public final class BedabinNomadDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (npc.getId()) { case 834:// guard switch (quest.getStage(player)) { diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/CaptainSiadDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/CaptainSiadDialogue.java index 447929a6c..9de225a64 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/CaptainSiadDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/CaptainSiadDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -42,7 +43,7 @@ public final class CaptainSiadDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (quest.getStage(player)) { default: player.getPacketDispatch().sendMessage("The captain looks up from his work as you address him."); diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java index e979daefd..9c9a08367 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java @@ -2,6 +2,7 @@ package content.region.desert.quest.thetouristrap; import java.util.List; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; @@ -57,7 +58,7 @@ public final class DesertGuardDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (npc.getId()) { case 5001: switch (quest.getStage(player)) { diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/IrenaDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/IrenaDialogue.java index 19b3b768f..8788371b8 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/IrenaDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/IrenaDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; @@ -48,7 +49,7 @@ public final class IrenaDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); if (quest.getStage(player) == 95 && player.getInventory().containsItem(TouristTrap.ANNA_BARREL)) { npc("Hey, great you've found Ana!"); stage = 900; diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/MaleSlaveDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/MaleSlaveDialogue.java index 2ee5e3456..269f4185a 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/MaleSlaveDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MaleSlaveDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -47,7 +48,7 @@ public final class MaleSlaveDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (npc.getShownNPC(player).getId()) { case 4985: case 825: diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryCaptainDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryCaptainDialogue.java index f959a1fbe..e020f6793 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryCaptainDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryCaptainDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; @@ -52,7 +53,7 @@ public final class MercenaryCaptainDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (quest.getStage(player)) { case 11: interpreter.sendDialogue("You approach the Mercenary Captain."); @@ -183,7 +184,7 @@ public final class MercenaryCaptainDialogue extends DialoguePlugin { super.finalizeDeath(killer); if (killer instanceof Player) { final Player player = (Player) killer; - final Quest quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (quest.getStage(player)) { case 0: case 10: diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryDialogue.java index c9fd6d862..4f063203c 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; @@ -46,7 +47,7 @@ public final class MercenaryDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (quest.getStage(player)) { default: npc("What are you doing here?"); diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/MinecartDriverDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/MinecartDriverDialogue.java index e9ef2dbb6..a748227fd 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/MinecartDriverDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MinecartDriverDialogue.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; @@ -46,7 +47,7 @@ public final class MinecartDriverDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); switch (quest.getStage(player)) { case 90: npc("Quickly, get in the back of the cart."); diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/MiningCampZone.java b/Server/src/main/content/region/desert/quest/thetouristrap/MiningCampZone.java index 75aa27944..ae06c8e0a 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/MiningCampZone.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MiningCampZone.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -83,7 +84,7 @@ public final class MiningCampZone extends MapZone implements Plugin { * @return {@code True} if removed. */ public boolean checkAnna(final Player p) { - final Quest quest = p.getQuestRepository().getQuest(TouristTrap.NAME); + final Quest quest = p.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); if (p.getAttribute("ana-delay", 0) > GameWorld.getTicks()) { return false; } diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java index e81eb6764..75f4d6134 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java @@ -14,6 +14,7 @@ import core.plugin.Initializable; import core.plugin.ClassScanner; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * The main type for the tourist trap quest. @@ -23,12 +24,6 @@ import static core.api.ContentAPIKt.*; */ @Initializable public final class TouristTrap extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "The Tourist Trap"; - /** * The metal key item. */ @@ -113,7 +108,7 @@ public final class TouristTrap extends Quest { * Constructs a new {@code TouristTrap} {@code Object}. */ public TouristTrap() { - super(NAME, 123, 122, 2, 197, 0, 1, 30); + super(Quests.THE_TOURIST_TRAP, 123, 122, 2, 197, 0, 1, 30); } @Override diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java index e8a9750dd..463d61272 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java @@ -1,5 +1,6 @@ package content.region.desert.quest.thetouristrap; +import content.data.Quests; import core.cache.def.impl.AnimationDefinition; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; @@ -135,7 +136,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public boolean handle(final Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); final int id = node.getId(); switch (option) { case "read": @@ -356,7 +357,7 @@ public final class TouristTrapPlugin extends OptionHandler { player.getInventory().add(TouristTrap.CELL_DOOR_KEY, player); player.getDialogueInterpreter().sendItemMessage(TouristTrap.CELL_DOOR_KEY, "You find a cell door key."); break; - } else if (hasItem(player, TouristTrap.WROUGHT_IRON_KEY) && player.getQuestRepository().isComplete(TouristTrap.NAME)) { + } else if (hasItem(player, TouristTrap.WROUGHT_IRON_KEY) && player.getQuestRepository().isComplete(Quests.THE_TOURIST_TRAP)) { player.getInventory().add(TouristTrap.WROUGHT_IRON_KEY, player); player.getDialogueInterpreter().sendItemMessage(TouristTrap.WROUGHT_IRON_KEY, "You find the key to the main gate."); break; @@ -633,7 +634,7 @@ public final class TouristTrapPlugin extends OptionHandler { case 516: player.getInventory().remove(TouristTrap.ANNA_BARREL); player.getBank().remove(TouristTrap.ANNA_BARREL); - player.getQuestRepository().getQuest(TouristTrap.NAME).setStage(player, 71); + player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP).setStage(player, 71); end(); break; case 0: @@ -700,7 +701,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public boolean handle(final NodeUsageEvent event) { final Player player = event.getPlayer(); - final Quest quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); if (event.getUsedWith().getId() == 18958) {// cart if (quest.getStage(player) == 72) { player.lock(4); @@ -800,7 +801,7 @@ public final class TouristTrapPlugin extends OptionHandler { } break; case 33: - player.getQuestRepository().getQuest(TouristTrap.NAME).setStage(player, 70); + player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP).setStage(player, 70); player.getInventory().remove(TouristTrap.ANNA_BARREL); player.removeAttribute("ana-delay"); AnnaCartCutscene.this.stop(true); @@ -905,7 +906,7 @@ public final class TouristTrapPlugin extends OptionHandler { case 6: player.unlock(); player.getInterfaceManager().closeOverlay(); - player.getQuestRepository().getQuest(TouristTrap.NAME).setStage(player, 95); + player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP).setStage(player, 95); player.getInterfaceManager().close(); player.getProperties().setTeleportLocation(Location.create(3258, 3029, 0)); player.getInventory().add(TouristTrap.ANNA_BARREL); @@ -1303,7 +1304,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public boolean open(Object... args) { barrel = (Scenery) args[0]; - quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); if ((quest.getStage(player) == 70 || quest.getStage(player) == 72) && !player.hasItem(TouristTrap.ANNA_BARREL)) { interpreter.sendDialogue("You search the barrels and find Ana."); stage = 400; @@ -1402,7 +1403,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - final Quest quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); if (quest.getStage(player) == 54 && player.getInventory().containsItem(TouristTrap.TECHNICAL_PLANS)) { player.getDialogueInterpreter().open("bedabin-anvil"); return true; @@ -1436,7 +1437,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - final Quest quest = player.getQuestRepository().getQuest(TouristTrap.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.THE_TOURIST_TRAP); if (!event.getUsedWith().getLocation().equals(Location.create(3292, 9423, 0))) { return false; } diff --git a/Server/src/main/content/region/desert/sophanem/handlers/SophanemPlugin.java b/Server/src/main/content/region/desert/sophanem/handlers/SophanemPlugin.java index 776b39973..219c1caa9 100644 --- a/Server/src/main/content/region/desert/sophanem/handlers/SophanemPlugin.java +++ b/Server/src/main/content/region/desert/sophanem/handlers/SophanemPlugin.java @@ -2,6 +2,7 @@ package content.region.desert.sophanem.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -13,10 +14,11 @@ import core.plugin.Initializable; import core.plugin.Plugin; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * The plugin for handling stuff in Sophanem. - * @author jamix77 + * @author jamix77, Player Name * */ @Initializable @@ -26,6 +28,8 @@ public class SophanemPlugin extends OptionHandler { public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.forId(20277).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(20275).getHandlers().put("option:climb-down", this); + SceneryDefinition.forId(20391).getHandlers().put("option:open", this); + SceneryDefinition.forId(28514).getHandlers().put("option:open", this); return this; } @@ -34,15 +38,22 @@ public class SophanemPlugin extends OptionHandler { final int id = node instanceof Scenery ? ((Scenery) node).getId() : ((Item) node).getId(); switch (id) { case 20275: - if (!hasRequirement(player, "Contact!")) - break; + if (!hasRequirement(player, Quests.CONTACT)) { + break; + } ClimbActionHandler.climb(player, new Animation(827), Location.create(2799, 5160, 0)); break; case 20277: ClimbActionHandler.climb(player, new Animation(828), Location.create(3315,2796,0)); break; + case 20391: + case 28514: + if (!hasRequirement(player, Quests.ICTHLARINS_LITTLE_HELPER)) { + break; + } + DoorActionHandler.handleDoor(player, (Scenery) node); + break; } return true; } - } diff --git a/Server/src/main/content/region/desert/ullek/handlers/UllekListeners.kt b/Server/src/main/content/region/desert/ullek/handlers/UllekListeners.kt index b654b8b27..f2b790453 100644 --- a/Server/src/main/content/region/desert/ullek/handlers/UllekListeners.kt +++ b/Server/src/main/content/region/desert/ullek/handlers/UllekListeners.kt @@ -1,8 +1,11 @@ package content.region.desert.ullek.handlers +import core.api.* import core.game.interaction.IntType import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength import core.game.world.map.Location +import core.game.world.update.flag.context.Animation import org.rs09.consts.Scenery class UllekListeners : InteractionListener { @@ -23,5 +26,17 @@ class UllekListeners : InteractionListener { player.properties.teleportLocation = Location.create(3419, 2803, 1) return@on true } + on(Scenery.REEDS_28474, IntType.SCENERY, "push through") { player, node -> + animate(player, 7633) + animateScenery(node as core.game.node.scenery.Scenery, 7634) + queueScript(player, animationDuration(Animation(7633)), QueueStrength.SOFT) { + val newLoc = node.location.transform(Location.getDelta(player.location, node.location)) + player.walkingQueue.reset() + player.walkingQueue.addPath(newLoc.x, newLoc.y) + + return@queueScript stopExecuting(player) + } + return@on true + } } } \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/dialogue/JossikDialogue.java b/Server/src/main/content/region/fremennik/dialogue/JossikDialogue.java index 4fed3f6b9..2af173c7d 100644 --- a/Server/src/main/content/region/fremennik/dialogue/JossikDialogue.java +++ b/Server/src/main/content/region/fremennik/dialogue/JossikDialogue.java @@ -10,8 +10,11 @@ import core.plugin.Initializable; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.addItemOrDrop; +import static core.api.ContentAPIKt.hasAnItem; + /** - * Handles the dialogue for jossik. + * Handles the dialogue for Jossik. * @author Vexia */ @Initializable @@ -71,23 +74,24 @@ public class JossikDialogue extends DialoguePlugin { case 20: boolean missing = false; for (GodBook book : GodBook.values()) { - if (player.getSavedData().getGlobalData().hasCompletedGodBook(book) && !player.hasItem(book.getBook())) { + if (player.getSavedData().getGlobalData().hasCompletedGodBook(book) && hasAnItem(player, book.getBook().getId(), true).getContainer() == null) { + // i.e.: if you have a completed book on file but you lost it missing = true; - player.getInventory().add(book.getBook(), player); - npc("As a matter of fact, I did! This book washed up on the", "beach, and I recognised it as yours!"); + addItemOrDrop(player, book.getBook().getId(), 1); } } int damaged = player.getSavedData().getGlobalData().getGodBook(); - if (damaged != -1 && !player.hasItem(GodBook.values()[damaged].getDamagedBook())) { + if (damaged != -1 && hasAnItem(player, GodBook.values()[damaged].getDamagedBook().getId(), true).getContainer() == null) { + // i.e.: if you have an uncompleted book on file but you lost it missing = true; - player.getInventory().add(GodBook.values()[damaged].getDamagedBook(), player); - npc("As a matter of fact, I did! This book washed up on the", "beach, and I recognised it as yours!"); + addItemOrDrop(player, GodBook.values()[damaged].getDamagedBook().getId(), 1); } if (missing) { + npc("As a matter of fact, I did! This book washed up on the", "beach, and I recognised it as yours!"); stage = 23; return true; } - uncompleted = new ArrayList<>(5); + uncompleted = new ArrayList<>(3); for (GodBook book : GodBook.values()) { if (!player.getSavedData().getGlobalData().hasCompletedGodBook(book)) { uncompleted.add(book); @@ -95,12 +99,12 @@ public class JossikDialogue extends DialoguePlugin { } boolean hasUncompleted = false; for (GodBook book : GodBook.values()) { - if (player.hasItem(book.getDamagedBook())) { + if (hasAnItem(player,book.getDamagedBook().getId(), true).getContainer() != null) { + // i.e.: you have an uncompleted book on file and you still have it -> do not allow the player to get a new one, GL #2035 hasUncompleted = true; } } - if (uncompleted.size() == 0 || hasUncompleted) {// all - // completed. + if (uncompleted.isEmpty() || hasUncompleted) {// all completed. npc("No, sorry adventurer, I haven't."); stage = 23; return true; diff --git a/Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java b/Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java index 1b2af20f7..19e639678 100644 --- a/Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java +++ b/Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java @@ -12,6 +12,7 @@ import core.net.packet.context.MinimapStateContext; import core.plugin.Initializable; import core.net.packet.out.MinimapState; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles the lokar searunner dialogue. @@ -110,7 +111,7 @@ public class LokarSearunnerDialogue extends DialoguePlugin { * @param location the location. */ private void travel(final Player player, final Location location) { - if (!hasRequirement(player, "Lunar Diplomacy")) + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return; player.lock(); GameWorld.getPulser().submit(new Pulse(1, player) { diff --git a/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt b/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt index e6367c387..51d160974 100644 --- a/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt +++ b/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt @@ -17,6 +17,7 @@ import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills import core.game.world.map.zone.ZoneBorders import org.rs09.consts.* +import content.data.Quests class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { companion object { @@ -322,7 +323,7 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { } // You can alternatively browse her regular clothing store to complete the task, no purchase necessary. - if (event.target.id == NPCs.YRSA_1301 && event.option == "trade" && player.questRepository.isComplete("Fremennik Trials") && inBorders(player, YRSA_SHOP_BORDERS)) { + if (event.target.id == NPCs.YRSA_1301 && event.option == "trade" && player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS) && inBorders(player, YRSA_SHOP_BORDERS)) { finishTask( player, DiaryLevel.MEDIUM, diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt index b4e2b45bf..e4ed6b691 100644 --- a/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt @@ -8,6 +8,7 @@ import content.region.fremennik.rellekka.handlers.RellekkaDestination import content.region.fremennik.rellekka.handlers.RellekkaUtils import core.tools.END_DIALOGUE import core.api.* +import content.data.Quests @Initializable class MordGunnarsDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { @@ -35,7 +36,7 @@ class MordGunnarsDialogue(player: Player? = null) : core.game.dialogue.DialogueP 2 -> { end() - if (!hasRequirement(player, "Fremennik Trials")) + if (!hasRequirement(player, Quests.THE_FREMENNIK_TRIALS)) return true RellekkaUtils.sail(player, if(npc.id == NPCs.MORD_GUNNARS_5481) RellekkaDestination.RELLEKKA_TO_JATIZSO else RellekkaDestination.JATIZSO_TO_RELLEKKA) } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/SirsalBankerDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/SirsalBankerDialogue.kt deleted file mode 100644 index ebe225e11..000000000 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/SirsalBankerDialogue.kt +++ /dev/null @@ -1,209 +0,0 @@ -package content.region.fremennik.lunarisle.dialogue - -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable -import org.rs09.consts.NPCs -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE - -/** - * Handles Sirsal banker dialogue tree. - * - * @author vddCore - */ -@Initializable -class SirsalBankerDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - START_DIALOGUE -> if (hasSealOfPassage(player)) { - if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { - npcl( - FacialExpression.NEUTRAL, - "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( - FacialExpression.NEUTRAL, - "Good day, how may I help you?" - ).also { - if (hasAwaitingGrandExchangeCollections(player)) { - stage++ - } else { - stage += 2 - } - } - } - } else { - playerl(FacialExpression.HALF_WORRIED, "Hi, I...") - stage = 30 - } - - 1 -> npcl( - FacialExpression.NEUTRAL, - "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(FacialExpression.NEUTRAL, "I'd like to access my bank account, please.", 10), - IfTopic( - FacialExpression.NEUTRAL, - "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", - 13, - hasActivatedSecondaryBankAccount(player) - ), - IfTopic( - FacialExpression.NEUTRAL, - "I'd like to open a secondary bank account.", - 20, - !hasActivatedSecondaryBankAccount(player) - ), - Topic(FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 11), - Topic(FacialExpression.NEUTRAL, "I'd like to collect items.", 12), - Topic(FacialExpression.ASKING, "What is this place?", 3), - ) - - 3 -> npcl( - FacialExpression.NEUTRAL, - "This is a branch of the Bank of Gielinor. We have branches in many towns." - ).also { stage++ } - - 4 -> playerl( - FacialExpression.ASKING, - "And what do you do?" - ).also { stage++ } - - 5 -> npcl( - FacialExpression.NEUTRAL, - "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( - FacialExpression.NEUTRAL, - "Your active bank account has been switched. " + - "You can now access your ${getBankAccountName(player)} account." - ).also { stage = END_DIALOGUE } - } - - 20 -> npcl( - FacialExpression.NEUTRAL, - "Certainly. We offer secondary accounts to all our customers." - ).also { stage++ } - - 21 -> npcl( - FacialExpression.NEUTRAL, - "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( - FacialExpression.NEUTRAL, - "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( - FacialExpression.ASKING, - "Knowing all this, would you like to proceed with opening your secondary bank account?" - ).also { stage++ } - - 24 -> showTopics( - Topic(FacialExpression.NEUTRAL, "Yes, I am still interested.", 25), - Topic(FacialExpression.NEUTRAL, "Actually, I've changed my mind.", 26) - ) - - 25 -> { - when (activateSecondaryBankAccount(player)) { - SecondaryBankAccountActivationResult.ALREADY_ACTIVE -> { - npcl( - FacialExpression.NEUTRAL, - "Your bank account was already activated, there is no need to pay twice." - ).also { stage = END_DIALOGUE } - } - - SecondaryBankAccountActivationResult.INTERNAL_FAILURE -> { - npcl( - FacialExpression.NEUTRAL, - "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( - FacialExpression.NEUTRAL, - "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( - FacialExpression.NEUTRAL, - "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( - FacialExpression.NEUTRAL, - "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 } - - 30 -> npcl( - FacialExpression.ANNOYED, - "What are you doing here, Fremennik?!" - ).also { stage++ } - - 31 -> playerl( - FacialExpression.WORRIED, - "I have a Seal of Pass..." - ).also { stage++ } - - 32 -> npcl( - FacialExpression.ANGRY, - "No you don't! Begone!" - ).also { stage = END_DIALOGUE } - - /* TODO: Is the above related to Lunar Diplomacy? */ - } - - return true - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.SIRSAL_BANKER_4519) - } -} diff --git a/Server/src/main/content/region/fremennik/neitiznot/handlers/YakArmourPlugin.java b/Server/src/main/content/region/fremennik/neitiznot/handlers/YakArmourPlugin.java index e5c86e266..135470838 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/handlers/YakArmourPlugin.java +++ b/Server/src/main/content/region/fremennik/neitiznot/handlers/YakArmourPlugin.java @@ -71,6 +71,7 @@ public class YakArmourPlugin extends UseWithHandler { * The index. */ private final int index; + private final int YAK_BODY_INDEX = 1; /** * The ticks. @@ -96,7 +97,7 @@ public class YakArmourPlugin extends UseWithHandler { @Override public boolean checkRequirements() { - int level = (index == 1 ? 46 : 43); + int level = (index == YAK_BODY_INDEX ? 46 : 43); if (player.getSkills().getLevel(Skills.CRAFTING) < level) { player.getDialogueInterpreter().sendDialogue("You need a Crafting level of at least " + level + " in order to do this."); return false; @@ -108,7 +109,7 @@ public class YakArmourPlugin extends UseWithHandler { player.getDialogueInterpreter().sendDialogue("You need some thread to make anything out of leather."); return false; } - int reqAmount = index == 1 ? 1 : 2; + int reqAmount = index == YAK_BODY_INDEX ? 2 : 1; if (!player.getInventory().contains(10820, reqAmount)) { player.getDialogueInterpreter().sendDialogue("You don't have the required amount of yak-hide in order to do this."); return false; @@ -129,14 +130,11 @@ public class YakArmourPlugin extends UseWithHandler { if (++ticks % 5 != 0) { return false; } - int reqAmount = index == 1 ? 1 : 2; + int reqAmount = index == YAK_BODY_INDEX ? 2 : 1; if (player.getInventory().remove(new Item(10820, reqAmount))) { player.getInventory().add(node); player.getSkills().addExperience(Skills.CRAFTING, 32, true); LeatherCrafting.decayThread(player); - if (LeatherCrafting.isLastThread(player)) { - LeatherCrafting.removeThread(player); - } player.sendMessage("You make " + node.getName().toLowerCase() + "."); } amount--; diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/BjornAndEldgrimDialogues.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/BjornAndEldgrimDialogues.kt index 216967b80..47b7a42e5 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/BjornAndEldgrimDialogues.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/BjornAndEldgrimDialogues.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,9 +19,9 @@ class BjornAndEldgrimDialogues(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 0 } - } else if (isQuestComplete(player, "Fremennik Trials")) { + } else if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { npc(FacialExpression.DRUNK, "Hey! Itsh you again! Whatshyerfashe!").also { stage = 10 } } return true diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/BlaninDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/BlaninDialogue.kt index 1bc5c3712..52a931e01 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/BlaninDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/BlaninDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,7 +19,7 @@ class BlaninDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { player(FacialExpression.FRIENDLY, "Good day.").also { stage = 0 } } else { player(FacialExpression.FRIENDLY, "That's one less thing to worry about.").also { stage = 10 } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt index 8e6434f54..c54085080 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt @@ -7,12 +7,13 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType import core.plugin.Initializable +import content.data.Quests @Initializable class CouncilWorkerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if(getQuestStage(player, "Fremennik Trials") in 1..99){ + if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) in 1..99){ player.dialogueInterpreter.open((CouncilWorkerFTDialogue(1))) } else if(player.achievementDiaryManager.getDiary(DiaryType.FREMENNIK).isComplete(0, true)){ diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/DronDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/DronDialogue.kt index 586034287..d2f20660e 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/DronDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/DronDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,7 +19,7 @@ class DronDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Making History")) { + if (!isQuestComplete(player, Quests.MAKING_HISTORY)) { player(FacialExpression.FRIENDLY, "Excuse me.").also { stage = 0 } } else { player(FacialExpression.FRIENDLY, "Excuse me.").also { stage = 10 } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/FishmongerRellekkaDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/FishmongerRellekkaDialogue.kt index 8a35e9dc3..ae896659c 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/FishmongerRellekkaDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/FishmongerRellekkaDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,7 +19,7 @@ class FishmongerRellekkaDialogue(player: Player? = null) : DialoguePlugin(player override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } } else { npcl(FacialExpression.FRIENDLY,"Hello there, ${player.getAttribute("fremennikname","fremmyname")}. Looking for fresh fish?").also { stage = 0 } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/FurTraderDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/FurTraderDialogue.kt index e8f11f81c..a117d29ed 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/FurTraderDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/FurTraderDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,7 +19,7 @@ class FurTraderDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } } else { npcl(FacialExpression.FRIENDLY,"Welcome back, ${player.getAttribute("fremennikname","fremmyname")}. Have you seen the furs I have today?").also { stage = 10 } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/IngridHradsonDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/IngridHradsonDialogue.kt index bf81e8182..ba86e5be8 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/IngridHradsonDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/IngridHradsonDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,11 +19,11 @@ class IngridHradsonDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { npcl(FacialExpression.ANNOYED, "Outlander, I have work to be getting on with... Please stop bothering me.").also { stage = END_DIALOGUE } - } else if (isQuestComplete(player, "Fremennik Trials") && !isQuestComplete(player, "Olaf's Quest")) { + } else if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) && !isQuestComplete(player, Quests.OLAFS_QUEST)) { npc(FacialExpression.FRIENDLY, "Good afternoon! How do you like our village?").also { stage = 0 } - } else if (isQuestComplete(player, "Fremennik Trials") && isQuestComplete(player, "Olaf's Quest")) { + } else if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) && isQuestComplete(player, Quests.OLAFS_QUEST)) { npc(FacialExpression.ASKING, "Hello again! Have you any word from my husband?").also { stage = 10 } } return true diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/JarvaldDialogue.java b/Server/src/main/content/region/fremennik/rellekka/dialogue/JarvaldDialogue.java deleted file mode 100644 index 9816e764b..000000000 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/JarvaldDialogue.java +++ /dev/null @@ -1,251 +0,0 @@ -package content.region.fremennik.rellekka.dialogue; - -import core.game.component.Component; -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import kotlin.Unit; -import core.game.world.GameWorld; -import core.game.world.map.Location; - -import static core.api.ContentAPIKt.*; - -/** - * Handles the jarvald dialogue. - * @author Vexia - */ -public final class JarvaldDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code JarvaldDialogue} {@code Object}. - */ - public JarvaldDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code JarvaldDialogue} {@code Object}. - * @param player the player. - */ - public JarvaldDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new JarvaldDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (args.length > 1) { - handleTravelStage(); - return true; - } - if (npc.getId() == 2438) { - npc("Ah, you live yet, outerlander!"); - stage = 37; - return true; - } - npc("What do you want from me outerlander?", "It is our policy not to associate with those not of our", "tribe."); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("Where is your chieftain?", "What Jarvald is doing.", "Nothing"); - stage++; - break; - case 1: - switch (buttonId) { - case 1: - player("Where is your chieftain?", "I find it highly discriminatory to refuse to talk to", "someone on the grounds that they are not part of your", "tribe."); - stage = 10; - break; - case 2: - player("So what are you doing here?"); - stage = 20; - break; - case 3: - end(); - break; - } - break; - case 10: - npc("I don't rightly understand your speech outerlander, but", "my loyality is with Chieftain Brundt."); - stage++; - break; - case 11: - npc("He resides in our longhall; it is the large building over", "there, you should speak to him for he speaks for us all."); - stage++; - break; - case 12: - end(); - break; - case 20: - handleTravelStage(); - break; - case 21: - player("Hey, scary barbarian type guy, think I can join you on", "this expedition?"); - stage++; - break; - case 22: - npc("An outerlander join us on a honoured hunt???"); - stage++; - break; - case 23: - npc("Well.....", "I gues...", "I might be able to allow you to join us, although it is a", "breach of many of our customs..."); - stage++; - break; - case 24: - player("Oh, pleeeeeeease?", "I really LOVE killing stuff!"); - stage++; - break; - case 25: - npc("Well...", "I remain unconvinced that it would be wise to allow an", "outerlander to join us in such dangerous battle, but", "your ethusiasm seems genuine enough..."); - stage++; - break; - case 26: - npc("I will allow you to escort us, but you must pay me a", "sum of money first."); - stage++; - break; - case 27: - player("What?", "That's outrageous, why charge me money?", "And, uh, how much does it cost me?"); - stage++; - break; - case 28: - npc("Ah, the outerlander have stolen from my people for", "many years, in this way you can help my community", "with a small amount of money..."); - stage++; - break; - case 29: - npc("Let us say...", "1000 coins.", "Payable in advance, of course."); - stage++; - break; - case 30: - npc("For this I will take you to Waterbirth Island on my", "boat, and will bring you back here when you have had", "your fill of the hunt.", "Assuming you are still alive to wish to leave, of course."); - stage++; - break; - case 31: - setVarp(player, 520, 1 << 13, true); - end(); - break; - case 32: - options("YES", "NO"); - stage++; - break; - case 33: - switch (buttonId) { - case 1: - if (!player.getInventory().contains(995, 1000)) { - player("Sorry, I don't have enough coins."); - stage = 35; - break; - } - if (player.getInventory().remove(new Item(995, 1000))) { - sail(player, true); - } - end(); - break; - case 2: - player("No, actually I have some stuff to do here first."); - stage = 34; - break; - } - break; - case 34: - npc("As you wish.", "Come and see me when your bloodlust needs sating."); - stage++; - break; - case 35: - end(); - break; - case 36: - switch (buttonId) { - case 1: - npc("Then let us away;", "There will be death to bring here another day!"); - stage = 39; - break; - case 2: - end(); - break; - } - break; - case 37: - npc("Have you had your fill of the hunt and wish to return,", "or are you still feeling the joy of the cull?"); - stage++; - break; - case 38: - interpreter.sendOptions("Leave island?", "YES", "NO"); - stage = 36; - break; - case 39: - end(); - sail(player, false); - break; - } - return true; - } - - /** - * Sails the player to and from waterbirth. - * @param player the player. - */ - public void sail(final Player player, final boolean to) { - player.lock(); - player.getInterfaceManager().open(new Component(224)); - player.logoutListeners.put("jarvald", player1 -> { - player.setLocation(to ? Location.create(2544, 3759, 0) : Location.create(2620, 3685, 0)); - return Unit.INSTANCE; - }); - GameWorld.getPulser().submit(new Pulse(1, player) { - int count; - - @Override - public boolean pulse() { - switch (++count) { - case 5: - player.unlock(); - player.getInterfaceManager().close(); - player.getProperties().setTeleportLocation(to ? Location.create(2544, 3759, 0) : Location.create(2620, 3685, 0)); - player.getDialogueInterpreter().close(); - player.getDialogueInterpreter().sendDialogue("The ship arrives at " + (to ? "Waterbirth Island" : "Rellekka") + "."); - player.logoutListeners.remove("jarvald"); - return true; - } - return false; - } - - }); - } - - /** - * Handles the travel stage. - */ - private void handleTravelStage() { - if (npc.getId() == 2438) { - interpreter.sendOptions("Leave island?", "YES", "NO"); - stage = 36; - return; - } - if (getVarp(player, 520) == 0) { - npc("This should not concern you, outerlander.", "I am awaiting other Fremenniks to join me on an", "expedition to Waterbirth Island."); - stage = 21; - } else { - npc("So do you have the 1000 coins for my service, and are", "you ready to leave?"); - stage = 32; - } - } - - @Override - public int[] getIds() { - return new int[] { 2435, 2436, 2437, 2438 }; - } - -} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/JarvaldDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/JarvaldDialogue.kt new file mode 100644 index 000000000..b4dfcbdf2 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/JarvaldDialogue.kt @@ -0,0 +1,205 @@ +package content.region.fremennik.rellekka.dialogue + +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import content.data.Quests + + +/** + * @author Player Name + */ + +@Initializable +class JarvaldDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + val travelOption = args.size > 1 + val fremname = player.getAttribute("fremennikname","lebron james") + if (npc.id == NPCs.JARVALD_2438) { + // We're on Waterbirth Island + if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + if (travelOption) { + npc("So what say you, stay here for the hunt,","or return home to sweet Rellekka to feast","and drink with your tribe?").also { stage = 201 } + } else { + npc("Ah, ${fremname}! Such glorious battle","makes you feel glad of life, does it not?").also { stage = 220 } + } + } else { + if (travelOption) { + npc("Have you had your fill of the hunt and wish to return,", "or are you still feeling the joy of the cull?").also { stage = 201 } + } else { + npc("Ah, you live yet, outerlander!").also { stage = 200 } + } + } + } else { + // We're in Rellekka + if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + if (travelOption) { + npc("Of course, ${fremname}! Your presence is more than welcome","on this cull! You wish to leave now?").also { stage = 131 } + } else { + npc("Greetings, ${fremname}!").also { stage = if (fremname == "Jarvald") 100 else 102 } + } + } else { + if (travelOption) { + npc("So do you have the 1,000 coins for my service, and are", "you ready to leave?").also { stage = 41 } + } else if (isQuestInProgress(player, Quests.THE_FREMENNIK_TRIALS, 1, 100)) { + player("Hi, I don't suppose you are a member of", "the council of elders are you?").also { stage = 0 } + } else { + npc("What do you want from me outerlander?", "It is our policy not to associate with those not of our", "tribe.").also { stage = 3 } + } + } + } + return true + } + + fun sail(to: Boolean) { + lock(player, 5) + closeAllInterfaces(player) + openInterface(player, 224) + queueScript(player, 5, QueueStrength.SOFT) { + closeInterface(player) + unlock(player) + if (to) { + player.teleport(Location.create(2544, 3759, 0)) + sendDialogue("The ship arrives at Waterbirth Island.") + } else { + player.teleport(Location.create(2620, 3685, 0)) + sendDialogue("The ship arrives at Rellekka.") + } + return@queueScript stopExecuting(player) + } + } + + override fun handle(interfaceId: Int, buttonID: Int): Boolean { + val fremname = player.getAttribute("fremennikname","lebron james") + when (stage) { + 0 -> npc("No, outerlander, I have never had the honour to be asked.").also { stage++ } + 1 -> npc("I am but a lowly warrior, fighting for my people","wherever threats may appear against us...").also { stage++ } + 2 -> npc("So what do you want from me, outerlander?").also { stage++ } + 3 -> options("Where is your chieftain?", "What Jarvald is doing.", "Nothing").also { stage++ } + 4 -> when (buttonID) { + 1 -> player("Where is your chieftain?", "I find it highly discriminatory to refuse to talk to", "someone on the grounds that they are not part of your", "tribe.").also { stage++ } + 2 -> player("So what are you doing here?").also { stage = if (getVarp(player, 520) == 0) 10 else 40 } + 3 -> player("Actually, I don't think I have","anything to speak to you about...").also { stage = END_DIALOGUE } + } + 5 -> npc("I don't rightly understand your speech outerlander, but", "my loyality is with Chieftain Brundt.").also { stage++ } + 6 -> npc("He resides in our longhall; it is the large building over", "there, you should speak to him for he speaks for us all.").also { stage++ } + 7 -> end().also { stage = END_DIALOGUE } + 10 -> npc("This should not concern you, outerlander.", "I am awaiting other Fremenniks to join me on an", "expedition to Waterbirth Island.").also { stage++ } + 11 -> options("Waterbirth Island?", "Can I come?", "Nice hat!", "Ok, 'bye.").also { stage++ } + 12 -> when (buttonID) { + 1 -> npc("It is a small crescent-shaped island","just north-west of here, outerlander.").also { stage++ } + 2 -> player("Can I come?").also { stage = 20 } + 3 -> player("Hey, I have to say, that's a fine looking","hat you are wearing there.").also { stage = 30 } + 4 -> player("Wow, you Fremenniks sure know how to party.","Well, see ya around.").also { stage = END_DIALOGUE } + } + 13 -> npc("We have many legends about it,","such as the tale of the broken sky,","and the day of the green seas.").also { stage++ } + 14 -> npc("The reason I am travelling there is more serious","Fremennik business, however. I doubt an outerlander","would be interested.").also { stage++ } + 15 -> options("Can I come?", "Nice hat!", "Ok, 'bye.").also { stage++ } + 16 -> when (buttonID) { + 1 -> player("Can I come?").also { stage = 20 } + 2 -> player("Hey, I have to say, that's a fine looking","hat you are wearing there.").also { stage = 30 } + 3 -> player("Wow, you Fremenniks sure know how to party.","Well, see ya around.").also { stage = END_DIALOGUE } + } + 20 -> npc("An outerlander join us on an honoured hunt???").also { stage++ } + 21 -> npc("Well....", "I guess...", "I might be able to allow you to join us, although it is a", "breach of many of our customs...").also { stage++ } + 22 -> player("Oh, pleeeeease?", "I really LOVE killing stuff!").also { stage++ } + 23 -> npc("Well...", "I remain unconvinced that it would be wise to allow an", "outerlander to join us in such dangerous battle, but", "your ethusiasm seems genuine enough...").also { stage++ } + 24 -> npc("I will allow you to escort us, but you must pay me a", "sum of money first.").also { stage++ } + 25 -> player("What?", "That's outrageous, why charge me money?", "And, uh, how much does it cost me?").also { stage++ } + 26 -> npc("Ah, the outerlanders have stolen from my people for", "many years, in this way you can help my community", "with a small amount of money...").also { stage++ } + 27 -> npc("Let us say...", "1,000 coins.", "Payable in advance, of course.").also { stage++ } + 28 -> npc("For this I will take you to Waterbirth Island on my", "boat, and will bring you back here when you have had", "your fill of the hunt.", "Assuming you are still alive to wish to leave, of course.").also { setVarp(player, 520, 1 shl 13, true) }.also{ stage = END_DIALOGUE } + 29 -> npc("Wow, you Fremenniks sure know how to party.","Well, see ya around.").also { stage = END_DIALOGUE } + 30 -> npc("It is actually a helm, outerlander,","but the sentiment is appreciated nonetheless.").also { stage = END_DIALOGUE } + 40 -> npc("So do you have the 1,000 coins for my service, and are", "you ready to leave?").also { stage++ } + 41 -> options("YES", "NO").also { stage++ } + 42 -> when (buttonID) { + 1 -> if (player.inventory.contains(Items.COINS_995, 1000)) { + if (player.inventory.remove(Item(Items.COINS_995, 1000))) { + sail(true) + } + } else { + player("Sorry, I don't have enough coins.").also { stage = END_DIALOGUE } + } + 2 -> player("No, actually I have some stuff to do here first.").also { stage++ } + } + 43 -> npc("As you wish.", "Come and see me when your bloodlust needs sating.").also { stage = END_DIALOGUE } + 100 -> npc("Ah, and what a glorious name that is! Worthy of only the finest warriors!").also { stage++ } + 101 -> player("Heh. Yup, you're right there.").also { stage++ } + + 102 -> npc("So what brings you back to fair Rellekka?","It has been too long since you have drunk in the longhall","with us and sang of your battles!").also { stage++ } + 103 -> options("What Jarvald is doing.", "Nothing").also { stage++ } + 104 -> when (buttonID) { + 1 -> player("So what are you doing here?").also { stage = if (getVarp(player, 520) and (1 shl 13) == 0) 105 else 130 } + 2 -> player("Actually, I don't think I have","anything to speak to you about...").also { stage = END_DIALOGUE } + } + 105 -> npc("You have not heard, ${fremname}?","I am leading an expedition to Waterbirth Island!").also { stage++ } + 106 -> options("Waterbirth Island?", "Can I come?", "Nice hat!", "Ok, 'bye.").also { stage++ } + 107 -> when (buttonID) { + 1 -> npc("You have not ever travelled to Waterbirth Island, ${fremname}?","I am surprised, it is a place of outstanding natural beauty.").also { stage++ } + 2 -> player("Can I come?").also { stage = 130 } + 3 -> player("Hey, I have to say, that's a fine looking","hat you are wearing there.").also { stage = 140 } + 4 -> player("Wow, you Fremenniks sure know how to party. Well, see ya around.").also { stage = END_DIALOGUE } + } + 108 -> npc("Or at least it used to be! But things have now changed...").also { stage++ } + 109 -> player("Changed? How do you mean, changed?").also { stage++ } + 110 -> npc("It seems as though the sea-beasts known to us as","the dagger-mouths have begun their hatching once again...","And there may be others of their ilk there too.").also { stage++ } + 111 -> player("Dagger-mouths?").also { stage++ } + 112 -> npc("Aye, the dagger-mouths! The vile creatures lived","near here once, but we had thought them all driven back","to the ocean depths many moons past.").also { stage++ } + 113 -> npc("I can only imagine a dagger-mouth queen has nested","somewhere nearby and spawned her foul brood","under the sea once more, and some of them have","migrated to fair Waterbirth Island.").also { stage++ } + 114 -> player("So you're scared they might attack Rellekka?").also { stage++ } + 115 -> npc("Scared? Ha ha ha!").also { stage++ } + 116 -> npc("You wound us with your questioning, ${fremname}!").also { stage++ } + 117 -> npc("We are glad the dagger-mouths have returned to","these shores, for it means we will get the chance","to hunt them once again as our ancestors did!").also { stage++ } + 118 -> npc("When treated in the correct manner, the creatures'","remains can be used to make fine battleworthy armour!").also { setVarp(player, 520, 1 shl 13, true) }.also{ stage++ } + 119 -> options("Can I come?", "Nice hat!", "Ok, 'bye.").also { stage++ } + 120 -> when (buttonID) { + 1 -> player("Can I come?").also { stage = 130 } + 2 -> player("Hey, I have to say, that's a fine looking","hat you are wearing there.").also { stage = 140 } + 3 -> player("Wow, you Fremenniks sure know how to party. Well, see ya around.").also { stage = END_DIALOGUE } + } + 130 -> npc("Of course, ${fremname}! Your presence is more than welcome","on this cull! You wish to leave now?").also { stage++ } + 131 -> options("YES", "NO").also { stage++ } + 132 -> when (buttonID) { + 1 -> sail(true) + 2 -> player("No, actually I have some stuff to do here first.").also { stage = 43 } + } + 140 -> npc("Aye, that it is ${fremname}!","Skulgrimen fashioned it for me from the carcass of one","of the monsters on Waterbirth Island after our last hunt!").also { stage++ } + 141 -> npc("I hope to kill enough creatures to fashion some fine","armour as well when next we leave!").also { stage++ } + 142 -> options("Waterbirth Island?", "Can I come?", "Ok, 'bye.").also { stage++ } + 143 -> when (buttonID) { + 1 -> npc("You have not ever travelled to Waterbirth Island, ${fremname}?","I am surprised, it is a place of outstanding natural beauty.").also { stage = 108 } + 2 -> player("Can I come?").also { stage = 130 } + 3 -> player("Wow, you Fremenniks sure know how to party.","Well, see ya around.").also { stage = END_DIALOGUE } + } + 200 -> npc("Have you had your fill of the hunt and wish to return,", "or are you still feeling the joy of the cull?").also { stage++ } + 201 -> options("I wish to return to Rellekka.", "I want to stay here.").also { stage++ } + 202 -> when (buttonID) { + 1 -> player("I wish to return to Rellekka.").also { stage++ } + 2 -> player("I want to stay here.").also { stage = 210 } + } + 203 -> npc("Then let us away;", "There will be death to bring here another day!").also{ sail(false) } + 210 -> npc("Ha Ha Ha! A true huntsman at heart!").also { stage++ } + 211 -> npc("I myself have killed over a hundred of the dagger-","mouths, and did not think it too many!").also { stage = END_DIALOGUE } + 220 -> npc("So what say you, stay here for the hunt,","or return home to sweet Rellekka to feast","and drink with your tribe?").also { stage = 201 } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return JarvaldDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.JARVALD_2435, NPCs.JARVALD_2436, NPCs.JARVALD_2437, NPCs.JARVALD_2438) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/LonghallBouncerDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/LonghallBouncerDialogue.kt index 6e7db868e..296b0a7fc 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/LonghallBouncerDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/LonghallBouncerDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,7 +19,7 @@ class LonghallBouncerDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { npcl(FacialExpression.ANNOYED, "Hey, outerlander. You can't go through there. Talent only, backstage.").also { stage = END_DIALOGUE } } else{ npcl(FacialExpression.ANNOYED, "You can't go through there. Talent only, backstage.").also { stage = 0 } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java b/Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java index 766227b3a..b998fa8af 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java @@ -7,6 +7,7 @@ import content.region.fremennik.rellekka.handlers.RellekkaDestination; import content.region.fremennik.rellekka.handlers.RellekkaUtils; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles the maria gunnars dialogue. @@ -65,7 +66,7 @@ public class MariaGunnarsDialogue extends DialoguePlugin { break; case 3: end(); - if (!hasRequirement(player, "Fremennik Trials")) + if (!hasRequirement(player, Quests.THE_FREMENNIK_TRIALS)) break; if (npc.getId() == 5508) { RellekkaUtils.sail(player, RellekkaDestination.RELLEKKA_TO_NEITIZNOT); diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/ReesoDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/ReesoDialogue.kt index b1ae7e78d..9636f23ae 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/ReesoDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/ReesoDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,7 +19,7 @@ class ReesoDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { npcl(FacialExpression.ANNOYED, "Please do not disturb me, outerlander. I have much to do.").also { stage = END_DIALOGUE } } else { npcl(FacialExpression.STRUGGLE, "Sorry, ${player.getAttribute("fremennikname","fremmyname")}, I must get on with my work.").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/TalkToChiefDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/TalkToChiefDialogue.kt index ecb633408..6c371733a 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/TalkToChiefDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/TalkToChiefDialogue.kt @@ -8,10 +8,11 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz - * There is no available dialogue for after Fremennik Trials, + * There is no available dialogue for after The Fremennik Trials, * only after Hero's Welcome which isn't in this revision. */ @@ -20,7 +21,7 @@ class TalkToChiefDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { npcl(FacialExpression.ANNOYED, "I cannot speak to you outerlander! Talk to Brundt, the Chieftain!").also { stage = END_DIALOGUE } } else { player(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/VolfOlasfsonDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/VolfOlasfsonDialogue.kt index 59e104ce2..315312236 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/VolfOlasfsonDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/VolfOlasfsonDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests /** * @author qmqz @@ -18,11 +19,11 @@ class VolfOlasfsonDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { npc(FacialExpression.ANNOYED, "Sorry, outlander, but I have things to be doing.").also { stage = END_DIALOGUE } - } else if (isQuestComplete(player, "Fremennik Trials") && !isQuestComplete(player, "Olaf's Quest")) { + } else if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) && !isQuestComplete(player, Quests.OLAFS_QUEST)) { npc(FacialExpression.FRIENDLY, "Hello there. Enjoying the view?").also { stage = 0 } - } else if (isQuestComplete(player, "Fremennik Trials") && isQuestComplete(player, "Olaf's Quest")) { + } else if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) && isQuestComplete(player, Quests.OLAFS_QUEST)) { npcl(FacialExpression.ASKING, "Hello again, friend! Does my father send any word... or treasures like before?").also { stage = 10 } } return true diff --git a/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt index 33fc0d94f..cac9e67e8 100644 --- a/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt +++ b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt @@ -5,6 +5,7 @@ import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.world.map.Location import org.rs09.consts.NPCs +import content.data.Quests /** * File to be used for anything Rellekka related. @@ -48,7 +49,7 @@ class RellekkaListeners : InteractionListener { } on(NPCs.MARIA_GUNNARS_5508, IntType.NPC, "ferry-neitiznot"){ player, _ -> - if (!hasRequirement(player, "Fremennik Trials")) + if (!hasRequirement(player, Quests.THE_FREMENNIK_TRIALS)) return@on true RellekkaUtils.sail(player, RellekkaDestination.RELLEKKA_TO_NEITIZNOT) playJingle(player, 171) @@ -62,7 +63,7 @@ class RellekkaListeners : InteractionListener { } on(NPCs.MORD_GUNNARS_5481, IntType.NPC, "ferry-jatizso"){ player, node -> - if (!hasRequirement(player, "Fremennik Trials")) + if (!hasRequirement(player, Quests.THE_FREMENNIK_TRIALS)) return@on true RellekkaUtils.sail(player, RellekkaDestination.RELLEKKA_TO_JATIZSO) playJingle(player, 171) diff --git a/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaZone.java b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaZone.java index 75d7c1e34..4698885bb 100644 --- a/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaZone.java +++ b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaZone.java @@ -1,6 +1,5 @@ package content.region.fremennik.rellekka.handlers; -import content.region.fremennik.rellekka.dialogue.JarvaldDialogue; import content.region.fremennik.rellekka.dialogue.MariaGunnarsDialogue; import core.cache.def.impl.SceneryDefinition; import core.game.system.task.Pulse; @@ -41,7 +40,6 @@ public final class RellekkaZone extends MapZone implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); - ClassScanner.definePlugin(new JarvaldDialogue()); ClassScanner.definePlugins(new RellekaOptionHandler(), new MariaGunnarsDialogue()); ClassScanner.definePlugin(new OptionHandler() { diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/AskeladdenDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/AskeladdenDialogue.kt index 387ccc2e7..526732374 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/AskeladdenDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/AskeladdenDialogue.kt @@ -6,6 +6,7 @@ import core.game.dialogue.FacialExpression import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable //Disabled because the quest isn't done yet. @@ -27,12 +28,12 @@ class AskeladdenDialogue(player: Player? = null) : DialoguePlugin(player) { stage = 35 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(FacialExpression.HAPPY,"Hello again Askeladden.") stage = 40 return true } - else if (it.questRepository.getStage("Fremennik Trials") > 0) { + else if (it.questRepository.getStage(Quests.THE_FREMENNIK_TRIALS) > 0) { player("Hello there.") stage = 0 return true diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ChieftanBrundtDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ChieftanBrundtDialogue.kt index cd70b295b..ac6cc0c24 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ChieftanBrundtDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ChieftanBrundtDialogue.kt @@ -10,6 +10,7 @@ import core.tools.END_DIALOGUE import kotlin.random.Random import org.rs09.consts.* +import content.data.Quests @Initializable class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ @@ -42,33 +43,33 @@ class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ stage = 500 return true } - else if(player.getAttribute("fremtrials:votes",0) >= 7){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player.getAttribute("fremtrials:votes",0) >= 7) { npcl(FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") stage = 545 return true } - else if(player.getAttribute("fremtrials:votes",0) in 3..6){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player.getAttribute("fremtrials:votes",0) in 3..6) { npcl(FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") stage = 540 return true } - else if(player.getAttribute("fremtrials:votes",0) == 1){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player.getAttribute("fremtrials:votes",0) == 1) { npcl(FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") stage = 535 return true } - else if(player.getAttribute("fremtrials:votes",-1) == 0){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player.getAttribute("fremtrials:votes",-1) == 0) { npcl(FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") stage = 530 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ npcl(FacialExpression.HAPPY,"Hello again, $gender $fName. I hope your travels have brought you wealth and joy! What compels you to visit me on this day?") stage = 600 return true } - else if(player?.questRepository?.getStage("Fremennik Trials")!! == 0) { - npc("Greetings outlander!") + else if(player?.questRepository?.getStage(Quests.THE_FREMENNIK_TRIALS)!! == 0) { + npc("Greetings outerlander!") stage = 0 } return true @@ -99,7 +100,7 @@ class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ //Do you have any quests? - 300 -> {npc("Quests, you say outlander? Well, I would not call it a","quest as such, but if you are brave of heart and strong","of body, perhaps..."); stage++} + 300 -> {npc("Quests, you say outerlander? Well, I would not call it a","quest as such, but if you are brave of heart and strong","of body, perhaps..."); stage++} 301 -> {npc("No, you would not be interested. Forget I said","anything, outerlander."); stage++ } 302 -> {options("Yes, I am interested.","No, I'm not interested."); stage++ } 303 -> when(buttonId){ @@ -124,7 +125,7 @@ class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ //I think I would enjoy the challenge of becoming an honorary fremennik 320 -> {npc("As I say outerlander, you must find and speak to the","twelve members of the council of elders, and see what","tasks they might set you.");stage++} - 321 -> {npc("If you can gain the support of seven of the twelve, then","you will be accepted as one of us without question.");stage = 1000;player?.questRepository?.getQuest("Fremennik Trials")?.start(player)} + 321 -> {npc("If you can gain the support of seven of the twelve, then","you will be accepted as one of us without question.");stage = 1000;player?.questRepository?.getQuest(Quests.THE_FREMENNIK_TRIALS)?.start(player)} //That sounds too complicated for me. 322 -> {npc("Well, that's what I expect from an outerlander.");stage = 1000} @@ -172,21 +173,15 @@ class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ 545 -> playerl(FacialExpression.HAPPY,"I have seven members of the council prepared to vote in my favour now!").also { stage++ } 546 -> npcl(FacialExpression.HAPPY,"I know outerlander, for I have been closely monitoring your progress so far!").also {stage++} 547 -> npcl(FacialExpression.HAPPY,"Then let us put the formality aside, and let me personally welcome you into the Fremennik! May you bring us honour!").also { - if(player.inventory.freeSlots() >= 10){ - println(GenerateFremennikName()) player.setAttribute("/save:fremennikname", GenerateFremennikName()) stage = 560 - } - else stage = 548 - } - 548 -> sendDialogue("You require 10 free spaces in your backpack to claim your reward.").also { stage = 1000 } - 550 -> npcl(FacialExpression.HAPPY,"If you need any help with your trials, I suggest you speak to Askeladden. He is currently doing his own trials of manhood to become a true Fremennik.") + 550 -> npcl(FacialExpression.HAPPY,"If you need any help with your trials, I suggest you speak to Askeladden. He is currently doing his own trials of manhood to become a true Fremennik.").also { stage = END_DIALOGUE } 560 -> npcl(FacialExpression.HAPPY,"From this day onward, you are outerlander no more! In honour of your acceptance into the Fremennik, you gain a new name: ${player.getAttribute("fremennikname","how did u break this")}.").also { cleanupAttributes(player) - player.questRepository.getQuest("Fremennik Trials").finish(player) + player.questRepository.getQuest(Quests.THE_FREMENNIK_TRIALS).finish(player) stage = 1000 } @@ -239,7 +234,7 @@ class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ 642 -> npcl(FacialExpression.HAPPY,"As I say, my knowledge outside of this town is rather limited, and I know no more than this.").also { stage++ } 643 -> npcl(FacialExpression.HAPPY,"Was there anything else you wished to hear tell of?").also { stage = 616 } - 645 -> npcl(FacialExpression.HAPPY,"Ah, now that is something I know a great deal about. Believe it or not, all outerlanders were once orginally Fremenniks.").also { stage++ } + 645 -> npcl(FacialExpression.HAPPY,"Ah, now that is something I know a great deal about. Believe it or not, all outerlanders were once originally Fremenniks.").also { stage++ } 646 -> npcl(FacialExpression.HAPPY,"When first man came to these lands all were Fremenniks, and followed our ways. We lived a happy life, and never settled in one place for long.").also { stage++ } 647 -> npcl(FacialExpression.HAPPY,"We travelled by boat along the coastlines, never taking more from the land than could be regrown by the same time in the following year.").also { stage++ } 648 -> npcl(FacialExpression.HAPPY,"However, many moons past, some of our tribesmen were weary of constantly travelling the lands, and decided to build themselves permanent homes.").also { stage++ } @@ -264,7 +259,7 @@ class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ 1201 -> npcl(FacialExpression.HALF_THINKING, "I suppose I can grant you one temporarily, provided you meet certain requirements.").also { stage++ } 1202 -> npcl(FacialExpression.HAPPY, "Very well, $fName! Let me look you over and see if you're strong enough for this boon.").also { stage++ } 1203 -> { - if (!hasRequirement(player, "Lunar Diplomacy") || player!!.hasItem(Item(Items.SEAL_OF_PASSAGE_9083))) + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY) || player!!.hasItem(Item(Items.SEAL_OF_PASSAGE_9083))) npcl(FacialExpression.HALF_GUILTY, "I'm sorry, $fName. You just don't have the experience needed for this gift. Please come back when you've learned more.").also { stage = END_DIALOGUE } else npcl(FacialExpression.HAPPY, "Yes, yes... I see it. You've got the strength and wisdom for this gift. Please, take this. For now.").also { stage++ } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/CouncilWorkerFTDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/CouncilWorkerFTDialogue.kt index f3cb507fd..f9b6223af 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/CouncilWorkerFTDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/CouncilWorkerFTDialogue.kt @@ -5,6 +5,7 @@ import org.rs09.consts.Items import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests const val COUNCIL_WORKER = 1287 @@ -32,7 +33,7 @@ class CouncilWorkerFTDialogue(val questStage: Int, var isBeerInteraction: Boolea else if(questStage in 1..99){ when(stage){ START_DIALOGUE -> - if(getQuestStage(player!!, "Fremennik Trials") > 0) { + if(getQuestStage(player!!, Quests.THE_FREMENNIK_TRIALS) > 0) { player("I know this is an odd question, but are you","a member of the elder council?"); stage = 1 } else { end() diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FishermanDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FishermanDialogue.kt index 30f9bb225..c0c3f4f3f 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FishermanDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FishermanDialogue.kt @@ -8,6 +8,7 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable import core.tools.END_DIALOGUE +import content.data.Quests @Initializable class FishermanDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -37,9 +38,9 @@ class FishermanDialogue(player: Player? = null) : DialoguePlugin(player) { playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find an exotic and extremely odd fish, do you?") stage = 1 return true - } else if (isQuestComplete(player, "Fremennik Trials")){ + } else if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)){ player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 100 } - } else if (!isQuestComplete(player, "Fremennik Trials")) { + } else if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 200 } } return true diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikTrials.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikTrials.kt index d50e8f42f..ec693af62 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikTrials.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikTrials.kt @@ -1,16 +1,14 @@ package core.game.content.quest.fremtrials import core.game.node.entity.player.Player -import core.api.* import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills -import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items -import content.minigame.allfiredup.AFUBeacon +import content.data.Quests @Initializable -class FremennikTrials : Quest("Fremennik Trials",64,63,3,347,0,1,10){ +class FremennikTrials : Quest(Quests.THE_FREMENNIK_TRIALS,64,63,3,347,0,1,10){ class SkillRequirement(val skill: Int?, val level: Int?) @@ -19,7 +17,7 @@ class FremennikTrials : Quest("Fremennik Trials",64,63,3,347,0,1,10){ override fun drawJournal(player: Player?, stage: Int) { super.drawJournal(player, stage) var line = 11 - val started = player?.questRepository?.getStage("Fremennik Trials")!! > 0 + val started = player?.questRepository?.getStage(Quests.THE_FREMENNIK_TRIALS)!! > 0 if(!started){ line(player,"Requirements to complete quest:",line++) diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/LalliDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/LalliDialogue.kt index e4f2c9f65..a6d1f37de 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/LalliDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/LalliDialogue.kt @@ -9,13 +9,14 @@ import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.item.Item import org.rs09.consts.Items +import content.data.Quests @Initializable class LalliDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { player?.let { println(it.getAttribute("lalliEatStew", false)) - if (it.questRepository.isComplete("Fremennik Trials")){ + if (it.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(FacialExpression.NEUTRAL,"Hello there.") stage = 100 return true @@ -45,12 +46,12 @@ class LalliDialogue(player: Player? = null) : DialoguePlugin(player){ stage = 50 return true } - if(player.questRepository.isComplete("Fremennik Trials")){ + if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(FacialExpression.HAPPY,"Hello there.") stage = 100 return true } - if (it.questRepository.getStage("Fremennik Trials") > 0) { + if (it.questRepository.getStage(Quests.THE_FREMENNIK_TRIALS) > 0) { player("Hello there.").also { stage = 0; return true } } } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ManniDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ManniDialogue.kt index 689fc14d1..425c617cf 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ManniDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ManniDialogue.kt @@ -1,6 +1,7 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.node.entity.impl.Animator import core.game.node.entity.npc.NPC @@ -12,13 +13,14 @@ import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable class ManniDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ var curNPC: NPC? = NPC(0,Location(0,0,0)) override fun open(vararg args: Any?): Boolean { curNPC = args[0] as? NPC - if(player?.questRepository?.getStage("Fremennik Trials")!! > 0){ + if(player?.questRepository?.getStage(Quests.THE_FREMENNIK_TRIALS)!! > 0){ if(player?.inventory?.contains(3707, 1) == true){ playerl(core.game.dialogue.FacialExpression.HAPPY,"Hey. I got your cocktail for you.") stage = 170 @@ -55,12 +57,12 @@ class ManniDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( return true } } - else if(player?.getAttribute("fremtrials:manni-vote",false) == true){ - npc("e have my vote!") + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player?.getAttribute("fremtrials:manni-vote",false) == true) { + npc("Ye have my vote!") stage = 1000 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(core.game.dialogue.FacialExpression.HAPPY,"Howdy!") stage = 190 return true diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/OlafTheBard.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/OlafTheBard.kt index 8af316688..baffb1143 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/OlafTheBard.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/OlafTheBard.kt @@ -5,6 +5,7 @@ import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable +import content.data.Quests @Initializable class OlafTheBard(player: Player? = null) : DialoguePlugin(player){ @@ -43,12 +44,12 @@ class OlafTheBard(player: Player? = null) : DialoguePlugin(player){ stage = 1000 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ npcl(FacialExpression.HAPPY,"Hello again to you, ${player.getAttribute("fremennikname","schlonko")}. Us bards should stick together, what can I do for you?") stage = 98 return true } - else if(player.questRepository.hasStarted("Fremennik Trials")){ + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0){ npc("Hello? Yes? You want something outerlander?") stage = 0 return true diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PeerTheSeerDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PeerTheSeerDialogue.kt index 88ea2456b..c30737ab4 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PeerTheSeerDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PeerTheSeerDialogue.kt @@ -1,18 +1,20 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials -import core.api.addItem -import core.api.dumpContainer -import core.api.getQuestStage -import core.api.removeItem import core.game.node.entity.player.Player -import core.game.node.entity.player.link.diary.DiaryType import core.plugin.Initializable import core.tools.RandomFunction import core.tools.END_DIALOGUE import kotlin.random.Random +import content.data.Quests +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.link.IronmanMode +import org.rs09.consts.Items +import org.rs09.consts.NPCs @Initializable -class PeerTheSeerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class PeerTheSeerDialogue(player: Player? = null) : DialoguePlugin(player) { val predictionOne = arrayOf("one","two","three","four","five","six","seven","eight","ten") val predictionTwo = arrayOf("black","blue","brown","cyan","green","pink","purple","red","yellow") val predictionThree = arrayOf("fire giant","ghosts","giant","goblin","green dragon","hobgoblin","lesser demon","moss giant","ogre","zombie") @@ -30,232 +32,230 @@ class PeerTheSeerDialogue(player: Player? = null) : core.game.dialogue.DialogueP override fun open(vararg args: Any?): Boolean { if(player.inventory.contains(3710,1)){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Can I have a weather forecast now please?") + playerl(FacialExpression.HAPPY,"Can I have a weather forecast now please?") stage = 15 return true } else if(player.inventory.contains(3705,1)){ - playerl(core.game.dialogue.FacialExpression.ASKING,"So, about this forecast...") + playerl(FacialExpression.ASKING,"So, about this forecast...") stage = 20 return true } else if(player.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I've got an item to trade but I don't know if it's for you.") + playerl(FacialExpression.ASKING,"I've got an item to trade but I don't know if it's for you.") stage = 26 return true } else if(player.getAttribute("sigmund-steps", 0) == 10){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a brave and powerful warrior to act as a bodyguard?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a brave and powerful warrior to act as a bodyguard?") stage = 8 return true } else if(player.getAttribute("sigmund-steps", 0) == 9){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a weather forecast from the Fremennik Seer do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a weather forecast from the Fremennik Seer do you?") stage = 1 return true } - else if(player.getAttribute("PeerStarted",false) && !player.inventory.isEmpty || !player.equipment.isEmpty){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + else if(player.getAttribute("PeerStarted",false) && !(player.inventory.isEmpty && player.equipment.isEmpty)){ + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 100 return true } else if(player.getAttribute("PeerStarted",false) && player.inventory.isEmpty && player.equipment.isEmpty){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 110 return true } - else if(player.getAttribute("fremtrials:peer-vote",false)){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player.getAttribute("fremtrials:peer-vote",false)) { + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 120 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 150 return true } - else if(player.questRepository.hasStarted("Fremennik Trials")){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0){ + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 50 return true } - if (getQuestStage(player, "Fremennik Trials") == 0) { - npc(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?").also { stage = 300 } + if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) == 0) { + npc(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?").also { stage = 300 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Er.... Yes, because I AM the Fremennik Seer.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can I have a weather forecast then please?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.THINKING,"You require a divination of the weather? This is a simple matter for me, but I will require something in return from you for this small service.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I knew you were going to say that...").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Do not fret, outerlander; it is a fairly simple matter. I require a bodyguard for protection. Find someone willing to offer me this service.").also { stage++ } - 6 -> playerl(core.game.dialogue.FacialExpression.ASKING,"That's all?").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That is all.").also { + 1 -> npcl(FacialExpression.ANNOYED,"Er.... Yes, because I AM the Fremennik Seer.").also { stage++ } + 2 -> playerl(FacialExpression.ASKING,"Can I have a weather forecast then please?").also { stage++ } + 3 -> npcl(FacialExpression.THINKING,"You require a divination of the weather? This is a simple matter for me, but I will require something in return from you for this small service.").also { stage++ } + 4 -> playerl(FacialExpression.ASKING,"I knew you were going to say that...").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY,"Do not fret, outerlander; it is a fairly simple matter. I require a bodyguard for protection. Find someone willing to offer me this service.").also { stage++ } + 6 -> playerl(FacialExpression.ASKING,"That's all?").also { stage++ } + 7 -> npcl(FacialExpression.HAPPY,"That is all.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 10 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"If I did, then I would simply have asked them myself now, wouldn't I, outerlander?").also { stage = 1000 } + 10 -> npcl(FacialExpression.ANNOYED,"If I did, then I would simply have asked them myself now, wouldn't I, outerlander?").also { stage = 1000 } - 15 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I have already told you outerlander; You may have a reading from me when I have a signed contract from a warrior guaranteeing my protection.").also { stage++ } - 16 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Yeah, I know; I have one right here from Thorvald.").also { + 15 -> npcl(FacialExpression.ANNOYED,"I have already told you outerlander; You may have a reading from me when I have a signed contract from a warrior guaranteeing my protection.").also { stage++ } + 16 -> playerl(FacialExpression.HAPPY,"Yeah, I know; I have one right here from Thorvald.").also { removeItem(player,3710) addItem(player,3705) stage++ } - 17 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"You have not only persuaded one of the Fremennik to act as a servant to me, but you have enlisted the aid of mighty Thorvald himself???").also { stage++ } - 18 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You may take this forecast with my blessing outerlander. You have offered me the greatest security I can imagine.").also { stage = 1000 } + 17 -> npcl(FacialExpression.AMAZED,"You have not only persuaded one of the Fremennik to act as a servant to me, but you have enlisted the aid of mighty Thorvald himself???").also { stage++ } + 18 -> npcl(FacialExpression.HAPPY,"You may take this forecast with my blessing outerlander. You have offered me the greatest security I can imagine.").also { stage = 1000 } - 20 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Yes, outerlander?").also { stage++ } - 21 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I still don't know why you didn't just let me have one anyway in the first place. Surely it means nothing to you?").also { stage++ } - 22 -> npcl(core.game.dialogue.FacialExpression.THINKING,"That is not true, outerlander. Although I see glimpses of the future all of the time, using my powers brings the attention of the gods to me.").also { stage++ } - 23 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Some of the gods are spiteful and cruel, and I fear if I use my powers too much then I will meet with unpredictable accidents.").also { stage++ } - 24 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This is why I needed protection.").also { stage++ } - 25 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Okay... I... think I understand...").also { stage = 1000 } + 20 -> npcl(FacialExpression.THINKING,"Yes, outerlander?").also { stage++ } + 21 -> playerl(FacialExpression.ASKING,"I still don't know why you didn't just let me have one anyway in the first place. Surely it means nothing to you?").also { stage++ } + 22 -> npcl(FacialExpression.THINKING,"That is not true, outerlander. Although I see glimpses of the future all of the time, using my powers brings the attention of the gods to me.").also { stage++ } + 23 -> npcl(FacialExpression.THINKING,"Some of the gods are spiteful and cruel, and I fear if I use my powers too much then I will meet with unpredictable accidents.").also { stage++ } + 24 -> npcl(FacialExpression.HAPPY,"This is why I needed protection.").also { stage++ } + 25 -> playerl(FacialExpression.THINKING,"Okay... I... think I understand...").also { stage = 1000 } - 26 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Not me, I'm afraid.").also { stage++ } + 26 -> npcl(FacialExpression.ANNOYED,"Not me, I'm afraid.").also { stage++ } //The Seer's Trial - 50 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 51 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } - 52 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Hello. I'm looking for members of the council of elders to vote for me to become a Fremennik.").also { stage++ } - 53 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Are you now? Well that is interesting. Usually outerlanders do not concern themselves with our ways like that.").also { stage++ } - 54 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I am one of the members of the council of elders, and should you be able to prove to me that you have something to offer my clan I will vote in your favour at the next meeting.").also { stage++ } - 55 -> playerl(core.game.dialogue.FacialExpression.ASKING,"How can I prove that to you?").also { stage++ } - 56 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, I have but a simple test. This building behind me is my house. Inside I have constructed a puzzle.").also { stage++ } - 57 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As a Seer to the clan, I value intelligence very highly, so you may think of it as an intelligence test of sorts.").also { stage++ } - 58 -> playerl(core.game.dialogue.FacialExpression.THINKING,"An intelligence test? I thought barbarians were stupid!").also { stage++ } - 59 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"That is the opinion that outerlanders usually hold of my people, it is true. But that is because people often confuse knowledge with wisdom.").also { stage++ } - 60 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"My puzzle tests not what you know, but what you can work out. All members of our clan have been tested when they took their trials.").also { stage++ } - 61 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So what exactly does this puzzle consist of, then?").also { stage++ } - 62 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, firstly you must enter my house with no items, weapons or armour. Then it is a simple matter of entering through one door and leaving by the other.").also { stage++ } - 63 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I can't take anything in there with me?").also { stage++ } - 64 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That is correct outerlander. Everything you need to complete the puzzle you will find inside the building. Nothing more.").also { stage++ } - 65 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"So what say you outerlander? You think you have the wit to earn yourself my vote?").also { stage++ } + 50 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 51 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } + 52 -> playerl(FacialExpression.ASKING,"Hello. I'm looking for members of the council of elders to vote for me to become a Fremennik.").also { stage++ } + 53 -> npcl(FacialExpression.THINKING,"Are you now? Well that is interesting. Usually outerlanders do not concern themselves with our ways like that.").also { stage++ } + 54 -> npcl(FacialExpression.HAPPY,"I am one of the members of the council of elders, and should you be able to prove to me that you have something to offer my clan I will vote in your favour at the next meeting.").also { stage++ } + 55 -> playerl(FacialExpression.ASKING,"How can I prove that to you?").also { stage++ } + 56 -> npcl(FacialExpression.HAPPY,"Well, I have but a simple test. This building behind me is my house. Inside I have constructed a puzzle.").also { stage++ } + 57 -> npcl(FacialExpression.HAPPY,"As a Seer to the clan, I value intelligence very highly, so you may think of it as an intelligence test of sorts.").also { stage++ } + 58 -> playerl(FacialExpression.THINKING,"An intelligence test? I thought barbarians were stupid!").also { stage++ } + 59 -> npcl(FacialExpression.ANNOYED,"That is the opinion that outerlanders usually hold of my people, it is true. But that is because people often confuse knowledge with wisdom.").also { stage++ } + 60 -> npcl(FacialExpression.ANNOYED,"My puzzle tests not what you know, but what you can work out. All members of our clan have been tested when they took their trials.").also { stage++ } + 61 -> playerl(FacialExpression.ASKING,"So what exactly does this puzzle consist of, then?").also { stage++ } + 62 -> npcl(FacialExpression.HAPPY,"Well, firstly you must enter my house with no items, weapons or armour. Then it is a simple matter of entering through one door and leaving by the other.").also { stage++ } + 63 -> playerl(FacialExpression.ASKING,"I can't take anything in there with me?").also { stage++ } + 64 -> npcl(FacialExpression.HAPPY,"That is correct outerlander. Everything you need to complete the puzzle you will find inside the building. Nothing more.").also { stage++ } + 65 -> npcl(FacialExpression.HAPPY,"So what say you outerlander? You think you have the wit to earn yourself my vote?").also { stage++ } 66 -> options("Yes","No").also { stage++ } 67 -> when(buttonId){ - 1 ->{ playerl(core.game.dialogue.FacialExpression.HAPPY,"Yes, I accept your challenge, I have one small question, however...") + 1 ->{ playerl(FacialExpression.HAPPY,"Yes, I accept your challenge, I have one small question, however...") player?.setAttribute("/save:PeerStarted",true) player?.setAttribute("/save:PeerRiddle", Random.nextInt(0,3)) stage = 70 } - 2 ->{ playerl(core.game.dialogue.FacialExpression.HAPPY,"No, thinking about stuff isn't really my 'thing'. I'd rather go kill something. I'll find someone else to vote for me") + 2 ->{ playerl(FacialExpression.HAPPY,"No, thinking about stuff isn't really my 'thing'. I'd rather go kill something. I'll find someone else to vote for me") stage++ } } //No to challenge - 68 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As you wish, outerlander.").also { stage = 1000 } + 68 -> npcl(FacialExpression.HAPPY,"As you wish, outerlander.").also { stage = 1000 } //Yes to challenge - 70 -> npcl(core.game.dialogue.FacialExpression.ASKING,"Yes outerlander?").also { stage++ } - 71 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Well... you say I can bring nothing with me when I enter your house...").also { stage++ } - 72 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Yes outerlander??").also { stage++ } - 73 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Well...").also { stage++ } - 74 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"Yes, outerlander???").also { stage++ } - 75 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Where is the nearest bank?").also { stage++ } - 76 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah, I see your problem outerlander. The nearest bank to here is the place known to outerlanders as the Seers Village.").also { stage++ } - 77 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"It is some way South. I do however have an alternative, should you wish to take it.").also { stage++ } - 78 -> playerl(core.game.dialogue.FacialExpression.ASKING,"And what is that?").also { stage++ } - 79 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I can store all the weapons, armour and items that you have upon you directly into your bank account.").also { stage++ } - 80 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This will tax what little magic I possess however, so you will have to travel to the bank to withdraw them again.").also { stage++ } - 81 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"What say you outerlander? Do you wish me to do this for you?").also { stage++ } + 70 -> npcl(FacialExpression.ASKING,"Yes outerlander?").also { stage++ } + 71 -> playerl(FacialExpression.THINKING,"Well... you say I can bring nothing with me when I enter your house...").also { stage++ } + 72 -> npcl(FacialExpression.ANNOYED,"Yes outerlander??").also { stage++ } + 73 -> playerl(FacialExpression.THINKING,"Well...").also { stage++ } + 74 -> npcl(FacialExpression.ANGRY,"Yes, outerlander???").also { stage++ } + 75 -> playerl(FacialExpression.ASKING,"Where is the nearest bank?").also { stage++ } + 76 -> npcl(FacialExpression.HAPPY,"Ah, I see your problem outerlander. The nearest bank to here is the place known to outerlanders as the Seers Village.").also { stage++ } + 77 -> npcl(FacialExpression.HAPPY,"It is some way South. I do however have an alternative, should you wish to take it.").also { stage++ } + 78 -> playerl(FacialExpression.ASKING,"And what is that?").also { stage++ } + 79 -> npcl(FacialExpression.HAPPY,"I can store all the weapons, armour and items that you have upon you directly into your bank account.").also { stage++ } + 80 -> npcl(FacialExpression.HAPPY,"This will tax what little magic I possess however, so you will have to travel to the bank to withdraw them again.").also { stage++ } + 81 -> npcl(FacialExpression.HAPPY,"What say you outerlander? Do you wish me to do this for you?").also { stage++ } 82 -> options("Yes","No").also { stage++ } 83 -> when(buttonId){ 1 -> { val slotAmount = player.inventory.itemCount() + player.equipment.itemCount() - if (slotAmount < player.bank.freeSlots()){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"The task is done. I wish you luck with your test, outerlander.") - dumpContainer(player,player.inventory) - dumpContainer(player,player.equipment) + if (slotAmount < player.bank.freeSlots() && slotAmount == dumpContainer(player,player.inventory) + dumpContainer(player,player.equipment)){ + npcl(FacialExpression.HAPPY,"The task is done. I wish you luck with your test, outerlander.") stage = 1000 } else { - npcl(core.game.dialogue.FacialExpression.SAD,"I am sorry outerlander, the spell is not working. I believe you may have some objects that you cannot bank with you") + npcl(FacialExpression.SAD,"I am sorry outerlander, the spell is not working. I believe you may have some objects that you cannot bank with you.") stage = 1000 } } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"No thanks. Nobody touches my stuff but me!").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"No thanks. Nobody touches my stuff but me!").also { stage++ } } //No to banking - 84 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As you wish, outerlander. You may attempt my little task when you have deposited your equipment in the bank").also { + 84 -> npcl(FacialExpression.HAPPY,"As you wish, outerlander. You may attempt my little task when you have deposited your equipment in the bank.").also { stage = 1000 } //Yes to banking but cannot bank - 90 -> npcl(core.game.dialogue.FacialExpression.SAD,"I am sorry outerlander, the spell is not working. I believe you may have some objects that you cannot bank with you").also { + 90 -> npcl(FacialExpression.SAD,"I am sorry outerlander, the spell is not working. I believe you may have some objects that you cannot bank with you.").also { stage = 1000 } //Returning after accepting with items. - 100 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 101 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } - 102 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So I can bring nothing with me when I enter your house?").also { stage++ } - 103 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That is correct outerlander, but as I say, I can use my small skill in magic to send your items directly into your bank account from here.").also { stage++ } - 104 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You will need to manually go to the bank to withdraw them again however.").also { stage++ } - 105 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Would you like me to perform this small spell upon you, outerlander?").also { stage = 82 } + 100 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 101 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } + 102 -> playerl(FacialExpression.ASKING,"So I can bring nothing with me when I enter your house?").also { stage++ } + 103 -> npcl(FacialExpression.HAPPY,"That is correct outerlander, but as I say, I can use my small skill in magic to send your items directly into your bank account from here.").also { stage++ } + 104 -> npcl(FacialExpression.HAPPY,"You will need to manually go to the bank to withdraw them again however.").also { stage++ } + 105 -> npcl(FacialExpression.HAPPY,"Would you like me to perform this small spell upon you, outerlander?").also { stage = 82 } //Returning after accepting without items. - 110 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 111 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } - 112 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So I just have to enter by one door of your house, and leave by the other?").also { stage++ } - 113 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That is correct outerlander. Be warned it is not as easy as it may at first sound...").also { stage = 1000 } + 110 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 111 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } + 112 -> playerl(FacialExpression.ASKING,"So I just have to enter by one door of your house, and leave by the other?").also { stage++ } + 113 -> npcl(FacialExpression.HAPPY,"That is correct outerlander. Be warned it is not as easy as it may at first sound...").also { stage = 1000 } //After completing the Seer's Trial. - 120 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 121 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that.").also { stage++ } - 122 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"So you will vote for me at the council?").also { stage++ } - 123 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Absolutely, outerlander. Your wisdom in passing my test marks you as worthy in my eyes.").also { stage = 1000 } + 120 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 121 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that.").also { stage++ } + 122 -> playerl(FacialExpression.HAPPY,"So you will vote for me at the council?").also { stage++ } + 123 -> npcl(FacialExpression.HAPPY,"Absolutely, outerlander. Your wisdom in passing my test marks you as worthy in my eyes.").also { stage = 1000 } - //After Fremennik Trials - 150 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 151 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that.").also { - stage = if(player.achievementDiaryManager.getDiary(DiaryType.FREMENNIK).isComplete(0)){ + //After The Fremennik Trials + 150 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 151 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that.").also { + stage = if(anyInEquipment(player, Items.FREMENNIK_SEA_BOOTS_1_14571, Items.FREMENNIK_SEA_BOOTS_2_14572, Items.FREMENNIK_SEA_BOOTS_3_14573) && !hasIronmanRestriction(player, IronmanMode.ULTIMATE)){ 200 }else 152 } - 152 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello Peer.").also { stage++ } - 153 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings to you, brother ${player.getAttribute("fremennikname","dingle")}! What brings you to see me again?").also { stage++ } + 152 -> playerl(FacialExpression.HAPPY,"Hello Peer.").also { stage++ } + 153 -> npcl(FacialExpression.HAPPY,"Greetings to you, brother ${player.getAttribute("fremennikname","dingle")}! What brings you to see me again?").also { stage++ } 154 -> options("Can you tell my future?","Nothing really.").also{stage++} 155 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I was wondering if you could give me a reading on my future...?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } + 1 -> playerl(FacialExpression.ASKING,"I was wondering if you could give me a reading on my future...?").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } } - 156 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah, you would like a prediction? I do not see that that would be so difficult... Wait a moment...").also { + 156 -> npcl(FacialExpression.HAPPY,"Ah, you would like a prediction? I do not see that that would be so difficult... Wait a moment...").also { stage++ } - 157 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here is your prediction: ${PREDICTIONS[prediction]}").also { stage = 1000 } + 157 -> npcl(FacialExpression.HAPPY,"Here is your prediction: ${PREDICTIONS[prediction]}").also { stage = 1000 } - 160 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, hello to you too!").also { stage = 1000 } + 160 -> npcl(FacialExpression.HAPPY,"Well, hello to you too!").also { stage = 1000 } 200 -> options("Deposit service","Can you tell my future?","Nothing really.").also{ stage++ } 201 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Could you deposit some things for me, please?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I was wondering if you could give me a reading on my future...?").also { stage = 156 } - 3 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } + 1 -> playerl(FacialExpression.HAPPY,"Could you deposit some things for me, please?").also { stage++ } + 2 -> playerl(FacialExpression.ASKING,"I was wondering if you could give me a reading on my future...?").also { stage = 156 } + 3 -> playerl(FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } } - 202 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Of course, ${player.getAttribute("fremennikname","dingle")}. I am always happy to aid those who have earned the right to wear Fremennik sea boots.").also { + 202 -> npcl(FacialExpression.HAPPY,"Of course, ${player.getAttribute("fremennikname","dingle")}. I am always happy to aid those who have earned the right to wear Fremennik sea boots.").also { player.bank.openDepositBox() stage = 1000 } - 300 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"!").also { stage++ } - 301 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Ahem, sorry about that. I have no interest in talking to you just now outerlander.").also { stage = END_DIALOGUE } + 300 -> npc(FacialExpression.NEUTRAL,"!").also { stage++ } + 301 -> npcl(FacialExpression.NEUTRAL,"Ahem, sorry about that. I have no interest in talking to you just now outerlander.").also { stage = END_DIALOGUE } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return PeerTheSeerDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(1288) + return intArrayOf(NPCs.PEER_THE_SEER_1288) } } \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt index e3d874010..2e0fd8671 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt @@ -1,32 +1,46 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.inInventory +import core.api.setAttribute import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import content.data.Quests @Initializable class PoisonSalesman(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { - options("Talk about the Murder Mystery Quest","Talk about the Fremennik Trials") + options("Talk about the Murder Mystery Quest","Talk about the The Fremennik Trials") stage = START_DIALOGUE return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - //val murderMysteryStage = player.questRepository.isComplete("Murder Mystery") - val fremennikTrialsStage = player.questRepository.getStage("Fremennik Trials") + val murderMysteryStage = player.questRepository.getStage(Quests.MURDER_MYSTERY) + val fremennikTrialsStage = player.questRepository.getStage(Quests.THE_FREMENNIK_TRIALS) when (stage) { START_DIALOGUE -> when (buttonId) { - 1 -> { player("Err... nevermind"); stage = END_DIALOGUE } + 1 -> { + when (murderMysteryStage) { + 0 -> npcl(FacialExpression.NEUTRAL, "I'm afraid I'm all sold out of poison at the moment. People know a bargain when they see it!").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.NEUTRAL, "I'm investigating the murder at the Sinclair house.").also { stage = 50 } + 100 -> npcl(FacialExpression.NEUTRAL, "I hear you're pretty smart to have solved the Sinclair Murder!").also { stage = END_DIALOGUE } + } + } 2 -> { player("Hello."); stage = 10 } } - //Fremennik Trials + //The Fremennik Trials 10 -> { /**when (fremennikTrialsStage) { 0 -> { npc("Come see me if you ever need low-alcohol beer!"); stage = END_DIALOGUE } @@ -87,6 +101,42 @@ class PoisonSalesman(player: Player? = null) : DialoguePlugin(player) { stage++ } 45 -> { npc("Well come back when you do!"); stage = END_DIALOGUE } + + //Murder Mystery + 50 -> npcl(FacialExpression.NEUTRAL, "There was a murder at the Sinclair house??? That's terrible! And I was only there the other day too! They bought the last of my Patented Multi Purpose Poison!").also { stage++ } + 51 -> showTopics( + Topic("Patented Multi Purpose Poison?", 52), + Topic("Who did you sell Poison to at the house?", 61), + Topic("Can I buy some Poison?", 65), + IfTopic("I have this pot I found at the murder scene...", 69, inInventory(player, Items.PUNGENT_POT_1812)) + ) + 52 -> npcl(FacialExpression.NEUTRAL, "Aaaaah... a miracle of modern apothecaries!").also { stage++ } + 53 -> npcl(FacialExpression.NEUTRAL, "This exclusive concoction has been tested on all known forms of life and been proven to kill them all in varying dilutions from cockroaches to king dragons!").also { stage++ } + 54 -> npcl(FacialExpression.NEUTRAL, "So incredibly versatile, it can be used as pest control, a cleansing agent, drain cleaner, metal polish and washes whiter than white,").also { stage++ } + 55 -> npcl(FacialExpression.NEUTRAL, "all with our uniquely fragrant concoction that is immediately recognisable across the land as Peter Potter's Patented Poison potion!!!").also { stage++ } + 56 -> sendDialogue("The salesman stops for breath.").also { stage ++ } + 57 -> npcl(FacialExpression.NEUTRAL, "I'd love to sell you some but I've sold out recently. That's just how good it is! Three hundred and twenty eight people in this area alone cannot be wrong!").also { stage++ } + 58 -> npcl(FacialExpression.NEUTRAL, "Nine out of Ten poisoners prefer it in controlled tests!").also { stage++ } + 59 -> npcl(FacialExpression.NEUTRAL, "Can I help you with anything else? Perhaps I can take your name and add it to our mailing list of poison users? We will only send you information related to the use of poison and other Peter Potter Products!").also { stage++ } + 60 -> playerl(FacialExpression.NEUTRAL, "Uh... no, it's ok. Really.").also { stage = END_DIALOGUE } + + 61 -> npcl(FacialExpression.HAPPY, "Well, Peter Potter's Patented Multi Purpose Poison is a product of such obvious quality that I am glad to say I managed to sell a bottle to each of the Sinclairs!").also { stage++ } + 62 -> npcl(FacialExpression.HAPPY, "Anna, Bob, Carol, David, Elizabeth and Frank all bought a bottle! In fact they bought the last of my supplies!").also { stage++ } + 63 -> npcl(FacialExpression.HAPPY, "Maybe I can take your name and address and I will personally come and visit you when stocks return?").also { stage++ } + 64 -> playerl(FacialExpression.THINKING, "Uh...no, it's ok.") + .also { setAttribute(player, attributePoisonClue, 1)} + .also { stage = END_DIALOGUE } + + 65 -> npcl(FacialExpression.NEUTRAL, "I'm afraid I am totally out of stock at the moment after my successful trip to the Sinclairs' House the other day.").also { stage++ } + 66 -> npcl(FacialExpression.NEUTRAL, "But don't worry! Our factories are working overtime to produce Peter Potter's Patented Multi Purpose Poison!").also { stage++ } + 67 -> npcl(FacialExpression.NEUTRAL, "Possibly the finest multi purpose poison and cleaner yet available to the general market.").also { stage++ } + 68 -> npcl(FacialExpression.NEUTRAL, "And its unique fragrance makes it the number one choice for cleaners and exterminators the whole country over!").also { stage = END_DIALOGUE } + + 69 -> sendDialogue("You show the poison salesman the pot you found at the murder", "scene with the unusual smell.").also { stage ++ } + 70 -> npcl(FacialExpression.THINKING, "Hmmm... yes, that smells exactly like my Patented Multi Purpose Poison, but I don't see how it could be. It quite clearly says on the label of all bottles").also { stage++ } + 71 -> npcl(FacialExpression.THINKING, "'Not to be taken internally - EXTREMELY POISONOUS'.").also { stage++ } + 72 -> playerl(FacialExpression.THINKING, "Perhaps someone else put it in his wine?").also { stage++ } + 73 -> npcl(FacialExpression.THINKING, "Yes... I suppose that could have happened...").also { stage = END_DIALOGUE } END_DIALOGUE -> end() } return true diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SeersHouseListeners.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SeersHouseListeners.kt index 37e7434e9..684eece92 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SeersHouseListeners.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SeersHouseListeners.kt @@ -1,5 +1,6 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials +import content.data.Quests import core.api.* import core.game.node.entity.impl.Animator import core.game.node.entity.player.Player @@ -102,7 +103,7 @@ class SeersHouseListeners : InteractionListener { if(!player.getAttribute("PeerStarted",false)){ sendDialogue(player,"You should probably talk to the owner of this home.") } - if(getAttribute(player, "fremtrials:peer-vote", false)) + if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && getAttribute(player, "fremtrials:peer-vote", false)) { sendDialogue(player, "I don't need to go through that again.") return@on true diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigliTheHuntsman.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigliTheHuntsman.kt index d987e061e..8fd16e3fc 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigliTheHuntsman.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigliTheHuntsman.kt @@ -1,18 +1,20 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.node.entity.player.Player -import core.game.node.entity.player.info.PlayerDetails import core.game.node.item.Item import core.plugin.Initializable import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression +import content.data.Quests +import org.rs09.consts.Items @Initializable class SigliTheHuntsman(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { - if(player?.inventory?.contains(3702,1) == true){ + if(player?.inventory?.contains(Items.CUSTOM_BOW_STRING_3702, 1) == true){ npcl(FacialExpression.HAPPY,"Greetings outerlander.") stage = 165 return true @@ -37,7 +39,7 @@ class SigliTheHuntsman(player: Player? = null) : DialoguePlugin(player){ stage = 150 return true } - else if(player?.getAttribute("fremtrials:sigli-vote",false)!!){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player?.getAttribute("fremtrials:sigli-vote",false)!!) { npc("You have my vote!") stage = 1000 return true @@ -47,12 +49,12 @@ class SigliTheHuntsman(player: Player? = null) : DialoguePlugin(player){ stage = 100 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(FacialExpression.HAPPY,"Hello again Sigli.") stage = 180 return true } - else if(player.questRepository.hasStarted("Fremennik Trials")){ + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0){ npc("What do you want outerlander?") stage = 0 return true @@ -103,13 +105,13 @@ class SigliTheHuntsman(player: Player? = null) : DialoguePlugin(player){ //Draugen killed 100 -> player("Thanks!").also { - player.removeAttribute("fremtrials:draugen-killed") - player.setAttribute("/save:fremtrials:sigli-vote",true) - player?.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1) - player?.inventory?.remove(Item(3697)) - stage = 1000 - } - + if (player.inventory.remove(Item(Items.HUNTERS_TALISMAN_3697))) { + player.removeAttribute("fremtrials:draugen-killed") + player.setAttribute("/save:fremtrials:sigli-vote", true) + player.setAttribute("/save:fremtrials:votes", player.getAttribute("fremtrials:votes", 0) + 1) + } + stage = 1000 + } 150 -> playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a map to unspoiled hunting grounds, do you?").also { stage++ } 151 -> npcl(FacialExpression.HAPPY,"Well, of course I do. I wouldn't be much of a huntsman if I didn't know where to find my prey now, would I outerlander?").also { stage++ } 152 -> playerl(FacialExpression.ASKING,"No, I guess not. So can I have it?").also { stage++ } @@ -126,9 +128,10 @@ class SigliTheHuntsman(player: Player? = null) : DialoguePlugin(player){ 161 -> npcl(FacialExpression.ANNOYED,"If I knew I would not have asked you to go and get me one, now would I?").also { stage = 1000 } 165 -> playerl(FacialExpression.HAPPY,"Here. I have your bowstring. Give me your map to the hunting grounds.").also { - removeItem(player,3702) - addItem(player,3701) - stage++ + if (removeItem(player, Items.CUSTOM_BOW_STRING_3702)) { + addItem(player, Items.TRACKING_MAP_3701) + stage++ + } } 166 -> npcl(FacialExpression.HAPPY,"Well met, outerlander. I see some hunting potential within you. Here, take my map, I was getting too dependent on it for my skill anyway.").also { stage = 1000 } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigmundDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigmundDialogue.kt index d00e6cf7f..947ffee6c 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigmundDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigmundDialogue.kt @@ -1,6 +1,7 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -8,6 +9,8 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests +import core.api.addItemOrDrop @Initializable class SigmundDialogue (player: Player? = null) : DialoguePlugin(player) { @@ -22,12 +25,12 @@ class SigmundDialogue (player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if(player.questRepository.isComplete("Fremennik Trials")){ + if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(FacialExpression.HAPPY,"Hello there!") stage = 50 return true } - else if(!player.questRepository.hasStarted("Fremennik Trials")){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) <= 0) { playerl(FacialExpression.HAPPY,"Hello there!") stage = 60 return true @@ -47,7 +50,7 @@ class SigmundDialogue (player: Player? = null) : DialoguePlugin(player) { stage = 25 return true } - else if(!player?.getAttribute("fremtrials:sigmund-vote",false)!!){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && !player?.getAttribute("fremtrials:sigmund-vote",false)!!) { playerl(FacialExpression.HAPPY,"Hello there!") stage = 1 return true @@ -107,8 +110,9 @@ class SigmundDialogue (player: Player? = null) : DialoguePlugin(player) { 36 -> npcl(FacialExpression.ASKING,"I suggest you ask around the other Fremennik in the town. A good merchant will find exactly what their customer needs somewhere.").also { stage++ } 37 -> playerl(FacialExpression.ASKING,"I was making some trades, but then I lost the goods...").also { stage++ } 38 -> npcl(FacialExpression.THINKING,"Hmmm... well try and start again at the beginning. And try to be more careful of your wares in future.").also { - addItem(player, Items.PROMISSORY_NOTE_3709) - stage = 1000 } + addItemOrDrop(player, Items.PROMISSORY_NOTE_3709) + stage = 1000 + } 40 -> npcl(FacialExpression.HAPPY,"Hello again outerlander! I am amazed once more at your apparent skill at merchanting!").also { stage++ } 41 -> playerl(FacialExpression.HAPPY,"So I can count on your vote at the council of elders?").also { stage++ } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SkulgrimenDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SkulgrimenDialogue.kt index f51b1cf71..4ca7edd4f 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SkulgrimenDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SkulgrimenDialogue.kt @@ -7,18 +7,20 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable +import content.data.Quests +import org.rs09.consts.Items @Initializable class SkulgrimenDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if(player?.inventory?.contains(3703,1) == true){ + if(player?.inventory?.contains(Items.UNUSUAL_FISH_3703,1) == true){ playerl(FacialExpression.HAPPY,"Hi there. I got your fish, so can I have that bowstring for Sigli now?") stage = 20 return true } - else if(player?.inventory?.contains(3702,1) == true){ + else if(player?.inventory?.contains(Items.CUSTOM_BOW_STRING_3702,1) == true){ playerl(FacialExpression.ASKING,"So about this bowstring... was it hard to make or something?") stage = 25 return true @@ -38,7 +40,7 @@ class SkulgrimenDialogue(player: Player? = null) : DialoguePlugin(player) { stage = 1 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ npcl(FacialExpression.HAPPY,"Hello again, ${player.getAttribute("fremennikname","ringo")}. Come to see what's for sale?") stage = 1001 return true @@ -71,9 +73,9 @@ class SkulgrimenDialogue(player: Player? = null) : DialoguePlugin(player) { 17 -> npcl(FacialExpression.ANNOYED,"Ah. I see. I already told you. Some guy down by the docks was bragging. Best ask there, I reckon.").also { stage = 1000 } 20 -> npcl(FacialExpression.HAPPY,"Ohh... That's a nice fish. Very pleased. Here. Take the bowstring. You fulfilled agreement. Only fair I do same. Good work outerlander.").also { - removeItem(player,3703) - addItem(player,3702) - stage++ + if (removeItem(player, Items.UNUSUAL_FISH_3703) && addItem(player, Items.CUSTOM_BOW_STRING_3702)) { + stage++ + } } 21 -> playerl(FacialExpression.HAPPY,"Thanks!").also { stage = 1000 } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SwensenTheNavigator.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SwensenTheNavigator.kt index c06ff2266..0f7762d8e 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SwensenTheNavigator.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SwensenTheNavigator.kt @@ -1,24 +1,26 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.node.entity.player.Player -import core.game.node.entity.player.info.PlayerDetails import core.plugin.Initializable import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression +import content.data.Quests +import org.rs09.consts.Items @Initializable class SwensenTheNavigator(player: Player? = null) : DialoguePlugin(player){ val gender = if (player?.isMale == true){"brother"} else "sister" val fName = player?.getAttribute("fremennikname","doug hug'em") override fun open(vararg args: Any?): Boolean { - if(player?.inventory?.contains(3705,1) == true){ + if(player?.inventory?.contains(Items.WEATHER_FORECAST_3705, 1) == true){ playerl(FacialExpression.HAPPY,"I would like your map of fishing spots.") stage = 120 return true } - else if(player?.inventory?.contains(3704,1) == true){ + else if(player?.inventory?.contains(Items.SEA_FISHING_MAP_3704, 1) == true){ playerl(FacialExpression.ASKING,"If this map of fishing spots is so valuable, why did you give it away to me so easily?") stage = 125 return true @@ -47,12 +49,12 @@ class SwensenTheNavigator(player: Player? = null) : DialoguePlugin(player){ stage = 1000 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if (player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(FacialExpression.HAPPY,"Hello!") stage = 140 return true } - else if(player.questRepository.hasStarted("Fremennik Trials")){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0) { player("Hello!") stage = 0 return true @@ -118,9 +120,9 @@ class SwensenTheNavigator(player: Player? = null) : DialoguePlugin(player){ 121 -> playerl(FacialExpression.HAPPY,"What, like this one I have here?").also { stage++ } 122 -> npcl(FacialExpression.AMAZED,"W-what...? I don't believe it! How did you...?").also { stage++ } 123 -> npcl(FacialExpression.HAPPY,"I suppose it doesn't matter, you have my gratitude outerlander! With this forecast I will be able to plan a safe course for our next raiding expedition!").also { - removeItem(player,3705) - addItem(player,3704) - stage++ + if (removeItem(player,Items.WEATHER_FORECAST_3705) && addItem(player, Items.SEA_FISHING_MAP_3704)) { + stage++ + } } 124 -> npcl(FacialExpression.HAPPY,"Here, outerlander; you may take my map of local fishing patterns with my gratitude!").also { stage = 1000 } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt index 5a6686c29..db98b2314 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt @@ -21,6 +21,8 @@ import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.system.config.ItemConfigParser import core.game.world.GameWorld.Pulser +import content.data.Quests +import core.game.interaction.QueueStrength class TFTInteractionListeners : InteractionListener { @@ -82,8 +84,7 @@ class TFTInteractionListeners : InteractionListener { } onUseWith(IntType.ITEM,TINDERBOX,CHERRY_BOMB){ player, _, _ -> - if(removeItem(player,CHERRY_BOMB)){ - addItem(player,LIT_BOMB) + if (removeItem(player,CHERRY_BOMB) && addItem(player,LIT_BOMB)) { sendMessage(player,"You light the strange object.") } return@onUseWith true @@ -154,7 +155,7 @@ class TFTInteractionListeners : InteractionListener { sendNPCDialogue(player,1278,"Yeah you're good to go through. Olaf tells me you're some kind of outerlander bard here on tour. I doubt you're worse than Olaf is.") core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,door.asScenery()) } - getAttribute(player,"lyreConcertPlayed",false) -> { + getAttribute(player,"lyreConcertPlayed",false) || isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) -> { core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,door.asScenery()) } else -> { @@ -166,8 +167,8 @@ class TFTInteractionListeners : InteractionListener { on(LYRE_IDs, IntType.ITEM, "play"){ player, lyre -> if(getAttribute(player,"onStage",false) && !getAttribute(player,"lyreConcertPlayed",false)){ - Pulser.submit(LyreConcertPulse(player,lyre.id)) - } else if(getQuestStage(player, "Fremennik Trials") < 20 || !isQuestComplete(player, "Fremennik Trials")){ + playLyreConcert(player, lyre.id) + } else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) < 20 || !isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)){ sendMessage(player,"You lack the knowledge to play this.") } else if(LYRE_IDs.isLast(lyre.id)){ sendMessage(player,"Your lyre is out of charges!") @@ -208,15 +209,17 @@ class TFTInteractionListeners : InteractionListener { return@on true } - on(SWENSEN_LADDER, IntType.SCENERY, "climb"){ player, _ -> - if(!getAttribute(player,"fremtrials:swensen-accepted",false)){ + on(SWENSEN_LADDER, IntType.SCENERY, "climb-down") { player, _ -> + if (!getAttribute(player,"fremtrials:swensen-accepted",false)) { sendNPCDialogue(player,1283,"Where do you think you're going?", core.game.dialogue.FacialExpression.ANGRY) + return@on true } + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2631, 10006, 0)) return@on true } on(THORVALD_LADDER, IntType.SCENERY, "climb-down") { player, _ -> - if (isQuestComplete(player, "Fremennik Trials") || getAttribute(player, "fremtrials:thorvald-vote", false)) { + if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) || getAttribute(player, "fremtrials:thorvald-vote", false)) { sendMessage(player,"You have no reason to go back down there.") return@on true } else if (!getAttribute(player,"fremtrials:warrior-accepted",false)) { @@ -263,7 +266,7 @@ class TFTInteractionListeners : InteractionListener { } on(SHOPNPCS, IntType.NPC, "Trade") { player, npc -> - if(isQuestComplete(player, "Fremennik Trials")){ + if(isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)){ npc.asNpc().openShop(player) } else when(npc.id){ NPCs.THORA_THE_BARKEEP_1300 -> sendDialogue(player,"Only Fremenniks may buy drinks here.") @@ -352,7 +355,7 @@ class TFTInteractionListeners : InteractionListener { } } - class LyreConcertPulse(val player: Player, val Lyre: Int) : Pulse(){ + fun playLyreConcert(player: Player, lyre: Int) { val GENERIC_LYRICS = arrayOf( "${player.username?.capitalize()} is my name,", "I haven't much to say", @@ -377,7 +380,6 @@ class TFTInteractionListeners : InteractionListener { "I will simply tell you this:", "I've joined the Legends' Guild!" ) - var counter = 0 val questPoints = getQuestPoints(player) val champGuild = player.achievementDiaryManager?.hasCompletedTask(DiaryType.VARROCK, 1, 1)?: false val legGuild = questPoints >= 111 @@ -400,15 +402,17 @@ class TFTInteractionListeners : InteractionListener { else -> GENERIC_LYRICS } - override fun pulse(): Boolean { - when(counter++){ + queueScript(player, 0, QueueStrength.SOFT) { stage -> + when (stage) { 0 -> { player.lock() animate(player,1318,true) + return@queueScript delayScript(player, 1) } 2 -> { animate(player,1320,true) player.musicPlayer.play(MusicEntry.forId(165)) + return@queueScript delayScript(player, 1) } 4 -> { animate(player,1320,true) @@ -432,13 +436,15 @@ class TFTInteractionListeners : InteractionListener { } 14 ->{ setAttribute(player,"/save:lyreConcertPlayed",true) - player.removeAttribute("LyreEnchanted") - if(removeItem(player,Lyre)) - addItem(player,Items.ENCHANTED_LYRE_3690) + removeAttribute(player, "/save:LyreEnchanted") + if (removeItem(player,lyre)) { + addItem(player, Items.ENCHANTED_LYRE_3690) + } player.unlock() + return@queueScript stopExecuting(player) } } - return false + return@queueScript delayScript(player, 1) } } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThoraDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThoraDialogue.kt index 647d74df1..51159d5d6 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThoraDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThoraDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.item.Item import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable class ThoraDialogue(player: Player? = null) : DialoguePlugin(player){ @@ -36,7 +37,7 @@ class ThoraDialogue(player: Player? = null) : DialoguePlugin(player){ playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find the longhall barkeeps' legendary cocktail, do you?") stage = 1 } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ npcl(FacialExpression.HAPPY,"Hello again, $fName. I suppose you want a drink? Or are you going to try another scam with that terrible Askeladden again?") stage = 50 } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThorvaldDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThorvaldDialogue.kt index 0d3523007..e64080988 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThorvaldDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThorvaldDialogue.kt @@ -1,20 +1,23 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.node.entity.player.Player import core.plugin.Initializable +import org.rs09.consts.Items import org.rs09.consts.NPCs +import content.data.Quests @Initializable class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { - if(player?.inventory?.contains(3706,1) == true){ + if(player?.inventory?.contains(Items.CHAMPIONS_TOKEN_3706, 1) == true){ playerl(core.game.dialogue.FacialExpression.HAPPY,"I would like your contract to offer your services as a bodyguard.") stage = 215 return true } - else if(player?.inventory?.contains(3710,1) == true){ + else if(player?.inventory?.contains(Items.WARRIORS_CONTRACT_3710, 1) == true){ playerl(core.game.dialogue.FacialExpression.ASKING,"You didn't take much persuading to 'lower' yourself to a bodyguard.") stage = 220 return true @@ -39,27 +42,27 @@ class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug stage = 150 return true } - else if (player?.questRepository?.isComplete("Fremennik Trials")!!) { + else if (player?.questRepository?.isComplete(Quests.THE_FREMENNIK_TRIALS)!!) { playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Howdy Thorvald!") stage = 0 return true } - else if(player!!.getAttribute("fremtrials:thorvald-vote", false)!!){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player!!.getAttribute("fremtrials:thorvald-vote", false)!!) { playerl(core.game.dialogue.FacialExpression.FRIENDLY, "So can I count on your vote at the council of elders now Thorvald?") stage = 160 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(core.game.dialogue.FacialExpression.HAPPY,"Howdy Thorvald!") stage = 250 return true } - else if(!player.questRepository.hasStarted("Fremennik Trials")){ + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) == 0){ npcl(core.game.dialogue.FacialExpression.ANNOYED, "Leave me be, outerlander. I have nothing to say to the likes of you.") stage = 1000 return true } - else if (!player!!.getAttribute("fremtrials:thorvald-vote", false)!!) { + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && !player!!.getAttribute("fremtrials:thorvald-vote", false)!!) { if (player!!.getAttribute("fremtrials:warrior-accepted", false)!!) { options("What do I have to do again?", "Who is my opponent?", "Can't I do something else?") stage = 100 @@ -75,7 +78,7 @@ class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - //After Fremennik Trials + //After The Fremennik Trials 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "And greetings to you too. It is good to see new blood entering the Fremennik; we gain our strength by bringing new warriors into the tribe.").also { stage = 1000 } //Warrior Trial @@ -177,9 +180,10 @@ class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug 216 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"It's a good thing I have the Champions' Token right here then, isn't it?").also { stage++ } 217 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah... well this is a different matter. With that token I can claim my rightful place as a champion in the Long hall!").also { stage++ } 218 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here outerlander, I can suffer the indignity of playing babysitter if it means that I can then revel with my warrior equals in the Long Hall afterwards!").also { - removeItem(player,3706) - addItem(player,3710) - stage++ + if (removeItem(player, Items.CHAMPIONS_TOKEN_3706)) { + addItem(player, Items.WARRIORS_CONTRACT_3710) + stage++ + } } 219 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here outerlander, take this contract; I will fulfill it to my utmost.").also { stage = 1000 } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt index ef174fa42..3114af8ab 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt @@ -7,18 +7,20 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable +import content.data.Quests +import org.rs09.consts.Items @Initializable class YrsaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if(player?.inventory?.contains(3708,1) == true){ + if(player?.inventory?.contains(Items.FISCAL_STATEMENT_3708,1) == true){ playerl(FacialExpression.HAPPY,"Hello. Can I have those boots now? Here is a written statement from Brundt outlining future tax burdens upon Fremennik merchants and shopkeepers for the year.") stage = 15 return true } - else if(player?.inventory?.contains(3700,1) == true){ + else if(player?.inventory?.contains(Items.STURDY_BOOTS_3700,1) == true){ playerl(FacialExpression.ASKING,"Hey, these shoes look pretty comfy. Think you could make me a pair like them?") stage = 20 return true @@ -38,7 +40,7 @@ class YrsaDialogue(player: Player? = null) : DialoguePlugin(player) { stage = 1 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ npcl(FacialExpression.HAPPY,"Welcome to my clothes shop. I can change your shoes, or I've got a fine selection of clothes for sale.") stage = 30 //Uncomment this out when we got the shoe shop up and running @@ -66,9 +68,9 @@ class YrsaDialogue(player: Player? = null) : DialoguePlugin(player) { 10 -> npcl(FacialExpression.NEUTRAL,"Yes I do outerlander. Only the Chieftain may permit such a thing. Talk to him.").also { stage = 1000 } 15 -> npcl(FacialExpression.HAPPY,"Certainly! Let me have a look at what he has written here, just give me a moment...").also { - removeItem(player,3708) - addItem(player,3700) - stage++ + if (removeItem(player, Items.FISCAL_STATEMENT_3708) && addItem(player, Items.STURDY_BOOTS_3700)) { + stage++ + } } 16 -> npcl(FacialExpression.HAPPY,"Yes, that all appears in order. Tell Olaf to come to me next time for shoes!").also { stage = 1000 } diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/PenguinKeeperDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/PenguinKeeperDialogue.kt new file mode 100644 index 000000000..9a6ccdff9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/PenguinKeeperDialogue.kt @@ -0,0 +1,62 @@ +package content.region.kandarin.ardougne.dialogue + +import core.api.addItem +import core.api.getDynLevel +import core.api.hasAnItem +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class PenguinKeeperDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun getIds(): IntArray { + return intArrayOf(NPCs.PENGUIN_KEEPER_6891) + } + + companion object{ + const val YES = 20 + const val NO = 40 + const val FULL = 50 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> playerl(FacialExpression.FRIENDLY, "Hello there. How are the penguins doing today?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "They are doing fine, thanks.").also{ + if (getDynLevel(player, Skills.SUMMONING) < 30 || hasAnItem(player, Items.PENGUIN_EGG_12483).exists()) + stage = END_DIALOGUE + else + stage++ + } + 2 -> npcl(FacialExpression.HALF_ASKING,"Actually, you might be able to help me with something - if you are interested.").also { stage++ } + 3 -> playerl(FacialExpression.ASKING, "What do you mean?").also { stage++ } + 4 -> npcl(FacialExpression.NEUTRAL, "Well, you see, the penguins have been laying so many eggs recently that we can't afford to raise them all ourselves.").also { stage++ } + 5 -> npcl(FacialExpression.ASKING, " You seem to know a bit about raising animals - would you like to raise a penguin for us? ").also { stage++ } + 6 -> showTopics( + Topic("Yes, of course.", YES), + Topic("No thanks.", NO) + ) + + YES -> npcl(FacialExpression.HAPPY, "Wonderful!").also { + if (addItem(player, Items.PENGUIN_EGG_12483)) + stage++ + else + stage = FULL + } + YES + 1 -> npcl(FacialExpression.HAPPY, "Here you go - this egg will hatch into a baby penguin.").also { stage++ } + YES + 2 -> npcl(FacialExpression.HAPPY, "They eat raw fish and aren't particularly fussy about anything, so it won't be any trouble to raise.").also { stage++ } + YES + 3 -> playerl(FacialExpression.HAPPY, "Okay, thank you very much.").also { stage = END_DIALOGUE } + + NO -> npcl(FacialExpression.NEUTRAL, " Fair enough. The offer still stands if you change your mind. ").also { stage = END_DIALOGUE } + + FULL -> npcl(FacialExpression.SAD, "You don't have inventory space though.").also { stage = END_DIALOGUE } + } + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java b/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java index 5b8c59117..117dbf5da 100644 --- a/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java +++ b/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java @@ -1,5 +1,6 @@ package content.region.kandarin.ardougne.handlers; +import core.ServerConstants; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; import core.game.dialogue.DialogueInterpreter; @@ -8,7 +9,6 @@ import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.TeleportManager; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; @@ -20,10 +20,11 @@ import core.plugin.ClassScanner; import org.rs09.consts.Sounds; import static core.api.ContentAPIKt.playAudio; +import static content.region.wilderness.handlers.WildernessGateHandlerKt.enterDeepWilderness; /** * Handles wilderness levers. - * @author 'Vexia + * @author 'Vexia, Player Name * @version 1.0 */ @Initializable @@ -279,7 +280,6 @@ public final class WildernessLeverPlugin extends OptionHandler { public Location[] getLocations() { return locations; } - } /** @@ -325,7 +325,14 @@ public final class WildernessLeverPlugin extends OptionHandler { public boolean open(Object... args) { lever = (LeverSets) args[0]; id = (int) args[1]; - interpreter.sendDialogue("Warning! Pulling the lever will teleport you deep into the wilderness."); + if (ServerConstants.ENHANCED_DEEP_WILDERNESS) { + enterDeepWilderness(player, (player) -> { + lever.pull(player, lever.getIndex(id), true); + return null; + }, "Pulling the lever will teleport you into the deep wilderness."); + } else { + interpreter.sendDialogue("Warning! Pulling the lever will teleport you deep into the wilderness."); + } return true; } diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/CivilianDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/CivilianDialogue.kt deleted file mode 100644 index 0dc0d3c96..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/CivilianDialogue.kt +++ /dev/null @@ -1,44 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue - -import core.api.inEquipment -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import core.tools.END_DIALOGUE -import org.rs09.consts.Items -import org.rs09.consts.NPCs - -@Initializable -class CivilianDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - if(inEquipment(player, Items.GAS_MASK_1506)) { - playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 4 } - } else { - playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 0 } - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - 0 -> npcl(FacialExpression.FRIENDLY, "I'm a bit busy to talk right now, sorry.").also { stage++ } - 1 -> playerl(FacialExpression.FRIENDLY, "Why? What are you doing?").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "Trying to kill these mice! What I really need is a cat!").also { stage++ } - 3 -> playerl(FacialExpression.FRIENDLY, "No, you're right, you don't see many around.").also { stage = END_DIALOGUE } - 4 -> npcl(FacialExpression.FRIENDLY, "If you Mourners really wanna help, why don't you do something about these mice?!").also { stage = END_DIALOGUE } - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return CivilianDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.CIVILIAN_785, NPCs.CIVILIAN_786, NPCs.CIVILIAN_787) - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/KilronDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/KilronDialogue.kt deleted file mode 100644 index 7cba51211..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/KilronDialogue.kt +++ /dev/null @@ -1,53 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue - -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import core.tools.END_DIALOGUE -import org.rs09.consts.NPCs - -@Initializable -class KilronDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - if (player.questRepository.getQuest("Plague City").isCompleted(player)){ - npcl(FacialExpression.FRIENDLY, "Looks like you won't be needing the rope ladder any more, adventurer. I heard it was you who started the revolution and freed West Ardougne!").also { stage = END_DIALOGUE } - } else { - playerl(FacialExpression.FRIENDLY, "Hello there.") - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - 0 -> npcl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } - 1 -> playerl(FacialExpression.FRIENDLY, "How are you?").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "Busy.").also { stage = END_DIALOGUE } - } - return true - } - - /* After Biohazard and before Plague's End - 0 -> playerl(FacialExpression.FRIENDLY, "Hello Kilron.").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "Hello traveller. Do you need to go back over?").also { stage++ } - 2 -> options("Not yet Kilron", "Yes I do").also { stage++ } - 3 -> when(buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Not yet Kilron.").also { stage = 4 } - 2 -> playerl(FacialExpression.FRIENDLY, "Yes I do.").also { stage = 5 } - } - 4 -> npcl(FacialExpression.FRIENDLY, "Okay, just give me the word.").also { stage = END_DIALOGUE } - 5 -> npcl(FacialExpression.FRIENDLY, "Okay, quickly now!").also { stage = END_DIALOGUE } - */ - - override fun newInstance(player: Player?): DialoguePlugin { - return KilronDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.KILRON_349) - } - -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/ManDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/ManDialogue.kt deleted file mode 100644 index 006f72819..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/ManDialogue.kt +++ /dev/null @@ -1,24 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue - -import core.game.dialogue.DialogueFile -import core.game.dialogue.FacialExpression -import core.plugin.Initializable -import core.tools.END_DIALOGUE - -@Initializable -class ManDialogue : DialogueFile() { - - override fun handle(componentID: Int, buttonID: Int) { - when (stage) { - 0 -> npcl(FacialExpression.HALF_GUILTY, "We don't have good days here anymore. Curse King Tyras.").also { stage++ } - 1 -> options("Oh okay, bad day then.", "Why, what has he done?", "I'm looking for a woman called Elena.").also { stage++ } - 2 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Oh okay, bad day then.").also { stage = END_DIALOGUE } - 2 -> playerl(FacialExpression.FRIENDLY, "Why, what has he done?").also { stage = 3 } - 3 -> playerl(FacialExpression.FRIENDLY, "I'm looking for a woman called Elena.").also { stage = 4 } - } - 3 -> npcl(FacialExpression.FRIENDLY, "His army curses our city with this plague then wanders off again, leaving us to clear up the pieces.").also { stage = END_DIALOGUE } - 4 -> npcl(FacialExpression.THINKING, "Not heard of her.").also { stage = END_DIALOGUE } - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/MournersDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/MournersDialogue.kt deleted file mode 100644 index 2b1ceaac5..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/MournersDialogue.kt +++ /dev/null @@ -1,41 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue - -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import core.tools.END_DIALOGUE -import org.rs09.consts.NPCs - -@Initializable -class MournersDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - playerl(FacialExpression.FRIENDLY, "Hi.").also { stage = 0 } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - 0 -> npcl(FacialExpression.SAD, "What are you up to?").also { stage++ } - 1 -> playerl(FacialExpression.NEUTRAL,"Just sight-seeing.").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "This is no place for sight-seeing. Don't you know there's been a plague outbreak?").also { stage++ } - 3 -> playerl(FacialExpression.FRIENDLY, "Yes, I had heard.").also { stage++ } - 4 -> npcl(FacialExpression.FRIENDLY, "Then I suggest you leave as soon as you can.").also { stage++ } - 5 -> playerl(FacialExpression.FRIENDLY, "Thanks for the advice.").also { stage = END_DIALOGUE } - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return MournersDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf( - NPCs.MOURNER_347, NPCs.MOURNER_348, NPCs.MOURNER_357, NPCs.MOURNER_369, NPCs.MOURNER_371, NPCs.MOURNER_370) - } - -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/WomanDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/WomanDialogue.kt deleted file mode 100644 index 7e7b14fdd..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/WomanDialogue.kt +++ /dev/null @@ -1,61 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue - -import core.game.dialogue.DialogueFile -import core.game.dialogue.FacialExpression -import core.plugin.Initializable -import core.tools.END_DIALOGUE -import core.tools.RandomFunction - -@Initializable -class WomanDialogue : DialogueFile() { - - override fun handle(componentID: Int, buttonID: Int) { - when (RandomFunction.random(1,4)) { - 1 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello, how's it going?").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "Bah, those mourners... they're meant to be helping us, but I think they're doing more harm here than good. They won't even let me send a letter out to my family.").also { stage++ } - 2 -> options("Have you seen a lady called Elena around here?", "You should stand up to them more.").also { stage++ } - 3 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Have you seen a lady called Elena around here?").also { stage = 4 } - 2 -> playerl(FacialExpression.FRIENDLY, "You should stand up to them more.").also { stage = 6 } - } - 4 -> npcl(FacialExpression.FRIENDLY, "Yes, I've seen her. Very helpful person.").also { stage++ } - 5 -> npcl(FacialExpression.FRIENDLY, "Not for the last few days though... I thought maybe she'd gone home.").also { stage = END_DIALOGUE } - 6 -> npcl(FacialExpression.FRIENDLY, "Oh I'm not one to cause a fuss.").also { stage = END_DIALOGUE } - } - - 2 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello, how's it going?").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "Life is tough.").also { stage++ } - 2 -> options("Yes, living in a plague city must be hard.", "I'm sorry to hear that.", "I'm looking for a lady called Elena.").also { stage++ } - 3 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Yes, living in a plague city must be hard.").also { stage = 4 } - 2 -> playerl(FacialExpression.FRIENDLY, "I'm sorry to hear that.").also { stage = 6 } - 3 -> playerl(FacialExpression.FRIENDLY, "I'm looking for a lady called Elena.").also { stage = 7 } - } - 4 -> npcl(FacialExpression.FRIENDLY, "Plague? Pah, that's no excuse for the treatment we've received. It's obvious pretty quickly if someone has the plague.").also { stage++ } - 5 -> npcl(FacialExpression.FRIENDLY, "I'm thinking about making a break for it. I'm perfectly healthy, not gonna infect anyone.").also { stage = END_DIALOGUE } - 6 -> npcl(FacialExpression.FRIENDLY, "Well, ain't much either you or me can do about it.").also { stage = END_DIALOGUE } - 7 -> npcl(FacialExpression.FRIENDLY, "I've not heard of her. Old Jethick knows a lot of people, maybe he'll know where you can find her.").also { stage = END_DIALOGUE } - } - - 3 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello, how's it going?").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "We don't have good days here anymore. Curse King Tyras.").also { stage++ } - 2 -> options("Oh okay, bad day then.", "Why, what has he done?", "I'm looking for a woman called Elena.").also { stage++ } - 3 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Oh okay, bad day then.").also { stage = END_DIALOGUE } - 2 -> playerl(FacialExpression.FRIENDLY, "Why, what has he done?").also { stage = 4 } - 3 -> playerl(FacialExpression.FRIENDLY, "I'm looking for a lady called Elena.").also { stage = 5 } - } - 4 -> npcl(FacialExpression.FRIENDLY, "His army curses our city with this plague then wanders off again, leaving us to clear up the pieces.").also { stage = END_DIALOGUE } - 5 -> npcl(FacialExpression.FRIENDLY, "Not heard of her.").also { stage = END_DIALOGUE } - } - 4 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello, how's it going?").also { stage++ } - 1 -> npcl(FacialExpression.HALF_ASKING, "An outsider! Can you get me out of this hell hole?").also { stage++ } - 2 -> playerl(FacialExpression.HALF_GUILTY, "Sorry, that's not what I'm here to do.").also { stage = END_DIALOGUE } - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/BravekDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/BravekDialogue.kt deleted file mode 100644 index bdee4777d..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/BravekDialogue.kt +++ /dev/null @@ -1,116 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena - -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import core.tools.END_DIALOGUE -import org.rs09.consts.Items -import org.rs09.consts.NPCs - -@Initializable -class BravekDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - if (player.questRepository.getStage("Plague City") == 0) { - npcl(FacialExpression.ANGRY, "Go away, I'm busy! I'm... Um... In a meeting!").also { stage = END_DIALOGUE } - } else if (player.questRepository.getStage("Plague City") == 13) { - npcl(FacialExpression.NEUTRAL, "My head hurts! I'll speak to you another day...").also { stage = 1 } - } else if (player.questRepository.getStage("Plague City") == 14) { - npcl(FacialExpression.NEUTRAL, "Uurgh! My head still hurts too much to think straight. Oh for one of Trudi's hangover cures!").also { stage = 1 } - } else if (player.questRepository.getStage("Plague City") >= 16) { - npcl(FacialExpression.NEUTRAL, "Thanks again for the hangover cure.").also { stage = 1 } - } else { - npcl(FacialExpression.ANGRY, "Go away, I'm busy! I'm... Um... In a meeting!").also { stage = END_DIALOGUE } - } - return true - } - - override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { - - 13 -> when (stage) { - 1 -> playerl(FacialExpression.FRIENDLY, "This is really important though!").also { stage = 2 } - 2 -> npcl(FacialExpression.FRIENDLY, "I can't possibly speak to you with my head spinning like this... I went a bit heavy on the drink last night.").also { stage++ } - 3 -> npcl(FacialExpression.FRIENDLY, "Curse my herbalist, she made the best hangover cures. Darn inconvenient of her catching the plague.").also { stage++ } - 4 -> options("You shouldn't drink so much then!", "Do you know what's in the cure?", "Okay, goodbye.").also { stage++ } - 5 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "You shouldn't drink so much then!").also { stage = 7 } - 2 -> playerl(FacialExpression.FRIENDLY, "Do you know what's in the cure?").also { stage = 20 } - 3 -> playerl(FacialExpression.FRIENDLY, "Okay, goodbye.").also { stage = END_DIALOGUE } - } - 7 -> npcl(FacialExpression.FRIENDLY, "Well positions of responsibility are hard, I need something to take my mind off things... Especially with the problems this place has..").also { stage++ } - 8 -> playerl(FacialExpression.FRIENDLY, "I don't think drink is the solution.").also { stage++ } - 9 -> npcl(FacialExpression.FRIENDLY, "Uurgh! My head still hurts too much to think straight. Oh for one of Trudi's hangover cures!").also { stage++ } - 10 -> npcl(FacialExpression.FRIENDLY, "I'll see what I can do I suppose. Mr. Bravek, there's someone here who really needs to speak to you.").also { stage++ } - 11 -> sendNPCDialogue(player!!, NPCs.BRAVEK_711, "I suppose they can come in then. If they keep it short.").also { stage = END_DIALOGUE } - 20 -> npcl(FacialExpression.FRIENDLY, "Hmmm let me think... Ouch! Thinking isn't clever. Ah here, she did scribble it down for me.").also { stage++ } - 21 -> if (freeSlots(player!!) == 0) { - end() - sendItemDialogue(player!!, Items.A_SCRUFFY_NOTE_1508, "Bravek waves a tatty piece of paper at you, but you don't have room to take it.").also { stage = END_DIALOGUE } - } else { - end() - sendItemDialogue(player!!, Items.A_SCRUFFY_NOTE_1508, "Bravek hands you a tatty piece of paper.").also { stage++ } - addItem(player!!, Items.A_SCRUFFY_NOTE_1508) - setQuestStage(player!!, "Plague City", 14) - } - } - - 14 -> when (stage) { - 1 -> if(removeItem(player!!, Items.HANGOVER_CURE_1504)) { - playerl(FacialExpression.FRIENDLY, "Try this.").also { stage++ } - } else { - end() - stage = END_DIALOGUE - } - 2 -> { - animate(npc, 1330) // Drink hangover cure. - findLocalNPC(player!!, NPCs.BRAVEK_711)!!.sendChat("Grruurgh!") - sendItemDialogue(player!!, Items.HANGOVER_CURE_1504, "You give Bravek the hangover cure.").also { stage++ } - } - 3 -> sendDialogue(player!!, "Bravek gulps down the foul-looking liquid.").also { stage++ } - 4 -> npcl(FacialExpression.NEUTRAL, "Ooh that's much better! Thanks, that's the clearest my head has felt in a month. Ah now, what was it you wanted me to do for you?").also { stage++ } - 5 -> playerl(FacialExpression.FRIENDLY, "I need to rescue a kidnap victim named Elena. She's being held in a plague house, I need permission to enter.").also { stage++ } - 6 -> options("Ok, I'll go speak to them.", "Is that all anyone says around here?", "They won't listen to me!").also { stage++ } - 7 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Ok, I'll go speak to them.").also { stage = 6 } - 2 -> playerl(FacialExpression.FRIENDLY, "Is that all anyone says around here?").also { stage = 11 } - 3 -> playerl(FacialExpression.FRIENDLY, "They won't listen to me! They say I'm not properly equipped to go in the house, though I do have a very effective gasmask.").also { stage++ } - } - 8 -> npcl(FacialExpression.FRIENDLY, "Hmmm, well I guess they're not taking the issue of a kidnapping seriously enough. They do go a bit far sometimes.").also { stage++ } - 9 -> npcl(FacialExpression.FRIENDLY, "I've heard of Elena, she has helped us a lot... Ok, I'll give you this warrant to enter the house.").also { stage = 17 } - 11 -> npcl(FacialExpression.FRIENDLY, "Well, they know best about plague issues.").also { stage++ } - 12 -> playerl(FacialExpression.FRIENDLY, "Don't you want to take an interest in it at all?").also { stage++ } - 13 -> npcl(FacialExpression.FRIENDLY, "Nope, I don't wish to take a deep interest in plagues. That stuff is too scary for me!").also { stage++ } - 14 -> playerl(FacialExpression.FRIENDLY, "I can see why people say you're a weak leader.").also { stage++ } - 15 -> npcl(FacialExpression.FRIENDLY, "Bah, people always criticise their leaders but delegating is the only way to lead. I delegate all plague issues to the mourners.").also { stage++ } - 16 -> playerl(FacialExpression.FRIENDLY, "This whole city is a plague issue!").also { stage = 6 } - 17 -> if (freeSlots(player!!) == 0) { - end() - sendItemDialogue(player!!, Items.WARRANT_1503, "Bravek waves a warrant at you, but you don't have room to take it.").also { stage = END_DIALOGUE } - } else { - end() - sendItemDialogue(player!!, Items.WARRANT_1503, "Bravek hands you a warrant.").also { stage = END_DIALOGUE } - addItem(player!!, Items.WARRANT_1503) - setQuestStage(player!!, "Plague City", 16) - } - } - - 16 -> when (stage) { - 1 -> playerl(FacialExpression.NEUTRAL, "Not a problem, happy to help out.").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "I'm just having a little drop of whisky, then I'll feel really good.").also { stage = END_DIALOGUE } - } - - in 17..100 -> when (stage) { - 1 -> playerl(FacialExpression.FRIENDLY, "Not a problem, happy to help out.").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "I'm just having a little drop of whisky, then I'll feel really good.").also { stage = END_DIALOGUE } - } - } - return true - } - - override fun getIds(): IntArray = intArrayOf(NPCs.BRAVEK_711) -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/ClerkDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/ClerkDialogue.kt deleted file mode 100644 index 3f44c539e..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/ClerkDialogue.kt +++ /dev/null @@ -1,94 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena - -import core.api.getQuestStage -import core.api.sendNPCDialogue -import core.api.setQuestStage -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import core.tools.END_DIALOGUE -import org.rs09.consts.NPCs - -@Initializable -class ClerkDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - npcl(FacialExpression.NEUTRAL, "Hello, welcome to the Civic Office of West Ardougne. How can I help you?").also { stage = 0 } - return true - } - - override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { - - 11 -> when (stage) { - 0 -> options("Who is through that door?", "I'm just looking thanks.").also { stage++ } - 1 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Who is through that door?").also { stage = 2 } - 2 -> playerl(FacialExpression.FRIENDLY, "I'm just looking thanks.").also { stage = END_DIALOGUE } - } - 2 -> npcl(FacialExpression.FRIENDLY, "The city warder Bravek is in there.").also { stage++ } - 3 -> playerl(FacialExpression.FRIENDLY,"Can I go in?").also { stage++ } - 4 -> npcl(FacialExpression.FRIENDLY, "He has asked not to be disturbed.").also { stage = END_DIALOGUE } - } - - 12 -> when (stage) { - 0 -> options("I need permission to enter a plague house.", "Who is through that door?", "I'm just looking thanks.").also { stage++ } - 1 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "I need permission to enter a plague house.").also { stage = 2 } - 2 -> playerl(FacialExpression.FRIENDLY, "Who is through that door?").also { stage = 12 } - 3 -> playerl(FacialExpression.FRIENDLY, "I'm just looking thanks.").also { stage = END_DIALOGUE } - } - 2 -> npcl(FacialExpression.FRIENDLY, "Rather you than me! The mourners usually deal with that stuff, you should speak to them. Their headquarters are right near the city gate.").also { stage++ } - 3 -> options("I'll try asking them then.", "Surely you don't let them run everything for you?").also { stage++ } - 4 -> when (buttonID) { - 1 -> playerl(FacialExpression.HALF_GUILTY, "I'll try asking them then.").also { stage = END_DIALOGUE } - 2 -> playerl(FacialExpression.HALF_GUILTY, "Surely you don't let them run everything for you?").also { stage = 5 } - } - 5 -> npcl(FacialExpression.FRIENDLY, "Well, they do know what they're doing here. If they did start doing something badly Bravek, the city warder, would have the power to override them. I can't see that happening though.").also { stage++ } - 6 -> options("I'll try asking them then.", "Can I speak to Bravek anyway?", "This is urgent though! Someone's been kidnapped!").also { stage++ } - 7 -> when (buttonID) { - 1 -> playerl(FacialExpression.HALF_GUILTY, "I'll try asking them then.").also { stage = END_DIALOGUE } - 2 -> playerl(FacialExpression.HALF_GUILTY, "Can I speak to Bravek anyway?").also { stage = 8 } - 3 -> playerl(FacialExpression.HALF_GUILTY, "This is urgent though! Someone's been kidnapped!").also { stage = 11 } - } - 8 -> npcl(FacialExpression.FRIENDLY, "He has asked not to be disturbed.").also { stage++ } - 9 -> options("This is urgent though! Someone's been kidnapped!", "Okay, I'll leave him alone.", "Do you know when he will be available?").also { stage++ } - 10 -> when (buttonID) { - 1 -> playerl(FacialExpression.HALF_GUILTY, "This is urgent though! Someone's been kidnapped!").also { stage = 11 } - 2 -> playerl(FacialExpression.HALF_GUILTY, "Okay, I'll leave him alone.").also { stage = END_DIALOGUE } - 3 -> playerl(FacialExpression.HALF_GUILTY, "Do you know when he will be available?").also { stage = 14 } - } - 11 -> npcl(FacialExpression.HALF_GUILTY, "I'll see what I can do I suppose.").also { stage++ } - 12 -> npcl(FacialExpression.HALF_GUILTY, "Mr Bravek, there's a man here who really needs to speak to you.").also { stage++ } - 13 -> { - end() - setQuestStage(player!!, "Plague City", 13) - sendNPCDialogue(player!!, NPCs.BRAVEK_711, "I suppose they can come in then. If they keep it short.").also { stage++ } - } - 14 -> npcl(FacialExpression.HALF_GUILTY, "Oh I don't know, an hour or so maybe.").also { stage = END_DIALOGUE } - } - - in 13..15 -> when (stage) { - 0 -> npcl(FacialExpression.FRIENDLY, "Bravek will see you now but keep it short!").also { stage++ } - 1 -> playerl(FacialExpression.FRIENDLY, "Thanks, I won't take much of his time.").also { stage = END_DIALOGUE } - } - - in 16..100 -> when (stage) { - 0 -> options("Who is through that door?", "I'm just looking thanks.").also { stage++ } - 1 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Who is through that door?").also { stage = 2 } - 2 -> playerl(FacialExpression.FRIENDLY, "I'm just looking thanks.").also { stage = END_DIALOGUE } - } - 2 -> npcl(FacialExpression.FRIENDLY, "The city warder Bravek is in there.").also { stage++ } - 3 -> playerl(FacialExpression.FRIENDLY,"Can I go in?").also { stage++ } - 4 -> npcl(FacialExpression.FRIENDLY, "I suppose so.").also { stage = END_DIALOGUE } - } - } - return true - } - - override fun getIds(): IntArray = intArrayOf(NPCs.CLERK_713) -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/EdmondDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/EdmondDialogue.kt deleted file mode 100644 index 9d77ce77b..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/EdmondDialogue.kt +++ /dev/null @@ -1,167 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena - -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import core.tools.END_DIALOGUE -import org.rs09.consts.Items -import org.rs09.consts.NPCs - -@Initializable -class EdmondDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - if(inEquipmentOrInventory(player, Items.GAS_MASK_1506) && (player.questRepository.getStage("Plague City") == 2)) { - playerl(FacialExpression.FRIENDLY, "Hi Edmond, I've got the gas mask now.").also { stage++ } - } else if(player.questRepository.getStage("Plague City") > 2) { - playerl(FacialExpression.FRIENDLY, "Hello Edmond.").also { stage++ } - } else { - playerl(FacialExpression.FRIENDLY, "Hello old man.").also { stage++ } - } - return true - } - - override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { - - 0 -> when (stage) { - 1 -> npcl(FacialExpression.NEUTRAL, "Sorry, I can't stop to talk...").also { stage++ } - 2 -> playerl(FacialExpression.FRIENDLY, "Why, what's wrong?").also { stage++ } - 3 -> npcl(FacialExpression.FRIENDLY, "I've got to find my daughter. I pray that she is still alive...").also { stage++ } - 4 -> options("What's happened to her?", "Well, good luck finding her.").also { stage++ } - 5 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "What's happened to her?").also { stage = 6 } - 2 -> playerl(FacialExpression.FRIENDLY, "Well, good luck finding her.").also { stage = END_DIALOGUE } - } - 6 -> npcl(FacialExpression.NEUTRAL, "Elena's a missionary and a healer. Three weeks ago she managed to cross the Ardougne wall...").also { stage++ } - 7 -> npcl(FacialExpression.NEUTRAL, "No-one's allowed to cross the wall in case they spread the plague. But after hearing the screams of suffering she felt she had to help.").also { stage++ } - 8 -> npcl(FacialExpression.NEUTRAL, "She said she'd be gone for a few days but we've heard nothing since.").also { stage++ } - 9 -> options("Tell me more about the plague.", "Can I help find her?", "I'm sorry, I have to go.").also { stage++ } - 10 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Tell me more about the plague.").also { stage = 12 } - 2 -> playerl(FacialExpression.FRIENDLY, "Can I help find her?").also { stage = 13 } - 3 -> playerl(FacialExpression.FRIENDLY, "I'm sorry, I have to go.").also { stage = END_DIALOGUE } - } - 12 -> npcl(FacialExpression.FRIENDLY, "The mourners can tell you more than me. They're the only ones allowed to cross the border. I do know the plague is a horrible way to go... That's why Elena felt she had to go help.").also { stage = 9 } - 13 -> npcl(FacialExpression.FRIENDLY, "Really, would you? I've been working on a plan to get into West Ardougne, but I'm too old and tired to carry it through.").also { stage++ } - 14 -> npcl(FacialExpression.FRIENDLY, "If you're going into West Ardougne you'll need protection from the plague. My wife made a special gas mask for Elena with dwellberries rubbed into it.").also { stage++ } - 15 -> npcl(FacialExpression.FRIENDLY, "Dwellberries help repel the virus! We need some more though...").also { stage++ } - 16 -> playerl(FacialExpression.ASKING, "Where can I find these dwellberries?").also { stage++ } - 17 -> npcl(FacialExpression.FRIENDLY, "The only place I know of is McGrubor's Wood just north of the Rangers' Guild.").also { stage++ } - 18 -> playerl(FacialExpression.FRIENDLY, "Ok, I'll go and get some.").also { stage++ } - 19 -> npcl(FacialExpression.NEUTRAL, "The foresters keep a close eye on it, but there is a back way in.").also { stage++ } - 20 -> { - end() - setQuestStage(player!!, PlagueCity.PlagueCityQuest, 1) - } - } - - 1 -> when (stage) { - 1 -> npcl(FacialExpression.NEUTRAL, "Have you got the dwellberries yet?").also { stage++ } - 2 -> if (!inInventory(player, Items.DWELLBERRIES_2126)) { - playerl(FacialExpression.FRIENDLY, "Sorry, I'm afraid not.").also { stage = 3 } - } else { - playerl(FacialExpression.FRIENDLY, "Yes I've got some here.").also { stage = 6 } - } - 3 -> npcl(FacialExpression.NEUTRAL, "You'll probably find them in McGrubor's Wood it's just west of Seers village.").also { stage++ } - 4 -> playerl(FacialExpression.NEUTRAL, "Ok, I'll go and get some.").also { stage++ } - 5 -> npcl(FacialExpression.NEUTRAL, "The foresters keep a close eye on it, but there is a back way in.").also { stage = END_DIALOGUE } - 6 -> npcl(FacialExpression.NEUTRAL, "Take them to my wife Alrena, she's inside.").also { stage = END_DIALOGUE } - } - - 2 -> when (stage) { - 1 -> npcl(FacialExpression.NEUTRAL, "Good stuff, now for the digging. Beneath us are the Ardougne sewers, there you'll find the access to West Ardougne.").also { stage++ } - 2 -> npcl(FacialExpression.NEUTRAL, "The problem is the soil is rock hard. You'll need to pour on several buckets of water to soften it up. I'll keep an eye out for the mourners.").also { stage++ } - 3 -> { - end() - setQuestStage(player!!, "Plague City", 3) - } - } - - 3 -> when (stage) { - 1 -> if (player!!.getAttribute("/save:elena:dig", false) == true) { - playerl(FacialExpression.NEUTRAL, "I've soaked the soil with water.").also { stage = 3 } - } else { - npcl(FacialExpression.FRIENDLY, "How's it going?").also { stage = 2 } - } - 2 -> if (player.getAttribute(PlagueCityListeners.BUCKET_USES_ATTRIBUTE, 0) == 1) { - playerl(FacialExpression.NEUTRAL, "I still need to pour three more buckets of water on the soil.").also { stage = END_DIALOGUE } - } else if (player.getAttribute(PlagueCityListeners.BUCKET_USES_ATTRIBUTE, 0) == 2){ - playerl(FacialExpression.NEUTRAL, "I still need to pour two more buckets of water on the soil.").also { stage = END_DIALOGUE } - } else if (player.getAttribute(PlagueCityListeners.BUCKET_USES_ATTRIBUTE, 0) == 3) { - playerl(FacialExpression.NEUTRAL, "I still need to pour one more bucket of water on the soil.").also { stage = END_DIALOGUE } - } - 3 -> npcl(FacialExpression.FRIENDLY, "That's great, it should be soft enough to dig through now.").also { stage = END_DIALOGUE } - } - - 4 -> when (stage) { - 1 -> npcl(FacialExpression.FRIENDLY, "I think it's the pipe to the south that comes up in West Ardougne.").also { stage++ } - 2 -> playerl(FacialExpression.NEUTRAL, "Alright I'll check it out.").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "Once you're in the city look for a man called Jethick, he's an old friend and should help you. Send him my regards, I haven't seen him since before Elena was born.").also { stage++ } - 4 -> playerl(FacialExpression.NEUTRAL, "Alright, thanks I will.").also { stage = END_DIALOGUE } - } - - 5 -> when (stage) { - 1 -> playerl(FacialExpression.NEUTRAL, "Edmond, I can't get through to West Ardougne! There's an iron grill blocking my way, I can't pull it off alone.").also { stage++ } - 2 -> npcl(FacialExpression.NEUTRAL, "If you get some rope you could tie to the grill, then we could both pull it at the same time.").also { stage = END_DIALOGUE } - } - - 6 -> when (stage) { - 1 -> playerl(FacialExpression.NEUTRAL, "I've tied a rope to the grill over there, will you help me pull it off?").also { stage++ } - 2 -> npcl(FacialExpression.NEUTRAL, "Alright, let's get to it...").also { stage++ } - 3 -> { - end() - UndergroundCutscene(player!!).start() - } - } - - 7 -> when (stage) { - 1 -> npcl(FacialExpression.NEUTRAL, "Have you found Elena yet?").also { stage++ } - 2 -> playerl(FacialExpression.NEUTRAL, "Not yet, it's a big city over there.").also { stage++ } - 3 -> npcl(FacialExpression.FRIENDLY, "Don't forget to look for my friend Jethick. He may be able to help.").also { stage = END_DIALOGUE } - } - - 8 -> when (stage) { - 1 -> npcl(FacialExpression.NEUTRAL, "Have you found Elena yet?").also { stage++ } - 2 -> playerl(FacialExpression.NEUTRAL, "Not yet, it's a big city over there. Do you have a picture of Elena?").also { stage++ } - 3 -> npcl(FacialExpression.FRIENDLY, "There should be a picture of Elena in the house. Please find her quickly, I hope it's not too late.").also { stage = END_DIALOGUE } - } - - 99 -> when (stage) { - 1 -> npcl(FacialExpression.NEUTRAL, "Thank you, thank you! Elena beat you back by minutes.").also { stage++ } - 2 -> npcl(FacialExpression.NEUTRAL, "Now I said I'd give you a reward. What can I give you as a reward I wonder?").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "Here take this magic scroll, I have little use for it but it may help you.").also { stage++ } - 4 -> { - end() - player!!.questRepository.getQuest("Plague City").finish(player) - } - } - - 100 -> when (stage) { - 1 -> if (!inInventory(player!!, Items.ARDOUGNE_TELEPORT_8011)) { - npcl(FacialExpression.FRIENDLY, "Ah hello again, and thank you again for rescuing my daughter.").also { stage = 2 } - } else { - npcl(FacialExpression.FRIENDLY, "Ah hello again, and thank you again for rescuing my daughter.").also { stage = 5 } - } - 2 -> options("Do you have any more of those scrolls?", "No problem.").also { stage++ } - 3 -> when (buttonID) { - 1 -> playerl(FacialExpression.NEUTRAL, "Do you have any more of those scrolls?").also { stage = 4 } - 2 -> playerl(FacialExpression.NEUTRAL, "No problem.").also { stage = END_DIALOGUE } - } - 4 -> npcl(FacialExpression.FRIENDLY, "Here take this magic scroll, I have little use for it but it may help you.").also { stage = 6 } - 5 -> playerl(FacialExpression.NEUTRAL, "No problem.").also { stage = END_DIALOGUE } - 6 -> { - end() - addItemOrDrop(player!!, Items.A_MAGIC_SCROLL_1505) - } - } - } - return true - } - - override fun getIds(): IntArray = intArrayOf(NPCs.EDMOND_714) -} diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MournerDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MournerDialogue.kt deleted file mode 100644 index 7dc148c27..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MournerDialogue.kt +++ /dev/null @@ -1,86 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena - -import core.api.* -import core.game.dialogue.DialogueFile -import core.game.dialogue.FacialExpression -import core.game.global.action.DoorActionHandler -import core.game.node.entity.npc.NPC -import core.game.world.map.RegionManager.getObject -import core.plugin.Initializable -import core.tools.END_DIALOGUE -import org.rs09.consts.NPCs - -@Initializable -class MournerDialogue : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - npc = NPC(NPCs.MOURNER_3216) - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { - - in 0..6 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } - 1 -> npcl(FacialExpression.NEUTRAL, "What are you up to with old man Edmond?").also { stage++ } - 2 -> playerl(FacialExpression.FRIENDLY, "Nothing, we've just been chatting.").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "What about his daughter?").also { stage++ } - 4 -> playerl(FacialExpression.FRIENDLY, "you know about that then?").also { stage++ } - 5 -> npcl(FacialExpression.NEUTRAL, "We know about everything that goes on in Ardougne. We have to if we are to contain the plague.").also { stage++ } - 6 -> playerl(FacialExpression.FRIENDLY, "Have you see his daughter recently?").also { stage++ } - 7 -> npcl(FacialExpression.NEUTRAL, "I imagine she's caught the plague. Either way she won't be allowed out of West Ardougne, the risk is too great.").also { stage == END_DIALOGUE } - } - - 7 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } - 1 -> npcl(FacialExpression.NEUTRAL, "Been digging have we?").also { stage++ } - 2 -> playerl(FacialExpression.FRIENDLY, "What do you mean?").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "Your hands are covered in mud.Player: Oh that...").also { stage++ } - 4 -> npcl(FacialExpression.NEUTRAL, "Funny, you don't look like the gardening type.").also { stage++ } - 5 -> playerl(FacialExpression.FRIENDLY, "Oh no, I love gardening! It's my favorite pastime.").also { stage = END_DIALOGUE } - } - - 8 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } - 1 -> npcl(FacialExpression.NEUTRAL, "Do you have a problem traveller?").also { stage++ } - 2 -> playerl(FacialExpression.NEUTRAL, "No, I just wondered why you're wearing that outfit... Is it fancy dress?").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "No! It's for protection.").also { stage++ } - 4 -> playerl(FacialExpression.NEUTRAL, "Protection from what?").also { stage++ } - 5 -> npcl(FacialExpression.FRIENDLY, "The plague of course...").also { stage = END_DIALOGUE } - } - - in 9..15 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } - 1 -> npcl(FacialExpression.NEUTRAL, "Can I help you?").also { stage++ } - 2 -> playerl(FacialExpression.NEUTRAL, "What are you doing?").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "I'm guarding the border to West Ardougne. No-one except we mourners can pass through.").also { stage++ } - 4 -> playerl(FacialExpression.NEUTRAL, "Why?").also { stage++ } - 5 -> npcl(FacialExpression.FRIENDLY, "The plague of course. We can't risk cross contamination.").also { stage++ } - 6 -> playerl(FacialExpression.FRIENDLY, "Ok then, see you around.").also { stage++ } - 7 -> npcl(FacialExpression.FRIENDLY, "Maybe...").also { stage = END_DIALOGUE } - } - - 16 -> when (stage) { - 0 -> if (inBorders(player!!, 2532, 3272, 2534, 3273)) { - player!!.dialogueInterpreter.sendDialogue("The door won't open.", "You notice a black cross on the door.").also { stage = END_DIALOGUE } - } else { - playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } - } - 1 -> playerl(FacialExpression.FRIENDLY, "I have a warrant from Bravek to enter here.").also { stage++ } - 2 -> npcl(FacialExpression.NEUTRAL, "This is highly irregular. Please wait...").also { stage++ } - 3 -> { - runTask(player!!, 0) { - findLocalNPC(player!!, NPCs.MOURNER_717)!!.sendChat("Hay... I got someone here with a warrant from Bravek, what should we do?") - findLocalNPC(player!!, NPCs.MOURNER_3216)!!.sendChat("Well you can't let them in...", 1) - }.also { - end() - setQuestStage(player!!, "Plague City", 17) - DoorActionHandler.handleAutowalkDoor(player, getObject(location(2540, 3273, 0))!!.asScenery()) - sendDialogue(player!!, "You wait until the mourner's back is turned and sneak into the building.") - } - } - } - - in 17..100 -> when (stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "I'd stand away from there. That black cross means that house has been touched by the plague.").also { stage = END_DIALOGUE } - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/PlagueCityListeners.kt b/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/PlagueCityListeners.kt deleted file mode 100644 index 469f9302a..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/PlagueCityListeners.kt +++ /dev/null @@ -1,456 +0,0 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena - -import content.region.kandarin.ardougne.plaguecity.dialogue.ManDialogue -import content.region.kandarin.ardougne.plaguecity.dialogue.WomanDialogue -import core.api.* -import core.game.dialogue.DialogueFile -import core.game.dialogue.FacialExpression -import core.game.global.action.DoorActionHandler -import core.game.interaction.IntType -import core.game.interaction.InteractionListener -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.map.Direction -import core.game.world.map.Location -import core.tools.END_DIALOGUE -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import org.rs09.consts.Scenery - -class PlagueCityListeners : InteractionListener { - companion object { - - const val BUCKET_USES_ATTRIBUTE = "/save:elena:bucket" - - const val BRAVEK = NPCs.BRAVEK_711 - const val HEAD_MOURNER = NPCs.HEAD_MOURNER_716 - const val BILLI = 723 - - val MANS = intArrayOf(NPCs.MAN_728,NPCs.MAN_729, NPCs.MAN_351) - val WOMANS = intArrayOf(NPCs.WOMAN_352, NPCs.WOMAN_353, NPCs.WOMAN_354, NPCs.WOMAN_360, NPCs.WOMAN_362, NPCs.WOMAN_363) - - const val MUD_PILE = Scenery.MUD_PILE_2533 - const val GRILL = Scenery.GRILL_11423 - const val MUD_PATCH = Scenery.MUD_PATCH_11418 - const val PIPE = Scenery.PIPE_2542 - const val WARDROBE = Scenery.WARDROBE_2525 - const val LEFT_DOOR = Scenery.ARDOUGNE_WALL_DOOR_9738 - const val RIGHT_DOOR = Scenery.ARDOUGNE_WALL_DOOR_9330 - const val PLAGUE_TED_DOORS = Scenery.DOOR_2537 - const val HEAD_DOORS = Scenery.DOOR_35991 - const val BARREL = Scenery.BARREL_2530 - const val SPOOKY_STAIRS_DOWN = Scenery.SPOOKY_STAIRS_2522 - const val SPOOKY_STAIRS_UP = Scenery.SPOOKY_STAIRS_2523 - const val PRISON_DOORS = Scenery.DOOR_2526 - const val BRAVEK_DOORS = Scenery.DOOR_2528 - const val MANHOLE_CLOSED = Scenery.MANHOLE_2543 - const val MANHOLE_OPEN = Scenery.MANHOLE_2544 - const val MANHOLE_COVER = Scenery.MANHOLE_COVER_2545 - - private const val SNAPE_GRASS = Items.SNAPE_GRASS_231 - private const val SPADE = Items.SPADE_952 - private const val ROPE = Items.ROPE_954 - private const val HANGOVER_CURE = Items.HANGOVER_CURE_1504 - private const val MAGIC_SCROLL = Items.A_MAGIC_SCROLL_1505 - private const val GAS_MASK = Items.GAS_MASK_1506 - private const val SMALL_KEY = Items.A_SMALL_KEY_1507 - private const val SCRUFFY_NOTE = Items.A_SCRUFFY_NOTE_1508 - private const val BOOK = Items.BOOK_1509 - private const val EMPTY_BUCKET = Items.BUCKET_1925 - private const val BUCKET_OF_MILK = Items.BUCKET_OF_MILK_1927 - private const val BUCKET_OF_WATER = Items.BUCKET_OF_WATER_1929 - private const val CHOCOLATE_DUST = Items.CHOCOLATE_DUST_1975 - private const val CHOCOLATE_MILK = Items.CHOCOLATEY_MILK_1977 - - private const val TRYING_TO_OPEN_GRILL = 3192 - private const val POUR_THE_WATER = 2283 - private const val CLIMB_LADDER = 828 - private const val GO_INTO_PIPE = 10580 - private const val TIE_THE_ROPE = 3191 - private const val DIG_WITH_SPADE = 830 - - } - - override fun defineListeners() { - - on(BILLI, IntType.NPC, "talk-to") { player, _ -> - sendMessage(player, "Billy isn't interested in talking.") - return@on true - } - - on(HEAD_MOURNER, IntType.NPC, "talk-to") { player, _ -> - openDialogue(player, HeadMournerDialogue()) - return@on true - } - - on(MANS, IntType.NPC, "talk-to") { player, _ -> - if(inBorders(player, 2496, 3280,2557, 3336)) { - openDialogue(player, ManDialogue()) - } - return@on true - } - - on(WOMANS, IntType.NPC, "talk-to") { player, _ -> - if(inBorders(player, 2496, 3280,2557, 3336)) { - openDialogue(player, WomanDialogue()) - } - return@on true - } - - on(LEFT_DOOR, IntType.SCENERY, "open") { player, node -> - if (player.questRepository.getQuest("Plague City").isCompleted(player)) { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } else if(inBorders(player, 2556, 3298, 2557, 3301)){ - lock(player,2) - sendMessage(player, "You pull on the large wooden doors...") - runTask(player,2){ - sendMessage(player, "...But they will not open.") - } - } else { - face(player, Location.create(2559, 3302, 0)) - sendNPCDialogue(player, NPCs.MOURNER_2349, "Oi! What are you doing? Get away from there!") - } - return@on true - } - - on(RIGHT_DOOR, IntType.SCENERY, "open") { player, node -> - if (player.questRepository.getQuest("Plague City").isCompleted(player)) { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } else if(inBorders(player, 2556, 3298, 2557, 3301)){ - lock(player,2) - sendMessage(player, "You pull on the large wooden doors...") - runTask(player,2){ - sendMessage(player, "...But they will not open.") - } - } else { - face(player, Location.create(2559, 3302, 0)) - sendNPCDialogue(player, NPCs.MOURNER_2349, "Oi! What are you doing? Get away from there!") - } - return@on true - } - - on(MANHOLE_CLOSED, IntType.SCENERY, "open") { player, node -> - replaceScenery(node.asScenery(), MANHOLE_OPEN, -1) - addScenery(MANHOLE_COVER, Location(2529, 3302, 0),0,10) - sendMessage(player, "You pull back the manhole cover.") - return@on true - } - - on(MANHOLE_COVER, IntType.SCENERY, "close") { player, node -> - removeScenery(node.asScenery()) - getScenery(location(2529, 3303, 0))?.let { replaceScenery(it, MANHOLE_CLOSED, -1) } - sendMessage(player, "You close the manhole cover.") - return@on true - } - - on(MANHOLE_OPEN, IntType.SCENERY, "climb-down") { player, _ -> - teleport(player, Location(2514, 9739, 0)) - sendMessage(player, "You climb down through the manhole.") - return@on true - } - - on(BRAVEK_DOORS, IntType.SCENERY, "open") { player, node -> - if (player.questRepository.getStage("Plague City") >= 13) { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } else { - sendNPCDialogue(player,BRAVEK,"Go away, I'm busy! I'm... Umm... In a meeting!") - } - return@on true - } - - on(MUD_PILE, IntType.SCENERY, "climb") { player, _ -> - animate(player, CLIMB_LADDER) - runTask(player, 2){ - teleport(player, Location(2566, 3332)) - sendDialogue(player, "You climb up the mud pile.") - } - return@on true - } - - on(MAGIC_SCROLL, IntType.ITEM, "read") { player, _ -> - sendItemDialogue(player, MAGIC_SCROLL, "You memorise what is written on the scroll.") - removeItem(player, MAGIC_SCROLL) - sendDialogue(player, "You can now cast the Ardougne Teleport spell provided you have the required runes and magic level.") - return@on true - } - - on(SCRUFFY_NOTE, IntType.ITEM, "read") { player, _ -> - sendMessage(player, "You guess it really says something slightly different.") - openInterface(player, 222).also { scruffyNote(player) } - return@on true - } - - on(HEAD_DOORS, IntType.SCENERY, "open") { player, node -> - if (player.questRepository.getStage("Plague City") == 11) { - openDialogue(player, HeadMournerDialogue()) - } else if (player.questRepository.getStage("Plague City") == 16) { - openDialogue(player, MournerDialogue()) - } else if (player.questRepository.getStage("Plague City") > 16) { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } else { - openDialogue(player, MournerDialogue()) - } - return@on true - } - - on(WARDROBE, IntType.SCENERY, "search") { player, _ -> - if (freeSlots(player) == 0 && !inEquipmentOrInventory(player, GAS_MASK)) { - sendItemDialogue(player, GAS_MASK, "You find a protective mask but you don't have enough room to take it.") - } else if (inEquipmentOrInventory(player, GAS_MASK)) { - sendMessage(player, "You search the wardrobe but you find nothing.") - } else if (player.questRepository.getStage("Plague City") >= 2) { - sendItemDialogue(player, GAS_MASK, "You find a protective mask.") - addItem(player, GAS_MASK) - } - return@on true - } - - onUseWith(IntType.SCENERY, BUCKET_OF_WATER, MUD_PATCH) { player, _, _ -> - if (player.getAttribute(BUCKET_USES_ATTRIBUTE, 0) in 0..2 && removeItem(player, BUCKET_OF_WATER)) { - animate(player, POUR_THE_WATER) - player.dialogueInterpreter.sendDialogue( - "You pour water onto the soil.", - "The soil softens slightly." - ) - player.incrementAttribute(BUCKET_USES_ATTRIBUTE, 1) - addItem(player, EMPTY_BUCKET) - return@onUseWith true - } else if (player.getAttribute(BUCKET_USES_ATTRIBUTE, 0) == 3 && removeItem(player, BUCKET_OF_WATER)) { - animate(player, POUR_THE_WATER) - player.dialogueInterpreter.sendDialogue( - "You pour water onto the soil.", - "The soil is now soft enough to dig into." - ) - player.setAttribute("/save:elena:dig", true) - addItem(player, EMPTY_BUCKET) - } else { - sendMessage(player, "Nothing interesting happens.") - } - return@onUseWith true - } - - onUseWith(IntType.SCENERY, SPADE, MUD_PATCH) { player, _, _ -> - if (player.getAttribute("/save:elena:dig", false) == true) { - player.pulseManager.run(object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 0 -> sendItemDialogue(player, SPADE,"You dig deep into the soft soil... Suddenly it crumbles away!") - 1 -> animate(player, DIG_WITH_SPADE) - 3 -> { - teleport(player, Location(2518, 9759)) - setQuestStage(player, "Plague City", 4) - player.dialogueInterpreter.sendDialogue( - "You fall through...", - "...you land in the sewer.", - "Edmond follows you down the hole." - ) - return true - } - } - return false - } - }) - } else { - sendMessage(player, "Nothing interesting happens.") - } - return@onUseWith true - } - - on(GRILL, IntType.SCENERY, "open") { player, _ -> - if (player.questRepository.getStage("Plague City") == 4) { - sendDialogue(player, "The grill is too secure. You can't pull it off alone.") - animate(player, TRYING_TO_OPEN_GRILL) - setQuestStage(player, "Plague City", 5) - } else { - sendDialogue(player, "There is a grill blocking your way") - } - return@on true - } - - on(PIPE, IntType.SCENERY, "climb-up") { player, _ -> - if (player.questRepository.getStage("Plague City") >= 7 && inEquipment(player, GAS_MASK)) { - animate(player, GO_INTO_PIPE,true) - forceMove(player, Location(2514, 9739, 0), Location(2514, 9734, 0), 0, 4,Direction.SOUTH) - runTask(player, 3) { - teleport(player, Location(2529, 3304, 0)) - sendDialogue(player, "You climb up through the sewer pipe.") - } - } else if (player.questRepository.getStage("Plague City") >= 7 && !inEquipment(player, GAS_MASK)) { - sendNPCDialogue(player, NPCs.EDMOND_714, "I can't let you enter the city without your gasmask on.") - } else { - sendDialogue(player, "There is a grill blocking your way") - } - return@on true - } - - onUseWith(IntType.SCENERY, ROPE, PIPE) { player, _, _ -> - sendPlayerDialogue(player, "Maybe I should try opening it first.") - return@onUseWith true - } - - onUseWith(IntType.SCENERY, ROPE, GRILL) { player, _, _ -> - if(removeItem(player, ROPE)) { - player.pulseManager.run(object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 0 -> forceWalk(player, Location.create(2514, 9740, 0), "SMART") - 2 -> face(player, Location.create(2514, 9739, 0), -1) - 3 -> { - animate(player, TIE_THE_ROPE) - setVarbit(player, 1787, 5, true) // Tied rope to the grill. - } - 4 -> { - setQuestStage(player, "Plague City", 6) - sendItemDialogue(player, ROPE, "You tie the end of the rope to the sewer pipe's grill.") - } - } - return false - } - }) - } else { - sendMessage(player, "Nothing interesting happens.") - } - return@onUseWith true - } - - class TedRehnisonDoors : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - npc = NPC(NPCs.TED_REHNISON_721) - when (stage) { - 0 -> if(removeItem(player!!, BOOK)){ - playerl(FacialExpression.NEUTRAL, "I'm a friend of Jethick's, I have come to return a book he borrowed.").also { stage++ } - } else { - npcl(FacialExpression.FRIENDLY, "Go away. We don't want any.").also { stage = END_DIALOGUE } - } - 1 -> npcl(FacialExpression.FRIENDLY, "Oh... why didn't you say, come in then.").also { stage++ } - 2 -> sendItemDialogue(player!!, BOOK, "You hand the book to Ted as you enter.").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "Thanks, I've been missing that.").also { stage++ } - 4 -> { - end() - DoorActionHandler.handleAutowalkDoor(player, getScenery(2531, 3328, 0)) - setQuestStage(player!!, "Plague City", 9) - } - } - } - } - - on(PLAGUE_TED_DOORS, IntType.SCENERY, "open") { player, node -> - if (player.questRepository.getStage("Plague City") >= 9) { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } else { - openDialogue(player, TedRehnisonDoors()) - } - return@on true - } - - on(BARREL, IntType.SCENERY, "search") { player, _ -> - if (inInventory(player, SMALL_KEY)) { - sendMessage(player, "You don't find anything interesting.") - return@on true - } else { - sendItemDialogue(player, SMALL_KEY, "You find a small key in the barrel.") - addItem(player, SMALL_KEY) - } - } - - onUseWith(IntType.ITEM, CHOCOLATE_DUST, BUCKET_OF_MILK) { player, _, _ -> - if (player.questRepository.hasStarted("Plague City") && removeItem(player, CHOCOLATE_DUST) && removeItem(player, BUCKET_OF_MILK)) { - sendItemDialogue(player, CHOCOLATE_MILK, "You mix the chocolate into the bucket.") - addItem(player, CHOCOLATE_MILK) - } else { - sendMessage(player, "Nothing interesting happens.") - } - return@onUseWith true - } - - onUseWith(IntType.ITEM, SNAPE_GRASS, CHOCOLATE_MILK) { player, _, _ -> - if (player.questRepository.hasStarted("Plague City") && removeItem(player, SNAPE_GRASS) && removeItem(player, CHOCOLATE_MILK)) { - sendItemDialogue(player, HANGOVER_CURE, "You mix the snape grass into the bucket.") - addItem(player, HANGOVER_CURE) - } else { - sendMessage(player, "Nothing interesting happens.") - } - return@onUseWith true - } - - on(SPOOKY_STAIRS_DOWN, IntType.SCENERY, "walk-down") { player, _ -> - sendMessage(player, "You walk down the stairs...") - teleport(player, Location.create(2537, 9671)) - return@on true - } - - on(SPOOKY_STAIRS_UP, IntType.SCENERY, "walk-up") { player, _ -> - teleport(player, Location.create(2536, 3271, 0)) - sendMessage(player, "You walk up the stairs...") - return@on true - } - - class ElenaDoorDialogue : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - npc = NPC(NPCs.ELENA_3215) - when (stage) { - 0 -> sendDialogue(player!!, "The door is locked.").also { stage++ } - 1 -> npcl(FacialExpression.CRYING, "Hey get me out of here please!").also { stage++ } - 2 -> playerl(FacialExpression.FRIENDLY, "I would do but I don't have a key.").also { stage++ } - 3 -> npcl(FacialExpression.SAD, "I think there may be one around somewhere. I'm sure I heard them stashing it somewhere.").also { stage++ } - 4 -> options("Have you caught the plague?", "Okay, I'll look for it.").also { stage++ } - 5 -> when (buttonID) { - 1 -> playerl(FacialExpression.FRIENDLY, "Have you caught the plague?").also { stage = 6 } - 2 -> playerl(FacialExpression.FRIENDLY, "Okay, I'll look for it.").also { stage = END_DIALOGUE } - } - 6 -> npcl(FacialExpression.HALF_WORRIED, "No, I have none of the symptoms.").also { stage++ } - 7 -> playerl(FacialExpression.THINKING, "Strange, I was told this house was plague infected.").also { stage++ } - 8 -> playerl(FacialExpression.THINKING, "I suppose that was a cover up by the kidnappers.").also { stage = 4 } - } - } - } - - onUseWith(IntType.SCENERY, SMALL_KEY, PRISON_DOORS) { player, _, _ -> - if (player.questRepository.getStage("Plague City") >= 16) { - DoorActionHandler.handleAutowalkDoor(player, core.game.world.map.RegionManager.getObject(Location(2539, 9672, 0))!!.asScenery()) - sendDialogue(player, "You unlock the door.") - } else { - sendMessage(player, "Nothing interesting happens.") - } - return@onUseWith true - } - - on(PRISON_DOORS, IntType.SCENERY, "open") { player, node -> - if (player.questRepository.getStage("Plague City") >= 99) { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } else { - openDialogue(player, ElenaDoorDialogue()) - } - return@on true - } - } - - private fun scruffyNote(player: Player) { - val scruffynotes = - arrayOf( - "Got a bncket of nnilk", - "Tlen grind sorne lhoculate", - "vnith a pestal and rnortar", - "ald the grourd dlocolate to tho milt", - "finales add 5cme snape gras5", - ) - setInterfaceText(player, scruffynotes.joinToString("
"), 222, 5) - } - - override fun defineDestinationOverrides() { - setDest(IntType.SCENERY, intArrayOf(GRILL), "open") { _, _ -> - return@setDest Location.create(2514, 9739, 0) - } - - setDest(IntType.SCENERY, intArrayOf(PIPE), "climb-up") { _, _ -> - return@setDest Location.create(2514, 9739, 0) - } - - setDest(IntType.SCENERY, intArrayOf(MANHOLE_OPEN), "climb-down") { _, _ -> - return@setDest Location.create(2529, 3304, 0) - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArena.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArena.kt index 20ac88e15..9c97b7110 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArena.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArena.kt @@ -6,13 +6,12 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable -import org.rs09.consts.Items import org.rs09.consts.Items.COINS_995 +import content.data.Quests @Initializable -class FightArena : Quest("Fight Arena", 61, 60, 2, 17, 0, 1, 14) { +class FightArena : Quest(Quests.FIGHT_ARENA, 61, 60, 2, 17, 0, 1, 14) { override fun newInstance(`object`: Any?): Quest { return this } - companion object { const val FightArenaQuest = "Fight Arena" } override fun drawJournal(player: Player?, stage: Int) { super.drawJournal(player, stage) var line = 11 diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArenaListeners.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArenaListeners.kt index 1150597ca..8e4526390 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArenaListeners.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArenaListeners.kt @@ -13,6 +13,7 @@ import core.game.world.map.Location import org.rs09.consts.Items import org.rs09.consts.NPCs import org.rs09.consts.Scenery +import content.data.Quests class FightArenaListeners : InteractionListener { companion object { @@ -100,7 +101,7 @@ class FightArenaListeners : InteractionListener { } on(FULL_ARMOR_STAND, IntType.SCENERY, "borrow") { player, _ -> - if (player.questRepository.getStage("Fight Arena") >= 10 && !inEquipmentOrInventory(player, HELMET) && !inEquipmentOrInventory(player, ARMOR) && freeSlots(player) >= 2) { + if (player.questRepository.getStage(Quests.FIGHT_ARENA) >= 10 && !inEquipmentOrInventory(player, HELMET) && !inEquipmentOrInventory(player, ARMOR) && freeSlots(player) >= 2) { replaceScenery(FULL_ARMOR_STAND_1!!.asScenery(), EMPTY_STAND, 10,location(2619, 3196, 0)) sendMessage(player, "You borrow the suit of armour. It looks like it's just your size.") addItem(player, ARMOR, 1) @@ -162,7 +163,7 @@ class FightArenaListeners : InteractionListener { } onUseWith(IntType.SCENERY, CELL_KEY, CELL_DOOR_1) { player, _, _ -> - if (player.questRepository.getStage("Fight Arena") >= 68){ + if (player.questRepository.getStage(Quests.FIGHT_ARENA) >= 68){ sendDialogue(player, "I don't want to attract too much attention by freeing all the prisoners. I need to find Jeremy and he's not in this cell.") } else { sendMessage(player, "The cell gate is securely locked.") @@ -181,9 +182,9 @@ class FightArenaListeners : InteractionListener { } on(CENTER_DOOR, IntType.SCENERY, "open") { player, node -> - if (player.questRepository.getStage("Fight Arena") >= 91) { + if (player.questRepository.getStage(Quests.FIGHT_ARENA) >= 91) { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } else if (player.questRepository.getStage("Fight Arena") < 91) { + } else if (player.questRepository.getStage(Quests.FIGHT_ARENA) < 91) { sendNPCDialogue(player, NPCs.KHAZARD_GUARD_255, "And where do you think you're going? Only General Khazard decides who fights in the arena. Get out of here.", FacialExpression.ANNOYED) } else { sendMessage(player, "The gate is locked.") @@ -254,7 +255,7 @@ class FightArenaListeners : InteractionListener { 2 -> { end() lock(player!!, 2) - setQuestStage(player!!, FightArena.FightArenaQuest, 20) + setQuestStage(player!!, Quests.FIGHT_ARENA, 20) DoorActionHandler.handleAutowalkDoor(player, getScenery(2617, 3172, 0)) } } @@ -276,7 +277,7 @@ class FightArenaListeners : InteractionListener { 2 -> { end() lock(player!!, 2) - setQuestStage(player!!, FightArena.FightArenaQuest, 20) + setQuestStage(player!!, Quests.FIGHT_ARENA, 20) DoorActionHandler.handleAutowalkDoor(player, getScenery(2584, 3141, 0)) } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/ALazyGuardDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/ALazyGuardDialogue.kt index 945a4826b..2a4ffa5a8 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/ALazyGuardDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/ALazyGuardDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.ardougne.quest.arena.dialogue -import content.region.kandarin.ardougne.quest.arena.FightArena +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -15,7 +15,7 @@ import org.rs09.consts.NPCs class ALazyGuardDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { npc = NPC(NPCs.A_LAZY_KHAZARD_GUARD_8498) - when (getQuestStage(player!!, FightArena.FightArenaQuest)) { + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { in 40..49 -> when (stage) { 0 -> { @@ -27,7 +27,7 @@ class ALazyGuardDialogue : DialogueFile() { 3 -> npcl(FacialExpression.FRIENDLY, "Now I just want a decent drink. Mind you, too much Khali brew and I'll fall asleep.").also { stage++ } 4 -> { end() - setQuestStage(player!!, FightArena.FightArenaQuest, 50) + setQuestStage(player!!, Quests.FIGHT_ARENA, 50) } } @@ -71,7 +71,7 @@ class ALazyGuardDialogue : DialogueFile() { 14 -> { end() setVarbit(player!!, 5627, 2) - setQuestStage(player!!, FightArena.FightArenaQuest, 68) + setQuestStage(player!!, Quests.FIGHT_ARENA, 68) } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GeneralKhazardDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GeneralKhazardDialogue.kt index a7eb7248f..11b516bbc 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GeneralKhazardDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GeneralKhazardDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.ardougne.quest.arena.dialogue -import content.region.kandarin.ardougne.quest.arena.FightArena +import content.data.Quests import content.region.kandarin.ardougne.quest.arena.FightArenaListeners.Companion.General import content.region.kandarin.ardougne.quest.arena.cutscenes.JailCutscene import content.region.kandarin.ardougne.quest.arena.cutscenes.ThirdFightCutscene @@ -16,7 +16,7 @@ import org.rs09.consts.NPCs class GeneralKhazardDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { npc = NPC(NPCs.GENERAL_KHAZARD_258) - when (getQuestStage(player!!, FightArena.FightArenaQuest)) { + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { in 68..70 -> when(stage){ 0 -> npcl(FacialExpression.FRIENDLY, "Out of the way, guard! I don't tolerate disruption when I'm watching slaves being slaughtered.").also { stage = END_DIALOGUE } @@ -109,7 +109,7 @@ class GeneralKhazardDialogue : DialogueFile() { 4 -> npcl(FacialExpression.OLD_EVIL_LAUGH, "You however have coused me much trouble today. You must remain here so that I may at least have the pleasure of killing you myself.").also { stage++ } 5 -> { end() - setQuestStage(player!!, FightArena.FightArenaQuest, 97) + setQuestStage(player!!, Quests.FIGHT_ARENA, 97) RegionManager.getNpc(player!!.location, NPCs.GENERAL_KHAZARD_258, 15) General.attack(player!!) } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GuardsDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GuardsDialogue.kt index 5d830e076..77fd6c123 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GuardsDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GuardsDialogue.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import org.rs09.consts.Items import org.rs09.consts.NPCs +import content.data.Quests @Initializable @@ -16,7 +17,7 @@ class GuardsDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Fight Arena") == 100) { + if (player.questRepository.getStage(Quests.FIGHT_ARENA) == 100) { npcl(FacialExpression.FRIENDLY, "It's you! I don't believe it. You beat the General! You are a traitor to the uniform!").also { stage = END_DIALOGUE } } else if (allInEquipment(player, Items.KHAZARD_HELMET_74, Items.KHAZARD_ARMOUR_75)) { playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } @@ -51,7 +52,7 @@ class GuardsDialogue(player: Player? = null) : DialoguePlugin(player) { class KhazardGuard254Dialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Fight Arena") == 100) { + if (player.questRepository.getStage(Quests.FIGHT_ARENA) == 100) { npcl(FacialExpression.FRIENDLY, "It's you! I don't believe it. You beat the General! You are a traitor to the uniform!").also { stage = END_DIALOGUE } } else if (allInEquipment(player, Items.KHAZARD_HELMET_74, Items.KHAZARD_ARMOUR_75)) { playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } @@ -95,7 +96,7 @@ class KhazardGuard254Dialogue(player: Player? = null) : DialoguePlugin(player) { class KhazardGuard255Dialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Fight Arena") == 100) { + if (player.questRepository.getStage(Quests.FIGHT_ARENA) == 100) { npcl(FacialExpression.FRIENDLY, "It's you! I don't believe it. You beat the General! You are a traitor to the uniform!").also { stage = END_DIALOGUE } } else if (allInEquipment(player, Items.KHAZARD_HELMET_74, Items.KHAZARD_ARMOUR_75)) { playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } @@ -130,7 +131,7 @@ class KhazardGuard256Dialogue(player: Player? = null) : DialoguePlugin(player) { npc = args[0] as NPC if (allInEquipment(player, Items.KHAZARD_HELMET_74, Items.KHAZARD_ARMOUR_75)) { playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } - } else if (player.questRepository.getStage("Fight Arena") == 100) { + } else if (player.questRepository.getStage(Quests.FIGHT_ARENA) == 100) { npcl(FacialExpression.FRIENDLY, "It's you! I don't believe it. You beat the General! You are a traitor to the uniform!").also { stage = END_DIALOGUE } } else { playerl(FacialExpression.FRIENDLY, "Hi.").also { stage = 3 } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/HengradDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/HengradDialogue.kt index 8d6eccb01..bd31acf9f 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/HengradDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/HengradDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.ardougne.quest.arena.dialogue -import content.region.kandarin.ardougne.quest.arena.FightArena +import content.data.Quests import content.region.kandarin.ardougne.quest.arena.cutscenes.SecondFightCutscene import core.api.* import core.game.dialogue.DialogueFile @@ -11,7 +11,7 @@ import org.rs09.consts.NPCs class HengradDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { npc = NPC(NPCs.HENGRAD_263) - when (getQuestStage(player!!, FightArena.FightArenaQuest)) { + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { in 72..87 -> when (stage) { 0 -> { @@ -34,7 +34,7 @@ class HengradDialogue : DialogueFile() { 10 -> { end() SecondFightCutscene(player!!).start() - setQuestStage(player!!, FightArena.FightArenaQuest, 88) + setQuestStage(player!!, Quests.FIGHT_ARENA, 88) } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilADialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilADialogue.kt index 026f61151..e92454d6f 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilADialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilADialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.ardougne.quest.arena.dialogue -import content.region.kandarin.ardougne.quest.arena.FightArena +import content.data.Quests import content.region.kandarin.ardougne.quest.arena.cutscenes.EscapeCutscene import core.api.* import core.game.dialogue.DialogueFile @@ -13,7 +13,7 @@ class JeremyServilADialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { npc = NPC(NPCs.JEREMY_SERVIL_265) - when (getQuestStage(player!!, FightArena.FightArenaQuest)) { + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { 20 -> when (stage) { 0 -> { @@ -29,7 +29,7 @@ class JeremyServilADialogue : DialogueFile() { 4 -> playerl(FacialExpression.FRIENDLY, "Don't lose heart, I'll be back.").also { stage++ } 5 -> { end() - setQuestStage(player!!, FightArena.FightArenaQuest, 40) + setQuestStage(player!!, Quests.FIGHT_ARENA, 40) } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilBDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilBDialogue.kt index 7e4260779..7f87687c2 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilBDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilBDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.ardougne.quest.arena.dialogue -import content.region.kandarin.ardougne.quest.arena.FightArena +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -12,7 +12,7 @@ class JeremyServilBDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { npc = NPC(NPCs.JEREMY_SERVIL_266) - when (getQuestStage(player!!, FightArena.FightArenaQuest)) { + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { in 1..84 -> when (stage) { 0 -> { @@ -31,7 +31,7 @@ class JeremyServilBDialogue : DialogueFile() { 1 -> npcl(FacialExpression.FRIENDLY, "Thank you, we are truly indebted to you.").also { stage++ } 2 -> { end() - setQuestStage(player!!, FightArena.FightArenaQuest, 99) + setQuestStage(player!!, Quests.FIGHT_ARENA, 99) } } @@ -47,7 +47,7 @@ class JeremyServilBDialogue : DialogueFile() { } 2 -> { end() - setQuestStage(player!!, FightArena.FightArenaQuest, 99) + setQuestStage(player!!, Quests.FIGHT_ARENA, 99) } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JustinServilDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JustinServilDialogue.kt index a761d2b1e..3244d30f0 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JustinServilDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JustinServilDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.ardougne.quest.arena.dialogue -import content.region.kandarin.ardougne.quest.arena.FightArena +import content.data.Quests import core.api.face import core.api.findNPC import core.api.getQuestStage @@ -14,7 +14,7 @@ import org.rs09.consts.NPCs class JustinServilDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { npc = NPC(NPCs.JUSTIN_SERVIL_267) - when (getQuestStage(player!!, FightArena.FightArenaQuest)) { + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { in 1..68 -> when (stage) { 0 -> playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/KhazardBarmanDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/KhazardBarmanDialogue.kt index d10c13e6a..96c91c8e1 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/KhazardBarmanDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/KhazardBarmanDialogue.kt @@ -1,10 +1,7 @@ package content.region.kandarin.ardougne.quest.arena.dialogue -import content.region.kandarin.ardougne.quest.arena.FightArena -import core.api.addItem -import core.api.getQuestStage -import core.api.removeItem -import core.api.setQuestStage +import content.data.Quests +import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -17,14 +14,14 @@ import org.rs09.consts.NPCs class KhazardBarmanDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { npc = NPC(NPCs.KHAZARD_BARMAN_259) - when (getQuestStage(player!!, FightArena.FightArenaQuest)) { + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { in 0..49 -> { when (stage) { 0 -> playerl(FacialExpression.HAPPY, "Hello. I'll have a beer please.").also { stage = 1 } 1 -> npcl(FacialExpression.FRIENDLY, "There you go, that's two gold coins.").also { stage = 2 } 2 -> if (removeItem(player!!, Item(COINS_995, 2))) { end() - addItem(player!!, Items.BEER_1917, 1) + addItemOrDrop(player!!, Items.BEER_1917, 1) stage = END_DIALOGUE } else { end() @@ -49,7 +46,7 @@ class KhazardBarmanDialogue : DialogueFile() { 7 -> npcl(FacialExpression.FRIENDLY, "There you go, that's five gold coins. I suggest lying down before you drink it. That way you have less distance to collapse.").also { stage = 9 } 8 -> if (removeItem(player!!, Item(COINS_995, 2))){ end() - addItem(player!!, Items.BEER_1917, 1) + addItemOrDrop(player!!, Items.BEER_1917, 1) stage = END_DIALOGUE } else { end() @@ -57,8 +54,8 @@ class KhazardBarmanDialogue : DialogueFile() { } 9 -> if (removeItem(player!!, Item(COINS_995, 5))){ end() - addItem(player!!, Items.KHALI_BREW_77, 1) - setQuestStage(player!!, FightArena.FightArenaQuest, 60) + addItemOrDrop(player!!, Items.KHALI_BREW_77, 1) + setQuestStage(player!!, Quests.FIGHT_ARENA, 60) stage = END_DIALOGUE } else { end() diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LadyServilDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LadyServilDialogue.kt index afaadc7fe..fc30c1ca1 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LadyServilDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LadyServilDialogue.kt @@ -1,6 +1,5 @@ package content.region.kandarin.ardougne.quest.arena.dialogue -import content.region.kandarin.ardougne.quest.arena.FightArena.Companion.FightArenaQuest import core.api.getQuestStage import core.api.setQuestStage import core.game.dialogue.DialoguePlugin @@ -10,17 +9,18 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import core.tools.END_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests @Initializable class LadyServilDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC playerl(FacialExpression.FRIENDLY, "Hi there, looks like you're in some trouble.") - if (player.questRepository.getStage("Fight Arena") == 10) { + if (player.questRepository.getStage(Quests.FIGHT_ARENA) == 10) { playerl(FacialExpression.FRIENDLY, "Hello Lady Servil.") - } else if (player.questRepository.getStage("Fight Arena") == 30) { + } else if (player.questRepository.getStage(Quests.FIGHT_ARENA) == 30) { playerl(FacialExpression.FRIENDLY, "Lady Servil, I have managed to infiltrate General Khazard's arena.") - } else if (player.questRepository.getStage("Fight Arena") == 70) { + } else if (player.questRepository.getStage(Quests.FIGHT_ARENA) == 70) { playerl(FacialExpression.FRIENDLY, "Lady Servil. I freed your son, however he has returned to the arena to help your husband.").also { stage++ } } else { playerl(FacialExpression.FRIENDLY, "Hello Lady Servil.") @@ -29,7 +29,7 @@ class LadyServilDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, FightArenaQuest)) { + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { 0 -> when (stage) { 0 -> npcl(FacialExpression.SAD, "Oh I wish this broken cart was my only problem. *sob* I've got to find my family.. **sob**").also { stage++ } @@ -46,7 +46,7 @@ class LadyServilDialogue(player: Player? = null) : DialoguePlugin(player) { 8 -> playerl(FacialExpression.FRIENDLY, "I'll try my best to return your family.").also { stage++ } 9 -> { end() - setQuestStage(player!!, FightArenaQuest, 10) + setQuestStage(player!!, Quests.FIGHT_ARENA, 10) npcl(FacialExpression.SAD, "Please do. My family is wealthy and can reward you handsomely. I'll be waiting here for you.").also { stage = END_DIALOGUE } } } @@ -81,7 +81,7 @@ class LadyServilDialogue(player: Player? = null) : DialoguePlugin(player) { 2 -> npcl(FacialExpression.FRIENDLY, "All I can offer in return is material wealth. Please take these coins as a sign of my gratitude.").also { stage++ } 3 -> { end() - player!!.questRepository.getQuest("Fight Arena").finish(player) + player!!.questRepository.getQuest(Quests.FIGHT_ARENA).finish(player) } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LocalDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LocalDialogue.kt index d43e5589a..84382f980 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LocalDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LocalDialogue.kt @@ -7,16 +7,17 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import core.tools.END_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests @Initializable class LocalDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Fight Arena") == 100) { + if (player.questRepository.getStage(Quests.FIGHT_ARENA) == 100) { npcl(FacialExpression.FRIENDLY, "Hey, you're the guy from the arena! How'd you get out?").also { stage = END_DIALOGUE } - } else if (player.questRepository.getStage("Fight Arena") in 91..99) { + } else if (player.questRepository.getStage(Quests.FIGHT_ARENA) in 91..99) { playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 9 } - } else if (player.questRepository.getStage("Fight Arena") >= 10) { + } else if (player.questRepository.getStage(Quests.FIGHT_ARENA) >= 10) { playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } } else { playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 7 } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/BouncerNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/BouncerNPC.kt index f61475d19..f79aefcb1 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/BouncerNPC.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/BouncerNPC.kt @@ -1,6 +1,5 @@ package content.region.kandarin.ardougne.quest.arena.npc -import content.region.kandarin.ardougne.quest.arena.FightArena import content.region.kandarin.ardougne.quest.arena.dialogue.GeneralKhazardDialogue import core.api.* import core.game.node.entity.Entity @@ -11,6 +10,7 @@ import core.game.world.GameWorld import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.NPCs +import content.data.Quests @Initializable class BouncerNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { @@ -52,9 +52,8 @@ class BouncerNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, loca override fun finalizeDeath(killer: Entity?) { if (killer is Player) { - val quest = "Fight Arena" - if (getQuestStage(killer, quest) >= 89) { - setQuestStage(killer, FightArena.FightArenaQuest, 91) + if (getQuestStage(killer, Quests.FIGHT_ARENA) >= 89) { + setQuestStage(killer, Quests.FIGHT_ARENA, 91) } removeAttribute(killer, "spawn-bouncer") openDialogue(killer, GeneralKhazardDialogue()) diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/GeneralNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/GeneralNPC.kt index 20e75588a..700876e14 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/GeneralNPC.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/GeneralNPC.kt @@ -1,6 +1,5 @@ package content.region.kandarin.ardougne.quest.arena.npc -import content.region.kandarin.ardougne.quest.arena.FightArena import content.region.kandarin.ardougne.quest.arena.FightArenaListeners.Companion.General import content.region.kandarin.ardougne.quest.arena.dialogue.JeremyServilBDialogue import core.api.openDialogue @@ -12,6 +11,7 @@ import core.game.node.entity.player.Player import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.NPCs +import content.data.Quests @Initializable class GeneralNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { @@ -31,9 +31,8 @@ class GeneralNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, loca override fun finalizeDeath(killer: Entity?) { if (killer is Player) { - val quest = "Fight Arena" - if (getQuestStage(killer, quest) == 97) { - setQuestStage(killer, FightArena.FightArenaQuest, 98) + if (getQuestStage(killer, Quests.FIGHT_ARENA) == 97) { + setQuestStage(killer, Quests.FIGHT_ARENA, 98) } openDialogue(killer, JeremyServilBDialogue()) } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/OgreNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/OgreNPC.kt index 51010d3e4..67e2ae7fd 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/OgreNPC.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/OgreNPC.kt @@ -1,6 +1,5 @@ package content.region.kandarin.ardougne.quest.arena.npc -import content.region.kandarin.ardougne.quest.arena.FightArena import content.region.kandarin.ardougne.quest.arena.dialogue.GeneralKhazardDialogue import core.api.* import core.game.node.entity.Entity @@ -11,6 +10,7 @@ import core.game.world.GameWorld import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.NPCs +import content.data.Quests @Initializable class OgreNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { @@ -53,9 +53,8 @@ class OgreNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, locatio override fun finalizeDeath(killer: Entity?) { if (killer is Player) { - val quest = "Fight Arena" - if (getQuestStage(killer, quest) == 68 || getQuestStage(killer, quest) == 88) { - setQuestStage(killer, FightArena.FightArenaQuest, 72) + if (getQuestStage(killer, Quests.FIGHT_ARENA) == 68 || getQuestStage(killer, Quests.FIGHT_ARENA) == 88) { + setQuestStage(killer, Quests.FIGHT_ARENA, 72) } clearHintIcon(killer) removeAttribute(killer, "spawn-ogre") diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/ScorpionNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/ScorpionNPC.kt index 17910e77b..164464901 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/ScorpionNPC.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/ScorpionNPC.kt @@ -1,6 +1,5 @@ package content.region.kandarin.ardougne.quest.arena.npc -import content.region.kandarin.ardougne.quest.arena.FightArena import content.region.kandarin.ardougne.quest.arena.dialogue.GeneralKhazardDialogue import core.api.* import core.game.node.entity.Entity @@ -11,6 +10,7 @@ import core.game.world.GameWorld import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.NPCs +import content.data.Quests @Initializable class ScorpionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { @@ -52,9 +52,8 @@ class ScorpionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, loc override fun finalizeDeath(killer: Entity?) { if (killer is Player) { - val quest = "Fight Arena" - if (getQuestStage(killer, quest) == 88) { - setQuestStage(killer, FightArena.FightArenaQuest, 89) + if (getQuestStage(killer, Quests.FIGHT_ARENA) == 88) { + setQuestStage(killer, Quests.FIGHT_ARENA, 89) } removeAttribute(killer, "spawn-scorpion") openDialogue(killer, GeneralKhazardDialogue()) diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/biohazard/dialogue/KilronDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/biohazard/dialogue/KilronDialogue.kt new file mode 100644 index 000000000..83be1b46c --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/biohazard/dialogue/KilronDialogue.kt @@ -0,0 +1,51 @@ +package content.region.kandarin.ardougne.quest.biohazard.dialogue + +import content.data.Quests +import core.api.getQuestStage +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class KilronDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + playerl(FacialExpression.FRIENDLY, "Hello there.") + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + if (getQuestStage(player, Quests.BIOHAZARD) > 0){ + when(stage){ + 0 -> playerl(FacialExpression.FRIENDLY, "Hello Kilron.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello traveller. Do you need to go back over?").also { stage++ } + 2 -> showTopics( + Topic("Not yet Kilron.", 4), + Topic("Yes I do.", 5) + ) + 4 -> npcl(FacialExpression.FRIENDLY, "Okay, just give me the word.").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.FRIENDLY, "Okay, quickly now!").also { stage = END_DIALOGUE } + } + + } + else { + when (stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "How are you?").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Busy.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KILRON_349) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/biohazard/dialogue/MournerBossDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/biohazard/dialogue/MournerBossDialogue.kt new file mode 100644 index 000000000..0bad783a8 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/biohazard/dialogue/MournerBossDialogue.kt @@ -0,0 +1,149 @@ +package content.region.kandarin.ardougne.quest.biohazard.dialogue + +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.interaction.QueueStrength +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.world.map.Location +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + + +/** + * For some reason the level 13 mourner upstairs is called the boss + * And despite the fact that other NPCs say there's one sick person upstairs they're 2 up there. + * + * We should be using key 423 but that got incorrectly used for Lost Tribe + */ + +@Initializable +class MournerBossDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object{ + const val HOLD_BREATH = 10 + const val PRAY = 20 + const val FATAL = 30 + const val HAVE_KEY = 40 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (inEquipment(player!!, Items.DOCTORS_GOWN_430)){ + playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = if (hasAnItem(player!!, Items.KEY_5010).exists()) HAVE_KEY else START_DIALOGUE + 1 } + return true + } + else { + sendDialogue("The mourner doesn't feel like talking.").also { stage = END_DIALOGUE } + return false + } + } + + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE + 1 -> npcl(FacialExpression.ASKING, "A doctor? At last! I don't know what I've eaten but I feel like I'm on death's door.").also { stage++ } + START_DIALOGUE + 2 -> playerl(FacialExpression.NEUTRAL, "Hmm... interesting, sounds like food poisoning.").also { stage++ } + // Jagex didn't include a question mark here + START_DIALOGUE + 3 -> npcl(FacialExpression.ASKING, "Yes, I'd figured that out already. What can you give me to help.").also { stage++ } + START_DIALOGUE + 4 -> showTopics( + Topic("Just hold your breath and count to ten.", HOLD_BREATH), + Topic("The best I can do is pray for you.", PRAY), + Topic("There's nothing I can do, it's fatal.", FATAL) + ) + + + HOLD_BREATH -> npcl(FacialExpression.SUSPICIOUS, "What? How will that help? What kind of doctor are you?").also { stage++ } + HOLD_BREATH + 1 -> player(FacialExpression.HALF_GUILTY, "Erm... I'm new, I just started.").also { stage++ } + HOLD_BREATH + 2 -> npcl(FacialExpression.ANGRY, "You're no doctor!").also { + stage = END_DIALOGUE + fight(player) + } + + PRAY -> npcl(FacialExpression.ANGRY, "Pray for me? You're no doctor... You're an imposter!").also { + stage = END_DIALOGUE + fight(player) + } + + FATAL -> npcl(FacialExpression.PANICKED, "No, I'm too young to die! I've never even had a girlfriend.").also { stage++ } + FATAL + 1 -> playerl(FacialExpression.SAD, "That's life for you.").also { stage++ } + FATAL + 2 -> npcl(FacialExpression.ASKING, "Wait a minute, where's your equipment?").also { stage++ } + FATAL + 3 -> playerl(FacialExpression.HALF_GUILTY, "It's erm... at home.").also { stage++ } + FATAL + 4 -> npcl(FacialExpression.ANGRY, "You're no doctor!").also { + stage = END_DIALOGUE + fight(player) + } + + HAVE_KEY -> npcl(FacialExpression.NEUTRAL, "Sorry, I'd like to be left in peace.").also { stage = END_DIALOGUE } + + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MOURNER_370) + } + + private fun fight(player: Player){ + queueScript(player) { stage: Int -> + if (stage == 1){ + npc.attack(player) + return@queueScript stopExecuting(player) + } + return@queueScript delayScript(player, 1) + } + } +} + +/** + * Handles the key drop from the mourner boss + */ +@Initializable +class MournerBossNPC : AbstractNPC { + var target: Player? = null + private val supportRange: Int = 5 + + //Constructor spaghetti because Arios I guess + constructor() : super(NPCs.MOURNER_370, null, true) {} + private constructor(id: Int, location: Location) : super(id, location) {} + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return MournerBossNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MOURNER_370) + } + + override fun finalizeDeath(killer: Entity?) { + val p = killer as Player + if (!hasAnItem(p, Items.KEY_5010).exists()){ + queueScript(p, 1, QueueStrength.NORMAL){ stage: Int -> + when(stage){ + 0 -> { + sendMessage(p, "You search the mourner...") + } + 2 ->{ + sendMessage(p, "and find a key.") + // If the player doesn't have space bad luck + // They can kill another mourner boss + // todo change this key and fix lost tribe key at the same time + // It should be 423 here and 5010 over there + // addItemOrDrop(p, Items.KEY_5010) + return@queueScript stopExecuting(p) + } + } + return@queueScript delayScript(p, 1) + } + } + super.finalizeDeath(killer) + } + +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/BrotherKojoDialogueFile.kt b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/BrotherKojoDialogueFile.kt index 5d4a0dba2..c06a15558 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/BrotherKojoDialogueFile.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/BrotherKojoDialogueFile.kt @@ -1,5 +1,6 @@ package content.region.kandarin.ardougne.quest.clocktower +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -22,7 +23,7 @@ class BrotherKojoDialogueFile : DialogueFile() { } return } - when (getQuestStage(player!!, ClockTower.questName)) { + when (getQuestStage(player!!, Quests.CLOCK_TOWER)) { 0 -> { when (stage) { START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hello monk.").also { stage++ } @@ -49,7 +50,7 @@ class BrotherKojoDialogueFile : DialogueFile() { 32 -> player(FacialExpression.FRIENDLY, "Well, I'll do my best.").also { stage++ } 33 -> npcl(FacialExpression.HAPPY, "Thank you again! And remember to be careful, the cellar is full of strange beasts!").also { stage = END_DIALOGUE - setQuestStage(player!!, ClockTower.questName, 1) + setQuestStage(player!!, Quests.CLOCK_TOWER, 1) } } } @@ -93,7 +94,7 @@ class BrotherKojoDialogueFile : DialogueFile() { 2 -> npcl(FacialExpression.FRIENDLY, "The townsfolk will all be able to know the correct time now! Thank you so much for all of your help! And as promised, here is your reward!").also { stage++ } 3 -> { end() - finishQuest(player!!, ClockTower.questName) + finishQuest(player!!, Quests.CLOCK_TOWER) } } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTower.kt b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTower.kt index f5e284234..2be89b1be 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTower.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTower.kt @@ -6,14 +6,14 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * https://www.youtube.com/watch?v=Cl68Z0bsRq4 */ @Initializable -class ClockTower : Quest("Clock Tower",38, 37, 1, 10, 0, 1, 8) { +class ClockTower : Quest(Quests.CLOCK_TOWER,38, 37, 1, 10, 0, 1, 8) { companion object { - const val questName = "Clock Tower" const val attributeBlueCog = "/save:quest:clocktower-bluecogplaced" const val attributeBlackCog = "/save:quest:clocktower-blackcogplaced" const val attributeWhiteCog = "/save:quest:clocktower-whitecogplaced" diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTowerListeners.kt b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTowerListeners.kt index 1daa7e3ed..a769bac13 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTowerListeners.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTowerListeners.kt @@ -1,5 +1,6 @@ package content.region.kandarin.ardougne.quest.clocktower +import content.data.Quests import core.api.* import core.game.global.action.DoorActionHandler import core.game.interaction.IntType @@ -47,8 +48,12 @@ class ClockTowerListener : InteractionListener { if (removeItem(player, Items.WHITE_COG_20)) { sendMessage(player, "The cog fits perfectly.") setAttribute(player, ClockTower.attributeWhiteCog, true) - if (!isQuestComplete(player, ClockTower.questName)) { - setQuestStage(player, ClockTower.questName, getQuestStage(player, ClockTower.questName) + 1) + if (!isQuestComplete(player, Quests.CLOCK_TOWER)) { + setQuestStage( + player, + Quests.CLOCK_TOWER, + getQuestStage(player, Quests.CLOCK_TOWER) + 1 + ) } } } else { @@ -62,8 +67,12 @@ class ClockTowerListener : InteractionListener { if (removeItem(player, Items.BLACK_COG_21)) { sendMessage(player, "The cog fits perfectly.") setAttribute(player, ClockTower.attributeBlackCog, true) - if (!isQuestComplete(player, ClockTower.questName)) { - setQuestStage(player, ClockTower.questName, getQuestStage(player, ClockTower.questName) + 1) + if (!isQuestComplete(player, Quests.CLOCK_TOWER)) { + setQuestStage( + player, + Quests.CLOCK_TOWER, + getQuestStage(player, Quests.CLOCK_TOWER) + 1 + ) } } } else { @@ -77,8 +86,12 @@ class ClockTowerListener : InteractionListener { if (removeItem(player, Items.BLUE_COG_22)) { sendMessage(player, "The cog fits perfectly.") setAttribute(player, ClockTower.attributeBlueCog, true) - if (!isQuestComplete(player, ClockTower.questName)) { - setQuestStage(player, ClockTower.questName, getQuestStage(player, ClockTower.questName) + 1) + if (!isQuestComplete(player, Quests.CLOCK_TOWER)) { + setQuestStage( + player, + Quests.CLOCK_TOWER, + getQuestStage(player, Quests.CLOCK_TOWER) + 1 + ) } } } else { @@ -92,8 +105,12 @@ class ClockTowerListener : InteractionListener { if (removeItem(player, Items.RED_COG_23)) { sendMessage(player, "The cog fits perfectly.") setAttribute(player, ClockTower.attributeRedCog, true) - if (!isQuestComplete(player, ClockTower.questName)) { - setQuestStage(player, ClockTower.questName, getQuestStage(player, ClockTower.questName) + 1) + if (!isQuestComplete(player, Quests.CLOCK_TOWER)) { + setQuestStage( + player, + Quests.CLOCK_TOWER, + getQuestStage(player, Quests.CLOCK_TOWER) + 1 + ) } } } else { diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherCedricNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherCedricNPC.kt index 3ae7c7dc4..64d4a7fa9 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherCedricNPC.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherCedricNPC.kt @@ -11,6 +11,7 @@ import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.tools.END_DIALOGUE +import content.data.Quests /** * Handles BrotherCedricDialogue Dialogue @@ -18,8 +19,7 @@ import core.tools.END_DIALOGUE */ class BrotherCedricDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - val questName = "Monk's Friend" - val questStage = getQuestStage(player!!, questName) + val questStage = getQuestStage(player!!, Quests.MONKS_FRIEND) when { questStage < 30 -> { when(stage) { @@ -36,7 +36,7 @@ class BrotherCedricDialogue : DialogueFile() { 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Brother Cedric are you okay?").also{stage++} 1 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "Yeesshhh, I'm very, very drunk..hic..up..").also{stage++} 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Brother Omad needs the wine for the party.").also{stage++} - 3 -> npcl(core.game.dialogue.FacialExpression.SAD, "Oh dear, oh dear, I knew I had to do something!").also{stage = END_DIALOGUE }.also{ setQuestStage(player!!, questName, 40) } + 3 -> npcl(core.game.dialogue.FacialExpression.SAD, "Oh dear, oh dear, I knew I had to do something!").also{stage = END_DIALOGUE }.also{ setQuestStage(player!!, Quests.MONKS_FRIEND, 40) } } } questStage == 40 -> { @@ -54,7 +54,7 @@ class BrotherCedricDialogue : DialogueFile() { sendItemDialogue(player!!, Items.JUG_OF_WATER_1937, "You hand the monk a jug of water.") stage=0 player!!.inventory.remove(Item(Items.JUG_OF_WATER_1937)) - setQuestStage(player!!, questName, 41) + setQuestStage(player!!, Quests.MONKS_FRIEND, 41) } } } @@ -72,7 +72,7 @@ class BrotherCedricDialogue : DialogueFile() { } 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "In that case I'd better drink more wine! It helps me think.").also {stage= END_DIALOGUE } 10 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Excellent, I just need some wood.").also{stage++} - 11 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Ok, I'll see what I can find.").also{stage = END_DIALOGUE }.also{setQuestStage(player!!, questName, 42)} + 11 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Ok, I'll see what I can find.").also{stage = END_DIALOGUE }.also{setQuestStage(player!!, Quests.MONKS_FRIEND, 42)} } } questStage == 42 -> { @@ -89,7 +89,7 @@ class BrotherCedricDialogue : DialogueFile() { 4 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Ok! I'll see you later!").also{ stage= END_DIALOGUE player!!.inventory.remove(Item(Items.LOGS_1511)) - setQuestStage(player!!, questName, 50) + setQuestStage(player!!, Quests.MONKS_FRIEND, 50) } } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherOmadNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherOmadNPC.kt index 74b322344..bea3883cc 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherOmadNPC.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherOmadNPC.kt @@ -14,6 +14,7 @@ import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.world.GameWorld.Pulser import core.tools.END_DIALOGUE +import content.data.Quests /** @@ -22,8 +23,7 @@ import core.tools.END_DIALOGUE */ class BrotherOmadDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - val questName = "Monk's Friend" - val questStage = getQuestStage(player!!, questName) + val questStage = getQuestStage(player!!, Quests.MONKS_FRIEND) when (questStage) { 0 -> { when(stage) { @@ -50,7 +50,7 @@ class BrotherOmadDialogue : DialogueFile() { } 10 -> npcl(core.game.dialogue.FacialExpression.HALF_WORRIED, "Please do. We won't be able to help you as we are peaceful men but we would be grateful for your help!").also { stage++ } 11 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Where are they?").also { stage++ } - 12 -> npcl(core.game.dialogue.FacialExpression.SAD, "They hide in a secret cave in the forest. It's hidden under a ring of stones. Please, bring back the blanket!").also { stage = END_DIALOGUE }.also { player!!.questRepository.getQuest("Monk's Friend").start(player) }.also { player!!.questRepository.syncronizeTab(player) } + 12 -> npcl(core.game.dialogue.FacialExpression.SAD, "They hide in a secret cave in the forest. It's hidden under a ring of stones. Please, bring back the blanket!").also { stage = END_DIALOGUE }.also { player!!.questRepository.getQuest(Quests.MONKS_FRIEND).start(player) }.also { player!!.questRepository.syncronizeTab(player) } } } 10 -> { @@ -70,7 +70,7 @@ class BrotherOmadDialogue : DialogueFile() { } 31 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Really, that's excellent, well done! Maybe now I will be able to get some rest.").also{stage++} 32 -> npcl(core.game.dialogue.FacialExpression.SAD, "*yawn*..I'm off to bed! Farewell brave traveller!").also{player!!.inventory.remove(Item(Items.CHILDS_BLANKET_90)) - setQuestStage(player!!, questName, 20); stage = END_DIALOGUE + setQuestStage(player!!, Quests.MONKS_FRIEND, 20); stage = END_DIALOGUE } } } @@ -105,7 +105,12 @@ class BrotherOmadDialogue : DialogueFile() { 996 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Okay traveller, take care.").also{stage = END_DIALOGUE } 997 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Of course, but we need the wine first.").also{stage = END_DIALOGUE } 42 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Oh, he won't be far. Probably out in the forest.").also{stage++} - 43 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Ok, I'll go and find him.").also { stage = END_DIALOGUE }.also{ setQuestStage(player!!, questName, 30)} + 43 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Ok, I'll go and find him.").also { stage = END_DIALOGUE }.also{ + setQuestStage( + player!!, + Quests.MONKS_FRIEND, + 30 + )} } } 30 -> { @@ -192,7 +197,7 @@ class BrotherOmadDialogue : DialogueFile() { monk.animator.animate(Animation(2109)) // Jump for joy } 25 -> if (questComplete) { - player!!.questRepository.getQuest("Monk's Friend").finish(player) + player!!.questRepository.getQuest(Quests.MONKS_FRIEND).finish(player) } } count++ diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonasteryMonkNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonasteryMonkNPC.kt index 8279ba5ce..cfe48d058 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonasteryMonkNPC.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonasteryMonkNPC.kt @@ -6,6 +6,7 @@ import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.tools.END_DIALOGUE +import content.data.Quests /** * Handles MonasteryMonkDialogue Dialogue @@ -13,7 +14,7 @@ import core.tools.END_DIALOGUE */ class MonasteryMonkDialogue : DialogueFile() { override fun handle(interfaceId: Int, buttonId: Int) { - var questStage = player!!.questRepository.getStage("Monk's Friend") + var questStage = player!!.questRepository.getStage(Quests.MONKS_FRIEND) if (questStage == 0){ when(stage) { 0 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Peace brother.").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonksFriend.kt b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonksFriend.kt index cf38a3a18..c559c6a85 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonksFriend.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonksFriend.kt @@ -8,6 +8,7 @@ import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * Represents the "Monk's Friend" quest. @@ -15,7 +16,7 @@ import org.rs09.consts.Items */ @Initializable -class MonksFriend: Quest("Monk's Friend", 89, 88, 1, 30, 0, 1, 80) { +class MonksFriend: Quest(Quests.MONKS_FRIEND, 89, 88, 1, 30, 0, 1, 80) { override fun newInstance(`object`: Any?): Quest { return this diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/PlagueCity.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCity.kt similarity index 92% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/PlagueCity.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCity.kt index 47585fbd1..d34a8904d 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/PlagueCity.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCity.kt @@ -1,6 +1,6 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity -import core.api.addItem +import content.data.Quests import core.api.addItemOrDrop import core.api.removeAttributes import core.api.rewardXP @@ -11,9 +11,8 @@ import core.plugin.Initializable import org.rs09.consts.Items @Initializable -class PlagueCity : Quest("Plague City", 98, 97, 1, 165, 0, 1, 29) { +class PlagueCity : Quest(Quests.PLAGUE_CITY, 98, 97, 1, 165, 0, 1, 29) { override fun newInstance(`object`: Any?): Quest { return this } - companion object { const val PlagueCityQuest = "Plague City" } override fun drawJournal(player: Player?, stage: Int) { super.drawJournal(player, stage) var line = 11 @@ -36,7 +35,7 @@ class PlagueCity : Quest("Plague City", 98, 97, 1, 165, 0, 1, 29) { if (stage >= 2) { line++ - line(player, "Alrena has given me a Gasmask to protect me", line++, stage >= 3) + line(player, "Alrena has given me a gas mask to protect me", line++, stage >= 3) line(player, "from the plague while in West Ardougne.", line++, stage >= 3) line++ } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCityListeners.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCityListeners.kt new file mode 100644 index 000000000..1b3855785 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCityListeners.kt @@ -0,0 +1,430 @@ +package content.region.kandarin.ardougne.quest.plaguecity + +import content.data.Quests +import content.region.kandarin.ardougne.quest.plaguecity.dialogue.mourners.MournerKidnapDialogueFile +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import core.tools.END_DIALOGUE +import org.rs09.consts.* +import core.game.node.scenery.Scenery as SceneryNode + +class PlagueCityListeners : InteractionListener { + companion object { + + const val BUCKET_USES_ATTRIBUTE = "/save:elena:bucket" + const val ARDOUGNE_TELE_ATTRIBUTE = "/save:Ardougne:teleport" + + private const val TRYING_TO_OPEN_GRILL = 3192 + private const val POUR_THE_WATER = 2283 + private const val CLIMB_LADDER = 828 + private const val GO_INTO_PIPE = 10580 + private const val TIE_THE_ROPE = 3191 + private const val DIG_WITH_SPADE = 830 + + } + + override fun defineListeners() { + + on(NPCs.BILLY_REHNISON_723, IntType.NPC, "talk-to") { player, _ -> + sendMessage(player, "Billy isn't interested in talking.") + return@on true + } + + on(Scenery.MANHOLE_2543, IntType.SCENERY, "open") { player, node -> + replaceScenery(node.asScenery(), Scenery.MANHOLE_2544, -1) + addScenery(Scenery.MANHOLE_COVER_2545, Location(node.location.x, node.location.y-1, 0),0,10) + sendMessage(player, "You pull back the manhole cover.") + return@on true + } + + on(Scenery.MANHOLE_COVER_2545, IntType.SCENERY, "close") { player, node -> + removeScenery(node.asScenery()) + getScenery(location(node.location.x, node.location.y+1, 0))?.let { replaceScenery(it, Scenery.MANHOLE_2543, -1) } + sendMessage(player, "You close the manhole cover.") + return@on true + } + + on(Scenery.MANHOLE_2544, IntType.SCENERY, "climb-down") { player, _ -> + teleport(player, Location(2514, 9739, 0)) + face(player, Location.create(2514, 9740)) + sendMessage(player, "You climb down through the manhole.") + return@on true + } + + on(Scenery.DOOR_2528, IntType.SCENERY, "open") { player, node -> + if (getQuestStage(player, Quests.PLAGUE_CITY) >= 13) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendNPCDialogue(player, NPCs.BRAVEK_711,"Go away, I'm busy! I'm... Umm... In a meeting!") + // This typo is authentic + sendMessage(player, "The door won't open") + } + return@on true + } + + on(Scenery.MUD_PILE_2533, IntType.SCENERY, "climb") { player, _ -> + animate(player, CLIMB_LADDER) + queueScript(player, 2){ + teleport(player, Location(2566, 3332)) + sendDialogue(player, "You climb up the mud pile.") + return@queueScript true + } + return@on true + } + + on(Scenery.DUG_HOLE_11417, IntType.SCENERY, "Climb-down") { player, _ -> + teleport(player, Location(2518, 9759)) + sendDialogue(player, "You climb down the tunnel into the sewer.") + return@on true + } + + on(Items.A_MAGIC_SCROLL_1505, IntType.ITEM, "read") { player, item -> + if (removeItem(player, item)){ + if (getAttribute(player, ARDOUGNE_TELE_ATTRIBUTE, false)){ + sendGraphics(Graphics(157,96), player.location) + impact(player, 0) + sendMessage(player, "The scroll explodes.") + addItem(player, Items.ASHES_592) + } + else{ + sendItemDialogue(player, Items.A_MAGIC_SCROLL_1505, "You memorise what is written on the scroll.") + sendDialogue(player, "You can now cast the Ardougne Teleport spell provided you have the required runes and magic level.") + setAttribute(player, ARDOUGNE_TELE_ATTRIBUTE, true) + } + return@on true + } + return@on false + } + + on(Items.A_SCRUFFY_NOTE_1508, IntType.ITEM, "read") { player, _ -> + sendMessage(player, "You guess it really says something slightly different.") + openInterface(player, 222).also { scruffyNote(player) } + return@on true + } + + class KidnapDoorDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + + when(stage) { + 0 -> { + // Face the door + face(player!!, Location.create(player!!.location.x, 3270, 0)) + sendDialogue(player!!, "The door won't open. You notice a black cross on the door.").also { stage++ } + } + 1 -> openDialogue(player!!, MournerKidnapDialogueFile(), NPC(NPCs.MOURNER_3216)) + } + } + + } + + on(Scenery.DOOR_35991, IntType.SCENERY, "open") { player, node -> + if (getQuestStage(player, Quests.PLAGUE_CITY) > 16) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + else { + // Make sure we are standing in front of the door + forceWalk(player, Location.create(node.location.x, node.location.y), "smart") + openDialogue(player, KidnapDoorDialogue()) + } + return@on true + } + + on(Scenery.WARDROBE_2525, IntType.SCENERY, "search") { player, _ -> + if(getQuestStage(player, Quests.PLAGUE_CITY) >= 2){ + if (!hasAnItem(player, Items.GAS_MASK_1506).exists()){ + if(freeSlots(player) == 0) { + sendItemDialogue(player, Items.GAS_MASK_1506, "You find a protective mask but you don't have enough room to take it.") + return@on true + } + else { + sendItemDialogue(player, Items.GAS_MASK_1506, "You find a protective mask.") + addItem(player, Items.GAS_MASK_1506) + return@on true + } + } + } + // The player should not be given a mask for whatever reason + sendMessage(player, "You search the wardrobe but you find nothing.") + return@on false + } + + onUseWith(IntType.SCENERY, Items.BUCKET_OF_WATER_1929, Scenery.MUD_PATCH_11418) { player, _, _ -> + if (getAttribute(player, BUCKET_USES_ATTRIBUTE, 0) in 0..2 && removeItem(player, Items.BUCKET_OF_WATER_1929)) { + animate(player, POUR_THE_WATER) + sendDialogueLines(player, "You pour water onto the soil.", "The soil softens slightly." + ) + player.incrementAttribute(BUCKET_USES_ATTRIBUTE, 1) + addItem(player, Items.BUCKET_1925) + return@onUseWith true + } else if (getAttribute(player, BUCKET_USES_ATTRIBUTE, 0) == 3 && removeItem(player, Items.BUCKET_OF_WATER_1929)) { + animate(player, POUR_THE_WATER) + player.incrementAttribute(BUCKET_USES_ATTRIBUTE, 1) + sendDialogueLines(player, + "You pour water onto the soil.", + "The soil is now soft enough to dig into." + ) + setAttribute(player, "/save:elena:dig", true) + addItem(player, Items.BUCKET_1925) + } else { + sendDialogue(player, "You don't need to pour on any more water the soil is soft enough already.") + } + return@onUseWith true + } + + + onDig(Location.create(2566, 3332, 0)){ player: Player -> + dig(player) + } + + onUseWith(IntType.SCENERY, Items.SPADE_952, Scenery.MUD_PATCH_11418) { player, _, _ -> + dig(player) + return@onUseWith true + } + + on(Scenery.GRILL_11423, IntType.SCENERY, "open") { player, _ -> + if (getQuestStage(player, Quests.PLAGUE_CITY) == 4) { + sendDialogue(player, "The grill is too secure. You can't pull it off alone.") + animate(player, TRYING_TO_OPEN_GRILL) + setQuestStage(player, Quests.PLAGUE_CITY, 5) + } else { + sendDialogue(player, "There is a grill blocking your way") + } + return@on true + } + + on(Scenery.PIPE_2542, IntType.SCENERY, "climb-up") { player, _ -> + if (getQuestStage(player, Quests.PLAGUE_CITY) >= 7) { + if (inEquipment(player, Items.GAS_MASK_1506)){ + animate(player, GO_INTO_PIPE,true) + queueScript(player, 3) { + teleport(player, Location(2529, 3304, 0)) + sendDialogue(player, "You climb up through the sewer pipe.") + return@queueScript stopExecuting(player) + } + forceMove(player, Location(2514, 9739, 0), Location(2514, 9734, 0), 0, 5,Direction.SOUTH) + } + else { + sendNPCDialogue(player, NPCs.EDMOND_714, "I can't let you enter the city without your gas mask on.") + } + } + else { + sendDialogue(player, "There is a grill blocking your way") + } + return@on true + } + + onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.PIPE_2542) { player, _, _ -> + sendPlayerDialogue(player, "Maybe I should try opening it first.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.GRILL_11423) { player, _, _ -> + if(removeItem(player, Items.ROPE_954)) { + queueScript(player, 1) { stage: Int -> + when (stage) { + 0 -> forceWalk(player, Location.create(2514, 9740, 0), "SMART") + 2 -> face(player, Location.create(2514, 9739, 0), -1) + 3 -> { + animate(player, TIE_THE_ROPE) + setVarbit(player, 1787, 5, true) // Tied rope to the grill. + } + + 4 -> { + setQuestStage(player, Quests.PLAGUE_CITY, 6) + sendItemDialogue( + player, + Items.ROPE_954, + "You tie the end of the rope to the sewer pipe's grill." + ) + return@queueScript stopExecuting(player) + } + } + return@queueScript delayScript(player, 1) + } + return@onUseWith true + } else { + sendMessage(player, "Nothing interesting happens.") + } + return@onUseWith true + } + + class TedRehnisonDoors : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.TED_REHNISON_721) + when (stage) { + 0 -> { + npcl(FacialExpression.FRIENDLY, "Go away. We don't want any.").also { + if(hasAnItem(player!!, Items.BOOK_1509).exists()){ + stage++ + } else { + stage = END_DIALOGUE } + } + } + 1 -> playerl(FacialExpression.NEUTRAL, "I'm a friend of Jethick's, I have come to return a book he borrowed.").also { stage++ } + // todo change this back after sendItemDialogue is fixed for having a single line before See #1885 + 2 -> npc(FacialExpression.FRIENDLY, "", "Oh... why didn't you say, come in then.", "").also { stage++ } + 3 -> sendItemDialogue(player!!, Items.BOOK_1509, "You hand the book to Ted as you enter.").also { + DoorActionHandler.handleAutowalkDoor(player, getScenery(2531, 3328, 0)) + setQuestStage(player!!, Quests.PLAGUE_CITY, 9) + removeItem(player!!, Items.BOOK_1509) + stage++ + } + 4 -> npcl(FacialExpression.NEUTRAL, "Thanks, I've been missing that.").also { stage = END_DIALOGUE } + } + } + } + + on(Scenery.DOOR_2537, IntType.SCENERY, "open") { player, node -> + if (getQuestStage(player, Quests.PLAGUE_CITY) >= 9) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + openDialogue(player, TedRehnisonDoors()) + } + return@on true + } + + on(Scenery.BARREL_2530, IntType.SCENERY, "search") { player, _ -> + if (inInventory(player, Items.A_SMALL_KEY_1507)) { + sendMessage(player, "You don't find anything interesting.") + return@on true + } else { + sendItemDialogue(player, Items.A_SMALL_KEY_1507, "You find a small key in the barrel.") + addItem(player, Items.A_SMALL_KEY_1507) + } + } + + on(Scenery.SPOOKY_STAIRS_2522, IntType.SCENERY, "walk-down") { player, _ -> + sendMessage(player, "You walk down the stairs...") + teleport(player, Location.create(2537, 9671)) + return@on true + } + + on(Scenery.SPOOKY_STAIRS_2523, IntType.SCENERY, "walk-up") { player, _ -> + teleport(player, Location.create(2536, 3271, 0)) + sendMessage(player, "You walk up the stairs...") + return@on true + } + + class ElenaDoorDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.ELENA_3215) + when (stage) { + 0 -> sendDialogue(player!!, "The door is locked.").also { stage++ } + 1 -> npcl(FacialExpression.CRYING, "Hey get me out of here please!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I would do but I don't have a key.").also { stage++ } + 3 -> npcl(FacialExpression.SAD, "I think there may be one around somewhere. I'm sure I heard them stashing it somewhere.").also { stage++ } + 4 -> options("Have you caught the plague?", "Okay, I'll look for it.").also { stage++ } + 5 -> when (buttonID) { + 1 -> playerl(FacialExpression.WORRIED, "Have you caught the plague?").also { stage = 6 } + 2 -> playerl(FacialExpression.NEUTRAL, "Okay, I'll look for it.").also { stage = END_DIALOGUE } + } + 6 -> npcl(FacialExpression.HALF_WORRIED, "No, I have none of the symptoms.").also { stage++ } + 7 -> playerl(FacialExpression.THINKING, "Strange, I was told this house was plague infected.").also { stage++ } + 8 -> npcl(FacialExpression.THINKING, "I suppose that was a cover up by the kidnappers.").also { stage = END_DIALOGUE } + } + } + } + + onUseWith(IntType.SCENERY, Items.A_SMALL_KEY_1507, Scenery.DOOR_2526) { player, _, node -> + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + sendDialogue(player, "You unlock the door.") + return@onUseWith true + } + + on(Scenery.DOOR_2526, IntType.SCENERY, "open") { player, node -> + if (getQuestStage(player, Quests.PLAGUE_CITY) >= 99 || hasAnItem(player, Items.A_SMALL_KEY_1507).exists()) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + openDialogue(player, ElenaDoorDialogue()) + } + return@on true + } + + onUseWith(IntType.NPC, Items.HANGOVER_CURE_1504, NPCs.BRAVEK_711) { player, _, _ -> + openDialogue(player, NPCs.BRAVEK_711) + return@onUseWith true + } + + on(Scenery.DOOR_2054, IntType.SCENERY, "open"){ player, node -> + if (isQuestComplete(player, Quests.PLAGUE_CITY)){ + DoorActionHandler.handleDoor(player, node as SceneryNode) + } + else{ + sendMessage(player, "This door is locked") + } + return@on true + } + + } + + private fun dig(p : Player) { + if (getAttribute(p, "/save:elena:dig", false)) { + // Only remove the counter now that you have dug a hole + removeAttribute(p, BUCKET_USES_ATTRIBUTE) + queueScript(p, 1) { stage: Int -> + when (stage) { + 0 -> sendItemDialogue( + p, Items.SPADE_952, + "You dig deep into the soft soil... Suddenly it crumbles away!" + ) + + 1 -> animate(p, DIG_WITH_SPADE) + 3 -> { + teleport(p, Location(2518, 9759)) + setQuestStage(p, Quests.PLAGUE_CITY, 4) + setVarbit(p, Vars.VARBIT_QUEST_PLAGUE_CITY_EDMOND_TUNNELS, 1) + setVarbit(p, Vars.VARBIT_QUEST_PLAGUE_CITY_MUD_PILE, 1) + sendDialogueLines( + p, + "You fall through...", + "...you land in the sewer.", + "Edmond follows you down the hole." + ) + // We've dug the hole so don't keep track + removeAttribute(p, "/save:elena:dig") + return@queueScript stopExecuting(p) + } + } + return@queueScript delayScript(p, 1) + + } + } else { + sendMessage(p, "Nothing interesting happens.") + } + } + + private fun scruffyNote(player: Player) { + val scruffynotes = + arrayOf( + "Got a bncket of nnilk", + "Tlen grind sorne lhoculate", + "vnith a pestal and rnortar", + "ald the grourd dlocolate to tho milt", + "finales add 5cme snape gras5", + ) + setInterfaceText(player, scruffynotes.joinToString("
"), 222, 5) + } + + override fun defineDestinationOverrides() { + setDest(IntType.SCENERY, intArrayOf(Scenery.GRILL_11423), "open") { _, _ -> + return@setDest Location.create(2514, 9739, 0) + } + + setDest(IntType.SCENERY, intArrayOf(Scenery.PIPE_2542), "climb-up") { _, _ -> + return@setDest Location.create(2514, 9739, 0) + } + + setDest(IntType.SCENERY, intArrayOf(Scenery.MANHOLE_2544), "climb-down") { _, node -> + return@setDest Location.create(node.location.x, node.location.y+1, 0) + } + } +} diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/UndergroundCutscene.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/UndergroundCutscene.kt similarity index 63% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/UndergroundCutscene.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/UndergroundCutscene.kt index c924cc595..eea5f2ffa 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/UndergroundCutscene.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/UndergroundCutscene.kt @@ -1,5 +1,6 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity +import content.data.Quests import core.api.* import core.game.activity.Cutscene import core.game.dialogue.FacialExpression @@ -28,12 +29,22 @@ class UndergroundCutscene(player: Player) : Cutscene(player) { } 1 -> { - teleport(player, 18, 13) + teleport(player, 18, 12) + setVarbit(player, 1787, 2, true) // Grill removal. timedUpdate(1) } 2 -> { move(player, 18, 12) + face(player, base.transform(18, 11, 0)) + // You have to remove the stubborn previous sceneries before adding your own. + removeScenery(getScenery(base.transform(18, 11, 0))!!) + removeScenery(getScenery(base.transform(18, 12, 0))!!) + removeScenery(getScenery(base.transform(18, 13, 0))!!) + // Add the rope sceneries to animate. + addScenery(11416, base.transform(18, 11, 0),2,10) + addScenery(11412, base.transform(18, 12, 0),2,10) + addScenery(11414, base.transform(18, 13, 0),2,10) fadeFromBlack() moveCamera(21, 16) rotateCamera(18, 13) @@ -41,20 +52,22 @@ class UndergroundCutscene(player: Player) : Cutscene(player) { } 3 -> { - visualize(getNPC(EDMOND)!!, ROPE_PULL, 2270) - visualize(player, ROPE_PULL, 2270) + animate(getNPC(EDMOND)!!, ROPE_PULL) + animate(player, ROPE_PULL) + animateScenery(player, getScenery(base.transform(18, 11, 0))!!, 3189) + animateScenery(player, getScenery(base.transform(18, 12, 0))!!, 3188) + animateScenery(player, getScenery(base.transform(18, 13, 0))!!, 3188) sendChat(player, "1... 2... 3... Pull!") timedUpdate(6) } 4 -> { - setVarbit(player, 1787, 6, true) // Grill removal. face(player, getNPC(EDMOND)!!.location) timedUpdate(2) } 5 -> { - dialogueUpdate(EDMOND, FacialExpression.FRIENDLY, "Once you're in the city look for a man called Jethick, he's an old friend and should help you. Send") + dialogueUpdate(EDMOND, FacialExpression.FRIENDLY, "Once you're in the city look for a man called Jethick, he's an old friend and should help you. Send", hide=true) sendChat(getNPC(EDMOND)!!, "Once you're in the city") timedUpdate(4) } @@ -75,7 +88,7 @@ class UndergroundCutscene(player: Player) : Cutscene(player) { } 9 -> { - dialogueUpdate(EDMOND, FacialExpression.FRIENDLY, "him my regards, I Haven't seen him since before Elena was born.") + dialogueUpdate(EDMOND, FacialExpression.FRIENDLY, "him my regards, I haven't seen him since before Elena was born.", hide=true) sendChat(getNPC(EDMOND)!!, "him my regards, I haven't") timedUpdate(4) } @@ -92,17 +105,13 @@ class UndergroundCutscene(player: Player) : Cutscene(player) { 12 -> { sendChat(player, "Alright, thanks I will.") - timedUpdate(3) + // todo change this to true once no continue button player model size is fixed + sendPlayerDialogue(player,"Alright, thanks I will.", hide = false) + timedUpdate(2) } - 13 -> { - sendPlayerDialogue(player,"Alright, thanks I will.") - timedUpdate(4) - } - - 14 -> { end { - setQuestStage(player, "Plague City", 7) + setQuestStage(player, Quests.PLAGUE_CITY, 7) } } } @@ -110,6 +119,6 @@ class UndergroundCutscene(player: Player) : Cutscene(player) { companion object { private const val EDMOND = 714 - private val ROPE_PULL = 3187 + private const val ROPE_PULL = 3187 } } diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/AlrenaDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/AlrenaDialogue.kt similarity index 79% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/AlrenaDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/AlrenaDialogue.kt index 1577dd348..56384c44b 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/AlrenaDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/AlrenaDialogue.kt @@ -1,5 +1,7 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity.dialogue +import content.data.Quests +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -15,8 +17,9 @@ class AlrenaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Plague City") == 1) { - playerl(FacialExpression.FRIENDLY, "Hello, Edmond has asked me to help find your daughter.").also { stage++ } + if (player.questRepository.getStage(Quests.PLAGUE_CITY) == 1) { + // playerl formats this weirdly + player(FacialExpression.FRIENDLY, "Hello, Edmond has asked me to help find your","daughter.").also { stage++ } } else { playerl(FacialExpression.FRIENDLY, "Hello Madam.").also { stage++ } } @@ -24,12 +27,12 @@ class AlrenaDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { + when (getQuestStage(player!!, Quests.PLAGUE_CITY)) { 0 -> when (stage) { 1 -> npcl(FacialExpression.NEUTRAL, "Oh, hello there.").also { stage++ } 2 -> playerl(FacialExpression.FRIENDLY, "Are you ok?").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "Not too bad... I've just got some troubles on my mind…").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.NEUTRAL, "Not too bad... I've just got some troubles on my mind...").also { stage = END_DIALOGUE } } 1 -> when (stage) { @@ -43,15 +46,15 @@ class AlrenaDialogue(player: Player? = null) : DialoguePlugin(player) { sendItemDialogue(player!!, Items.DWELLBERRIES_2126, "You give the dwellberries to Alrena.").also { stage++ } removeItem(player!!, Items.DWELLBERRIES_2126) } - 4 -> sendDialogue(player!!, "Alrena crushes the berries into a smooth paste. She then smears the paste over a strange mask.").also { stage++ } - 5 -> npcl(FacialExpression.FRIENDLY, "There we go, all done. While in West Ardougne you must wear this at all times, or you could catch the plague. I'll make a spare mask. I'll hide it in the wardrobe in case the mourners come in.").also { stage++ } - 6 -> sendItemDialogue(player!!, Items.GAS_MASK_1506, "Alrena gives you the mask.").also { stage++ } - 7 -> { - end() + // sendDialogue formats this weirdly + 4 -> sendDialogueLines(player!!, "Alrena crushes the berries into a smooth paste. She then smears the", "paste over a strange mask.").also { stage++ } + // npcl formats this weirdly + 5 -> npc(FacialExpression.FRIENDLY, "There we go, all done. While in West Ardougne you", "must wear this at all times, or you could catch the","plague.").also { stage++ } + 6 -> { + setQuestStage(player!!, Quests.PLAGUE_CITY, 2) addItem(player!!, Items.GAS_MASK_1506) - setQuestStage(player!!, "Plague City", 2) - setAttribute(player!!, PlagueCityListeners.BUCKET_USES_ATTRIBUTE, 0) - sendNPCDialogue(player!!, NPCs.ALRENA_710, "I'll make a spare mask. I'll hide it in the wardrobe in case the mourners come in.") + sendItemDialogue(player!!, Items.GAS_MASK_1506, "Alrena gives you the mask.").also { stage++ } + // Jump down to quest stage 2 from here } 8 -> playerl(FacialExpression.NEUTRAL, "Ok, I'll go and get some.").also { stage = END_DIALOGUE } } @@ -59,6 +62,9 @@ class AlrenaDialogue(player: Player? = null) : DialoguePlugin(player) { 2 -> when (stage) { 1 -> npcl(FacialExpression.FRIENDLY, "Hello darling, I think Edmond had a good idea of how to get into West Ardougne, you should hear his idea.").also { stage++ } 2 -> playerl(FacialExpression.FRIENDLY, "Alright I'll go and see him now.").also { stage = END_DIALOGUE } + + // This gets entered from quest stage 1 + 7 -> npcl(FacialExpression.FRIENDLY, "I'll make a spare mask. I'll hide it in the wardrobe in case the mourners come in.").also { stage = END_DIALOGUE } } 3 -> when (stage) { @@ -112,19 +118,26 @@ class AlrenaDialogue(player: Player? = null) : DialoguePlugin(player) { 5 -> npcl(FacialExpression.FRIENDLY, " Yes. There should be one in the house somewhere. Let me know if you need anything else.").also { stage = END_DIALOGUE } } - in 15..98 -> when (stage) { + 15 -> when (stage) { 1 -> npcl(FacialExpression.FRIENDLY, "Hello, any word on Elena?").also { stage++ } 2 -> playerl(FacialExpression.FRIENDLY, "Not yet I'm afraid, I need to find some Snape grass first, any idea where I'd find some?").also { stage++ } 3 -> npcl(FacialExpression.FRIENDLY, "It's not common round here, though I hear it's easy to find by the coast south west of Falador.").also { stage++ } 4 -> playerl(FacialExpression.FRIENDLY, "Thanks, I'll go take a look.").also { stage++ } 5 -> playerl(FacialExpression.FRIENDLY, "I also need to get some chocolate powder for a hangover cure for the city warder.").also { stage++ } 6 -> npcl(FacialExpression.FRIENDLY, "Well I don't have any chocolate, but this may help.").also { stage++ } - 7 -> sendItemDialogue(player!!, Items.PESTLE_AND_MORTAR_233, "Alrena hands you a pestle and mortar.").also { stage++ } - 8 -> playerl(FacialExpression.FRIENDLY, "Thanks.").also { stage++ } - 9 -> { - end() + 7 -> sendItemDialogue(player!!, Items.PESTLE_AND_MORTAR_233, "Alrena hands you a pestle and mortar.").also { addItem(player!!, Items.PESTLE_AND_MORTAR_233) + stage++ } + 8 -> playerl(FacialExpression.FRIENDLY, "Thanks.").also { stage = END_DIALOGUE } + } + + in 16 .. 98 -> when(stage){ + 1 -> npcl(FacialExpression.WORRIED, "Hello, any word on Elena?").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "Not yet I'm afraid.").also { stage++ } + 3 -> npcl(FacialExpression.WORRIED, "Is there anything else I can do to help?").also { stage++ } + 4 -> playerl(FacialExpression.SAD, "Sorry but not right now.").also {stage = END_DIALOGUE } + } 99 -> when (stage) { @@ -139,4 +152,4 @@ class AlrenaDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun getIds(): IntArray = intArrayOf(NPCs.ALRENA_710) -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/BravekDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/BravekDialogue.kt new file mode 100644 index 000000000..3eeb1e39e --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/BravekDialogue.kt @@ -0,0 +1,156 @@ +package content.region.kandarin.ardougne.quest.plaguecity.dialogue + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class BravekDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object{ + const val SPEAK_ANOTHER_DAY = 10 + const val WAITING_FOR_CURE = 20 + const val CURED = 30 + const val IMPORTANT = 50 + const val DRINK_TOO_MUCH = 60 + const val CURE = 70 + const val WHAT_HELP = 80 + const val ALL_PEOPLE_SAY = 90 + const val NOT_LISTEN = 100 + const val INTEREST = 110 + const val WEAK_LEADER = 120 + } + + override fun open(vararg args: Any?): Boolean { + npc = if (args[0] is Int) NPC(args[0] as Int) else args[0] as NPC + when(getQuestStage(player, Quests.PLAGUE_CITY)){ + in 0..13 -> npcl(FacialExpression.ANNOYED, "My head hurts! I'll speak to you another day...").also { stage = SPEAK_ANOTHER_DAY } + 14 -> npcl(FacialExpression.ANNOYED, " Uurgh! My head still hurts too much to think straight. " + + "Oh for one of Trudi's hangover cures!").also { stage = if(hasAnItem(player, Items.HANGOVER_CURE_1504).exists()) WAITING_FOR_CURE else END_DIALOGUE } + 15 -> npcl(FacialExpression.FRIENDLY, "Ah now, what was it you wanted me to do for you?").also { stage = WHAT_HELP } + else -> npcl(FacialExpression.FRIENDLY, "Thanks again for the hangover cure.").also { stage = if(hasAnItem(player, Items.WARRANT_1503).exists() || getQuestStage(player, Quests.PLAGUE_CITY) >= 99) CURED else WHAT_HELP } + + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + when (stage){ + + SPEAK_ANOTHER_DAY -> showTopics( + Topic("This is really important though!", IMPORTANT), + Topic("Ok, goodbye.", END_DIALOGUE) + ) + + IMPORTANT -> npc(FacialExpression.ANNOYED, + "I can't possibly speak to you with my head spinning like", + "this... I went a bit heavy on the drink again last night.", + "Curse my herbalist, she made the best hang over cures.", + "Darn inconvenient of her catching the plague.").also { stage++ } + IMPORTANT + 1 -> showTopics( + Topic("Ok, goodbye.", END_DIALOGUE), + Topic(" You shouldn't drink so much then!", DRINK_TOO_MUCH), + Topic("Do you know what's in the cure?", CURE) + ) + + DRINK_TOO_MUCH -> npcl(FacialExpression.ANNOYED, "Well positions of responsibility are hard, " + + "I need something to take my mind off things... Especially with the problems this place has.").also { stage++ } + DRINK_TOO_MUCH + 1 -> showTopics( + Topic("Ok, goodbye.", END_DIALOGUE), + Topic("Do you know what's in the cure?", CURE), + Topic("I don't think drink is the solution.", DRINK_TOO_MUCH+2) + ) + DRINK_TOO_MUCH + 2 -> npcl(FacialExpression.ANNOYED, "Uurgh! My head still hurts too much to think straight. Oh for one of Trudi's hangover cures!").also { stage = END_DIALOGUE } + + CURE -> npc(FacialExpression.NEUTRAL, + "Hmmm let me think... Ouch! Thinking isn't clever. Ah", + "here, she did scribble it down for me.").also { stage++ } + CURE + 1 -> { + if(hasSpaceFor(player!!, Item(Items.A_SCRUFFY_NOTE_1508))) { + sendItemDialogue( + player!!, + Items.A_SCRUFFY_NOTE_1508, + "Bravek hands you a tatty piece of paper." + ).also { stage++ } + } + else{ + sendItemDialogue(player!!, Items.A_SCRUFFY_NOTE_1508, "Bravek waves a note in front of you but you do not have space for it").also { stage = END_DIALOGUE } + } + } + CURE + 2 ->{ + addItem(player!!, Items.A_SCRUFFY_NOTE_1508) + setQuestStage(player!!, Quests.PLAGUE_CITY, 14) + end() + } + + WAITING_FOR_CURE -> playerl(FacialExpression.NEUTRAL, "Try this.").also { stage++ } + WAITING_FOR_CURE + 1 -> { + animate(npc, 1330) // Drink hangover cure. + findLocalNPC(player!!, NPCs.BRAVEK_711)!!.sendChat("Grruurgh!") + sendItemDialogue(player!!, Items.HANGOVER_CURE_1504, "You give Bravek the hangover cure. Bravek gulps down the foul-looking liquid.").also { stage++ } + setQuestStage(player, Quests.PLAGUE_CITY, 15) + removeItem(player, Items.HANGOVER_CURE_1504) + } + WAITING_FOR_CURE + 2 -> npcl(FacialExpression.HAPPY, "Ooh that's much better! Thanks, that's the clearest my head has felt in a month." + + "Ah now, what was it you wanted me to do for you?" + ).also { stage = WHAT_HELP } + + WHAT_HELP -> playerl(FacialExpression.ASKING, "I need to rescue a kidnap victim called Elena. She's being held in a plague house, I need permission to enter.").also { stage++ } + WHAT_HELP + 1 -> npcl(FacialExpression.HALF_GUILTY, "Well the mourners deal with that sort of thing...").also { stage++ } + WHAT_HELP + 2 -> showTopics( + Topic("Ok, I'll go speak to them.", END_DIALOGUE), + Topic("Is that all anyone says around here?", ALL_PEOPLE_SAY), + Topic("They won't listen to me!", NOT_LISTEN, skipPlayer = true) + ) + + ALL_PEOPLE_SAY -> npcl(FacialExpression.HALF_GUILTY, "Well, they know best about plague issues.").also { stage++ } + ALL_PEOPLE_SAY + 1 -> showTopics( + Topic("Don't you want to take an interest in it at all?", INTEREST), + Topic("They won't listen to me!", NOT_LISTEN, skipPlayer = true) + ) + + INTEREST -> npcl(FacialExpression.HALF_GUILTY, "Nope, I don't wish to take a deep interest in plagues. That stuff is too scary for me!").also { stage++ } + INTEREST + 1 -> showTopics( + Topic("I see why people say you're a weak leader.", WEAK_LEADER), + Topic("Ok, I'll talk to the mourners.", END_DIALOGUE), + Topic("They won't listen to me!", NOT_LISTEN, skipPlayer = true) + ) + + WEAK_LEADER -> npcl(FacialExpression.ANNOYED, "Bah, people always criticise their leaders but delegating is the only way to lead. I delegate all plague issues to the mourners.").also { stage++ } + WEAK_LEADER + 1 -> playerl(FacialExpression.ANNOYED, "This whole city is a plague issue!").also { stage = END_DIALOGUE } + + NOT_LISTEN -> playerl(FacialExpression.ANNOYED, "They won't listen to me! They say I'm not properly equipped to go in the house, though I do have a very effective gas mask.").also { stage++ } + // npcl does not work + NOT_LISTEN + 1 -> npc(FacialExpression.ANNOYED, + "Hmmm, well I guess they're not taking the issue of a", + "kidnapping seriously enough. They do go a bit far", + "sometimes. I've heard of Elena, she has helped us a lot...", + "Ok, I'll give you this warrant to enter the house." + ).also { stage++ } + NOT_LISTEN + 2 -> { + if (freeSlots(player!!) == 0) { + sendItemDialogue(player!!, Items.WARRANT_1503, "Bravek waves a warrant at you, but you don't have room to take it.").also { stage = END_DIALOGUE } + } else { + sendItemDialogue(player!!, Items.WARRANT_1503, "Bravek hands you a warrant.").also { stage = END_DIALOGUE } + addItem(player!!, Items.WARRANT_1503) + setQuestStage(player!!, Quests.PLAGUE_CITY, 16) + } + } + + CURED -> playerl(FacialExpression.FRIENDLY, "Not a problem, happy to help out.").also { stage++ } + CURED + 1 -> npcl(FacialExpression.FRIENDLY, " I'm just having a little drop of whisky, then I'll feel really good.").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.BRAVEK_711) +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/ClerkDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/ClerkDialogue.kt new file mode 100644 index 000000000..639cd0a8b --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/ClerkDialogue.kt @@ -0,0 +1,98 @@ +package content.region.kandarin.ardougne.quest.plaguecity.dialogue + +import content.data.Quests +import core.api.getQuestStage +import core.api.setQuestStage +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class ClerkDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object{ + const val TALK_AGAIN = 10 + + const val THROUGH_DOOR = 20 + const val PERMISSION = 30 + const val QUESTIONS = 40 + const val URGENT = 60 + const val RUN_EVERYTHING = 70 + const val WHEN_AVAILABLE = 80 + const val DISTURBED = 90 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (getQuestStage(player, Quests.PLAGUE_CITY) < 13) + npcl(FacialExpression.NEUTRAL, "Hello, welcome to the Civic Office of West Ardougne. How can I help you?").also { stage = QUESTIONS } + else + npcl(FacialExpression.FRIENDLY, "Bravek will see you now but keep it short!").also { stage = TALK_AGAIN } + + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + + when(stage){ + + QUESTIONS -> showTopics( + IfTopic("I need permission to enter a plague house.", PERMISSION, getQuestStage(player, Quests.PLAGUE_CITY) > 11), + Topic("Who is through that door?", THROUGH_DOOR), + Topic("I'm just looking thanks.", END_DIALOGUE), + ) + + // npcl does not wordwrap right + PERMISSION -> npc(FacialExpression.NEUTRAL, "Rather you than me! The mourners normally deal with", + "that stuff, you should speak to them. Their headquarters","are right near the city gate.").also { stage++ } + PERMISSION + 1 -> showTopics( + Topic("I'll try asking them then.", END_DIALOGUE), + Topic("Surely you don't let them run everything for you?", RUN_EVERYTHING), + Topic("This is urgent though!", URGENT, skipPlayer = true) + ) + + RUN_EVERYTHING -> npcl(FacialExpression.NEUTRAL, " Well, they do know what they're doing here. " + + "If they did start doing something badly Bravek, the city warder, would have the power to override them. " + + "I can't see that happening though.").also { stage++ } + RUN_EVERYTHING + 1 -> showTopics( + Topic("I'll try asking them then.", END_DIALOGUE), + Topic("Can I speak to Bravek anyway?", DISTURBED) + ) + + URGENT -> playerl(FacialExpression.PANICKED, " This is urgent though! Someone's been kidnapped and is being held in a plague house!").also { stage++ } + URGENT+ 1 -> npcl(FacialExpression.NEUTRAL, "I'll see what I can do I suppose.").also { stage++ } + URGENT + 2 -> npcl(FacialExpression.NEUTRAL, " Mr Bravek, there's a ${if (player.isMale) "man" else "lady"} here who really needs to speak to you.").also { + stage++ + npc = NPC(NPCs.BRAVEK_711) + } + URGENT + 3 -> npc(FacialExpression.ANNOYED, " I suppose they can come in then. If they keep it short.").also { + stage = END_DIALOGUE + setQuestStage(player, Quests.PLAGUE_CITY, 13) + } + + THROUGH_DOOR -> npcl(FacialExpression.NEUTRAL, "The city warder Bravek is in there.").also { stage++ } + THROUGH_DOOR + 1 -> playerl(FacialExpression.ASKING, " Can I go in?").also { stage = DISTURBED } + + DISTURBED -> npcl(FacialExpression.NEUTRAL, " He has asked not to be disturbed.").also { stage++ } + DISTURBED + 1 -> showTopics( + IfTopic("This is urgent though!", URGENT, getQuestStage(player, Quests.PLAGUE_CITY) > 11, skipPlayer = true), + Topic("Ok, I'll leave him alone.", END_DIALOGUE), + Topic("Do you know when he will be available?", WHEN_AVAILABLE) + ) + + WHEN_AVAILABLE -> npcl(FacialExpression.NEUTRAL, " Oh I don't know, an hour or so maybe.").also { stage = END_DIALOGUE } + + TALK_AGAIN -> playerl(FacialExpression.FRIENDLY, "Thanks, I won't take much of his time.").also { stage = END_DIALOGUE } + } + + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.CLERK_713) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/EdmondDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/EdmondDialogue.kt new file mode 100644 index 000000000..8b88a5c9e --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/EdmondDialogue.kt @@ -0,0 +1,268 @@ +package content.region.kandarin.ardougne.quest.plaguecity.dialogue + +import content.data.Quests +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners +import content.region.kandarin.ardougne.quest.plaguecity.UndergroundCutscene +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class EdmondDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + val questStage = getQuestStage(player, Quests.PLAGUE_CITY) + npc = args[0] as NPC + if(inEquipmentOrInventory(player, Items.GAS_MASK_1506) && (questStage == 2)) { + playerl(FacialExpression.FRIENDLY, "Hi Edmond, I've got the gas mask now.").also { stage++ } + } + else if (questStage == 4) { + // npcl not formatted quite right + npc(FacialExpression.FRIENDLY, "I think it's the pipe to the south that comes up in West", "Ardougne.").also { stage++ } + + } + else if(questStage == 5){ + playerl( FacialExpression.NEUTRAL, + "Edmond, I can't get through to West Ardougne! There's an iron grill blocking my way, I can't pull it off alone." + ).also { stage++ } + + } + else if(questStage == 6){ + // playerl not formatted quite right + player(FacialExpression.NEUTRAL, "I've tied a rope to the grill over there, will you help me","pull it off?").also { stage++ } + + } + else if(questStage > 2) { + playerl(FacialExpression.FRIENDLY, "Hello Edmond.").also { stage++ } + } + else { + playerl(FacialExpression.FRIENDLY, "Hello old man.").also { stage++ } + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + when (getQuestStage(player!!, Quests.PLAGUE_CITY)) { + + 0 -> when (stage) { + 1 -> npcl(FacialExpression.NEUTRAL, "Sorry, I can't stop to talk...").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Why, what's wrong?").also { stage++ } + 3 -> npcl( + FacialExpression.FRIENDLY, + "I've got to find my daughter. I pray that she is still alive..." + ).also { stage++ } + + 4 -> options("What's happened to her?", "Well, good luck finding her.").also { stage++ } + 5 -> when (buttonID) { + 1 -> playerl(FacialExpression.FRIENDLY, "What's happened to her?").also { stage = 6 } + 2 -> playerl(FacialExpression.FRIENDLY, "Well, good luck finding her.").also { + stage = END_DIALOGUE + } + } + // npcl does not word wrap correctly + 6 -> npc( + FacialExpression.NEUTRAL, + "Elena's a missionary and a healer. Three weeks ago she", + "managed to cross the Ardougne wall... No-one's allowed", + "to cross the wall in case they spread the plague. But", + "after hearing the screams of suffering she felt she had" + ).also { stage++ } + + 7 -> npcl( + FacialExpression.NEUTRAL, + "to help. She said she'd be gone for a few days but we've heard nothing since." + ).also { stage++ } + + 8 -> options( + "Tell me more about the plague.", + "Can I help find her?", + "I'm sorry, I have to go." + ).also { stage++ } + + 9 -> when (buttonID) { + 1 -> playerl(FacialExpression.FRIENDLY, "Tell me more about the plague.").also { stage = 10 } + 2 -> playerl(FacialExpression.FRIENDLY, "Can I help find her?").also { stage = 11 } + 3 -> playerl(FacialExpression.FRIENDLY, "I'm sorry, I have to go.").also { stage = END_DIALOGUE } + } + + 10 -> npcl( + FacialExpression.FRIENDLY, + "The mourners can tell you more than me. They're the only ones allowed to cross the border. I do know the plague is a horrible way to go... That's why Elena felt she had to go help." + ).also { stage = 8 } + + 11 -> npcl( + FacialExpression.FRIENDLY, + "Really, would you? I've been working on a plan to get into West Ardougne, but I'm too old and tired to carry it through. If you're going into West Ardougne you'll need protection from the plague. My wife made a" + ).also { stage++ } + + 12 -> npcl( + FacialExpression.FRIENDLY, + "special gas mask for Elena with dwellberries rubbed into it. Dwellberries help repel the virus! We need some more though..." + ).also { stage++ } + + 13 -> playerl(FacialExpression.ASKING, "Where can I find these dwellberries?").also { stage++ } + 14 -> npcl( + FacialExpression.FRIENDLY, + "The only place I know of is McGrubor's Wood just north of the Rangers' Guild." + ).also { stage++ } + + 15 -> playerl(FacialExpression.FRIENDLY, "Ok, I'll go and get some.").also { stage++ } + 16 -> npcl( + FacialExpression.NEUTRAL, + "The foresters keep a close eye on it, but there is a back way in." + ).also { stage++ } + + 17 -> { + end() + setQuestStage(player!!, Quests.PLAGUE_CITY, 1) + } + } + + 1 -> when (stage) { + 1 -> npcl(FacialExpression.NEUTRAL, "Have you got the dwellberries yet?").also { stage++ } + 2 -> if (!inInventory(player, Items.DWELLBERRIES_2126)) { + playerl(FacialExpression.FRIENDLY, "Sorry, I'm afraid not.").also { stage = 3 } + } else { + playerl(FacialExpression.FRIENDLY, "Yes I've got some here.").also { stage = 6 } + } + + 3 -> npcl( + FacialExpression.NEUTRAL, + "You'll probably find them in McGrubor's Wood it's just west of Seers village." + ).also { stage++ } + + 4 -> playerl(FacialExpression.NEUTRAL, "Ok, I'll go and get some.").also { stage++ } + 5 -> npcl( + FacialExpression.NEUTRAL, + "The foresters keep a close eye on it, but there is a back way in." + ).also { stage = END_DIALOGUE } + + 6 -> npcl(FacialExpression.NEUTRAL, "Take them to my wife Alrena, she's inside.").also { + stage = END_DIALOGUE + } + } + + 2 -> when (stage) { + // npcl formats this wrong + 1 -> npc( + FacialExpression.NEUTRAL, + "Good stuff, now for the digging. Beneath us are the", + "Ardougne sewers, there you'll find", + "access to West Ardougne." + ).also { stage++ } + + 2 -> npc( + FacialExpression.NEUTRAL, "The problem is the soil is rock hard. You'll need to pour", + "on several buckets of water to soften it up. I'll keep an", + "eye out for the mourners." + ).also { stage++ } + + 3 -> { + end() + setQuestStage(player!!, Quests.PLAGUE_CITY, 3) + } + } + + 3 -> when (stage) { + 1 -> if (player!!.getAttribute("/save:elena:dig", false) == true) { + playerl(FacialExpression.NEUTRAL, "I've soaked the soil with water.").also { stage = 3 } + } else { + npcl(FacialExpression.FRIENDLY, "How's it going?").also { stage = 2 } + } + + 2 -> if (player.getAttribute(PlagueCityListeners.BUCKET_USES_ATTRIBUTE, 0) == 1) { + playerl( + FacialExpression.NEUTRAL, + "I still need to pour three more buckets of water on the soil." + ).also { stage = END_DIALOGUE } + } else if (player.getAttribute(PlagueCityListeners.BUCKET_USES_ATTRIBUTE, 0) == 2) { + playerl( + FacialExpression.NEUTRAL, + "I still need to pour two more buckets of water on the soil." + ).also { stage = END_DIALOGUE } + } else if (player.getAttribute(PlagueCityListeners.BUCKET_USES_ATTRIBUTE, 0) == 3) { + playerl( + FacialExpression.NEUTRAL, + "I still need to pour one more bucket of water on the soil." + ).also { stage = END_DIALOGUE } + } + + 3 -> npcl( + FacialExpression.FRIENDLY, + "That's great, it should be soft enough to dig through now." + ).also { stage = END_DIALOGUE } + } + + 4 -> when (stage) { + 1 -> playerl(FacialExpression.NEUTRAL, "Alright I'll check it out.").also { stage = END_DIALOGUE } + } + + 5 -> when (stage) { + 1 -> npcl( + FacialExpression.NEUTRAL, + "If you get some rope you could tie to the grill, then we could both pull it at the same time." + ).also { stage = END_DIALOGUE } + } + + 6 -> when(stage){ + 1 -> npcl(FacialExpression.NEUTRAL, "Alright, let's get to it...").also { stage++ } + 2 -> end().also{ UndergroundCutscene(player!!).start() } + } + 7-> when (stage) { + 1 -> npcl(FacialExpression.NEUTRAL, "Have you found Elena yet?").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "Not yet, it's a big city over there.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Don't forget to look for my friend Jethick. He may be able to help.").also { stage = END_DIALOGUE } + } + + 8 -> when (stage) { + 1 -> npcl(FacialExpression.NEUTRAL, "Have you found Elena yet?").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "Not yet, it's a big city over there. Do you have a picture of Elena?").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "There should be a picture of Elena in the house. Please find her quickly, I hope it's not too late.").also { stage = END_DIALOGUE } + } + + // No source for this branch + in 9..98 -> when(stage) { + 1 -> npcl(FacialExpression.NEUTRAL, "Have you found Elena yet?").also { stage++ } + 2 -> playerl(FacialExpression.SAD, "Not yet.").also { stage++ } + 3 -> npcl(FacialExpression.WORRIED, "Please find her quickly, I hope it's not too late.").also { stage = END_DIALOGUE } + } + + 99 -> when (stage) { + 1 -> npcl(FacialExpression.NEUTRAL, "Thank you, thank you! Elena beat you back by minutes.").also { stage++ } + 2 -> npcl(FacialExpression.NEUTRAL, "Now I said I'd give you a reward. What can I give you as a reward I wonder?").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "Here take this magic scroll, I have little use for it but it may help you.").also { stage++ } + 4 -> { + end() + player!!.questRepository.getQuest(Quests.PLAGUE_CITY).finish(player) + } + } + + 100 -> when (stage) { + 1 -> npcl(FacialExpression.FRIENDLY, "Ah hello again, and thank you again for rescuing my daughter.").also { + stage = if ( + getAttribute(player, PlagueCityListeners.ARDOUGNE_TELE_ATTRIBUTE, false) + || hasAnItem(player, Items.A_MAGIC_SCROLL_1505).exists()) 5 else 2 + } + 2 -> showTopics( + Topic("Do you have any more of those scrolls?", 3), + Topic("No problem.", END_DIALOGUE) + ) + 3 -> npcl(FacialExpression.FRIENDLY, "Here take this magic scroll, I have little use for it but it may help you.").also { + stage = END_DIALOGUE + addItemOrDrop(player!!, Items.A_MAGIC_SCROLL_1505) + } + 5 -> playerl(FacialExpression.NEUTRAL, "No problem.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.EDMOND_3213, NPCs.TUNNEL_EDMOND_3214) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/JethickDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/JethickDialogue.kt similarity index 60% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/JethickDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/JethickDialogue.kt index 598354db4..4ab027d70 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/JethickDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/JethickDialogue.kt @@ -1,5 +1,6 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity.dialogue +import content.data.Quests import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -15,44 +16,53 @@ class JethickDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Plague City") in 0..11) { + if (player.questRepository.getStage(Quests.PLAGUE_CITY) in 0..11) { npcl(FacialExpression.FRIENDLY, "Hello I don't recognise you. We don't get many newcomers around here.").also { stage++ } - } else if (player.questRepository.getStage("Plague City") >= 12) { + } else if (player.questRepository.getStage(Quests.PLAGUE_CITY) >= 12) { npcl(FacialExpression.FRIENDLY,"Hello. We don't get many newcomers around here.").also { stage = END_DIALOGUE } } return true } override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { + when (getQuestStage(player!!, Quests.PLAGUE_CITY)) { in 0..1 -> when (stage) { 1 -> npcl(FacialExpression.FRIENDLY, "Well King Tyras has wandered off into the west kingdom. He doesn't care about the mess he's left here. The city warder Bravek is in charge at the moment... He's not much better.").also { stage = END_DIALOGUE } } in 7..9 -> when (stage) { - 1 -> options("Hi, I'm looking for a woman from East Ardougne called Elena.", "So who's in charge here?").also { stage++ } + 1 -> options("Hi, I'm looking for a woman from East Ardougne.", "So who's in charge here?").also { stage++ } 2 -> when (buttonID) { + // This line is authentically different than the question you click 1 -> playerl(FacialExpression.FRIENDLY, "Hi, I'm looking for a woman from East Ardougne called Elena.").also { stage = 3 } 2 -> playerl(FacialExpression.FRIENDLY, "So who's in charge here?").also { stage = END_DIALOGUE } } - 3 -> npcl(FacialExpression.FRIENDLY, "East Ardougnian women are easier to find in East Ardougne. Not many would come to West Ardougne to find one. Although the name is familiar, what does she look like?").also { stage++ } - 4 -> playerl(FacialExpression.NEUTRAL, "Um... brown hair... in her twenties...").also { stage++ } - 5 -> npcl(FacialExpression.NEUTRAL, "Hmm, that doesn't narrow it down a huge amount... I'll need to know more than that, or see a picture?").also { stage++ } - 6 -> if (inInventory(player!!, Items.PICTURE_1510)) { - sendItemDialogue(player!!, Items.PICTURE_1510, "You show Jethick the picture.").also { stage++ } + // word wrap is wrong with npcl + 3 -> npc(FacialExpression.FRIENDLY, "East Ardougnian women are easier to find in East", + "Ardougne. Not many would come to West Ardougne to", + "find one. Although the name is familiar, what does she", + "look like?").also { stage++ } + 4 -> if (inInventory(player!!, Items.PICTURE_1510)) { + sendItemDialogue(player!!, Items.PICTURE_1510, "You show Jethick the picture.").also { stage = 7 } } else { - end() - stage = END_DIALOGUE + playerl(FacialExpression.NEUTRAL, "Um... brown hair... in her twenties...").also { stage++ } } - 7 -> npcl(FacialExpression.FRIENDLY, "She came over here to help to aid plague victims. I think she is staying over with the Rehnison family. They live in the small timbered building at the far north side of town.").also { stage++ } - 8 -> npcl(FacialExpression.FRIENDLY, "I've not seen her around here for a while, mind. I don't suppose you could run me a little errand while you're over there? I borrowed this book from them, could you return it?").also { stage++ } - 9 -> options("Yes, I'll return it for you.", "No, I don't have time for that.").also { stage++ } - 10 -> when (buttonID) { - 1 -> playerl(FacialExpression.NEUTRAL, "Yes, I'll return it for you.").also { stage = 11 } + 5 -> npcl(FacialExpression.NEUTRAL, "Hmm, that doesn't narrow it down a huge amount... I'll need to know more than that, or see a picture?").also { stage = END_DIALOGUE } + + // npcl doesn't wrap right + 7 -> npc(FacialExpression.FRIENDLY, "She came over here to help to aid plague victims. I", + "think she is staying over with the Rehnison family. They", + "live in the small timbered building at the far north side", + "of town. I've not seen her around here in a while,").also { stage++ } + 8 -> npcl(FacialExpression.FRIENDLY, "mind. ").also { stage++ } + 9 -> npcl(FacialExpression.FRIENDLY, "I don't suppose you could run me a little errand while you're over there? I borrowed this book from them, can you return it?").also { stage++ } + 10 -> options("Yes, I'll return it for you.", "No, I don't have time for that.").also { stage++ } + 11 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "Yes, I'll return it for you.").also { stage = 12 } 2 -> playerl(FacialExpression.NEUTRAL, "No, I don't have time for that.").also { stage = END_DIALOGUE } } - 11 -> if(freeSlots(player) == 0) { + 12 -> if(freeSlots(player) == 0) { end() sendItemDialogue(player!!, Items.BOOK_1509, "Jethick shows you the book, but you don't have room to take it.") stage = END_DIALOGUE diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/ElenaDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/KidnappedElenaDialogue.kt similarity index 71% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/ElenaDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/KidnappedElenaDialogue.kt index 223bf156b..2fe574b61 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/ElenaDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/KidnappedElenaDialogue.kt @@ -1,6 +1,8 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity.dialogue +import content.data.Quests import core.api.setQuestStage +import core.api.setVarbit import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -8,13 +10,14 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import core.tools.END_DIALOGUE import org.rs09.consts.NPCs +import org.rs09.consts.Vars @Initializable -class ElenaDialogue(player: Player? = null) : DialoguePlugin(player) { +class KidnappedElenaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Plague City") >= 16) { + if (player.questRepository.getStage(Quests.PLAGUE_CITY) >= 16) { playerl(FacialExpression.FRIENDLY, "Hi, you're free to go! Your kidnappers don't seem to be about right now.").also { stage = 1 } } else { npcl(FacialExpression.FRIENDLY, "Go and see my father, I'll make sure he adequately rewards you. Now I'd better leave while I still can.").also { stage = END_DIALOGUE } @@ -29,12 +32,14 @@ class ElenaDialogue(player: Player? = null) : DialoguePlugin(player) { 3 -> npcl(FacialExpression.FRIENDLY, "Go and see my father, I'll make sure he adequately rewards you. Now I'd better leave while I still can.").also { stage++ } 4 -> { end() - setQuestStage(player!!, "Plague City", 99) + setQuestStage(player!!, Quests.PLAGUE_CITY, 99) + setVarbit(player, Vars.VARBIT_QUEST_PLAGUE_CITY_RESCUE_ELENA, 1) + setVarbit(player, Vars.VARBIT_QUEST_PLAGUE_CITY_EDMOND_TUNNELS, 0) stage = END_DIALOGUE } } return true } - override fun getIds(): IntArray = intArrayOf(NPCs.ELENA_3215) + override fun getIds(): IntArray = intArrayOf(NPCs.KIDNAPPED_ELENA_715) } \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MarthaRehnisonDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MarthaRehnisonDialogue.kt similarity index 89% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MarthaRehnisonDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MarthaRehnisonDialogue.kt index 79a130c2d..4b2aa051e 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MarthaRehnisonDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MarthaRehnisonDialogue.kt @@ -1,5 +1,6 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity.dialogue +import content.data.Quests import core.api.getQuestStage import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -14,7 +15,7 @@ class MarthaRehnisonDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Plague City") == 9) { + if (player.questRepository.getStage(Quests.PLAGUE_CITY) == 9) { playerl(FacialExpression.NEUTRAL, "Hi, I hear a woman called Elena is staying here.").also { stage++ } } else { npcl(FacialExpression.FRIENDLY, "Any luck finding Elena yet?").also { stage++ } @@ -23,7 +24,7 @@ class MarthaRehnisonDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { + when (getQuestStage(player!!, Quests.PLAGUE_CITY)) { 9 -> when (stage) { 1 -> npcl(FacialExpression.FRIENDLY, "Yes she was staying here, but slightly over a week ago she was getting ready to go back.").also { stage++ } diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MilliRehnisonDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MilliRehnisonDialogue.kt similarity index 52% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MilliRehnisonDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MilliRehnisonDialogue.kt index 811f069d7..3f5fe54a9 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/MilliRehnisonDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MilliRehnisonDialogue.kt @@ -1,5 +1,6 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity.dialogue +import content.data.Quests import core.api.getQuestStage import core.api.setQuestStage import core.game.dialogue.DialoguePlugin @@ -15,36 +16,38 @@ class MilliRehnisonDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Plague City") == 9) { + if (player.questRepository.getStage(Quests.PLAGUE_CITY) == 9) { playerl(FacialExpression.FRIENDLY, "Hello. Your parents say you saw what happened to Elena...").also { stage++ } } else { - npcl(FacialExpression.FRIENDLY, "Any luck finding Elena yet?").also { stage++ } + npcl(FacialExpression.CHILD_FRIENDLY, "Any luck finding Elena yet?").also { stage++ } } return true } override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { + when (getQuestStage(player!!, Quests.PLAGUE_CITY)) { 9 -> when(stage) { - 1 -> npcl(FacialExpression.NEUTRAL, "*sniff* Yes I was near the south east corner when I saw Elena walking by. I was about to run to greet her when some men jumped out. They shoved a sack over her head and dragged her into a building.").also { stage++ } + // Wordwrap doesn't match using npcl + 1 -> npc(FacialExpression.CHILD_SAD, "*sniff* Yes I was near the south east corner when I", + "saw Elena walking by. I was about to run to greet her", + "when some men jumped out. They shoved a sack over", + "her head and dragged her into a building.").also { stage++ } 2 -> playerl(FacialExpression.FRIENDLY, "Which building?").also { stage++ } - 3 -> npcl(FacialExpression.NEUTRAL, "It was the boarded up building with no windows in the south east corner of West Ardougne.").also { stage++ } - 4 -> { - end() - setQuestStage(player!!, "Plague City", 11) + 3 -> npcl(FacialExpression.CHILD_NORMAL, "It was the boarded up building with no windows in the south east corner of West Ardougne.").also { stage = END_DIALOGUE + setQuestStage(player!!, Quests.PLAGUE_CITY, 11) } } in 10..98 -> when (stage) { 1 -> playerl(FacialExpression.FRIENDLY, "Not yet...").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "I wish you luck, she did a lot for us.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.CHILD_FRIENDLY, "I wish you luck, she did a lot for us.").also { stage = END_DIALOGUE } } in 99..100 -> when (stage) { 1 -> playerl(FacialExpression.FRIENDLY, "Yes, she is safe at home now.").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "That's good to hear, she helped us a lot.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.CHILD_FRIENDLY, "That's good to hear, she helped us a lot.").also { stage = END_DIALOGUE } } } return true diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/TedRehnisonDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/TedRehnisonDialogue.kt similarity index 80% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/TedRehnisonDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/TedRehnisonDialogue.kt index f7534526d..b26c60d26 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/TedRehnisonDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/TedRehnisonDialogue.kt @@ -1,5 +1,6 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity.dialogue +import content.data.Quests import core.api.getQuestStage import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -14,9 +15,9 @@ class TedRehnisonDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.questRepository.getStage("Plague City") == 9) { + if (player.questRepository.getStage(Quests.PLAGUE_CITY) == 9) { playerl(FacialExpression.NEUTRAL, "Hi, I hear a woman called Elena is staying here.").also { stage++ } - } else if (player.questRepository.getStage("Plague City") > 9) { + } else if (player.questRepository.getStage(Quests.PLAGUE_CITY) > 9) { npcl(FacialExpression.FRIENDLY, "Any luck finding Elena yet?").also { stage++ } } else { npcl(FacialExpression.FRIENDLY, "Go away. We don't want any.").also { stage = END_DIALOGUE } @@ -25,11 +26,11 @@ class TedRehnisonDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun handle(componentID: Int, buttonID: Int): Boolean { - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { + when (getQuestStage(player!!, Quests.PLAGUE_CITY)) { 9 -> when (stage) { 1 -> npcl(FacialExpression.FRIENDLY, "Yes she was staying here, but slightly over a week ago she was getting ready to go back. However she never managed to leave. My daughter Milli was playing near the west wall when she saw some shadowy figures jump").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "out and grab her. Milli is upstairs if you wish to speak to her.").also { stage = END_DIALOGUE } + 2 -> npc(FacialExpression.FRIENDLY, "out and grab her. Milli is upstairs if you wish to speak", "to her.").also { stage = END_DIALOGUE } } in 10..98 -> when (stage) { diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerArdougneWallDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerArdougneWallDialogue.kt new file mode 100644 index 000000000..83b1c9d52 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerArdougneWallDialogue.kt @@ -0,0 +1,61 @@ +package content.region.kandarin.ardougne.quest.plaguecity.dialogue.mourners + +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * This is the Mourner patrolling close to the wall in E Ardougne + */ +@Initializable +class MournerArdougneWallDialogue(player: Player? = null) : DialoguePlugin(player){ + + companion object{ + const val START_DIALOGUE = 0 + const val PLAGUE = 20 + const val SYMPTOMS = 40 + } + + override fun open(vararg args: Any?): Boolean { + playerl(FacialExpression.NEUTRAL, "Hi.").also { stage = START_DIALOGUE } + return true + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.NEUTRAL, " What are you up to?").also { stage++ } + START_DIALOGUE + 1 -> playerl(FacialExpression.NEUTRAL, " Just sight-seeing.").also { stage++ } + START_DIALOGUE + 2 -> npcl(FacialExpression.NEUTRAL, "This is no place for sight-seeing. Don't you know there's been a plague outbreak?").also { stage++ } + START_DIALOGUE + 3 -> playerl(FacialExpression.NEUTRAL, " Yes, I had heard.").also { stage++ } + START_DIALOGUE + 4 -> npcl(FacialExpression.NEUTRAL, " Then I suggest you leave as soon as you can.").also { stage++ } + START_DIALOGUE + 5 -> showTopics( + Topic("What brought the plague to Ardougne?", PLAGUE), + Topic("What are the symptoms of the plague?", SYMPTOMS), + Topic("Thanks for the advice.", END_DIALOGUE), + ) + + PLAGUE -> npcl(FacialExpression.NEUTRAL, " It's all down to King Tyras of West Ardougne. " + + "'Rather than protecting his people he spends his time in the lands to the West. ").also { stage++ } + PLAGUE + 1 -> npcl(FacialExpression.NEUTRAL, "When he returned last he brought the plague with him then left before the problem became serious.").also { stage++ } + PLAGUE + 2 -> playerl(FacialExpression.ASKING, " Does he know how bad the situation is now?").also { stage++ } + PLAGUE + 3 -> npcl(FacialExpression.ANGRY, " If he did he wouldn't care. I believe he wants his people to suffer, he's an evil man.").also { stage++ } + PLAGUE + 4 -> playerl(FacialExpression.EXTREMELY_SHOCKED, " Isn't that treason?").also { stage++ } + PLAGUE + 5 -> npcl(FacialExpression.DISGUSTED_HEAD_SHAKE, " He's not my king.").also { stage = END_DIALOGUE } + + SYMPTOMS -> npcl(FacialExpression.NEUTRAL, "The first signs are typical flu symptoms. These tend to be followed by severe nightmares, horrifying hallucinations which drive many to madness.").also { stage++ } + SYMPTOMS + 1 -> playerl(FacialExpression.DISGUSTED, "Sounds nasty.").also { stage++ } + SYMPTOMS + 2 -> npcl(FacialExpression.NEUTRAL, " It gets worse. Next the victim's blood changes into a thick black tar-like liquid, at this point they're past help.").also { stage++ } + SYMPTOMS + 3 -> npcl(FacialExpression.NEUTRAL, "Their skin is cold to the touch, the victim is now brain dead. Their body however lives on driven by the virus, roaming like a zombie, spreading itself further wherever possible.").also { stage++ } + SYMPTOMS + 4 -> playerl(FacialExpression.DISGUSTED_HEAD_SHAKE, " I think I've heard enough.").also { stage = END_DIALOGUE } + + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MOURNER_719) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerEdmondHouseDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerEdmondHouseDialogue.kt new file mode 100644 index 000000000..97d62f0ff --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerEdmondHouseDialogue.kt @@ -0,0 +1,115 @@ +package content.region.kandarin.ardougne.quest.plaguecity.dialogue.mourners + +import content.data.Quests +import core.api.getQuestStage +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * This is for the mourner wandering around Edmonds house + */ + +@Initializable +class MournerEdmondHouseDialogue(player: Player? = null) : DialoguePlugin(player){ + + companion object { + const val BEFORE_PLAGUE_CITY = 10 + const val BEFORE_GAS_MASK = 20 + const val BEFORE_SOFTEN_GROUND = 30 + const val SYMPTOMS1 = 40 + const val FEEL_FINE = 50 + const val PLAGUE_SOURCE1 = 60 + const val BEFORE_DIG = 70 + const val AFTER_DIG = 80 + const val AFTER_ENTER_W_ARDOUGNE = 90 + } + + override fun open(vararg args: Any?): Boolean { + when (getQuestStage(player, Quests.PLAGUE_CITY)){ + 0 -> playerl(FacialExpression.NEUTRAL, "Hello there.").also { stage = BEFORE_PLAGUE_CITY } + 1 -> playerl(FacialExpression.NEUTRAL, "Hello.").also { stage = BEFORE_GAS_MASK } + 2 -> playerl(FacialExpression.NEUTRAL, "Hello.").also { stage = BEFORE_SOFTEN_GROUND } + 3 -> playerl(FacialExpression.NEUTRAL, "Hello.").also { stage = BEFORE_DIG } + 4 -> playerl(FacialExpression.NEUTRAL, "Hello there.").also { stage = AFTER_DIG } + else -> playerl(FacialExpression.NEUTRAL, "Hello.").also { stage = AFTER_ENTER_W_ARDOUGNE } + } + return true + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + BEFORE_PLAGUE_CITY -> npcl(FacialExpression.NEUTRAL, "Do you have a problem traveller?").also { stage++ } + BEFORE_PLAGUE_CITY + 1 -> playerl(FacialExpression.ASKING, "No, I just wondered why you're wearing that outfit... Is it fancy dress?").also { stage++ } + BEFORE_PLAGUE_CITY + 2 -> npcl(FacialExpression.NEUTRAL, "No! It's for protection.").also { stage++ } + BEFORE_PLAGUE_CITY + 3 -> playerl(FacialExpression.ASKING, "Protection from what?").also { stage++ } + BEFORE_PLAGUE_CITY + 4 -> npcl(FacialExpression.NEUTRAL, "The plague of course...").also { stage = END_DIALOGUE } + + BEFORE_GAS_MASK -> npcl(FacialExpression.NEUTRAL, "What do you want?").also { stage++ } + BEFORE_GAS_MASK + 1 -> showTopics( + Topic("Who are you?", BEFORE_GAS_MASK + 3), + Topic("Nothing, just being polite.", BEFORE_GAS_MASK + 2) + ) + BEFORE_GAS_MASK + 2 -> npcl(FacialExpression.NEUTRAL, "Hmmm, ok then. Be on your way.").also { stage = END_DIALOGUE } + BEFORE_GAS_MASK + 3 -> npcl(FacialExpression.NEUTRAL, "I'm a mourner. It's my job to help heal the plague victims of West Ardougne and to make sure the disease is contained.").also { stage++ } + BEFORE_GAS_MASK + 4 -> playerl(FacialExpression.THINKING, "Very noble of you.").also { stage++ } + BEFORE_GAS_MASK + 5 -> npcl(FacialExpression.NEUTRAL, "If you come down with any symptoms such as flu or nightmares let me know immediately.").also { stage = END_DIALOGUE } + + BEFORE_SOFTEN_GROUND -> npcl(FacialExpression.NEUTRAL, "Are you ok?").also { stage++ } + BEFORE_SOFTEN_GROUND + 1 -> playerl(FacialExpression.NEUTRAL, "Yes, I'm fine thanks.").also { stage++ } + BEFORE_SOFTEN_GROUND + 2 -> npcl(FacialExpression.NEUTRAL, "Have you experienced any plague symptoms?").also { stage++ } + BEFORE_SOFTEN_GROUND + 3 -> showTopics( + Topic("What are the symptoms?", SYMPTOMS1), + Topic("No, I feel fine", FEEL_FINE), + Topic("No, but can you tell me where the plague came from?", PLAGUE_SOURCE1) + ) + + SYMPTOMS1 -> npcl(FacialExpression.NEUTRAL, "First you'll come down with heavy flu, this is usually followed by horrifying nightmares.").also { stage++ } + SYMPTOMS1 + 1 -> playerl(FacialExpression.HALF_WORRIED, "I used to have nightmares when I was younger.").also { stage++ } + SYMPTOMS1 + 2 -> npcl(FacialExpression.NEUTRAL, "Not like these I assure you. Soon after a thick black liquid will seep from your nose and eyes.").also { stage++ } + SYMPTOMS1 + 3 -> playerl(FacialExpression.HALF_WORRIED, "Yuck!").also { stage++ } + SYMPTOMS1 + 4 -> npcl(FacialExpression.HALF_WORRIED, "When it gets to that stage there's nothing we can do for you.").also { stage = END_DIALOGUE } + + FEEL_FINE -> npcl(FacialExpression.NEUTRAL, "Well if you take a turn for the worse let me know straight away.").also { stage++ } + FEEL_FINE + 1 -> playerl(FacialExpression.HALF_WORRIED, "Can you cure it then?").also { stage++ } + FEEL_FINE + 2 -> npcl(FacialExpression.NEUTRAL, "No... But you will have to be treated.").also { stage++ } + FEEL_FINE + 3 -> playerl(FacialExpression.WORRIED, "Treated?").also { stage++ } + FEEL_FINE + 4 -> npcl(FacialExpression.NEUTRAL, "We have to take measures to contain the disease. " + + "That's why you must let us know immediately if you take a turn for the worse.").also { stage = END_DIALOGUE } + + PLAGUE_SOURCE1 -> npcl(FacialExpression.NEUTRAL, "It all started when King Tyras of West Ardougne came back from one of his visits to the lands west of here.").also { stage++ } + PLAGUE_SOURCE1 + 1 -> npcl(FacialExpression.NEUTRAL, "Some of his men must have unknowingly caught it out there and brought it back with them").also { stage = END_DIALOGUE } + + BEFORE_DIG -> npcl(FacialExpression.ASKING, "What are you up to with old man Edmond?").also { stage++ } + BEFORE_DIG + 1 -> playerl(FacialExpression.HALF_GUILTY, "Nothing, we've just been chatting.").also { stage++ } + BEFORE_DIG + 2 -> npcl(FacialExpression.ASKING, "What about his daughter?").also { stage++ } + BEFORE_DIG + 3 -> playerl(FacialExpression.HALF_GUILTY, "Oh, you know about that then?").also { stage++ } + BEFORE_DIG + 4 -> npcl(FacialExpression.NEUTRAL, "We know about everything that goes on in Ardougne. We have to if we are to contain the plague.").also { stage++ } + BEFORE_DIG + 5 -> playerl(FacialExpression.HALF_ASKING, "Have you see his daughter recently?").also { stage++ } + BEFORE_DIG + 6 -> npcl(FacialExpression.NEUTRAL, "I imagine she's caught the plague. Either way she won't be allowed out of West Ardougne, the risk is too great.").also { stage = END_DIALOGUE } + + AFTER_DIG -> npcl(FacialExpression.ASKING, "Been digging have we?").also { stage++ } + AFTER_DIG + 1 -> playerl(FacialExpression.HALF_GUILTY, "What do you mean?").also { stage++ } + AFTER_DIG + 2 -> npcl(FacialExpression.NEUTRAL, "Your hands are covered in mud.").also { stage++ } + AFTER_DIG + 3 -> playerl(FacialExpression.HALF_GUILTY, "Oh that...").also { stage++ } + AFTER_DIG + 4 -> npcl(FacialExpression.THINKING, "Funny, you don't look like the gardening type.").also { stage++ } + AFTER_DIG + 5 -> playerl(FacialExpression.NEUTRAL, "Oh no, I love gardening! It's my favorite pastime.").also { stage = END_DIALOGUE } + + AFTER_ENTER_W_ARDOUGNE -> npcl(FacialExpression.ASKING, " What are you up to?").also { stage++ } + AFTER_ENTER_W_ARDOUGNE + 1 -> playerl(FacialExpression.HALF_GUILTY, "Nothing.").also { stage++ } + AFTER_ENTER_W_ARDOUGNE + 2 -> npcl(FacialExpression.SUSPICIOUS, "I don't trust you.").also { stage++ } + AFTER_ENTER_W_ARDOUGNE + 3 -> playerl(FacialExpression.NEUTRAL, "You don't have to.").also { stage++ } + AFTER_ENTER_W_ARDOUGNE + 4 -> npcl(FacialExpression.SUSPICIOUS, "If I find you attempting to cross the wall I'll make sure you never return.").also { stage = END_DIALOGUE } + + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MOURNER_718) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/HeadMournerDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerGuardDialogue.kt similarity index 95% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/HeadMournerDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerGuardDialogue.kt index d3e8c6d48..529554cdb 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/quest/elena/HeadMournerDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerGuardDialogue.kt @@ -1,5 +1,6 @@ -package content.region.kandarin.ardougne.plaguecity.quest.elena +package content.region.kandarin.ardougne.quest.plaguecity.dialogue.mourners +import content.data.Quests import core.api.getQuestStage import core.api.setQuestStage import core.game.dialogue.DialogueFile @@ -10,10 +11,10 @@ import core.tools.END_DIALOGUE import org.rs09.consts.NPCs @Initializable -class HeadMournerDialogue : DialogueFile() { +class MournerGuardDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - npc = NPC(NPCs.HEAD_MOURNER_716) - when (getQuestStage(player!!, PlagueCity.PlagueCityQuest)) { + npc = NPC(NPCs.MOURNER_717) + when (getQuestStage(player!!, Quests.PLAGUE_CITY)) { in 8..10 -> when (stage) { 0 -> npcl(FacialExpression.FRIENDLY, "Hmmm, how did you get over here? You're not one of this rabble. Ah well, you'll have to stay. Can't risk you going back now.").also { stage++ } @@ -37,8 +38,8 @@ class HeadMournerDialogue : DialogueFile() { 2 -> options("But I think a kidnap victim is in here.", "I fear not a mere plague.", "Thanks for the warning.").also { stage++ } 3 -> when (buttonID) { 1 -> playerl(FacialExpression.FRIENDLY, "But I think a kidnap victim is in here.").also { stage = 5 } - 2 -> playerl(FacialExpression.FRIENDLY, "I fear not a mere plague.").also { stage = 4 } - 3 -> playerl(FacialExpression.FRIENDLY, "Thanks for the warning.").also { stage = END_DIALOGUE } + 2 -> playerl(FacialExpression.FRIENDLY, "I fear not a mere plague.").also { stage = 5 } + 3 -> playerl(FacialExpression.FRIENDLY, "Thanks for the warning.").also { stage = 4 } } 4 -> playerl(FacialExpression.FRIENDLY, "Thanks for the warning.").also { stage = END_DIALOGUE } @@ -48,7 +49,7 @@ class HeadMournerDialogue : DialogueFile() { 8 -> npcl(FacialExpression.NEUTRAL, "I wouldn't get your hopes up though.").also { stage++ } 9 -> { end() - setQuestStage(player!!, "Plague City", 12) + setQuestStage(player!!, Quests.PLAGUE_CITY, 12) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerKidnapDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerKidnapDialogue.kt new file mode 100644 index 000000000..671b7d38b --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerKidnapDialogue.kt @@ -0,0 +1,184 @@ +package content.region.kandarin.ardougne.quest.plaguecity.dialogue.mourners + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.global.action.DoorActionHandler +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * These are the mourners guarding the kidnap building + */ +@Initializable +class MournerKidnapDialogue(player: Player? = null) :DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MournerKidnapDialogueFile(), npc) + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MOURNER_3216) + } +} + +/** + * Most of the work is in the dialogue file so the player can access this by trying to enter the room + */ +class MournerKidnapDialogueFile : DialogueFile(){ + + companion object { + const val KIDNAP = 10 + const val FEAR = 30 + const val CLEARANCE = 40 + + var closeMourner : NPC? = null + var farMourner : NPC? = null + + val eDoor: Location = Location.create(2540, 3273, 0) + val wDoor: Location = Location.create(2533, 3273, 0) + + var east = false + + } + override fun handle(componentID: Int, buttonID: Int) { + // Figure out who we are. It needs to be this since we can enter this dialogue from a door + // Do this for the first time regardless of where we are + if (stage == 0) { + RegionManager.getLocalNpcs(player!!, 2).forEach { + if (it.id == NPCs.MOURNER_3216) { + closeMourner = it + resetFace(closeMourner!!) + face(closeMourner!!, player!!) + face(player!!, closeMourner!!) + } + } + + east = player!!.location.x > 2537 + } + if (hasAnItem(player!!, Items.WARRANT_1503).exists()){ + when (stage){ + 0 -> npcl( + FacialExpression.NEUTRAL, + " I'd stand away from there. That black cross means that house has been touched by the plague." + ).also { stage++ } + + 1 -> playerl(FacialExpression.FRIENDLY, " I have a warrant from Bravek to enter here.").also { stage++ } + 2 -> npcl(FacialExpression.HALF_WORRIED, " This is highly irregular. Please wait...").also { stage++ } + 3 -> { + // Look further for the other one + RegionManager.getLocalNpcs(player!!, 10).forEach { + if (it.id == NPCs.MOURNER_3216 && it != closeMourner){ + farMourner = it + resetFace(farMourner!!) + } + } + + // I don't know why the queue has to be on this mourner to get both talking + val mournerQueue = if (east) farMourner else closeMourner + + queueScript(mournerQueue!!, 1, QueueStrength.WEAK) { animStage: Int -> + when(animStage){ + 0 -> { + end() + forceWalk(player!!, if (east) eDoor else wDoor, "smart") + face(closeMourner!!, farMourner!!) + // Legit typo + sendChat(closeMourner!!, "Hay... I got someone here with a warrant from Bravek, what should we do?") + // Immediately set the quest stage in case the player clicks again + if (getQuestStage(player!!, Quests.PLAGUE_CITY) < 17){ + setQuestStage(player!!, Quests.PLAGUE_CITY, 17) + } + return@queueScript delayScript(mournerQueue, 1) + } + 3 -> { + face(farMourner!!, closeMourner!! ) + sendChat(farMourner!!, "Well you can't let them in...") + return@queueScript delayScript(mournerQueue, 1) + } + 5 -> { + resetFace(player!!) + return@queueScript delayScript(mournerQueue, 1) + } + 6 -> { + // only walk the player if they have not walked themselves through + if (player!!.location.y > 3272) + DoorActionHandler.handleAutowalkDoor(player, getScenery (if (east) eDoor else wDoor)) + sendDialogue(player!!, "You wait until the mourner's back is turned and sneak into the building.").also { stage = END_DIALOGUE} + resetFace(closeMourner!!) + resetFace(farMourner!!) + return@queueScript delayScript(mournerQueue, 1) + } + 10 -> { + // Face north again + face(closeMourner!!, Location.create(closeMourner!!.location.x, 3275, 0)) + face(farMourner!!, Location.create(farMourner!!.location.x, 3275, 0)) + return@queueScript stopExecuting(mournerQueue) + } + } + return@queueScript delayScript(mournerQueue, 1) + } + + } + + } + } + else { + + when (stage) { + 0 -> npcl( + FacialExpression.NEUTRAL, + " I'd stand away from there. That black cross means that house has been touched by the plague." + ).also { stage = if (getQuestStage(player!!, Quests.PLAGUE_CITY) == 11) stage + 1 else END_DIALOGUE } + + 1 -> showTopics( + Topic("But I think a kidnap victim is in here.", KIDNAP), + Topic("I fear not a mere plague.", FEAR), + Topic("Thanks for the warning.", END_DIALOGUE), + ) + + KIDNAP -> npcl( + FacialExpression.NEUTRAL, + "Sounds unlikely, even kidnappers wouldn't go in there. Even if someone is in there, they're probably dead by now." + ).also { stage++ } + + KIDNAP + 1 -> showTopics( + Topic("Good point.", END_DIALOGUE), + Topic("I want to check anyway.", KIDNAP + 2) + ) + + KIDNAP + 2 -> npcl(FacialExpression.NEUTRAL, "You don't have clearance to go in there.").also { + stage = CLEARANCE + } + + FEAR -> npcl( + FacialExpression.NEUTRAL, + " That's irrelevant. You don't have clearance to go in there." + ).also { stage = CLEARANCE } + + CLEARANCE -> playerl(FacialExpression.ASKING, " How do I get clearance?").also { stage++ } + CLEARANCE + 1 -> npcl( + FacialExpression.NEUTRAL, + " Well you'd need to apply to the head mourner, or I suppose Bravek the city warder." + ).also { stage++ } + + CLEARANCE + 2 -> npcl(FacialExpression.NEUTRAL, " I wouldn't get your hopes up though.").also { + stage = END_DIALOGUE + setQuestStage(player!!, Quests.PLAGUE_CITY, 12) + } + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HalgriveDialogue.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HalgriveDialogue.java index a7b3ccf2c..4f3dee3c2 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HalgriveDialogue.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HalgriveDialogue.java @@ -5,6 +5,7 @@ import core.plugin.Initializable; import core.game.dialogue.DialoguePlugin; import core.api.*; import org.rs09.consts.Items; +import content.data.Quests; @Initializable public class HalgriveDialogue extends DialoguePlugin { @@ -21,7 +22,7 @@ public class HalgriveDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - if(player.getQuestRepository().getStage("Sheep Herder") < 10) { + if(player.getQuestRepository().getStage(Quests.SHEEP_HERDER) < 10) { player("Hello. How are you?"); stage = 0; return true; @@ -116,7 +117,7 @@ public class HalgriveDialogue extends DialoguePlugin { stage++; break; case 105: - player.getQuestRepository().getQuest("Sheep Herder").start(player); + player.getQuestRepository().getQuest(Quests.SHEEP_HERDER).start(player); player.getDialogueInterpreter().sendDialogue("The councillor gives you some poisoned sheep feed."); player.getInventory().add(SheepHerder.POISON); stage++; @@ -168,7 +169,7 @@ public class HalgriveDialogue extends DialoguePlugin { stage++; break; case 207: - player.getQuestRepository().getQuest("Sheep Herder").finish(player); + player.getQuestRepository().getQuest(Quests.SHEEP_HERDER).finish(player); end(); break; } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/OrbonDialogue.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/OrbonDialogue.java index ec371b44a..55efedcf1 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/OrbonDialogue.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/OrbonDialogue.java @@ -5,6 +5,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.Initializable; import core.game.dialogue.DialoguePlugin; +import content.data.Quests; @Initializable public class OrbonDialogue extends DialoguePlugin { @@ -21,7 +22,7 @@ public class OrbonDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - if(player.getQuestRepository().getStage("Sheep Herder") == 10){ + if(player.getQuestRepository().getStage(Quests.SHEEP_HERDER) == 10){ player("Hello doctor. I need to acquire some protective clothing","so that I can dispose of some escaped sheep infected","with the plague."); stage = 100; return true; diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/SheepHerder.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/SheepHerder.java index f7a28c03b..75ed87dbf 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/SheepHerder.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/SheepHerder.java @@ -10,6 +10,7 @@ import core.plugin.Initializable; import org.rs09.consts.Items; import java.util.HashMap; +import content.data.Quests; @Initializable public class SheepHerder extends Quest { @@ -35,39 +36,71 @@ public class SheepHerder extends Quest { boneMap.put(BLUE_SHEEP,BLUE_SHEEP_BONES); } - public SheepHerder(){super("Sheep Herder",113,112,4,60,0,1,3);} + public SheepHerder(){super(Quests.SHEEP_HERDER,113,112,4,60,0,1,3);} @Override public void drawJournal(Player player, int stage) { boolean hasGear = (player.getInventory().containsItem(PLAGUE_BOTTOM) && player.getInventory().containsItem(PLAGUE_TOP) || (player.getEquipment().containsItem(PLAGUE_BOTTOM) && player.getEquipment().containsItem(PLAGUE_TOP))) || stage >= 20; - int line = 11; + int line = 12; boolean sheepDead = player.getAttribute("sheep_herder:all_dead",false); super.drawJournal(player, stage); - if(stage < 10){ - line(player,"I can start this quest by speaking to !!Councillor Halgrive??",line++); - line(player, "near to the !!Zoo?? in !!East Ardougne.??",line++); + if(stage == 0){ + line(player,"I can start this quest by speaking to !!Councillor Halgrive??", line++); + line(player, "near to the !!Zoo?? in !!East Ardougne??.", line++); } else { - switch(stage){ - case 10: - line(player, "!!Councillor Halgrive?? said I should speak to !!Doctor Orbon?? about", line++, hasGear); - line(player, "Getting some protective gear.", line++, hasGear); - line(player, "I need to !!locate the diseased sheep?? and corral them !!into the pin??", line++,sheepDead); - line(player, "After which, I need to !!poison them?? and !!incinerate their bones.??", line++,sheepDead); - if(sheepDead) { - line(player,"I should inform !!Councillor Halgrive?? that I have taken care of the problem.",line++); + line(player,"Councillor Halgrive asked me to dispose of four plague", line++, true); + line(player,"bearing sheep just north of Ardougne and I accepted.", line++, true); + line(player,"He gave me some poisoned sheep feed to do this.", line++, true); + if(hasGear) { + line(player, "I bought some protective clothing from Dr. Orbon in the", line++, true); + line(player, "chapel north of Ardougne Zoo. I could now kill the sheep.", line++, true); + } else { + line(player, "!!Councillor Halgrive?? said I should speak to !!Doctor Orbon??", line++); + line(player, "about getting some protective gear.", line++); + } + if(stage == 10) { + // This is not authentic. +// line(player, "I need to !!locate the diseased sheep?? and corral them !!into the pen??", line++,sheepDead); +// line(player, "After which, I need to !!poison them?? and !!incinerate their bones.??", line++,sheepDead); + line++; + if (sheepDead) { + line(player, "I equipped a prod and then I used it to to herd the diseased", line++, true); + line(player, "sheep to a pen where I could safely kill them and", line++, true); + line(player, "incinerate their bones.", line++, true); + line(player,"I should return to !!Councillor Halgrive?? to collect the reward", line++); + line(player,"he has promised me for my hard work.", line++); + } else { + if (player.getAttribute("sheep_herder:red_dead", false)) { + line(player, "I have killed the first sheep and incinerated its bones.", line++, true); } else { - line(player, "I still need:", line++); - line(player, "A !!Red Sheep??", line++, player.getAttribute("sheep_herder:red_dead", false)); - line(player, "A !!Blue Sheep??", line++, player.getAttribute("sheep_herder:blue_dead", false)); - line(player, "A !!Green Sheep??", line++, player.getAttribute("sheep_herder:green_dead", false)); - line(player, "A !!Yellow Sheep??", line++, player.getAttribute("sheep_herder:yellow_dead", false)); + line(player, "I must find the first sheep and herd it to the special pen.", line++); } - break; - case 100: - line(player,"I helped Councillor Halgrive by putting down",line++,true); - line(player,"plague-bearing sheep.",line++,true); - line(player,"%%QUEST COMPLETE!&&",line++); - break; + if (player.getAttribute("sheep_herder:green_dead", false)) { + line(player, "I have killed the second sheep and incinerated its bones.", line++, true); + } else { + line(player, "I must find the second sheep and herd it to the special", line++); + line(player, "pen.", line++); + } + if (player.getAttribute("sheep_herder:blue_dead", false)) { + line(player, "I have killed the third sheep and incinerated its bones.", line++, true); + } else { + line(player, "I must find the third sheep and herd it to the special pen.", line++); + } + if (player.getAttribute("sheep_herder:yellow_dead", false)) { + line(player, "I have killed the fourth sheep and incinerated its bones.", line++, true); + } else { + line(player, "I must find the fourth sheep and herd it to the special pen.", line++); + } + } + } + + if(stage >= 100) { + line(player, "I equipped a prod to herd the diseased sheep and then I", line++, true); + line(player, "used it to incinerate all four plagued sheep.", line++, true); + line(player, "I returned to let Councillor Halgrive know that the plagued", line++, true); + line(player, "sheep were no more and claimed my reward.", line++, true); + line++; + line(player, "%%QUEST COMPLETE!&&", line++, false); } } } diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/MournerUtilities.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/MournerUtilities.kt new file mode 100644 index 000000000..461a15a54 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/MournerUtilities.kt @@ -0,0 +1,57 @@ +package content.region.kandarin.ardougne.westardougne + +import core.api.EquipmentSlot +import core.api.allInEquipment +import core.api.getItemFromEquipment +import core.api.inEquipment +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +object MournerUtilities { + + const val NO_GEAR = 0 + const val JUST_MASK = 1 + const val JUST_GEAR = 2 + const val EXTRA_GEAR = 3 + + /** + * Check if the player is wearing just mourner gear + * @return 0 incomplete gear 1 mask only 2 complete gear only 3 complete gear with extras + */ + fun wearingMournerGear(player: Player): Int { + // Check that the play has all of these items + if (inEquipment(player, Items.GAS_MASK_1506)) { + // We have a mask + if (!allInEquipment( + player, Items.MOURNER_TOP_6065, Items.MOURNER_TROUSERS_6067, + Items.MOURNER_BOOTS_6069, Items.MOURNER_GLOVES_6068, Items.MOURNER_CLOAK_6070 + ) + ) { + // We have only a mask + return JUST_MASK + } + else { + // Check if we have other gear + + // These use up slots 0, 1, 4, 7, 9, 10. Check the others are empty + val mournerSlots = arrayOf( + EquipmentSlot.HEAD, EquipmentSlot.CAPE, EquipmentSlot.CHEST, + EquipmentSlot.LEGS, EquipmentSlot.HANDS, EquipmentSlot.FEET + ) + for (slot in EquipmentSlot.values()) { + // Skip the slots that we know have the gear equipped + if (mournerSlots.contains(slot)) continue + if (getItemFromEquipment(player, slot) != null) { + return EXTRA_GEAR + } + } + return JUST_GEAR + } + + } + else { + // We don't even have a mask + return NO_GEAR + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/CarlaDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/CarlaDialogue.kt similarity index 98% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/CarlaDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/CarlaDialogue.kt index 8e1ea65e5..a3431f603 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/CarlaDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/CarlaDialogue.kt @@ -1,4 +1,4 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue +package content.region.kandarin.ardougne.westardougne.dialogue import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/ChildDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ChildDialogue.kt similarity index 64% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/ChildDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ChildDialogue.kt index 96cbd880d..210d0ee8d 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/ChildDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ChildDialogue.kt @@ -1,5 +1,6 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue +package content.region.kandarin.ardougne.westardougne.dialogue +import content.region.kandarin.ardougne.westardougne.MournerUtilities import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -18,9 +19,8 @@ class ChildDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - 0 -> npcl(FacialExpression.FRIENDLY, "I'm not allowed to speak with strangers.").also { stage = END_DIALOGUE } - } + val noun = if (MournerUtilities.wearingMournerGear(player) > MournerUtilities.JUST_MASK) "mourners" else "strangers" + npcl(FacialExpression.CHILD_NORMAL, "I'm not allowed to speak with $noun.").also { stage = END_DIALOGUE } return true } @@ -29,7 +29,7 @@ class ChildDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun getIds(): IntArray { - return intArrayOf(NPCs.CHILD_6339, NPCs.CHILD_6345, NPCs.CHILD_356) + return intArrayOf(NPCs.CHILD_356, NPCs.CHILD_355) } } \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/CivilianDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/CivilianDialogue.kt new file mode 100644 index 000000000..dcf6f73fa --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/CivilianDialogue.kt @@ -0,0 +1,181 @@ +package content.region.kandarin.ardougne.westardougne.dialogue + +import content.region.kandarin.ardougne.westardougne.MournerUtilities +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + + +val cats = intArrayOf( + Items.PET_CAT_1561, + Items.PET_CAT_1562, + Items.PET_CAT_1563, + Items.PET_CAT_1564, + Items.PET_CAT_1565, + Items.PET_CAT_1566, + Items.OVERGROWN_CAT_1567, + Items.OVERGROWN_CAT_1568, + Items.OVERGROWN_CAT_1569, + Items.OVERGROWN_CAT_1570, + Items.OVERGROWN_CAT_1571, + Items.OVERGROWN_CAT_1572, + // todo implement these cats then uncomment this + // otherwise you get an exception which just steals your cat for no runes + // Items.LAZY_CAT_6549, + // Items.LAZY_CAT_6550, + // Items.LAZY_CAT_6551, + // Items.LAZY_CAT_6552, + // Items.LAZY_CAT_6553, + // Items.LAZY_CAT_6554, + // Items.WILY_CAT_6555, + // Items.WILY_CAT_6556, + // Items.WILY_CAT_6557, + // Items.WILY_CAT_6558, + // Items.WILY_CAT_6559, + // Items.WILY_CAT_6560, +) + +@Initializable +class CivilianDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object { + // Any of these + + + const val DIAG1 = 10 + const val DIAG2 = 20 + const val DIAG3 = 30 + + const val NO_CAT = 40 + const val BUY_CATS = 50 + const val REJECT_DEAL = 80 + const val WITCH_CAT = 90 + + const val MOURNER = 100 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (MournerUtilities.wearingMournerGear(player) > MournerUtilities.JUST_MASK){ + playerl(FacialExpression.NEUTRAL, "Hello.").also { stage = MOURNER } + } + else { + when (npc.id) { + NPCs.CIVILIAN_785 -> playerl(FacialExpression.NEUTRAL, "Hello there.").also { stage = DIAG1 } + NPCs.CIVILIAN_786 -> playerl(FacialExpression.NEUTRAL, "Hi there.").also { stage = DIAG2 } + NPCs.CIVILIAN_787 -> player(FacialExpression.NEUTRAL, "Hello there.").also { stage = DIAG3 } + } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + DIAG1 -> npcl(FacialExpression.HALF_GUILTY, "Oh hello, I'm sorry, I'm a bit worn out.").also { stage++ } + DIAG1 + 1 -> playerl(FacialExpression.NEUTRAL, "Busy day?").also { stage++ } + DIAG1 + 2 -> npcl(FacialExpression.HALF_GUILTY, "Oh, It's those mice! They're everywhere! " + + "What I really need is a cat. But they're hard to come by nowadays.").also { + stage = checkCat() + } + + DIAG2 -> npcl(FacialExpression.NEUTRAL, "Good day to you traveller.").also { stage++ } + DIAG2 + 1 -> playerl(FacialExpression.NEUTRAL, "What are you up to?").also { stage++ } + DIAG2 + 2 -> npcl(FacialExpression.NEUTRAL, "Chasing mice as usual! It's all I seem to do nowadays.").also { stage++ } + DIAG2 + 3 -> playerl(FacialExpression.NEUTRAL, "You must waste a lot of time?").also { stage++ } + DIAG2 + 4 -> npcl(FacialExpression.HALF_WORRIED, "Yes, but what can you do? It's not like there's many cats around here!").also{ stage = checkCat() + } + + DIAG3 -> npcl(FacialExpression.HALF_WORRIED, "I'm a bit busy to talk right now, sorry.").also { stage++ } + DIAG3 + 1 -> playerl(FacialExpression.NEUTRAL, "Why? What are you doing?").also { stage++ } + DIAG3 + 2 -> npcl(FacialExpression.HALF_WORRIED, "Trying to kill these mice! What I really need is a cat!").also { stage = checkCat() } + + NO_CAT -> playerl(FacialExpression.HALF_WORRIED, "No, you're right, you don't see many around.").also { stage = END_DIALOGUE } + + BUY_CATS -> showTopics( + Topic("I have a cat that I could sell.", BUY_CATS + 1), + Topic("Nope, they're not easy to get hold of.", END_DIALOGUE) + ) + // intentional whitespace typo + BUY_CATS + 1 -> npcl(FacialExpression.ASKING, "You don't say, is that it ?").also { stage++ } + BUY_CATS + 2 -> playerl(FacialExpression.HAPPY, "Say hello to a real mouse killer!").also { stage++ } + BUY_CATS + 3 -> npcl(FacialExpression.HALF_ASKING, "Hmmm, not bad, not bad at all. Looks like it's a lively one.").also { stage++ } + BUY_CATS + 4 -> playerl(FacialExpression.HALF_GUILTY, "Erm...kind of...").also { stage++ } + BUY_CATS + 5 -> npcl(FacialExpression.FRIENDLY, "I don't have much in the way of money. I do have these!").also { stage++ } + BUY_CATS + 6 -> sendDialogue("The peasant shows you a sack of Death Runes.").also { stage++ } + BUY_CATS + 7 -> npcl(FacialExpression.ASKING, "The dwarves bring them from the mine for us. Tell you what, I'll give you 100 Death Runes for the cat.").also { stage++ } + BUY_CATS + 8 -> showTopics( + Topic("Nope, I'm not parting for that.", REJECT_DEAL), + Topic("Ok then, you've got a deal.", BUY_CATS+9) + ) + BUY_CATS + 9 -> { + npcl(FacialExpression.HAPPY, "Great! Hand over the cat and I'll give you the runes.").also { + stage = END_DIALOGUE + for (cat in cats) { + if (removeItem(player, cat)){ + player.familiarManager.removeDetails(cat) + addItem(player, Items.DEATH_RUNE_560, 100) + break + } + } + } + } + + REJECT_DEAL -> npcl(FacialExpression.HALF_GUILTY, "Well, I'm not giving you anymore!").also { stage = END_DIALOGUE } + + WITCH_CAT -> playerl(FacialExpression.HAPPY, "I have a cat...look!").also { stage++ } + WITCH_CAT + 1 -> npcl(FacialExpression.HALF_WORRIED, "Hmmm...doesn't look like it's seen daylight in years. That's not going to catch any mice!").also { stage = END_DIALOGUE } + + + MOURNER -> npcl(FacialExpression.ANGRY, "If you Mourners really wanna help, why don't you do something about these mice?!").also { stage = END_DIALOGUE } + } + return true + } + + private fun checkCat(): Int { + return if (anyInInventory(player, *cats)) BUY_CATS + else if (inInventory(player, Items.WITCHS_CAT_1491)) WITCH_CAT + else NO_CAT + + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CIVILIAN_785, NPCs.CIVILIAN_786, NPCs.CIVILIAN_787) + } +} + + +class CatTrade : InteractionListener{ + override fun defineListeners() { + + class CatTradeDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when (stage){ + 0 -> sendDialogue(player!!, "You hand over the cat. You are given 100 Death Runes.").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY, "Great, thanks for that!").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "That's ok, take care.").also { stage = END_DIALOGUE } + } + } + } + onUseWith(IntType.NPC, cats, NPCs.CIVILIAN_785, NPCs.CIVILIAN_786, NPCs.CIVILIAN_787){ player, used, with -> + if(removeItem(player, used)){ + val dialogue = CatTradeDialogue() + // Remove the cat + player.familiarManager.removeDetails(used.id) + addItem(player, Items.DEATH_RUNE_560, 100) + openDialogue(player, dialogue, with as NPC) + } + + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/HeadMournerDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/HeadMournerDialogue.kt new file mode 100644 index 000000000..65671e952 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/HeadMournerDialogue.kt @@ -0,0 +1,115 @@ +package content.region.kandarin.ardougne.westardougne.dialogue + +import content.data.Quests +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCity +import content.region.kandarin.ardougne.westardougne.MournerUtilities +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs +import kotlin.properties.Delegates + +@Initializable +class HeadMournerDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object { + const val CLEARANCE = 40 + const val WHATS_A_MOURNER = 10 + const val NO_PLAGUE = 20 + const val ELENA = 30 + + const val CRAZY = 50 + const val KIDNAP = 60 + const val MASK = 70 + + var mourningGear by Delegates.notNull() + + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + mourningGear = MournerUtilities.wearingMournerGear(player) + if(mourningGear < MournerUtilities.JUST_GEAR){ + npcl(FacialExpression.ANGRY, "How did you get into West Ardougne? " + + "Ah well you'll have to stay, can't risk you spreading the plague outside.").also { stage++ } + } + else{ + npcl(FacialExpression.NEUTRAL, "Ahh... A new recruit.").also { stage++ } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(mourningGear){ + MournerUtilities.NO_GEAR, MournerUtilities.JUST_MASK -> when(stage){ + START_DIALOGUE + 1 -> showTopics( + IfTopic("I need clearance to enter a plague house.", CLEARANCE, + !isQuestComplete(player, Quests.PLAGUE_CITY) && (getQuestStage(player, Quests.PLAGUE_CITY) > 11)), + Topic("So what's a mourner?", WHATS_A_MOURNER), + Topic("I haven't got the plague though. ", NO_PLAGUE), + IfTopic("I'm looking for a woman named Elena.", ELENA, !isQuestComplete(player, Quests.PLAGUE_CITY)) + ) + + CLEARANCE -> npcl(FacialExpression.DISGUSTED, "You must be nuts, absolutely not!").also { stage++ } + CLEARANCE + 1 -> showTopics( + Topic("There's a kidnap victim inside!", KIDNAP), + Topic("I've got a gas mask though...", MASK), + Topic("Yes, I'm utterly crazy.", CRAZY) + ) + + KIDNAP -> npcl(FacialExpression.FRIENDLY, "Well they're as good as dead then, no point in trying to save them.").also { stage = END_DIALOGUE } + + MASK -> npcl(FacialExpression.FRIENDLY, "It's not regulation. Anyway you're not properly trained to deal with the plague.").also { stage++ } + MASK + 1 -> playerl(FacialExpression.FRIENDLY, "How do I get trained?").also { stage++ } + MASK + 2 -> npcl(FacialExpression.FRIENDLY, "It requires a strict 18 months of training.").also { stage++ } + MASK + 3 -> playerl(FacialExpression.FRIENDLY, "I don't have that sort of time.").also { stage = END_DIALOGUE } + + CRAZY -> npcl(FacialExpression.FRIENDLY, "You're wasting my time, I have a lot of work to do!").also { stage = END_DIALOGUE } + + WHATS_A_MOURNER -> npcl(FacialExpression.NEUTRAL, + "We're working for King Lathas of East Ardougne trying to contain the accursed plague sweeping West Ardougne." + + " We also do our best to ease these people's suffering.").also { stage++ } + WHATS_A_MOURNER + 1 -> npcl(FacialExpression.NEUTRAL, "We're nicknamed mourners because we spend a lot of" + + " time at plague victim funerals, no-one else is allowed to risk the funerals." + + " It's a demanding job, and we get little thanks from the people here.").also { stage = END_DIALOGUE } + + NO_PLAGUE -> npcl(FacialExpression.ANNOYED, "Can't risk you being a carrier. That protective clothing you have isn't regulation issue. It won't meet safety standards.").also { stage = END_DIALOGUE } + + ELENA -> npcl( + FacialExpression.NEUTRAL, + "Ah yes, I've heard of her. A healer I believe. She must be mad coming over here voluntarily." + ).also { stage++ } + + ELENA + 1 -> npcl( + FacialExpression.SAD, + "I hear rumours she has probably caught the plague now. Very tragic, a stupid waste of life." + ).also { stage = END_DIALOGUE } + + } + + MournerUtilities.JUST_GEAR, MournerUtilities.EXTRA_GEAR -> when(stage){ + START_DIALOGUE+1 ->playerl(FacialExpression.ASKING, " How do you know I'm new?").also { stage++ } + START_DIALOGUE+2 -> npcl(FacialExpression.NEUTRAL, "Because all the old members of the guard know to report to the real Head Mourner, not me.").also { if (mourningGear == MournerUtilities.EXTRA_GEAR) stage++ else stage+=2 } + START_DIALOGUE+3 -> npcl(FacialExpression.ANNOYED, "Also, none of the old timers use non-regulation gear.").also { stage++ } + START_DIALOGUE+4 -> playerl(FacialExpression.ASKING, " You're not the real overseer here?").also { stage++ } + START_DIALOGUE+5 -> npcl(FacialExpression.NEUTRAL, "No, I am just a front man, our true head is far too busy to deal with the requests of the citizens, so I conduct the day to day business here.").also { stage++ } + START_DIALOGUE+6 -> npcl(FacialExpression.NEUTRAL, "You should go and report in" + if(mourningGear == MournerUtilities.EXTRA_GEAR) ", I would lose the non-regulation gear as well if I were you." else "." ).also { stage++ } + START_DIALOGUE+7 -> playerl(FacialExpression.FRIENDLY, "Okay, thanks.").also { stage = END_DIALOGUE } + } + } + return true + } + + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HEAD_MOURNER_716) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ManWomanDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ManWomanDialogue.kt new file mode 100644 index 000000000..a86698f3d --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ManWomanDialogue.kt @@ -0,0 +1,81 @@ +package content.region.kandarin.ardougne.westardougne.dialogue + +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +@Initializable +class ManWomanDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object { + const val DIAG1 = 10 + const val DIAG2 = 20 + const val DIAG3 = 30 + const val DIAG4 = 40 + const val DIAG5 = 50 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + val path = RandomFunction.random(1, 6) + val msg = when(path){ + 1,5 -> "Good day." + 2 -> "Hi there." + 3, 4 -> "Hello, how's it going?" + else -> "Hello" + } + + playerl(FacialExpression.FRIENDLY, msg).also { stage = path * 10 } + + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + when (stage){ + DIAG1 -> npcl(FacialExpression.HALF_ASKING, "An outsider! Can you get me out of this hell hole?").also { stage++ } + DIAG1 + 1 -> playerl(FacialExpression.SAD, "Sorry, that's not what I'm here to do.").also { stage = END_DIALOGUE } + + DIAG2 -> npcl(FacialExpression.ANNOYED, "Go away. People from the outside shut us in like animals. I have nothing to say to you.").also { stage = END_DIALOGUE } + + DIAG3 -> npcl(FacialExpression.SAD, "Life is tough.").also { stage++ } + DIAG3 + 1 -> showTopics( + Topic("Yes, living in a plague city must be hard.", DIAG3 + 2), + Topic("I'm sorry to hear that.", DIAG3 + 3), + Topic("I'm looking for a lady called Elena.", DIAG3 + 4) + ) + DIAG3 + 2 -> npcl(FacialExpression.HALF_GUILTY, "Plague? Pah, that's no excuse for the treatment we've received. It's obvious pretty quickly if someone has the plague. I'm thinking about making a break for it. I'm perfectly healthy, not gonna infect anyone.").also { stage = END_DIALOGUE } + DIAG3 + 3 -> npcl(FacialExpression.SAD, "Well, aint much either you or me can do about it.").also { stage = END_DIALOGUE } + DIAG3 + 4 -> npcl(FacialExpression.NEUTRAL, "I've not heard of her. Old Jethick knows a lot of people, maybe he'll know where you can find her.").also { stage = END_DIALOGUE } + + DIAG4 -> npcl(FacialExpression.ANNOYED, "Bah, those mourners... they're meant to be helping us, but I think they're doing more harm here than good. They won't even let me send a letter out to my family.").also{ stage++} + DIAG4 + 1 -> showTopics( + Topic("Have you seen a lady called Elena around here?", DIAG4 + 2), + Topic("You should stand up to them more.", DIAG4 + 3) + ) + DIAG4 + 2 -> npcl(FacialExpression.SAD, "Yes, I've seen her. Very helpful person. Not for the last few days though... I thought maybe she'd gone home.").also { stage = END_DIALOGUE } + DIAG4 + 3 -> npcl(FacialExpression.HALF_GUILTY, " Oh I'm not one to cause a fuss.").also { stage = END_DIALOGUE } + + DIAG5 -> npcl(FacialExpression.ANGRY, "We don't have good days here anymore. Curse King Tyras.").also { stage++ } + DIAG5 + 1 -> showTopics( + Topic("Oh ok, bad day then.", END_DIALOGUE), + Topic("Why, what has he done?", DIAG5 + 2), + Topic("I'm looking for a woman called Elena.", DIAG5 + 3) + ) + DIAG5 + 2 -> npcl(FacialExpression.ANGRY, "His army curses our city with this plague then wanders off again, leaving us to clear up the pieces.").also { stage = END_DIALOGUE } + DIAG5 + 3 -> npcl(FacialExpression.NEUTRAL, "Not heard of her.").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + + return intArrayOf(NPCs.MAN_728, NPCs.MAN_729, NPCs.MAN_351, + NPCs.WOMAN_352, NPCs.WOMAN_353, NPCs.WOMAN_354, NPCs.WOMAN_360, NPCs.WOMAN_361, NPCs.WOMAN_362, NPCs.WOMAN_363) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/MournerDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/MournerDialogue.kt new file mode 100644 index 000000000..da0ef56ae --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/MournerDialogue.kt @@ -0,0 +1,114 @@ +package content.region.kandarin.ardougne.westardougne.dialogue + +import content.data.Quests +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCity +import content.region.kandarin.ardougne.westardougne.MournerUtilities.EXTRA_GEAR +import content.region.kandarin.ardougne.westardougne.MournerUtilities.JUST_GEAR +import content.region.kandarin.ardougne.westardougne.MournerUtilities.JUST_MASK +import content.region.kandarin.ardougne.westardougne.MournerUtilities.NO_GEAR +import content.region.kandarin.ardougne.westardougne.MournerUtilities.wearingMournerGear +import core.api.getQuestStage +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class MournerDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object{ + const val WHATS_A_MOURNER = 10 + const val NO_PLAGUE = 20 + const val ELENA = 30 + + const val CONVO_1 = 40 + const val CONVO_2 = 50 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(wearingMournerGear(player)){ + NO_GEAR, JUST_MASK -> { + if (npc.id == NPCs.MOURNER_717) { + when (stage) { + 0 -> npcl( + FacialExpression.HALF_ASKING, + "Hmmm, how did you get over here? You're not one of this rabble. Ah well, you'll have to stay. Can't risk you going back now." + ).also { stage++ } + + 1 -> showTopics( + Topic("So what's a mourner?", WHATS_A_MOURNER), + Topic("I haven't got the plague though... ", NO_PLAGUE), + IfTopic( + "I'm looking for a woman named Elena.", + ELENA, + getQuestStage(player, Quests.PLAGUE_CITY) in (6..98) + ) + ) + + WHATS_A_MOURNER -> npcl( + FacialExpression.NEUTRAL, + "We're working for King Lathas of East Ardougne trying to contain the accursed plague sweeping West Ardougne." + + " We also do our best to ease these people's suffering." + ).also { stage++ } + + WHATS_A_MOURNER + 1 -> npcl( + FacialExpression.NEUTRAL, "We're nicknamed mourners because we spend a lot of" + + " time at plague victim funerals, no-one else is allowed to risk the funerals." + + " It's a demanding job, and we get little thanks from the people here." + ).also { stage = END_DIALOGUE } + + ELENA -> npcl( + FacialExpression.NEUTRAL, + "Ah yes, I've heard of her. A healer I believe. She must be mad coming over here voluntarily." + ).also { stage++ } + + ELENA + 1 -> npcl( + FacialExpression.SAD, + "I hear rumours she has probably caught the plague now. Very tragic, a stupid waste of life." + ).also { stage = END_DIALOGUE } + + NO_PLAGUE -> npcl( + FacialExpression.ANNOYED, + "Can't risk you being a carrier. That protective clothing you have isn't regulation issue. It won't meet safety standards." + ).also { stage = END_DIALOGUE } + } + } + else{ + npcl(FacialExpression.ANNOYED, "Stand back citizen, do not approach me.").also { stage = END_DIALOGUE } + } + + } + JUST_GEAR -> { + when(stage){ + 0 -> playerl(FacialExpression.NEUTRAL, "Hello.").also { + stage = if((0..1).random() > 0) CONVO_1 else CONVO_2 + } + + CONVO_1 -> npcl(FacialExpression.NEUTRAL, "Good day. Are you in need of assistance?").also { stage++ } + CONVO_1 + 1 -> playerl(FacialExpression.NEUTRAL, " Yes, but I don't think you can help.").also { stage++ } + CONVO_1 + 2 -> npcl(FacialExpression.NEUTRAL, " You will be surprised at how much help the brute force of the Guard can be.").also { stage++ } + CONVO_1 + 3 -> playerl(FacialExpression.NEUTRAL, " Well I'll be sure to ask if I'm in need of some muscle.").also { stage = END_DIALOGUE } + + CONVO_2 -> npcl(FacialExpression.ANNOYED, " Good day. Are you in need of assistance?").also { stage++ } + CONVO_2 + 1 -> playerl(FacialExpression.NEUTRAL, " No, I just wanted to talk to a friendly face.").also { stage++ } + CONVO_2 + 2 -> npcl(FacialExpression.ANGRY, " Do I look friendly to you? I really must work on my scowl more.").also { stage = END_DIALOGUE } + } + + } + EXTRA_GEAR -> { + when(stage){ + 0 -> npcl(FacialExpression.ANNOYED, "You should know better than to wear non-regulation gear.").also { stage++ } + 1 -> playerl(FacialExpression.HALF_GUILTY, "Sorry, I'm new around here.").also { stage++ } + 2 -> npcl(FacialExpression.ANNOYED, "Well, you know the drill - lose the gear, I will let it pass this time.").also { stage = END_DIALOGUE } + } + } + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MOURNER_717, NPCs.MOURNER_348, NPCs.MOURNER_347, NPCs.MOURNER_371, NPCs.MOURNER_369) + } +} diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/NurseSarahDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/NurseSarahDialogue.kt similarity index 87% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/NurseSarahDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/NurseSarahDialogue.kt index d628c9cef..fd7fbcd9b 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/NurseSarahDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/NurseSarahDialogue.kt @@ -1,4 +1,4 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue +package content.region.kandarin.ardougne.westardougne.dialogue import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -20,7 +20,7 @@ class NurseSarahDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { 0 -> npcl(FacialExpression.FRIENDLY, "Hello my dear, how are you feeling?").also { stage++ } - 1 -> playerl(FacialExpression.FRIENDLY, "Hello my dear, how are you feeling?").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "I'm ok thanks.").also { stage++ } 2 -> npcl(FacialExpression.FRIENDLY, "Well in that case I'd better get back to work. Take care.").also { stage++ } 3 -> playerl(FacialExpression.FRIENDLY, "You too.").also { stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/PriestDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/PriestDialogue.kt similarity index 94% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/PriestDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/PriestDialogue.kt index 6e035d659..472884667 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/PriestDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/PriestDialogue.kt @@ -1,4 +1,4 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue +package content.region.kandarin.ardougne.westardougne.dialogue import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression diff --git a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/RecruiterDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/RecruiterDialogue.kt similarity index 95% rename from Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/RecruiterDialogue.kt rename to Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/RecruiterDialogue.kt index 672414c02..3e53af8c2 100644 --- a/Server/src/main/content/region/kandarin/ardougne/plaguecity/dialogue/RecruiterDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/RecruiterDialogue.kt @@ -1,4 +1,4 @@ -package content.region.kandarin.ardougne.plaguecity.dialogue +package content.region.kandarin.ardougne.westardougne.dialogue import core.api.sendNPCDialogue import core.game.dialogue.DialoguePlugin diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/MainGatesListener.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/MainGatesListener.kt new file mode 100644 index 000000000..82a8cd5e3 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/MainGatesListener.kt @@ -0,0 +1,32 @@ +package content.region.kandarin.ardougne.westardougne.handlers + +import content.data.Quests +import core.api.* +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class MainGatesListener : InteractionListener { + + override fun defineListeners() { + on(intArrayOf(Scenery.ARDOUGNE_WALL_DOOR_9738, Scenery.ARDOUGNE_WALL_DOOR_9330), IntType.SCENERY, "open") { player, node -> + if (hasRequirement(player, Quests.BIOHAZARD)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else if(inBorders(player, 2556, 3298, 2557, 3301)){ + lock(player,2) + sendMessage(player, "You pull on the large wooden doors...") + queueScript(player,2){ + sendMessage(player, "...but they will not open.") + return@queueScript stopExecuting(player) + } + } else { + face(player, Location.create(2559, 3302, 0)) + sendNPCDialogue(player, NPCs.MOURNER_2349, "Oi! What are you doing? Get away from there!") + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/MournerHQDoors.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/MournerHQDoors.kt new file mode 100644 index 000000000..2c540b027 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/MournerHQDoors.kt @@ -0,0 +1,66 @@ +package content.region.kandarin.ardougne.westardougne.handlers + +import content.data.Quests +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCity +// import core.api.hasAnItem +import core.api.isQuestComplete +import core.api.openDialogue +import core.api.teleport +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs +// import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class MournerHQDoors : InteractionListener { + + override fun defineListeners() { + class MournerHQDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.MOURNER_347) + // todo check only the mourner gear is equipped + when (stage){ + START_DIALOGUE -> npcl(FacialExpression.ANNOYED, "Who are you? Go away!").also { stage = END_DIALOGUE } + // Wearing extra gear + /* + Mourner: You should know better than to wear non-regulation gear. + Player: Sorry, I'm new around here. + Mourner: Well, you know the drill - lose the gear, I will let it pass this time. + */ + } + + } + } + + // Front door + on(Scenery.DOOR_2036, IntType.SCENERY, "open"){ player, node-> + //todo after Mourning's End I is implemented make this check for wearing mourner gear + if(isQuestComplete(player, Quests.PLAGUE_CITY)){ + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + else{ + openDialogue(player, MournerHQDialogue()) + } + return@on true + } + + on(Scenery.TRAPDOOR_8783, IntType.SCENERY, "open"){ player, _-> + // https://youtu.be/P-ns2kyvIGs?si=_DfI-V8KCyNoRtss&t=560 + //todo after Mourning's End II is implemented make this check for a New Key 6104 + // if(hasAnItem(player, Items.NEW_KEY_6104).exists()){ + teleport(player, Location.create(2044,4649, 0)) + //} + //else{ + // sendMessage(player, "The trapdoor appears locked") + //} + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/SarahsBox.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/SarahsBox.kt new file mode 100644 index 000000000..191c4dca5 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/SarahsBox.kt @@ -0,0 +1,38 @@ +package content.region.kandarin.ardougne.westardougne.handlers + +import content.data.Quests +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCity +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class SarahsBox : InteractionListener { + override fun defineListeners() { + + on(Scenery.BOX_2062, IntType.SCENERY, "open") { _, node -> + val box = node as core.game.node.scenery.Scenery + replaceScenery(box, Scenery.BOX_2063, -1) + return@on true + } + + on(Scenery.BOX_2063, IntType.SCENERY, "search"){ player, _ -> + if(isQuestComplete(player, Quests.PLAGUE_CITY)){ + if(hasSpaceFor(player, Item(Items.DOCTORS_GOWN_430)) && !hasAnItem(player, Items.DOCTORS_GOWN_430).exists()){ + sendMessage(player, "You find a medical gown in the box.") + addItem(player, Items.DOCTORS_GOWN_430) + return@on true + } + } + sendMessage(player, "You search the box but find nothing") + return@on true + } + + on(Scenery.BOX_2063, IntType.SCENERY, "close") { _, node -> + replaceScenery(node.asScenery(), Scenery.BOX_2062, -1) + return@on true + } + } +} diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/WearMaskListener.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/WearMaskListener.kt new file mode 100644 index 000000000..7ed43f74e --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/handlers/WearMaskListener.kt @@ -0,0 +1,42 @@ +package content.region.kandarin.ardougne.westardougne.handlers + +import content.data.Quests +import core.api.inBorders +import core.api.isQuestComplete +import core.api.openDialogue +import core.api.sendDialogue +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import core.tools.END_DIALOGUE +import org.rs09.consts.Items + +class WearMaskListener : InteractionListener { + override fun defineListeners() { + onUnequip(Items.GAS_MASK_1506){ player, _ -> + if (isQuestComplete(player, Quests.BIOHAZARD)){ + return@onUnequip true + } + else{ + if( + inBorders(player, 2511, 3266, 2556, 3334) || + inBorders(player, 2464, 3281, 2511, 3334) || + inBorders(player, 2461, 3281,2463, 3322) || + inBorders(player, 2435, 3307, 2463, 3322) + ){ + openDialogue(player, MaskChat()) + return@onUnequip false + } + return@onUnequip true + } + } + } +} + +class MaskChat : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + playerl(FacialExpression.WORRIED, "I should probably keep the gas mask on whilst I'm in West Ardougne.").also { stage = END_DIALOGUE } + } + +} + diff --git a/Server/src/main/content/region/kandarin/catherby/dialogue/ArheinDialogue.kt b/Server/src/main/content/region/kandarin/catherby/dialogue/ArheinDialogue.kt index ed8427af0..3300e43d5 100644 --- a/Server/src/main/content/region/kandarin/catherby/dialogue/ArheinDialogue.kt +++ b/Server/src/main/content/region/kandarin/catherby/dialogue/ArheinDialogue.kt @@ -11,6 +11,7 @@ import core.game.dialogue.Topic import core.tools.END_DIALOGUE import org.rs09.consts.Items import content.region.kandarin.seers.quest.merlinsquest.ArheinMCDialogue +import content.data.Quests /** @@ -113,7 +114,7 @@ class ArheinDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin 140 -> npcl(core.game.dialogue.FacialExpression.GUILTY,"Sorry pal, but I'm afraid I'm not quite ready to sail yet.").also { stage++ } 141 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"I'm waiting on a big delivery of candles which I need to deliver further along the coast.").also { stage = END_DIALOGUE } 500 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "Yes, I do have orders to deliver there from time to time. I think I may have some bits and pieces for them when I leave here next actually.").also { - val queststage = player.questRepository.getStage("Merlin's Crystal") + val queststage = player.questRepository.getStage(Quests.MERLINS_CRYSTAL) if(queststage == 30 || queststage == 40) { loadFile(ArheinMCDialogue(queststage)) } else { diff --git a/Server/src/main/content/region/kandarin/dialogue/AstronomyBook.kt b/Server/src/main/content/region/kandarin/dialogue/AstronomyBook.kt deleted file mode 100644 index 80df6aa8c..000000000 --- a/Server/src/main/content/region/kandarin/dialogue/AstronomyBook.kt +++ /dev/null @@ -1,130 +0,0 @@ -package content.region.kandarin.dialogue - -import content.global.handlers.iface.BookInterface -import content.global.handlers.iface.BookLine -import content.global.handlers.iface.Page -import content.global.handlers.iface.PageSet -import core.ServerConstants -import core.api.setAttribute -import core.game.interaction.IntType -import core.game.interaction.InteractionListener -import core.game.node.entity.player.Player -import core.plugin.Initializable -import org.rs09.consts.Items - -@Initializable -class AstronomyBook : InteractionListener { - companion object { - private val TITLE = "The tales of Scorpius" - private val CONTENTS = arrayOf( - PageSet( - Page( - BookLine("A History of Astronomy", 55), - BookLine("in ${ServerConstants.SERVER_NAME}.", 56), - BookLine("", 57), - BookLine("At the start of the 4th age,", 58), - BookLine("a learned man by the", 59), - BookLine("name of Scorpius, known well", 60), - BookLine("for his powers of vision and", 61), - BookLine("magic, sought communion with", 62), - BookLine("the gods of the world.", 63), - BookLine("So many unanswered questions", 64), - BookLine("had he that devoted his entire", 65), - ), - Page( - BookLine("life to this cause.", 66), - BookLine("After many years of study,", 67), - BookLine("seeking knowledge from the", 68), - BookLine("wise of that time, he developed", 69), - BookLine("a machine infused with", 70), - BookLine("magical power, infused with", 71), - BookLine("the ability to pierce", 72), - BookLine("into the heavens - a huge eye", 73), - BookLine("that gave the user", 74), - BookLine("incredible site, like never", 75), - BookLine("seen before.", 76), - ) - ), - PageSet( - Page( - BookLine("As time passed, Scorpius", 55), - BookLine("grew adept at using his", 56), - BookLine("specialized skills, and followed", 57), - BookLine("the movements of stars", 58), - BookLine("in ${ServerConstants.SERVER_NAME}, which are", 59), - BookLine("mapped and named, and still", 60), - BookLine("used to this day.", 61), - BookLine("", 62), - BookLine("Before long, Scorpius", 63), - BookLine("used his knowledge", 64), - BookLine("for predicting the future,", 65), - ), - Page( - BookLine("and, in turn, he called upon", 66), - BookLine("the dark knowledge of", 67), - BookLine("Zamorakian worshipers", 68), - BookLine("to further his cause. Living", 69), - BookLine("underground, the followers", 70), - BookLine("of the dark god remained", 71), - BookLine("until the civilization of", 72), - BookLine("Ardougne grew in strength", 73), - BookLine("and control.", 74), - ) - ), - PageSet( - Page( - BookLine("The kings of that time", 55), - BookLine("worked to banish the", 56), - BookLine("Zamorakian followers in", 57), - BookLine("the area, hiding all", 58), - BookLine("reference to Scorpius's", 59), - BookLine("invention, due to", 60), - BookLine("its 'evil nature'.", 61), - BookLine("", 62), - BookLine("Years later, when the", 63), - BookLine("minds of the kings lent", 64), - BookLine("more towards the research", 65), - ), - Page( - BookLine("of the unknown, the plans", 66), - BookLine("of Scorpius were uncovered", 67), - BookLine("and the heavenly eye", 68), - BookLine("constructed again. Since then,", 69), - BookLine("many have studied the ways of", 70), - BookLine("the astronomer, Scorpius", 71), - BookLine("and in his memory a grave", 72), - BookLine("was constructed near the", 73), - BookLine("Observatory. Some claim his", 74), - BookLine("ghost still wanders nearby,", 75), - BookLine("in torment as he seeks", 76), - ) - ), - PageSet( - Page( - BookLine("the secrets of the heavens", 55), - BookLine("that can never be solved.", 56), - BookLine("Tales tell that he will", 57), - BookLine("grant those adept in the", 58), - BookLine("arts of the astronomer a", 59), - BookLine("blessing of unusual power.", 60), - BookLine("", 61), - BookLine("Here ends the tale of", 62), - BookLine("how astronomy entered", 63), - BookLine("the known world.", 64), - ) - ), - ) - } - - private fun display(player: Player, pageNum: Int, buttonID: Int): Boolean { - BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) - return true - } - - override fun defineListeners() { - on(Items.ASTRONOMY_BOOK_600, IntType.ITEM, "read") { player, _ -> - BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) - return@on true - } - } -} diff --git a/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt b/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt index de5a3e3c8..fd294e309 100644 --- a/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt +++ b/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt @@ -1,8 +1,10 @@ package content.region.kandarin.dialogue +import core.ServerConstants import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic import core.game.node.entity.player.Player import core.game.node.entity.player.link.IronmanMode import core.plugin.Initializable @@ -54,11 +56,16 @@ class EniolaDialogue(player: Player? = null) : DialoguePlugin(player) { 4 -> showTopics( Topic(FacialExpression.HALF_THINKING, "If you work for the bank, what are you doing here?", 10), Topic(FacialExpression.NEUTRAL, "I'd like to access my bank account, please.", 30), + IfTopic(FacialExpression.NEUTRAL, "I'd like to open a secondary bank account.", 5, ServerConstants.SECOND_BANK && !hasActivatedSecondaryBankAccount(player)), + IfTopic(FacialExpression.NEUTRAL, "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", 40, hasActivatedSecondaryBankAccount(player)), Topic(FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 31), Topic(FacialExpression.NEUTRAL, "I'd like to see my collection box.", 32), - Topic(FacialExpression.NEUTRAL, "Never mind.", END_DIALOGUE) + IfTopic(FacialExpression.NEUTRAL, "Never mind.", END_DIALOGUE, !ServerConstants.SECOND_BANK) ) + 5 -> npcl(FacialExpression.ASKING, "Sorry, ${if (player.isMale) "sir" else "ma'am"}, the bank didn't license me for that.").also { stage++ } + 6 -> playerl(FacialExpression.HALF_GUILTY, "Oh, okay, I'll ask a banker who is stationed in an actual bank.").also { stage = END_DIALOGUE } + 10 -> npcl( FacialExpression.NEUTRAL, "My presence here is the start of a new enterprise of travelling banks. " + @@ -133,7 +140,7 @@ class EniolaDialogue(player: Player? = null) : DialoguePlugin(player) { 22 -> npcl( FacialExpression.NEUTRAL, - "I'm sorry to hear that, dear ${if (player.isMale) "sir" else "madam"}. " + "I'm sorry to hear that, dear ${if (player.isMale) "sir" else "madam"}." ).also { stage++ } 23 -> npcl( @@ -158,10 +165,15 @@ class EniolaDialogue(player: Player? = null) : DialoguePlugin(player) { openInterface(player, Components.BANK_CHARGE_ZMI_619) end() } + + 40 -> { + toggleBankAccount(player) + npcl( FacialExpression.NEUTRAL, "Your active bank account has been switched. You can now access your ${getBankAccountName(player)} account.").also { stage = END_DIALOGUE } + } } return true } override fun getIds(): IntArray = intArrayOf(NPCs.ENIOLA_6362) -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/kandarin/dialogue/ThormacDialogue.kt b/Server/src/main/content/region/kandarin/dialogue/ThormacDialogue.kt new file mode 100644 index 000000000..ed2bc611e --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/ThormacDialogue.kt @@ -0,0 +1,57 @@ +package content.region.kandarin.dialogue + +import content.region.kandarin.quest.scorpioncatcher.SCThormacDialogue +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs +import content.data.Quests + +@Initializable +class ThormacDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun getIds(): IntArray { + return intArrayOf(NPCs.THORMAC_389) + } + + companion object { + const val COMPLETED_QUEST = 1000 + const val ENCHANT_DIALOGUE = 2000 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (isQuestComplete(player, Quests.SCORPION_CATCHER)){ + npc(FacialExpression.HAPPY, "Thank you for rescuing my scorpions.").also {stage = COMPLETED_QUEST} + } + else{ + openDialogue(player, SCThormacDialogue(getQuestStage(player, Quests.SCORPION_CATCHER)), npc) + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + COMPLETED_QUEST -> showTopics( + Topic(FacialExpression.HAPPY, "That's okay.", END_DIALOGUE), + Topic(FacialExpression.NEUTRAL, "You said you'd enchant my battlestaff for me", ENCHANT_DIALOGUE) + ) + + ENCHANT_DIALOGUE -> { + npcl(FacialExpression.HAPPY, "Yes, it'll cost you 40,000 coins for the materials needed though. " + + "Which sort of staff did you want enchanting?").also { stage++ } + } + ENCHANT_DIALOGUE + 1 -> { + end() + player.interfaceManager.openComponent(332) + } + + } + return true + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/BalneaDialogue.kt b/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/BalneaDialogue.kt index 34b967093..2df29fe88 100644 --- a/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/BalneaDialogue.kt +++ b/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/BalneaDialogue.kt @@ -10,7 +10,7 @@ import core.tools.START_DIALOGUE /** * Provides dialogue tree for Balnea NPC involved in the - * "As a first resort..." quest. + * "As a First Resort..." quest. * * @author vddCore */ @@ -44,7 +44,7 @@ class BalneaDialogue(player: Player? = null) : DialoguePlugin(player) { ).also { stage = END_DIALOGUE } } - /* TODO: "As a First Resort..." quest dialogue file is required here. */ + /* TODO: "As a First Resort" quest dialogue file is required here. */ return true } diff --git a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt index 2865888b4..49a074680 100644 --- a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt @@ -17,6 +17,7 @@ import core.game.world.map.zone.ZoneBorders import core.game.world.map.RegionManager import core.game.world.map.Location import core.tools.RandomFunction +import content.data.Quests @Initializable class BloatedToadNPC : AbstractNPC { @@ -122,7 +123,7 @@ class BloatedToadListeners : InteractionListener, StartupListener, Commands { override fun defineListeners() { on(Items.BLOATED_TOAD_2875, IntType.ITEM, "drop") { player, used -> - val quest = player.questRepository.getQuest("Big Chompy Bird Hunting") + val quest = player.questRepository.getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING) val inExtraBorder = extraBorders.filter { it.insideBorder(player) }.count() > 0 if (!borders.insideBorder(player) && !inExtraBorder) { diff --git a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBird.kt b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBird.kt index 63df68706..2ca5b5079 100644 --- a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBird.kt +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBird.kt @@ -24,9 +24,10 @@ import core.game.world.GameWorld import kotlin.math.min import java.util.Random +import content.data.Quests @Initializable -class ChompyBird : Quest("Big Chompy Bird Hunting", 35, 34, 2, Vars.VARP_QUEST_CHOMPY, 0, 1, 65), InteractionListener { +class ChompyBird : Quest(Quests.BIG_CHOMPY_BIRD_HUNTING, 35, 34, 2, Vars.VARP_QUEST_CHOMPY, 0, 1, 65), InteractionListener { companion object { val CAVE_ENTRANCE = Location.create(2646, 9378, 0) val CAVE_EXIT = Location.create(2630, 2997, 0) @@ -255,27 +256,6 @@ class ChompyBird : Quest("Big Chompy Bird Hunting", 35, 34, 2, Vars.VARP_QUEST_C return@onUseWith true } - onUseWith(IntType.ITEM, Items.OGRE_ARROW_SHAFT_2864, Items.FEATHER_314) { player, used, with -> - val shaftAmount = amountInInventory(player, used.id) - val featherAmount = amountInInventory(player, with.id) - var maxAmount = min(shaftAmount, featherAmount) - - submitIndividualPulse(player, object : Pulse(3) { - override fun pulse() : Boolean { - val iterAmount = min(maxAmount, 6) - if (removeItem(player, Item(Items.OGRE_ARROW_SHAFT_2864, iterAmount)) && removeItem(player, Item(Items.FEATHER_314, iterAmount))) - { - addItem(player, Items.FLIGHTED_OGRE_ARROW_2865, iterAmount) - rewardXP(player, Skills.FLETCHING, 0.9 * iterAmount) - maxAmount -= iterAmount - } - return maxAmount == 0 - } - }) - - return@onUseWith true - } - onUseWith(IntType.ITEM, Items.WOLF_BONES_2859, Items.CHISEL_1755) { player, used, with -> val maxAmount = amountInInventory(player, used.id) @@ -323,6 +303,7 @@ class ChompyBird : Quest("Big Chompy Bird Hunting", 35, 34, 2, Vars.VARP_QUEST_C val amountThisIter = min(6, getMaxAmount()) if (removeItem(player, Item(used.id, amountThisIter)) && removeItem(player, Item(with.id, amountThisIter))) { addItem(player, Items.OGRE_ARROW_2866, amountThisIter) + sendMessage(player, "You make $amountThisIter ogre arrows.") rewardXP(player, Skills.FLETCHING, 6.0) } } diff --git a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdDialogues.kt b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdDialogues.kt index be7387f17..054159fd3 100644 --- a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdDialogues.kt +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdDialogues.kt @@ -18,6 +18,7 @@ import core.game.node.item.Item import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest +import content.data.Quests @Initializable class RantzDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -32,7 +33,7 @@ class RantzDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?) : Boolean { npc = args[0] as NPC - val chompyBird = player.questRepository.getQuest("Big Chompy Bird Hunting") + val chompyBird = player.questRepository.getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING) val chompyStage = chompyBird.getStage(player) val hasOgreBow = inInventory(player, Items.OGRE_BOW_2883) || inEquipment(player, Items.OGRE_BOW_2883) || inBank(player, Items.OGRE_BOW_2883) @@ -142,7 +143,7 @@ class BugsDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?) : Boolean { npc = args[0] as NPC - val chompyBird = player.questRepository.getQuest("Big Chompy Bird Hunting") + val chompyBird = player.questRepository.getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING) val chompyStage = chompyBird.getStage(player) when (chompyStage) { @@ -172,7 +173,7 @@ class FycieDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?) : Boolean { npc = args[0] as NPC - val chompyBird = player.questRepository.getQuest("Big Chompy Bird Hunting") + val chompyBird = player.questRepository.getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING) val chompyStage = chompyBird.getStage(player) when (chompyStage) { diff --git a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdNPC.kt b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdNPC.kt index 8f0b000ed..efff701ef 100644 --- a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdNPC.kt +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdNPC.kt @@ -153,7 +153,7 @@ class ChompyBirdNPC : AbstractNPC, InteractionListener { val bird = node.asNpc() if (!bird.getAttribute("plucked", false)) { - addItem(player, Items.FEATHER_314, RandomFunction.random(25, 32)) + addItemOrDrop(player, Items.FEATHER_314, RandomFunction.random(25, 32)) produceGroundItem(player, Items.BONES_526, 1, bird.location) produceGroundItem(player, Items.RAW_CHOMPY_2876, 1, bird.location) bird.clear() @@ -164,7 +164,7 @@ class ChompyBirdNPC : AbstractNPC, InteractionListener { return@on true } - on(Items.OGRE_BOW_2883, IntType.ITEM, "check kills") { player, _ -> + on(intArrayOf(Items.OGRE_BOW_2883, Items.COMP_OGRE_BOW_4827), IntType.ITEM, "check kills") { player, _ -> val amount = player.getAttribute("chompy-kills", 0) sendDialogue(player, "You have killed $amount chompy birds.") return@on true diff --git a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt index 94173377d..f0f1ca4ff 100644 --- a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt @@ -8,6 +8,7 @@ import core.plugin.Initializable import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.player.Player import core.game.world.map.Location +import content.data.Quests @Initializable class RantzNPC : AbstractNPC { @@ -27,7 +28,7 @@ class RantzNPC : AbstractNPC { val chompy = findLocalNPC(this, NPCs.CHOMPY_BIRD_1550) as? ChompyBirdNPC ?: return val owner = getAttribute(chompy, "owner", null) ?: return - val quest = owner.questRepository.getQuest("Big Chompy Bird Hunting") + val quest = owner.questRepository.getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING) if (quest.getStage(owner) !in 40..50 || chompy.getAttribute("attacked", false)) return diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/BartenderDialogueFile.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/BartenderDialogueFile.kt new file mode 100644 index 000000000..b93f3a8ed --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/BartenderDialogueFile.kt @@ -0,0 +1,62 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import org.rs09.consts.Items + +class BartenderDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onPredicate { _ -> dialogueNum == 1 } + .branch { player -> + return@branch if (getAttribute(player, ZogreFleshEaters.attributeAskedAboutTankard, false)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .playerl("Hello there, I found this tankard in an ogre tomb cavern. It has the emblem of this Inn on it and I wondered if you knew anything about it?") + .npcl("Oh yes, this is Brentle's mug...I'm surprised he left it just lying around down some cave. He's quite protective of it.") + .playerl("Brentle you say? So you knew him then?") + .npcl("Yeah, this belongs to 'Brentle Vahn', he's quite a common customer, though I've not seen him in a while.") + .npcl(FacialExpression.THINKING, "He was talking to some shifty looking wizard the other day. I don't know his name, but I'd recognise him if I saw him.") + .playerl("Hmm, I'm sorry to tell you this, but Brentle Vahn is dead - I believe he was murdered.") + .npcl(FacialExpression.EXTREMELY_SHOCKED, "Noooo! I'm shocked...") + .npcl("...but not surprised. He was a good customer...but I knew he would sell his sword arm and do many a dark deed if paid enough.") + .npcl(FacialExpression.SAD, "If you need help bringing the culprit to justice, you let me know.") + .endWith { _, player -> + setAttribute(player, ZogreFleshEaters.attributeAskedAboutTankard, true) + } + branch.onValue(1) + .playerl("Hello again. Can you tell me what you know about this tankard again please?") + .npcl("Oh yes, Brentle's tankard. Yeah, you've shown me this already. It belonged to Brentle Vahn, he was quite a common customer, though I've not seen him in a while.") + .npcl("He was talking to some shifty looking wizard the other day. I don't know his name, but I'd recognise him if I saw him.") + .npcl(FacialExpression.SAD, "If you need help bringing the culprit to justice, you let me know.") + .end() + } + + b.onPredicate { _ -> dialogueNum == 2 } + .iteml(Items.SITHIK_PORTRAIT_4814, "You show the portrait to the Inn keeper.") + .npcl("Yeah, that's the guy who was talking to Brentle Vahn the other day! Look at those eyes, never a more shifty looking pair will you ever see!") + .playerl("Hmm, you've just identified the man who I think sent Brentle Vahn to his death.") + .playerl("I'm trying to bring him to justice with the Wizards' Guild grand secretary. Do you think you could sign this portrait to say that he was talking to Brentle Vahn.") + .npcl("I can and I will!") + .betweenStage { df, player, _, _ -> + if (removeItem(player, Items.SITHIK_PORTRAIT_4814)) { + addItemOrDrop(player, Items.SIGNED_PORTRAIT_4816) + } + } + .iteml(Items.SIGNED_PORTRAIT_4816, "The Dragon Inn bartender signs the portrait.") + .playerl("Many thanks for your help, it's really very good of you.") + .npcl("Not at all, just doing my part.") + .end() + + + b.onPredicate { _ -> dialogueNum == 3 } + .iteml(Items.SITHIK_PORTRAIT_4815, "You show the sketch to the Inn keeper.") + .npcl("Who's that? I mean, I guess it's a picture of a person isn't it? Sorry...you've got me? And before you ask, you're not putting it up on my wall!") + .playerl("It's a portrait of Sithik Ints...don't you recognise him?") + .npcl("I'm sorry, I really am, but I just don't see it...can you make a better picture?") + .playerl("I'll try...") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrishDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrishDialogue.kt new file mode 100644 index 000000000..7e518f0ff --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrishDialogue.kt @@ -0,0 +1,184 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import content.region.kandarin.quest.templeofikov.TempleOfIkov +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class GrishDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return GrishDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GrishDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GRISH_2038) + } +} +class GrishDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(ZogreFleshEaters.questName, 0) + .playerl(FacialExpression.FRIENDLY, "Hello there, what's going on here?") + .npcl(FacialExpression.OLD_NORMAL, "Hey yous creature...wha's you's doing here? Yous be cleverer to be running so da sickies from da zogres don't dead ya.") + + .let { builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("I'm just looking around thanks.") + .npcl(FacialExpression.OLD_NORMAL, "Yous creature won'ts see muchly in dis place...just da zogries coming wiv da sickies.") + .goto(returnJoin) + optionBuilder.option_playerl("What do you mean sickies?") + .npc(FacialExpression.OLD_NORMAL, "Da zogries comin wiv da sickies...yous get bashed by da", "zogries and get da sickies...den you gonna be like da", "zogries.") + .playerl(FacialExpression.FRIENDLY, "Sorry, I just don't understand...") + .betweenStage { df, player, _, _ -> + animate(npc!!, 2090) + setAttribute(player, ZogreFleshEaters.attributeAskedAboutSickies, true) + } + .npc(FacialExpression.OLD_NORMAL, "Da sickies is when yous creature goes like orange till", "green and then goes 'Urggghhhh!'", "~ Grish imitates falling down with only the white of his", "eyes visible. ~") + .goto(returnJoin); + optionBuilder.option_playerl("What are Zogres?") + .npcl(FacialExpression.OLD_NORMAL, "a Zogres are da bigun nasties wiv da sickies, deys old pals of Grish but deys jig in Jiggig when dey's full home is deep in da dirt, dey's is not da same dead'uns like was before.") + .goto(returnJoin); + optionBuilder.optionIf("Can I help in any way?") { player -> return@optionIf getAttribute(player, ZogreFleshEaters.attributeAskedAboutSickies, false) } + .playerl(FacialExpression.FRIENDLY, "Can I help in any way?") + .branch { player -> + return@branch if (ZogreFleshEaters.requirements(player)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .npcl(FacialExpression.OLD_NORMAL, "Sorry, yous creatures, but yous is too green behind da ears for dis job Grish finks.") + .playerl(FacialExpression.ANGRY, "No, I'm not!") + .npcl(FacialExpression.OLD_ANGRY1, "Yes you are!") + .playerl(FacialExpression.ANGRY, "No, I'm not!") + .npcl(FacialExpression.OLD_ANGRY1, "Yes you are and that's final!") + .end() + branch.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Yes creatures...yous does good fings for Grish and learn why Zogries at Jiggig and den get da Zogries back in da ground.") + .playerl("Oh, so you want me to find out why the Zogres have appeared and then find a way of burying them?") + .npcl(FacialExpression.OLD_NORMAL, "Is what Grish says! But dis is da biggy danger fing yous creatures...yous be geddin' sickies most surely...yous needs be ready..wiv da foodies un da glug-glugs.") + .playerl("Right, so you think there's a good chance that I can get ill from this, so I need to get some food and something to drink?") + .npcl(FacialExpression.OLD_NORMAL, "Yea creatures, yous just say what Grish says...not know own wordies creature?") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Hmm, sorry, it sounds a bit too dangerous.") + .npcl(FacialExpression.OLD_NORMAL, "Yous creature is not a stoopid one...stays out of dere, like clever Grish. Yous can paint circles on chest and be da Shaman too!") + .playerl("Hmm, is it too late to reconsider?") + .end() + optionBuilder.option_playerl("Ok, I'll check things out then and report back.") + .npcl(FacialExpression.OLD_NORMAL, "Is yous creatures really, really sure yous wanna do dis creatures..we's got no glug-glugs for da sickies? We's knows nuffin for da going of da sickies?") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Yes, I'm really sure!") + .npcl(FacialExpression.OLD_NORMAL,"Dats da good fing yous creature...yous does Grish a good fing. But yous know dat yous get sickies and mebe get dead!") + .playerl("If that's your idea of a pep talk, I have to say that it leaves a lot to be desired.") + .npcl(FacialExpression.OLD_NORMAL,"Yous creatures is alus says funny stuff...speaks proper like Grish!") + .manualStage() { df, player, _, _ -> + sendDoubleItemDialogue(player, Items.COOKED_CHOMPY_2878, Items.SUPER_RESTORE3_3026, "Grish hands you some food and two potions.") + } + .npcl(FacialExpression.OLD_NORMAL,"Der's yous go creatures...da best me's do for yous...and be back wivout da sickies.") + .endWith { _, player -> + if(getQuestStage(player, ZogreFleshEaters.questName) == 0) { + // Trying to prevent players from spamming to get more super restores. + addItemOrDrop(player, Items.COOKED_CHOMPY_2878, 3) + addItemOrDrop(player, Items.SUPER_RESTORE3_3026, 2) + setQuestStage(player, ZogreFleshEaters.questName, 1) + } + } + optionBuilder2.option_playerl("Hmm, sorry, it sounds a bit too dangerous.") + .npcl(FacialExpression.OLD_NORMAL, "Yous creature is not a stoopid one...stays out of dere, like clever Grish. Yous can paint circles on chest and be da Shaman too!") + .end() + } + } + + } + optionBuilder.option_playerl("Sorry, I have to go.") + .end() + } + } + + b.onQuestStages(ZogreFleshEaters.questName, 1,2,3,4,5,6) + .npcl(FacialExpression.OLD_NORMAL, "Yous creature dun da fing yet? Da zogries going in da dirt full home?") + .playerl("Nope, I haven't figured out why the zogres are here yet.") + .end() + + b.onQuestStages(ZogreFleshEaters.questName, 7) + .npcl(FacialExpression.OLD_NORMAL,"Yous creature dun da fing yet? Da zogries going in da ground?") + .playerl("I found who's responsible for the Zogres being here.") + .npcl(FacialExpression.OLD_NORMAL,"Where is da creature? Me's wants to squeeze him till he's a deadun...") + .playerl("The person responsible is a wizard named 'Sithik Ints' and he's going to be in serious trouble. He told me that the spell which raised the zogres from the ground will last forever.") + .playerl("I'm sorry to say, but you'll have to move the site of your ceremonial dancing somewhere else.") + .npcl(FacialExpression.OLD_NORMAL,"Dat is da bad fing creature...we's needs new Jiggig for da fallin' down jig.") + .playerl("Yes, that's right, you'll need to create a new ceremonial dance area.") + .npcl(FacialExpression.OLD_NORMAL,"Urghhh...not good fing creature, yous gotta get da ogrish old fings for da making new jiggig special. You's creature needs da key for getting in da low bury place.") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.OGRE_GATE_KEY_4839) + } + .iteml(Items.OGRE_GATE_KEY_4839, "Grish gives you a crudely crafted key.") + .playerl("Oh, so you want me to go back in there and look for something for you?") + .npcl(FacialExpression.OLD_NORMAL,"Yeah creature, yous gotta get da ogrish old fings for da making new jiggig and proper in da special way.") + .endWith { _, player -> + if(getQuestStage(player, ZogreFleshEaters.questName) == 7) { + setQuestStage(player, ZogreFleshEaters.questName, 8) + } + } + + b.onQuestStages(ZogreFleshEaters.questName, 8) + .npcl(FacialExpression.OLD_NORMAL, "Hey, you's creature got da old fings?") + .branch { player -> + return@branch if (inInventory(player, Items.OGRE_GATE_KEY_4839)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("Nope, not yet.") + .npcl(FacialExpression.OLD_NORMAL, "Yous gets 'em quick tho, cos we'ze wonna do da new Jiggig place...") + .end() + branch.onValue(0) + .playerl("I lost the key you gave me.") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.OGRE_GATE_KEY_4839) + } + .iteml(Items.OGRE_GATE_KEY_4839, "Grish gives you a crudely crafted key.") + .end() + } + + b.onQuestStages(ZogreFleshEaters.questName, 9) + .branch { player -> + return@branch if (inInventory(player, Items.OGRE_ARTEFACT_4818)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .npcl(FacialExpression.OLD_NORMAL, "Hey, you's creature got da old fings?") + .playerl("No sorry, I don't have them yet.") + .npcl(FacialExpression.OLD_NORMAL, "Yous creatures get dem for me soon doh, yes?") + .end() // There's all the default dialogue here, but I'm lazy again. + + branch.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Hey, you's creature got da old fings?") + .playerl("Yeah, I have them here!") + .npcl(FacialExpression.OLD_NORMAL, "Dat is da goodly fing yous creature, now's we's can make da new Jiggig place away from zogries! Yous been da big helpy fing yous creature, Grish wishin' yous good stuff for da next fings for creature.") + .npcl(FacialExpression.OLD_HAPPY, "~ Grish seems very pleased about the return of the artefacts. ~") + .playerl("Thanks, that's very nice of you!") + .endWith { _, player -> + if (removeItem(player, Items.OGRE_ARTEFACT_4818)) { + if (getQuestStage(player, ZogreFleshEaters.questName) == 9) { + finishQuest(player, ZogreFleshEaters.questName) + } + } + } + } + + b.onQuestStages(ZogreFleshEaters.questName, 100) + .playerl("How's everything going now?") + .npcl(FacialExpression.OLD_NORMAL,"All da zogries stayin' in da oldie Jiggig, we's gonna do da new Jiggig someways else. Yous creature da good- un for geddin' da oldie fings...") + // More default dialogue, but lazy. + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrugDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrugDialogue.kt new file mode 100644 index 000000000..6081288cf --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrugDialogue.kt @@ -0,0 +1,31 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.openDialogue +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class GrugDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return GrugDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GrugDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GRUG_2041) + } +} +class GrugDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .npcl(FacialExpression.OLD_NORMAL, "Ukk...I's dun fer...me's don't feel legsies anymore!") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/JiggigListeners.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/JiggigListeners.kt new file mode 100644 index 000000000..718e26efc --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/JiggigListeners.kt @@ -0,0 +1,27 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class JiggigListeners : InteractionListener { + override fun defineListeners() { + on(Scenery.OGRE_COFFIN_6848, SCENERY, "open") { player, node -> + // https://youtu.be/HnRcW2iM8es + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.OGRE_COFFIN_6890, 10) + return@on true + } + + on(NPCs.UGLUG_NAR_2039, NPC, "trade") { player, node -> + if (getAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, false)) { + openNpcShop(player, NPCs.UGLUG_NAR_2039) + } else { + sendNPCDialogue(player, NPCs.UGLUG_NAR_2039, "Me's not got no glug-glugs to sell, yous bring me da sickies glug-glug den me's open da stufsies for ya.", FacialExpression.OLD_NORMAL) + } + return@on true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/OgreGuardDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/OgreGuardDialogue.kt new file mode 100644 index 000000000..6f5193687 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/OgreGuardDialogue.kt @@ -0,0 +1,73 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class OgreGuardDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return OgreGuardDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, OgreGuardDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.OGRE_GUARD_2042) + } +} +class OgreGuardDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(ZogreFleshEaters.questName, 0) + .npcl(FacialExpression.OLD_NORMAL, "Yous needs ta stay away from dis place... yous get da sickies and mebe yous goes to dead if yous da unlucky fing.") + .playerl("Ok, thanks.") + .end() + + b.onQuestStages(ZogreFleshEaters.questName, 1) + .npcl(FacialExpression.OLD_NORMAL, "Yous needs ta stay away from dis place... yous get da sickies and mebe yous goes to dead if yous da unlucky fing.") + .playerl(FacialExpression.FRIENDLY, "But Grish has asked me to look into this place and find out why all the undead ogres are here.") + .npcl(FacialExpression.OLD_NORMAL, "Ok, dat is da big, big scary, danger fing! You's sure you's wants to go in?") + .playerl(FacialExpression.FRIENDLY, "Yes, I'm sure.") + .npcl(FacialExpression.OLD_NORMAL, "Ok, I opens da stoppa's for yous creature.") + .endWith { _, player -> + lock(player, 4) + face(npc!!, Location(2456, 3049, 0)) + // Lesson learnt here, endWith kills the npc object so tie the queueScript with the npc instead, not the player. + queueScript(npc!!, 2, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + animate(npc!!, 2102) + return@queueScript delayScript(npc!!, Animation(2102).duration) + } + 1 -> { + if(getQuestStage(player, ZogreFleshEaters.questName) == 1) { + setQuestStage(player, ZogreFleshEaters.questName, 2) + } + setVarbit(player, ZogreFleshEaters.varbitGateBashed, 1) + unlock(player) + face(npc!!, player.location) + sendNPCDialogue(player, NPCs.OGRE_GUARD_2042, "Ok der' yous goes!", FacialExpression.OLD_NORMAL) + return@queueScript stopExecuting(npc!!) + } + else -> return@queueScript stopExecuting(npc!!) + } + } + } + + b.onQuestStages(ZogreFleshEaters.questName, 2,3,4,5,6,7,8,9,10,100) + .npcl(FacialExpression.OLD_NORMAL, "Hey yous tryin' not to get da sickies else yous be da sick-un and mebe get to be a dead-un if yous be da unlucky fing.") + .playerl(FacialExpression.FRIENDLY, "Don't worry, I know how to take care of myself.") + .end() + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/PilgDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/PilgDialogue.kt new file mode 100644 index 000000000..6eee48feb --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/PilgDialogue.kt @@ -0,0 +1,31 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class PilgDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return PilgDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PilgDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.PILG_2040) + } +} +class PilgDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .npcl(FacialExpression.OLD_NORMAL, "Dey got me in da belly, mees gutsies feel like had a dead dead dog dinner.") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SithikIntsDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SithikIntsDialogue.kt new file mode 100644 index 000000000..789251861 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SithikIntsDialogue.kt @@ -0,0 +1,388 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +/** This NPC is a scenery. */ +//@Initializable +//class SithikIntsDialogue (player: Player? = null) : DialoguePlugin(player) { +// override fun newInstance(player: Player): DialoguePlugin { +// return SithikIntsDialogue(player) +// } +// override fun handle(interfaceId: Int, buttonId: Int): Boolean { +// openDialogue(player, SithikIntsDialogueFile(), npc) +// return false +// } +// override fun getIds(): IntArray { +// return intArrayOf(NPCs.SITHIK_INTS_2061, NPCs.SITHIK_INTS_2062) +// } +//} +class SithikIntsDialogue : InteractionListener { + override fun defineListeners() { + on(Scenery.SITHIK_INTS_6888, SCENERY, "talk-to") { player, node -> + openDialogue(player, SithikIntsDialogueFile(), NPC(NPCs.SITHIK_INTS_2061)) + return@on true + } + on(Scenery.SITHIK_INTS_6889, SCENERY, "talk-to") { player, node -> + openDialogue(player, SithikIntsOgreFormDialogueFile(), NPC(NPCs.SITHIK_INTS_2062)) + return@on true + } + + on(Scenery.DRAWERS_6875, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) <= 2) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hey! What do you think you're doing?", FacialExpression.ANNOYED).also { stage++ } + 1 -> sendPlayerDialogue(player, "Erk! I'd better not start rifling through peoples things without permission.").also { stage = END_DIALOGUE } + } + } + }) + } else if (getQuestStage(player, ZogreFleshEaters.questName) in 3..4 && + (!inInventory(player, Items.BOOK_OF_PORTRAITURE_4817) || + !inInventory(player, Items.PAPYRUS_970) || + !inInventory(player, Items.CHARCOAL_973) + )) { + if (hasSpaceFor(player, Item(Items.BOOK_OF_PORTRAITURE_4817, 3))) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> { + if (!inInventory(player, Items.PAPYRUS_970) && !inInventory(player, Items.CHARCOAL_973)) { + sendDoubleItemDialogue(player, Items.CHARCOAL_973, Items.PAPYRUS_970, "You find some charcoal and papyrus.") + addItemOrDrop(player, Items.PAPYRUS_970) + addItemOrDrop(player, Items.CHARCOAL_973) + stage++ + } else if (!inInventory(player, Items.PAPYRUS_970)) { + sendItemDialogue(player, Items.PAPYRUS_970, "You find some papyrus.") + addItemOrDrop(player, Items.PAPYRUS_970) + stage++ + } else if (!inInventory(player, Items.CHARCOAL_973)) { + sendItemDialogue(player, Items.CHARCOAL_973, "You find some charcoal.") + addItemOrDrop(player, Items.CHARCOAL_973) + stage++ + } else { + sendItemDialogue(player, Items.BOOK_OF_PORTRAITURE_4817, "You also find a book on portraiture.") + addItemOrDrop(player, Items.BOOK_OF_PORTRAITURE_4817) + stage = END_DIALOGUE + } + } + 1 -> { + sendItemDialogue(player, Items.BOOK_OF_PORTRAITURE_4817, "You also find a book on portraiture.") + addItemOrDrop(player, Items.BOOK_OF_PORTRAITURE_4817) + stage = END_DIALOGUE + } + } + } + }) + } else { + sendDialogue(player, "You see some items in the drawer, but you need 3 free inventory spaces to take them.") + } + } else { + sendMessage(player, "You search but find nothing of significance.") + } + return@on true + } + + on(Scenery.CUPBOARD_6876, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) <= 2) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hey! What do you think you're doing?", FacialExpression.ANNOYED).also { stage++ } + 1 -> sendPlayerDialogue(player, "Erk! I'd better not start rifling through peoples things without permission.").also { stage = END_DIALOGUE } + } + } + }) + } else if (getQuestStage(player, ZogreFleshEaters.questName) in 3..4 && !inInventory(player, Items.NECROMANCY_BOOK_4837)) { + if (hasSpaceFor(player, Item(Items.NECROMANCY_BOOK_4837))) { + addItemOrDrop(player, Items.NECROMANCY_BOOK_4837) + sendItemDialogue(player, Items.NECROMANCY_BOOK_4837, "You find a book on Necromancy.") + setAttribute(player, ZogreFleshEaters.attributeFoundNecromanticBook, true) + } else { + sendDialogue(player, "You see an item in the cupboard, but you don't have space to put it in your inventory.") + } + } else { + sendMessage(player, "You search but find nothing of significance.") + } + return@on true + } + + onUseWith(IntType.SCENERY, Items.NECROMANCY_BOOK_4837, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player,"Aha! A necromantic book! What's this doing here then?").also { stage++ } + 1 -> sendItemDialogue(player, Items.NECROMANCY_BOOK_4837, "You show the Necromantic book to Sithik.").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Oh..I'm not quite sure actually...where did you find that then?").also { stage++ } + 3 -> sendPlayerDialogue(player,"I found it in this cupboard! What do you have to say for yourself?").also { stage++ } + 4 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Oh yes, that's right...I remember now. It's for my research, there's nothing really dangerous about it, unless it falls into the wrong hands. I'm sure it's pretty safe with me.").also { stage++ } + 5 -> sendPlayerDialogue(player,"Hmmm, likely story!").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + on(Scenery.WARDROBE_6877, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) <= 2) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hey! What do you think you're doing?", FacialExpression.ANNOYED).also { stage++ } + 1 -> sendPlayerDialogue(player, "Erk! I'd better not start rifling through peoples things without permission.").also { stage = END_DIALOGUE } + } + } + }) + } else if (getQuestStage(player, ZogreFleshEaters.questName) in 3..4 && !inInventory(player, Items.BOOK_OF_HAM_4829)) { + if (hasSpaceFor(player, Item(Items.BOOK_OF_HAM_4829))) { + addItemOrDrop(player, Items.BOOK_OF_HAM_4829) + sendItemDialogue(player, Items.BOOK_OF_HAM_4829, "You find a book on Philosophy written by the 'Human's Against Monsters' leader, Johanhus Albrect.") + setAttribute(player, ZogreFleshEaters.attributeFoundHamBook, true) + } else { + sendDialogue(player, "You see an item in the wardrobe, but you don't have space to put it in your inventory.") + } + } else { + sendMessage(player, "You search but find nothing of significance.") + } + return@on true + } + + onUseWith(IntType.SCENERY, Items.BOOK_OF_HAM_4829, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player,"What's this then?").also { stage++ } + 1 -> sendItemDialogue(player, Items.BOOK_OF_HAM_4829, "You show the HAM book to Sithik.").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "What do you mean? It's a book by the respected HAM leader Johanhus Ulsbrecht, that man speaks for a lot of people who are unhappy with the current state of affairs.").also { stage++ } + 3 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Can you honestly tell me that you've not had to fight for your life against the odd monster or two?").also { stage++ } + 4 -> sendPlayerDialogue(player,"Hmm, that may be true, but I don't universally hate all monsters, whereas I have a sneaking suspicion that you do...and ogres in particular!").also { stage++ } + 5 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hmm, that's an interesting theory, care to back it up with any facts?").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.PAPYRUS_970, Scenery.SITHIK_INTS_6888) { player, used, with -> + if(inInventory(player, Items.CHARCOAL_973)) { + if (removeItem(player, used)) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Oh lovely! You're making my portrait! Let me see it afterwards!", FacialExpression.FRIENDLY).also { stage++ } + 1 -> sendDialogue(player, "You begin sketching the irritable Sithik.").also { animate(player, 909); stage++ } + 2 -> { + val skill = Skills.CRAFTING + val level: Int = getDynLevel(player, skill) + getFamiliarBoost(player, skill) + val ratio = RandomFunction.getSkillSuccessChance(0.0, 80.0, level) + + if (ratio > 0.5) { + // Passed + sendItemDialogue(player, Items.SITHIK_PORTRAIT_4814, "You get a portrait of Sithik.") + addItemOrDrop(player, Items.SITHIK_PORTRAIT_4814) + } else { + // Failed + sendItemDialogue(player, Items.SITHIK_PORTRAIT_4815, "You get a portrait of Sithik.") + addItemOrDrop(player, Items.SITHIK_PORTRAIT_4815) + } + setAttribute(player, ZogreFleshEaters.attributeMadePortrait, true) + stage = END_DIALOGUE + } + } + } + }) + } + } else { + sendDialogue(player, "You have no charcoal with which to sketch this subject.") + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.BOOK_OF_PORTRAITURE_4817, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player,"Oh, so explain this then?").also { stage++ } + 1 -> sendItemDialogue(player, Items.BOOK_OF_PORTRAITURE_4817, "You show the book on portraiture to Sithik.").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "It's my hobby...I'm interested in portraiture, but all art in general. It's fun, you should try it.").also { stage++ } + 3 -> sendPlayerDialogue(player,"How do I do it...").also { stage++ } + 4 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Well...you could start by reading the book!").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.SITHIK_PORTRAIT_4814, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player, "Here you go, what do you think?").also { stage++ } + 1 -> sendItemDialogue(player, Items.SITHIK_PORTRAIT_4814, "You show the sketch...").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hmmm, well it's not the most flattering of portraits, but I like the 'honesty' of the work...well done.").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.SITHIK_PORTRAIT_4815, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player, "Here you go, what do you think?").also { stage++ } + 1 -> sendItemDialogue(player, Items.SITHIK_PORTRAIT_4815, "You show the sketch...").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hmmm, well it's an interesting interpretation, but not really classic realist representation is it? It's not my favourite, but I like the 'truth' of the work...well done.").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + on(Items.CUP_OF_TEA_4838, ITEM, "take") { player, node -> + sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hey! What do you think you're doing? Leave my tea alone!", FacialExpression.ANNOYED) + return@on true + } + + onUseWith(IntType.SCENERY, Items.STRANGE_POTION_4836, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player,"Here, try some of this potion, it'll make you feel better!").also { stage++ } + 1 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Err, yuck...no way am I taking any potions or medication off you...I don't trust you!").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + onUseWith(IntType.GROUNDITEM, Items.STRANGE_POTION_4836, Items.CUP_OF_TEA_4838) { player, _, with -> + if(getQuestStage(player, ZogreFleshEaters.questName) == 5) { + if (removeItem(player, Items.STRANGE_POTION_4836)) { + sendItemDialogue(player, Items.STRANGE_POTION_4836, "You pour some of the potion into the cup. Zavistic said it may take some time to have an effect.") + addItemOrDrop(player, Items.SAMPLE_BOTTLE_3377) + } + setQuestStage(player, ZogreFleshEaters.questName, 6) + } + return@onUseWith true + } + } +} + +class SithikIntsDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(ZogreFleshEaters.questName, 0, 1, 2) + .npcl(FacialExpression.ANNOYED, "Hey... who gave you permission to come in here! Get out, get out I say.") + .playerl("Alright, alright... keep your night cap on.") + .end() + + b.onQuestStages(ZogreFleshEaters.questName, 3, 4, 5, 6) + .npcl(FacialExpression.ANNOYED, "Hey... who gave you permission to come in here! Get out, get out I say.") + .playerl("Zavistic Rarve said that I could come and talk to you and ask you a few questions.") + .betweenStage { df, player, _, _ -> + if (getQuestStage(player, ZogreFleshEaters.questName) == 3) { + setQuestStage(player, ZogreFleshEaters.questName, 4) + } + } + .npcl(FacialExpression.ANNOYED, "Oh, Zavistic...why...why would he send you to me?") + + .let { builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Do you know anything about the undead ogres at Jiggig?") + .npcl("Er...undead ogres...no, sorry, no idea what you're talking about there.") + .playerl("Hmm, is that right...") + .npcl("Well, yes, yes it is. If I knew something, I'd tell you.") + .npcl("Anyway, dead ogres you say? How strange? That must be a strange sight?") + .playerl("Very well, if you don't know anything about it, you won't mind if I look around then?") + .npcl("Well,err....well, actually yes I do mind...it's my place and I don't want strangers going through my things.") + .goto(returnJoin) + + optionBuilder.option_playerl("What do you do?") + .npcl("I'm a scholarly student of the magical arts. When I was younger I used to be an adventurer, probably just like yourself. But I lost interest in the constant fighting, looting and gaining abilities.") + .npcl("Instead I decided to focus my attention and time to study the purer form of the lost arts.") + .playerl("The lost arts? What are they?") + .npcl("Ignorant people call them the 'dark arts'. I'm talking about Necromancy, the power to bring the dead back to life - the power of the gods! Surely the most awesome power known to man.") + .playerl("Hmm, well I guess I must be an ignorant person then, because bringing the dead back to life sounds very unnatural.") + .goto(returnJoin) + + optionBuilder.option_playerl("Do you mind if I look around?") + .npcl("Well,err....well, actually yes I do mind...it's my place and I don't want strangers going through my things.") + .playerl("Well, I'm going to have a look around anyway, if you're not involved in this whole thing, you won't have anything to hide.") + .npcl("Why, if I was a few years younger I'd give you a good hiding!") + .playerl("I'm sure!") + .goto(returnJoin) + + optionBuilder.option_playerl("Ok, thanks.") + .end() + } + builder.goto(returnJoin) + } + } +} + +class SithikIntsOgreFormDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(ZogreFleshEaters.questName, 7,8,9,100) + /* + There are some after first time dialogue, but who reads this... + .npcl("Arghhhh..what do you want now...you've turned me into a beast!") + .playerl("I've got some questions for you...and you'd better answer them well or else!") + .npcl("Ok, ok, I'll tell you anything, just turn me back into a human again!") + */ + .npcl(FacialExpression.OLD_DEFAULT, "Arghhhh..what's happened to me...you beast!") + .playerl("It's your own fault, you shouldn't have lied about your involvement with the undead Ogres at Jiggig. The potion will wear off once you've told the truth!") + .npcl(FacialExpression.OLD_DEFAULT, "Ok, ok, I admit it, I got Brentle Vahn to cast the spell to put an end to those awful Ogres...they're just disgusting creatures...") + .playerl("Ok, that's a start...now I want some answers.") + .let { builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option("How do I remove the effects of the spell from the area?") + .playerl("How do I remove the effects of the spell from the area? The ogres want to get their ceremonial dance area back and can't do that with undead walking all over it.") + .npcl(FacialExpression.OLD_DEFAULT, "Unfortunately you can't. The spell is permanent, it will last forever, the only option you have is to move the ceremonial area.") + .playerl("You're an evil man and I'm going to make you pay for this...you can stay like that forever as far as I'm concerned.") + .npcl(FacialExpression.OLD_DEFAULT, "No...no, let me try to make amends...please I can help you. Just don't leave me like this.") + .goto(returnJoin) + + optionBuilder.option_playerl("How do I get rid of the undead ogres?") + .npcl(FacialExpression.OLD_DEFAULT, "Ok, similar spells have been cast before and the only way to deal with the resulting creatures is to cordon off the area and not go in there again.") + .npcl(FacialExpression.OLD_DEFAULT, "The undead creatures usually manifest some sort of disease so it's best to attack them from a distance with a ranged weapon.") + .npcl(FacialExpression.OLD_DEFAULT, "Normal missiles like arrows and darts do very little damage to them because they're designed to destroy internal organs. This is a waste of time with undead creatures like undead ogres.") + .playerl("Yeah, clearly so what should we use?") + .npcl(FacialExpression.OLD_DEFAULT, "From my research it looks like a flat ended arrow was designed called a 'Brutal arrow'. This does large amounts of crushing damage to the creature. You can make them by using larger arrows.") + .npcl(FacialExpression.OLD_DEFAULT, "I think some Ogre hunters make them. But instead of adding an arrow tip, you hammer a large nail into the end of the shaft.") + .goto(returnJoin) + + optionBuilder.option_playerl("How do I get rid of the disease?") + .npcl(FacialExpression.OLD_DEFAULT, "My research shows that two jungle based herbs can be used, one is found near river tributaries and looks like a vine, the other is found in caves and grows on the wall.") + .npcl(FacialExpression.OLD_DEFAULT, "It's quite well camouflaged so it's unlikely that you'll find it.") + .playerl("We'll see about that!") + .goto(returnJoin) + + optionBuilder.option_playerl("Sorry, I have to go.") + .npcl(FacialExpression.OLD_DEFAULT, "But...you can't just leave me here like this!") + .end() + } + builder.goto(returnJoin) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SkogreBehavior.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SkogreBehavior.kt new file mode 100644 index 000000000..54363e66d --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SkogreBehavior.kt @@ -0,0 +1,48 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.getOrStartTimer +import core.api.inEquipment +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.spell.SpellType +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.system.timer.impl.Disease +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class SkogreBehavior : NPCBehavior(*skogreIds) { + companion object { + @JvmField + val skogreIds = intArrayOf( + NPCs.SKOGRE_2050, + NPCs.SKOGRE_2056, + NPCs.SKOGRE_2057, + ) + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (inEquipment(attacker, Items.COMP_OGRE_BOW_4827)) { + return + } + if (state.spell != null && state.spell.type == SpellType.CRUMBLE_UNDEAD) { + state.estimatedHit = (state.estimatedHit * 0.5).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.5).toInt() + } + return + } + state.estimatedHit = (state.estimatedHit * 0.25).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.25).toInt() + } + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + val disease = getOrStartTimer(victim, 10) + disease.hitsLeft = 10 + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SlashBashBehavior.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SlashBashBehavior.kt new file mode 100644 index 000000000..95fab62bc --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SlashBashBehavior.kt @@ -0,0 +1,100 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import content.global.handlers.item.equipment.special.DragonfireSwingHandler +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.CombatSwingHandler +import core.game.node.entity.combat.MultiSwingHandler +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.combat.spell.SpellType +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.system.timer.impl.Disease +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class SlashBashBehavior : NPCBehavior(NPCs.SLASH_BASH_2060) { + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + if (victim is Player) { + val disease = getOrStartTimer(victim, 25) + disease.hitsLeft = 25 + } + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (inEquipment(attacker, Items.COMP_OGRE_BOW_4827)) { + return + } + if (state.spell != null && state.spell.type == SpellType.CRUMBLE_UNDEAD) { + state.estimatedHit = (state.estimatedHit * 0.5).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.5).toInt() + } + return + } + state.estimatedHit = (state.estimatedHit * 0.25).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.25).toInt() + } + } + } + + override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { + super.onDropTableRolled(self, killer, drops) + if (killer is Player && getQuestStage(killer, ZogreFleshEaters.questName) == 8) { + drops.add(Item(Items.ZOGRE_BONES_4812, 2)) + drops.add(Item(Items.OURG_BONES_4834, 3)) + drops.add(Item(Items.OGRE_ARTEFACT_4818)) + setQuestStage(killer, ZogreFleshEaters.questName, 9) + removeAttribute(killer, ZogreFleshEaters.attributeSlashBashInstance) + } + } + + var clearTime = 0 + override fun tick(self: NPC): Boolean { + val player: Player? = getAttribute(self, "target", null) + // You have 500 ticks to kill this guy + if (clearTime++ > 500) { + poofClear(self) + clearTime = 0 + if (player != null) { + removeAttribute(player, ZogreFleshEaters.attributeSlashBashInstance) + } + + } + return true + } + + /** MELEE Swing */ + private val COMBAT_HANDLER = MultiSwingHandler(SwitchAttack(CombatStyle.MELEE.swingHandler, Animation(359))) + /** RANGE Swing (Projectile) */ + private val COMBAT_HANDLER_FAR = MultiSwingHandler(SwitchAttack(CombatStyle.RANGE.swingHandler, Animation(359), Graphics(499))) + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + val victim = self.properties.combatPulse.getVictim() ?: return original + if (victim !is Player) return original + + return if (victim.location.getDistance(self.location) >= 2) + COMBAT_HANDLER_FAR + else + COMBAT_HANDLER + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/UglugNarDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/UglugNarDialogue.kt new file mode 100644 index 000000000..aab0bfee9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/UglugNarDialogue.kt @@ -0,0 +1,78 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class UglugNarDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return UglugNarDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, UglugNarDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.UGLUG_NAR_2039) + } +} +class UglugNarDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(ZogreFleshEaters.questName, 0) + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Hey, what's going on here?") + .npcl(FacialExpression.OLD_NORMAL, "Dem's dead ogre's come out of the ground...dey's makin' da rest of us into sick-ums ...and dead-uns.") + .playerl("That doesn't sound good!") + .npcl(FacialExpression.OLD_NORMAL, "Grish want's da person go down der - see what's what!") + .end() + optionBuilder.option_playerl("What are you selling?") + .npcl(FacialExpression.OLD_NORMAL, "Me's not got no glug-glugs to sell, yous bring me da sickies glug-glug den me's open da stufsies for ya.") + .end() + optionBuilder.option_playerl("Ok, thanks.") + .end() + } + + b.onQuestStages(ZogreFleshEaters.questName, 1,2,3,4,5,6,7,8,9,10,100) + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Hello again.") + .branch { player -> + return@branch if (getAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, false)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Hey yous creature...yous did good fings gedin that glug-glugs for da sickies! All is ogries pepels are not gettin dead cos of you.") + .end() + branch.onValue(0) + .npcl(FacialExpression.OLD_NORMAL, "Hey yous creature...yous still here?") + .playerl("Yeah, I'm going to help Grish by figuring out what went on here.") + .npcl(FacialExpression.OLD_NORMAL, "If yous finds somefin for da sickies, yous brings to me...and I's give you bright pretties, den me make more for alls pepels.") + .playerl("Hmm, ok. I'll try to bear that in mind.") + .end() + } + + optionBuilder.option_playerl("What are you selling?") + .branch { player -> + return@branch if (getAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, false)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Me's showin' you da stufsies for yous creatures!") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + branch.onValue(0) + .npcl(FacialExpression.OLD_NORMAL, "Me's not got no glug-glugs to sell, yous bring me da sickies glug-glug den me's open da stufsies for ya.") + .end() + } + + optionBuilder.option_playerl("Ok, thanks.") + .end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZavisticRarveDialogueFile.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZavisticRarveDialogueFile.kt new file mode 100644 index 000000000..1954334b1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZavisticRarveDialogueFile.kt @@ -0,0 +1,290 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.item.Item +import org.rs09.consts.Items + +class ZavisticRarveDialogueFile : DialogueBuilderFile() { + + companion object { + private fun hasEvidences(player: Player): Int { + var count = 0 + if (inInventory(player, Items.NECROMANCY_BOOK_4837)) { count++ } + if (inInventory(player, Items.BOOK_OF_HAM_4829)) { count++ } + if (inInventory(player, Items.DRAGON_INN_TANKARD_4811)) { count++ } + if (inInventory(player, Items.SIGNED_PORTRAIT_4816)) { count++ } + return count + } + + fun dialogueBlackPrismAndTornPage(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("There's some undead ogre activity over at Jiggig, I've found some clues, I wondered if you'd have a look at them.") + .manualStage() { df, player, _, _ -> + sendDoubleItemDialogue(player, Items.BLACK_PRISM_4808, Items.TORN_PAGE_4809, "You show the prism and the necromantic half page to the aged wizard.") + } + .npcl("Hmmm, now this is interesting! Where did you get these from?") + .playerl("I got them from a nearby Ogre tomb, it's recently been infested with zombie ogres and I'm trying to work out what happened there.") + .npcl("This is very troubling @name, very troubling indeed. While it's permitted for learned members of our order to research the 'dark arts', it's absolutely forbidden to make use of such magic.") + .playerl("Do you have any leads on people that I might talk to regarding this?") + .npcl("Well a wizard by the name of 'Sithik Ints' was doing some research in this area. He may know something about it. He's lodged at that guest house to the North, though he's ill and isn't able to leave his room.") + .npcl("Why not go and talk to him, poke around a bit and see if anything comes up. Let me know how you get on. However, I doubt that 'Sithik' had anything to do with it.") + .npcl("Hmm, that black prism seems to have some magical protection. Once you've finished with this item, bring it back to me would you. I may have a reward for you.") + } + + fun dialogueGoodPortrait(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("Look, I made a portrait of Sithik.") + .iteml(Items.SITHIK_PORTRAIT_4814, "You show the portrait of Sithik to Zavistic.") + .npcl("Hmm, great...but I already know what he looks like!") + } + + fun dialogueBadPortrait(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("Look, I made a portrait of Sithik.") + .iteml(Items.SITHIK_PORTRAIT_4815, "You show the sketch...") + .npcl("Who the demonikin is that? Is it meant to be a portrait of Sithik, it doesn't look anything like him!") + } + + // Too lazy to do this, but this gets mentioned whenever you've shown him one of the evidence (but not all). + fun dialogueSeenEvidenceBefore(builder: DialogueBuilder): DialogueBuilder { + return builder + .npcl("Yeah, you've shown me this before...if this is all the evidence you have?") + .playerl("Please just look at it again...") + .npcl("Ok, let me look then.") + } + } + + override fun create(b: DialogueBuilder) { + b.onQuestStages(ZogreFleshEaters.questName, 2) + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.dialogueInitialTalk(it) } + .branch { player -> + return@branch if (inInventory(player, Items.BLACK_PRISM_4808) && inInventory(player, Items.TORN_PAGE_4809) ) { + 3 + } else if (inInventory(player, Items.BLACK_PRISM_4808) ) { + 2 + } else if (inInventory(player, Items.TORN_PAGE_4809) ) { + 1 + } else { + 0 + } + }.let { branch -> + branch.onValue(3) + .let{ dialogueBlackPrismAndTornPage(it) } + .endWith { _, player -> + if(getQuestStage(player, ZogreFleshEaters.questName) == 2) { + setQuestStage(player, ZogreFleshEaters.questName, 3) + } + } + + branch.onValue(2) + .playerl("There's some undead ogre activity over at 'Jiggig', and the ogres have asked me to look into it. I think I've found a clue and I wonder if you could take a look at it for me?") + .iteml(Items.BLACK_PRISM_4808, "You show the black prism to the aged wizard.") + .npcl("Hmmm, well this is an uncommon spell component. On its own it's useless, but with certain necromantic spells it can be very powerful. Did you find anything else there?") + .branch { player -> + return@branch if (inInventory(player, Items.DRAGON_INN_TANKARD_4811)) { 1 } else { 0 } + }.let { branch2 -> + val returnJoin = b.placeholder() + branch2.onValue(0) + .goto(returnJoin) + branch2.onValue(1) + .iteml(Items.DRAGON_INN_TANKARD_4811, "You show the tankard to Zavistic.") + .playerl("Well, I found this...") + .npcl("Hmmm, no, that's not really associated with this to be honest. Did you find anything else there?") + .goto(returnJoin) + return@let returnJoin.builder() + } + .playerl("Not really.") + .npcl("I don't know what to say then, there isn't enough to go on with the clues you've shown me so far. I'd suggest going back to search a bit more, but you may just be wasting your time?") + .npcl("Hmm, but this prism does seem to have some magical protection. Once you've finished with this item, bring it back to me would you? I may have a reward for you!") + .playerl("Sure...I mean, I'll try if I remember.") + .end() + + branch.onValue(1) + .playerl("There's some undead ogre activity over at Jiggig, I've found a clue that you may be able to help with.") + .iteml(Items.TORN_PAGE_4809, "You show the necromantic half page to the aged wizard.") + .npcl("Hmm, this is a half torn spell page, it requires another spell component to be effective. Did you find anything else there?") + .branch { player -> + return@branch if (inInventory(player, Items.DRAGON_INN_TANKARD_4811)) { 1 } else { 0 } + }.let { branch2 -> + val returnJoin = b.placeholder() + branch2.onValue(0) + .goto(returnJoin) + branch2.onValue(1) + .iteml(Items.DRAGON_INN_TANKARD_4811, "You show the tankard to Zavistic.") + .playerl("Well, I found this...") + .npcl("Hmmm, no, that's not really associated with this to be honest. Did you find anything else there?") + .goto(returnJoin) + return@let returnJoin.builder() + } + .playerl("Not really.") + .npcl("I don't know what to say then, there isn't enough to go on with the clues you've shown me so far. I'd suggest going back to search a bit more, but you may just be wasting your time?") + .end() + + branch.onValue(0) + .let { builder -> + content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.defaultTalk(builder) + } + } + + + b.onQuestStages(ZogreFleshEaters.questName, 3,4) + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.dialogueInitialTalk(it) } + .let { builder -> + val returnJoin = b.placeholder() + builder.goto(returnJoin) + return@let returnJoin.builder().options() + .let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option("What did you say I should do?") + .playerl("What did you say I should do?") + .npcl("You should go and have a chat with Sithik Ints, he's in that house just to the north. He's a lodger and has a room upstairs. Just tell him that I sent you to see him. He should be fine once you've mentioned my name.") + .goto(returnJoin) + optionBuilder.option_playerl("Where is Sithik?") + .npcl("He's in that house just to the north, less than a few seconds walk away. He's a lodger and has a room upstairs...he's not very well though.") + .goto(returnJoin) + optionBuilder.optionIf("I have an item that I'd like you to look at.") { player -> return@optionIf hasEvidences(player) == 1 } + .goto(continuePath) + optionBuilder.optionIf("I have an item that I'd like you to look at.") { player -> return@optionIf hasEvidences(player) > 1 } + .goto(continuePath) + optionBuilder.option("I want to ask about the magic guild") + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.defaultTalk(it) } + optionBuilder.option_playerl("Sorry, I have to go.") + .end() + + return@let continuePath.builder() + } + } + .branch { player -> + return@branch if (inInventory(player, Items.NECROMANCY_BOOK_4837) ) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .iteml(Items.NECROMANCY_BOOK_4837, "You show the Necromancy book to Zavistic.") + .playerl("I have this necromancy book as evidence that Sithik is involved with the undead ogres at Jiggig.") + .npcl("Ok, so he's researching necromancy...it doesn't mean anything in itself.") + .playerl("Yes, but if you look, you can see that there is a half torn page which matches the page I found at Jiggig.") + .npcl("Hmm, yes, but someone could have stolen that from him and then gone and cast it without his permission or to try and deliberately implicate him.") + .goto(continuePath) + branch.onValue(0) + .goto(continuePath) + return@let continuePath.builder() + } + .branch { player -> + return@branch if (inInventory(player, Items.BOOK_OF_HAM_4829) ) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .iteml(Items.BOOK_OF_HAM_4829, "You show the HAM book to Zavistic.") + .playerl("Look, this book proves that Sithik hates all monsters and most likely Ogres with a passion.") + .npcl("So what, hating monsters isn't a crime in itself...although I suppose that it does give a motive if Sithik was involved. On its own, it's not enough evidence though.") + .goto(continuePath) + branch.onValue(0) + .goto(continuePath) + return@let continuePath.builder() + } + .branch { player -> + return@branch if (inInventory(player, Items.DRAGON_INN_TANKARD_4811) ) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .iteml(Items.DRAGON_INN_TANKARD_4811, "You show the dragon Inn Tankard to Zavistic.") + .playerl("This is the tankard I found on the remains of Brentle Vahn!") + .npcl("That doesn't mean anything in itself, you could have gotten that from anywhere. Even from the Dragon Inn tavern! There isn't anything to link Brentle Vahn with Sithik Ints.") + .goto(continuePath) + branch.onValue(0) + .goto(continuePath) + return@let continuePath.builder() + } + .branch { player -> + return@branch if (inInventory(player, Items.SIGNED_PORTRAIT_4816) ) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .iteml(Items.SIGNED_PORTRAIT_4816, "You show the signed portrait of Sithik to Zavistic.") + .playerl("This is a portrait of Sithik, signed by the landlord of the Dragon Inn saying that he saw Sithik and Brentle Vahn together.") + .npcl("Hmmm, well that is interesting.") + .goto(continuePath) + branch.onValue(0) + .goto(continuePath) + return@let continuePath.builder() + } + .branch { player -> + return@branch if (hasEvidences(player) == 4) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .npcl("However, there isn't enough evidence for me to take the issue further at this point. If you find any further evidence bring it to me.") + .end() + return@let branch + } + .onValue(1) + .npcl("And I'm starting to think that Sithik may be involved. Here, take this potion and give some to Sithik. It'll bring on a change which should solicit some answers - tell him the effects won't revert until he's told the truth.") + .iteml(Items.STRANGE_POTION_4836 ,"Zavistic hands you a strange looking potion bottle and takes all the evidence you've accumulated so far.") + .endWith { _, player -> + if(getQuestStage(player, ZogreFleshEaters.questName) in 3..4) { + if (removeItem(player, Items.NECROMANCY_BOOK_4837) && + removeItem(player, Items.BOOK_OF_HAM_4829) && + removeItem(player, Items.DRAGON_INN_TANKARD_4811) && + removeItem(player, Items.SIGNED_PORTRAIT_4816)) { + addItemOrDrop(player, Items.STRANGE_POTION_4836) + } + setQuestStage(player, ZogreFleshEaters.questName, 5) + } + } + b.onQuestStages(ZogreFleshEaters.questName, 5) + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.dialogueInitialTalk(it) } + .npcl("Have you used that potion yet?") + .branch { player -> + return@branch if (inInventory(player, Items.STRANGE_POTION_4836) ) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("No, not yet, what was I supposed to do again?") + .npcl("Try to use the potion on Sithik somehow, he should undergo an interesting transformation, though you'll probably want to leave the house in case there are any side effects. Then go back and question Sithik and tell") + .npcl("him the effects won't wear off until he tells the truth. In fact, that's not exactly true, but I'm sure it'll be an extra incentive to get him to be honest.") + .let { builder -> + content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.defaultTalk(builder) + } + branch.onValue(0) + .playerl("Well, actually, I've lost it, could I have another one please?") + .npcl("Sure, but don't lose it this time.") + .iteml(Items.STRANGE_POTION_4836 ,"Zavistic hands you a bottle of strange potion.") + .endWith { _, player -> + addItemOrDrop(player, Items.STRANGE_POTION_4836) + } + } + + + b.onQuestStages(ZogreFleshEaters.questName, 6,7,8,9) + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.dialogueInitialTalk(it) } + .npcl("Don't you worry about Sithik, he's not likely to be moving from his bed for a long time. When he eventually does get better, he's going to be sent before a disciplinary tribunal, then we'll sort out what's what.") + .playerl("Thanks for your help with all of this.") + .npcl("Ooohh, no thanks required. It's I who should be thanking you my friend...your investigative mind has shown how vigilant we really should be for this type of evil use of the magical arts.") + .let { builder -> + content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.defaultTalk(builder) + } + } +} + +/** Dialogues when you use stuff on him. */ +class ZavisticRarveUseItemsDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + // SITHIK_PORTRAIT_4814 + b.onPredicate { _ -> dialogueNum == 3 } + .let{ZavisticRarveDialogueFile.dialogueGoodPortrait(it)} + .end() + + // SITHIK_PORTRAIT_4815 + b.onPredicate { _ -> dialogueNum == 4 } + .let{ZavisticRarveDialogueFile.dialogueBadPortrait(it)} + .end() + + // SIGNED_PORTRAIT_4816 + b.onPredicate { _ -> dialogueNum == 5 } + // That would continue the conversation as above if you normally talk to him. + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreBehavior.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreBehavior.kt new file mode 100644 index 000000000..871266c92 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreBehavior.kt @@ -0,0 +1,56 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.getOrStartTimer +import core.api.inEquipment +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.spell.SpellType +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.system.timer.impl.Disease +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class ZogreBehavior : NPCBehavior(*zogreIds) { + companion object { + @JvmField + val zogreIds = intArrayOf( + NPCs.ZOGRE_2044, + NPCs.ZOGRE_2045, + NPCs.ZOGRE_2046, + NPCs.ZOGRE_2047, + NPCs.ZOGRE_2048, + NPCs.ZOGRE_2049, + NPCs.ZOGRE_2051, + NPCs.ZOGRE_2052, + NPCs.ZOGRE_2053, + NPCs.ZOGRE_2054, + NPCs.ZOGRE_2055, + ) + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (inEquipment(attacker, Items.COMP_OGRE_BOW_4827)) { + return + } + if (state.spell != null && state.spell.type == SpellType.CRUMBLE_UNDEAD) { + state.estimatedHit = (state.estimatedHit * 0.5).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.5).toInt() + } + return + } + state.estimatedHit = (state.estimatedHit * 0.25).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.25).toInt() + } + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + val disease = getOrStartTimer(victim, 10) + disease.hitsLeft = 10 + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEaters.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEaters.kt new file mode 100644 index 000000000..6ed8c9010 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEaters.kt @@ -0,0 +1,353 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import content.data.Quests +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +/** + * Zogre Flesh Eaters Quest + * + * 1 - Talked to Grish + * 2 - Smashed Barricade + * 2 Cont' - Collected Stuff Underground + * A - Black Prism from Coffin + * B - Half Torn Page from Broken Lecturn + * C - Tankard from Backpack (Kill the Zombie which turns out to be Brentle Vahn) + * 3 - Talked to Zavistic Rarve with A & B only, points to where Sithik Ints is + * 4 - Talked to Sithik and challenged to incriminate him + * 4 Cont' - Collected Evidence + * A - Tankard from 2C after talking to Innkeeper + * B - Portrait of Sithik using papyrus and charcoal with questionable drawing skills + * C - Book of HAM philosophy from drawer + * D - Necromantic book + * 5 - Incriminate Sithik to Zavistic Rarve and given potion + * 6 - Turned Sithik into an Ogre with potion + * 7 - Talked to Grish + * 8 - Get Key from Grish + * 9 - Killed Slash Bash + * 100 - Returned to Grish with Ogre Artifact + * + * This quest journal is the worst; there's typos, and it is out of order. + * + * if (VARPBIT[487] > 12) return 2; if (VARPBIT[487 == 0) return 0; return 1; }; + * define_varbit 487 455 0 4 + * + */ +@Initializable +class ZogreFleshEaters : Quest(Quests.ZOGRE_FLESH_EATERS, 40, 39, 1, 455, 0, 1, 13) { + companion object { + val questName = Quests.ZOGRE_FLESH_EATERS + const val varbitGateBashed = 496 + const val varbitOgreCoffin = 488 + const val varbitSithikOgre = 495 + + const val attributeAskedAboutSickies = "quest:zogreflesheaters-askedaboutsickies" + + // 2A + const val attributeSearchedCoffin = "/save:quest:zogreflesheaters-searchedcoffin" + const val attributeBrokeLockCoffin = "/save:quest:zogreflesheaters-brokelockcoffin" + const val attributeOpenedCoffin = "/save:quest:zogreflesheaters-openedcoffin" // You can fail apparently... + const val attributeFoundBlackPrism = "/save:quest:zogreflesheaters-foundblackprism" + // 2B + const val attributeFoundHalfTornPage = "/save:quest:zogreflesheaters-foundhalftornpage" + // 2C + const val attributeFoughtZombie = "/save:quest:zogreflesheaters-foughtzombie" + const val attributeFoundTankard = "/save:quest:zogreflesheaters-foundtankard" + // 4A + const val attributeAskedAboutTankard = "quest:zogreflesheaters-askedabouttankard" + // 4B + const val attributeMadePortrait = "/save:quest:zogreflesheaters-madeportrait" + // 4C + const val attributeFoundHamBook = "/save:quest:zogreflesheaters-foundhambook" + // 4D + const val attributeFoundNecromanticBook = "/save:quest:zogreflesheaters-foundnecromanticbook" + + const val attributeSlashBashInstance = "/save:quest:zogreflesheaters-slashbashinstance" + + // Open Uglug Nar Shop with Relicym balm + const val attributeOpenUglugNarShop = "/save:quest:zogreflesheaters-openuglugnarshop" + + fun requirements(player: Player): Boolean { + return arrayOf( + hasLevelStat(player, Skills.RANGE, 30), + hasLevelStat(player, Skills.SMITHING, 4), + hasLevelStat(player, Skills.HERBLORE, 8), + isQuestComplete(player, Quests.JUNGLE_POTION), + isQuestComplete(player, Quests.BIG_CHOMPY_BIRD_HUNTING), + ).all { it } + } + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 11 + var stage = getStage(player) + + var started = getQuestStage(player, questName) > 0 + + if (!started) { + line(player, "I can !!start?? this quest by talking to !!Grish?? at the Ogrish", line++, false) + line(player, "ceremonial dance place called !!Jiggig??.", line++, false) + line(player, "To start this !!quest?? I should complete these quests:-", line++, false) + line(player, "!!Jungle Potion.??", line++, isQuestComplete(player, Quests.JUNGLE_POTION)) + line(player, "!!Big Chompy Bird Hunting.??", line++, isQuestComplete(player, Quests.BIG_CHOMPY_BIRD_HUNTING)) + line(player, "It would help if I had the following skills levels:-", line++, false) + line(player, "!!Ranged level : 30??", line++, hasLevelStat(player, Skills.RANGE, 30)) + line(player, "!!Fletching level : 30??", line++, hasLevelStat(player, Skills.FLETCHING, 30)) + line(player, "!!Smithing level : 4??", line++, hasLevelStat(player, Skills.SMITHING, 4)) + line(player, "!!Herblore level : 8??", line++, hasLevelStat(player, Skills.HERBLORE, 8)) + line(player, "Must be able to defeat a !!level 111?? foe.", line++, false) + } else if (stage < 100) { + + line(player, "I started this quest by talking to Grish, he asked me to", line++, true) + line(player, "check out the underground area where some Zombie ogres", line++, true) + line(player, "(Zogres) were coming from.", line++, true) + + if (stage >= 2) { + line(player, "I have to find a way into the ceremonial dance area and", line++, true) + line(player, "then underground.", line++, true) + line(player, "I persuaded a guard to let me past, I only had to mention", line++, true) + line(player, "Grish's name and the guard smashed the barricade down. I", line++, true) + line(player, "can enter now.", line++, true) + } else if (stage >= 1) { + line(player, "I have to find a way into the ceremonial dance area and", line++, false) + line(player, "then underground.", line++, false) + } + + if (stage >= 2) { + // Set 2A (Stays) + if (getAttribute(player, attributeFoundBlackPrism, false) || stage >= 3) { + line(player, "I have searched a coffin, it had a funny looking hole at the", line++, true) + line(player, "side.", line++, true) + line(player, "I have forced the lock on a coffin, maybe I can open it", line++, true) + line(player, "now?", line++, true) + line(player, "I've !!opened?? the !!coffin?? and retrieved a !!black prism??, this", line++, stage >= 3) + line(player, "may be useful.", line++, stage >= 3) + } else if (getAttribute(player, attributeOpenedCoffin, false)) { + line(player, "I have searched a coffin, it had a funny looking hole at the", line++, true) + line(player, "side.", line++, true) + line(player, "I have forced the lock on a coffin, maybe I can open it", line++, true) + line(player, "now?", line++, true) + line(player, "I've !!opened?? the !!coffin??, maybe there's something in it.", line++, false) + } else if (getAttribute(player, attributeBrokeLockCoffin, false)) { + line(player, "I have searched a coffin, it had a funny looking hole at the", line++, true) + line(player, "side.", line++, true) + line(player, "I have forced the lock on a !!coffin??, maybe I can open it", line++, false) + line(player, "now?", line++, false) + } else if (getAttribute(player, attributeSearchedCoffin, false)) { + line(player, "I have searched a coffin, it had a funny looking hole at the", line++, true) + line(player, "side.", line++, true) + } + } + + if (stage >= 2) { + // Set 2B (Stays) + if (getAttribute(player, attributeFoundHalfTornPage, false) || stage >= 3) { + line(player, "I found a !!half torn page?? from a !!necromatic spellbook??,", line++, stage >= 3) + line(player, "maybe this is a !!clue???", line++, stage >= 3) + } + } + + // 4 - This is the weirdest shit that is out of place. (Stays) This appears after talking to Sithik Int. + if (stage >= 4) { + line(player, "I have shown the prism and torn page to the grand", line++, true) + line(player, "secretary of the wizards guild.", line++, true) + } + + // Set 2C (Does not stay) + if (stage in 2..4 && getAttribute(player, attributeFoundTankard, false)) { + line(player, "I killed a !!human zombie?? which dropped a !!backpack??. The", line++, stage >= 3) + line(player, "!!backpack?? had the name !!'B. Vahn'?? on it, inside the !!backpack??", line++, stage >= 3) + line(player, "I found a !!tankard??.", line++, stage >= 3) + } else if (stage in 2..4 && getAttribute(player, attributeFoughtZombie, false)) { + line(player, "I killed a !!human zombie?? which dropped a !!backpack??.", line++, stage >= 3) + } + + // Stays until you find all the 3X stuff above. + if (stage in 2..4 && !(getAttribute(player, attributeFoundBlackPrism, false) && + getAttribute(player, attributeFoundHalfTornPage, false) && + getAttribute(player, attributeFoundTankard, false))) { + line(player, "I need to find out what happened here.", line++, false) // Cleared when the 3 sets above are done. + } + + // Set 4A (Does not stay) + if (stage in 2..4 && getAttribute(player, attributeAskedAboutTankard, false)) { + line(player, "The 'Dragon Inn' !!Innkeeper?? says the tankard belongs to", line++, false) + line(player, "one of his locals called !!Brentle Vahn??. He was seen talking", line++, false) + line(player, "to a !!wizard?? the other day.", line++, false) // Cleared after handed in with the rest + } + + // 3 Zavistic Rarve seen prism and page - lines disappears right after... + if (stage == 3) { + line(player, "I have shown the !!prism?? and the !!necromantic page?? to", line++, false) + line(player, "Zavistic Rarve. He's told me about a !!wizard?? named", line++, false) + line(player, "!!Sithik Ints?? who might have some information.", line++, false) + } + + // 4 Spoken with Sithik + if (stage >= 4) { + line(player, "I've spoken to !!Sithik??, I need to see if he was !!involved??", line++, stage >= 5) + line(player, "with the !!Undead Ogres at 'Jiggig'?? in some way.", line++, stage >= 5) + } + + // Only stays for 4 + if (stage == 4) { + if (getAttribute(player, attributeMadePortrait, false)) { + line(player, "I've made a !!portrait?? of !!Sithik??...not sure what this will do?", line++, false) + } + if (getAttribute(player, attributeFoundHamBook, false)) { + line(player, "I've found a !!book?? on !!HAM philosophy??...what does this prove?", line++, false) + } + if (getAttribute(player, attributeFoundNecromanticBook, false)) { + line(player, "I've found a !!necromantic book??...what does this prove?", line++, false) + } + } + + // 4 - Who the hell knows why this is here. (Stays) This appears after getting the potion. + if (stage >= 5) { + line(player, "I've talked to Zavistic Rarve regarding the prism and the", line++, true) + line(player, "torn page, he gave some information on a student called", line++, true) + line(player, "Sithik Ints, he may know more about what's happening", line++, true) + line(player, "here.", line++, true) + } + + // Beyond this is legit + if (stage >= 5) { + line(player, "Zavistic has given me some sort of !!potion??, apparently I", line++, stage >= 6) + line(player, "need to give it to !!Sithik??.", line++, stage >= 6) + } + + if (stage >= 7) { + line(player, "I came back into Sithik's room to find that he had been", line++, true) + line(player, "turned into an Ogre!", line++, true) + } else if (stage >= 6) { + line(player, "I have put some of the !!potion?? into !!Sithik's tea??, the !!potion??", line++, false) + line(player, "will take some time to act. Perhaps I should !!get out of??", line++, false) + line(player, "!!here?? in case there are any !!side effects???", line++, false) + } + + if (stage >= 8) { + line(player, "Sithik has told me how to make 'brutal arrows', which", line++, true) + line(player, "should be more effective against Zogres.", line++, true) + + line(player, "Sithik has given me some pointers on how I can make a", line++, true) + line(player, "cure disease potion, though I'm still not sure exactly which", line++, true) + line(player, "herbs I should use.", line++, true) + } else if (stage >= 7) { + line(player, "!!Sithik?? has told me that there is no way I can remove the", line++, false) + line(player, "effects of the !!necromantic curse spell?? from the !!Jiggig??", line++, false) + line(player, "area. I'll have to go back and let !!Grish?? know.", line++, false) + + line(player, "!!Sithik?? has told me how to make !!'brutal arrows'??, which", line++, false) + line(player, "should be more !!effective?? against !!Zogres??.", line++, false) + + line(player, "!!Sithik?? has given me some pointers on how I can make a", line++, false) + line(player, "!!cure disease potion??, though I'm still not sure exactly which", line++, false) + line(player, "!!herbs?? I should use.", line++, false) + } + + if (stage >= 9) { + line(player, "I've told Grish to relocated the dance area, but he needs", line++, true) + line(player, "me to get something from the tomb to so that he can do", line++, true) // "tomb to so" is authentic [sic] + line(player, "this.", line++, true) + line(player, "I need to go back into the tomb and look for some 'old'", line++, true) + line(player, "items that Grish has asked for.", line++, true) + line(player, "I should return the !!artifact?? to !!Grish??.", line++, false) + } else if (stage >= 8) { + line(player, "I've told Grish to relocated the dance area, but he needs", line++, true) + line(player, "me to get something from the tomb to so that he can do", line++, true) // "tomb to so" is authentic [sic] + line(player, "this.", line++, true) + line(player, "I need to go back into the !!tomb?? and look for some !!'old'??", line++, false) + line(player, "!!items?? that !!Grish?? has asked for.", line++, false) + } + + } else { + // The ending is COMPLETELY replaced from this entire shitshow of a quest log. + line(player, "I talked to Grish in the Jiggig area which is swarming with", line++, true) + line(player, "Zombie Ogres (Zogres) These disgusting creatures carry", line++, true) + line(player, "disease and are quite dangerous so the Ogres weren't", line++, true) + line(player, "too keen to try and sort them out.", line++, true) + + line(player, "I talked to an ogre called Grish who asked me to look into", line++, true) + line(player, "the problem. After some searching around in a tomb, I", line++, true) + line(player, "found some clues which pointed me to the human", line++, true) + line(player, "habitation of Yannile.", line++, true) + + line(player, "With the help of Zavistic Rarve, the grand secretary of", line++, true) + line(player, "the Wizards guild I was able to piece the clues together", line++, true) + line(player, "and discover that a Wizard named 'Sithik Ints' was", line++, true) + line(player, "responsible.", line++, true) + + line(player, "Unfortunately I couldn't remove the curse from the area,", line++, true) + line(player, "however, I was able to return some important artefacts to", line++, true) + line(player, "Grish, who can now set up a new ceremonial dance area for", line++, true) + line(player, "the ogres of Gu' Tanoth.", line++, true) + + line(player, "Sithik Ints also told me how to make Brutal arrows which are", line++, true) + line(player, "more effective against Zogres, and he also told me how to", line++, true) + line(player, "make a disease balm.", line++, true) + line++ + line(player,"QUEST COMPLETE!", line) + } + } + + override fun reset(player: Player) { + setVarp(player, varbitGateBashed, 0, true) + removeAttribute(player, attributeAskedAboutSickies) + removeAttribute(player, attributeSearchedCoffin) + removeAttribute(player, attributeBrokeLockCoffin) + removeAttribute(player, attributeOpenedCoffin) + removeAttribute(player, attributeFoundBlackPrism) + removeAttribute(player, attributeFoundHalfTornPage) + removeAttribute(player, attributeFoughtZombie) + removeAttribute(player, attributeFoundTankard) + removeAttribute(player, attributeAskedAboutTankard) + removeAttribute(player, attributeMadePortrait) + removeAttribute(player, attributeFoundHamBook) + removeAttribute(player, attributeFoundNecromanticBook) + removeAttribute(player, attributeOpenUglugNarShop) + + } + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed Zogre Flesh Eaters!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.OGRE_ARTEFACT_4818, 240, 277, 5) + + drawReward(player,"1 Quest Point.", ln++) + drawReward(player,"Can now make Brutal Arrows", ln++) + drawReward(player,"and cure disease potions.", ln++) + drawReward(player,"2000 Ranged, Fletching and", ln++) + drawReward(player,"Herblore XP", ln++) + + player.skills.addExperience(Skills.RANGE, 2000.0) + player.skills.addExperience(Skills.FLETCHING, 2000.0) + player.skills.addExperience(Skills.HERBLORE, 2000.0) + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + this.updateVarps(player) + } + + override fun updateVarps(player: Player) { + if(getQuestStage(player, questName) >= 2) { + setVarbit(player, varbitGateBashed, 1, true) + } else { + setVarbit(player, varbitGateBashed, 0, true) + } + if(getQuestStage(player, questName) >= 7) { + setVarbit(player, varbitSithikOgre, 1, true) + } else { + setVarbit(player, varbitSithikOgre, 0, true) + } + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEatersListeners.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEatersListeners.kt new file mode 100644 index 000000000..bd16a02b6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEatersListeners.kt @@ -0,0 +1,382 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.Direction +import core.game.world.map.Location +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class ZogreFleshEatersListeners : InteractionListener { + + companion object { + @JvmStatic + fun ladderMakesSithikTurnIntoOgre(player: Player) { + if(getQuestStage(player, ZogreFleshEaters.questName) == 6) { + setQuestStage(player, ZogreFleshEaters.questName, 7) + setVarbit(player, ZogreFleshEaters.varbitSithikOgre, 1) + } + } + } + + override fun defineListeners() { + // Stairs and doors + on(Scenery.STAIRS_6841, SCENERY, "climb-down") { player, node -> + sendMessage(player, "You climb down the steps.") + if (node.location == Location(2443, 9417, 2)) { + teleport(player, Location(2442, 9417, 0)) + } else { + teleport(player, Location(2477, 9437, 2)) + } + return@on true + } + on(Scenery.STAIRS_6842, SCENERY, "climb-up") { player, node -> + sendMessage(player, "You climb up the steps.") + if (node.location == Location(2443, 9417, 0)) { + teleport(player, Location(2447, 9417, 2)) + } else { + teleport(player, Location(2485, 3045, 0)) + } + return@on true + } + on(Scenery.OGRE_STONE_DOOR_6871, SCENERY, "open") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) >= 9) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else if (inInventory(player, Items.OGRE_GATE_KEY_4839)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + sendMessage(player, "You use the Ogre Tomb Key to unlock the door.") + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + + on(Scenery.OGRE_STONE_DOOR_6872, SCENERY, "open") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) >= 9) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else if (inInventory(player, Items.OGRE_GATE_KEY_4839)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + sendMessage(player, "You use the Ogre Tomb Key to unlock the door.") + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + + // Stage 2 + on(Scenery.CRUSHED_BARRICADE_6881, SCENERY, "climb-over") { player, node -> + if (player.location.x < 2456) { + val distance = player.location.getDistance(Location(2457, 3049, 0)).toInt() + forceMove(player, player.location, Location(2457, 3049, 0), 0, distance * 15, null, 1236) + } else { + val distance = player.location.getDistance(Location(2455, 3049, 0)).toInt() + forceMove(player, player.location, Location(2455, 3049, 0), 0, distance * 15, null, 1236) + } + return@on true + } + on(Scenery.CRUSHED_BARRICADE_6882, SCENERY, "climb-over") { player, node -> + if (player.location.x < 2456) { + val distance = player.location.getDistance(Location(2457, 3048, 0)).toInt() + forceMove(player, player.location, Location(2457, 3048, 0), 0, distance * 15, null, 1236) + } else { + val distance = player.location.getDistance(Location(2455, 3048, 0)).toInt() + forceMove(player, player.location, Location(2455, 3048, 0), 0, distance * 15, null, 1236) + } + return@on true + } + + // Stage 2A + on(Scenery.OGRE_COFFIN_6844, SCENERY, "search") { player, node -> + if (getVarbit(player, ZogreFleshEaters.varbitOgreCoffin) == 0) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogueLines(player, "You search the coffin and find a small geometrically shaped hole in", "the side. It looks as if this hole was made with a considerable amount", "of force, maybe the thing which made the hole is still inside?").also { stage++ } + 1 -> sendDialogueLines(player, "The lock looks quite crude, with some skill and a slender blade, you", "may be able to force it.").also { stage = END_DIALOGUE } + } + } + }) + } else if (getVarbit(player, ZogreFleshEaters.varbitOgreCoffin) == 1) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogueLines(player, "The lid looks heavy, but now that you've unlocked it, you may be", "able to lift it. You prepare yourself.").also { stage++ } + 1 -> playerl("Urrrgggg.").also { sendChat(player, "Urrrgggg."); stage++ } + 2 -> playerl("Aarrrgghhh!").also { sendChat(player, "Aarrrgghhh!"); stage++ } + // Supposed to have a failure state here. + 3 -> playerl("Raarrrggggg! Yes!").also { sendChat(player, "Raarrrggggg! Yes!"); stage++ } + 4 -> sendDialogueLines(player, "You eventually manage to lift the lid.").also { + setVarbit(player, ZogreFleshEaters.varbitOgreCoffin, 3, true) + stage = END_DIALOGUE + } + } + } + }) + } + return@on true + } + onUseWith(IntType.SCENERY, Items.KNIFE_946, Scenery.OGRE_COFFIN_6844) { player, _, _ -> + sendItemDialogue(player, Items.KNIFE_946, + "With some skill you manage to slide the blade along the lock edge and click into place the teeth of the primitive mechanism.") + setVarbit(player, ZogreFleshEaters.varbitOgreCoffin, 1, true) + return@onUseWith true + } + + on(Scenery.OGRE_COFFIN_6845, SCENERY, "search") { player, node -> + sendItemDialogue(player, Items.BLACK_PRISM_4808, + "You find a creepy looking black prism inside.") + addItemOrDrop(player, Items.BLACK_PRISM_4808) + return@on true + } + + on(Items.BLACK_PRISM_4808, ITEM, "look-at") { player, node -> + sendItemDialogue(player, Items.BLACK_PRISM_4808, + "It looks like a smokey black gem of some sort...very creepy. Some magical force must have prevented it from being shattered when it hit the coffin.") + return@on true + } + + // Stage 2B + on(Scenery.BROKEN_LECTURN_6846, SCENERY, "search") { player, node -> + sendMessage(player, "You search the broken down lecturn.") + if (inInventory(player, Items.TORN_PAGE_4809)) { + sendMessage(player, "You find nothing.") + } else { + sendItemDialogue(player, Items.TORN_PAGE_4809, + "You find a half torn page...it has spidery writing all over it.") + addItemOrDrop(player, Items.TORN_PAGE_4809) + } + return@on true + } + + on(Items.TORN_PAGE_4809, ITEM, "read") { player, node -> + sendDialogue(player, "You don't manage to understand all of it as there is only a half page here. But it seems the spell was used to place a curse on an area and for all time raise the dead.") + return@on true + } + + // Stage 2C + on(Scenery.SKELETON_6893, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) >= 2){ + if (getAttribute(player, ZogreFleshEaters.attributeFoughtZombie, false)) { + + if (inInventory(player, Items.RUINED_BACKPACK_4810)) { + sendMessage(player, "You find nothing on the corpse.") + } else { + sendMessage(player, "You find another backpack.") + addItemOrDrop(player, Items.RUINED_BACKPACK_4810) + } + } else { + // Zombie time. + sendMessage(player, "Something screams into life right in front of you.") + val npc = NPC(NPCs.ZOMBIE_1826) + npc.isRespawn = false + npc.isWalks = false + npc.location = Location(2442, 9459, 2) + npc.direction = Direction.NORTH + npc.init() + npc.attack(player) + } + } else { + // At no point should this be reached since you need to start the quest anyway. + sendMessage(player, "You find nothing on the corpse.") + } + return@on true + } + on(Items.RUINED_BACKPACK_4810, ITEM, "open") { player, node -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> { + sendItemDialogue(player, Items.RUINED_BACKPACK_4810, + "Just before you open the backpack, you notice a small leather patch with the moniker: 'B.Vahn', on it.").also { stage++ } + sendItemZoomOnInterface(player, 241, 1, Items.RUINED_BACKPACK_4810, 230) + } + 1 -> sendItemDialogue(player, Items.DRAGON_INN_TANKARD_4811, + "You find an interesting looking tankard.").also { + setAttribute(player, ZogreFleshEaters.attributeFoundTankard, true) + if(removeItem(player, node)) { + addItem(player, Items.ROTTEN_FOOD_2959) + addItem(player, Items.KNIFE_946) + addItem(player, Items.DRAGON_INN_TANKARD_4811) + } + stage++ + } + 2 -> sendDoubleItemDialogue(player, Items.KNIFE_946, Items.ROTTEN_FOOD_2959, "You find a knife and some rotten food, the backpack is ripped to shreds.").also { + sendMessage(player, "You find a knife and some rotten food.") + sendMessage(player, "You find an interesting looking tankard.") + stage = END_DIALOGUE + } + } + } + }) + return@on true + } + + + on(Items.DRAGON_INN_TANKARD_4811, ITEM, "look-at") { player, node -> + sendItemDialogue(player, Items.DRAGON_INN_TANKARD_4811, + "A stout ceramic tankard with a Dragon Emblem on the side, the words, 'Ye Olde Dragon Inn' are inscribed in the bottom.") + return@on true + } + + // Zavistic Bell, Stage 2,3,4,5,6 and on (Actually should be standalone. + on(Scenery.BELL_6847, SCENERY, "ring") { player, node -> + sendMessage(player, "You ring the bell.") + // TODO: Make Zavistic appear at the bell area. + openDialogue(player, content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile(), NPC(NPCs.ZAVISTIC_RARVE_2059)) + return@on true + } + + // Stage 4A + onUseWith(IntType.NPC, Items.DRAGON_INN_TANKARD_4811, NPCs.BARTENDER_739) { player, used, with -> + openDialogue(player, BartenderDialogueFile(1), with as NPC) + return@onUseWith true + } + onUseWith(IntType.NPC, Items.SITHIK_PORTRAIT_4814, NPCs.BARTENDER_739) { player, used, with -> + // The good portrait + openDialogue(player, BartenderDialogueFile(2), with as NPC) + return@onUseWith true + } + onUseWith(IntType.NPC, Items.SITHIK_PORTRAIT_4815, NPCs.BARTENDER_739) { player, used, with -> + // The bad portrait + openDialogue(player, BartenderDialogueFile(3), with as NPC) + return@onUseWith true + } + on(Items.SIGNED_PORTRAIT_4816, ITEM, "look-at") { player, node -> + sendItemDialogue(player, Items.SIGNED_PORTRAIT_4816, + "You see an image of Sithik with a message underneath 'I, the bartender of the Dragon Inn, do swear that this is a true likeness of the wizzy who was talking to Brentle Vahn, my customer the other day.'") + return@on true + } + onUseWith(IntType.NPC, Items.SITHIK_PORTRAIT_4814, NPCs.ZAVISTIC_RARVE_2059) { player, used, with -> + // The good portrait + openDialogue(player, ZavisticRarveUseItemsDialogueFile(3), with as NPC) + return@onUseWith true + } + onUseWith(IntType.NPC, Items.SITHIK_PORTRAIT_4815, NPCs.ZAVISTIC_RARVE_2059) { player, used, with -> + // The bad portrait + openDialogue(player, ZavisticRarveUseItemsDialogueFile(4), with as NPC) + return@onUseWith true + } + + + // Stage 4B + on(Items.BOOK_OF_PORTRAITURE_4817, ITEM, "read") { player, node -> + sendDialogueLines(player, + "All interested artisans should really consider taking up the hobby of", + "portraiture. To do so, one uses a piece of papyrus on the intended", + "subject to initiate a likeness drawing activity.") + return@on true + } + + // Stage 4C + on(Items.BOOK_OF_HAM_4829, ITEM, "read") { player, node -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> { + sendDialogue(player, + "You read this book for a while, it seems to be some sort of political "+ + "manifesto about how the king doesn't do enough to safeguard the "+ + "citizens of the realm from the monsters that still thrive within the "+ + "borders. ").also { stage++ } + // This is the original, but it is too big for this to handle. +// sendDialogueLines(player, +// "You read this book for a while, it seems to be some sort of political", +// "manifesto about how the king doesn't do enough to safeguard the", +// "citizens of the realm from the monsters that still thrive within the", +// "borders. It sends out a rallying to all people who would want to", +// "stop monsters, to join the HAM movement.").also { stage++ } + } + 1 -> sendDialogue(player, "It sends out a rallying to all people who would want to stop monsters, to join the HAM movement.").also { stage++ } + 2 -> sendPlayerDialogue(player, "Hmm, Sithik must really hate monsters then, I wonder if he hates ogres in particular?").also { + stage = END_DIALOGUE + } + } + } + }) + return@on true + } + + // Stage 4D + on(Items.NECROMANCY_BOOK_4837, ITEM, "read") { player, node -> + sendDialogueLines(player, + "This book uses very strange language and some", + "incomprehensible symbols. It has a very dark and evil feeling to", + "it. As you're looking through the book, you notice that", + "one of the pages has been torn and half of it is missing.") + return@on true + } + + onUseWith(IntType.ITEM, Items.NECROMANCY_BOOK_4837, Items.TORN_PAGE_4809) { player, _, _ -> + sendDoubleItemDialogue(player, Items.NECROMANCY_BOOK_4837, Items.TORN_PAGE_4809, + "The torn page matches exactly the part where a torn out page is missing from the book. You feel sure that this page came from this book.") + return@onUseWith true + } + + // Uglug Nar Shop + onUseWith(IntType.NPC, intArrayOf(Items.RELICYMS_BALM1_4848, Items.RELICYMS_BALM2_4846, Items.RELICYMS_BALM3_4844, Items.RELICYMS_BALM4_4842), NPCs.UGLUG_NAR_2039) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendItemDialogue(player, used.id,"You show the potion to Uglug Nar.").also { stage++ } + 1 -> playerl("Hey, here you go! I brought you some of the potion which should cure the disease. You said that you would buy some from me.").also { + if (getAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, false)) { + stage = 2 + } else { + stage = 3 + } + } + 2 -> sendNPCDialogue(player, NPCs.UGLUG_NAR_2039, "Yous creatures is da funny ones... yous already solds me's ones now..and us can now sell un to yous!", FacialExpression.OLD_NORMAL).also { stage = END_DIALOGUE } + 3 -> sendNPCDialogue(player, NPCs.UGLUG_NAR_2039, "Yous creatures done da good fing...yous get many bright pretties for dis...!", FacialExpression.OLD_NORMAL).also { stage++ } + 4 -> sendDoubleItemDialogue(player, Item(Items.COINS_995, 1000), used as Item, "You sell the potion and get 1000 coins in return.").also { + if (removeItem(player, used)) { + addItemOrDrop(player, Items.COINS_995, 1000) + setAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, true) + } + stage = END_DIALOGUE + } + } + } + }) + return@onUseWith true + } + + // Stage 8 to 9 + on(Scenery.STAND_6897, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) == 8 && + getAttribute(player, ZogreFleshEaters.attributeSlashBashInstance, null) == null + ) { + // Zombie time. + sendMessage(player, "Something stirs behind you!") + val npc = NPC(NPCs.SLASH_BASH_2060) + setAttribute(player, ZogreFleshEaters.attributeSlashBashInstance, npc) + setAttribute(npc, "target", player) + npc.isRespawn = false + npc.isWalks = false + npc.location = Location(2478, 9446, 0) + npc.direction = Direction.EAST + npc.init() + npc.attack(player) + } else if (getQuestStage(player, ZogreFleshEaters.questName) > 8) { + if (inInventory(player, Items.OGRE_ARTEFACT_4818)) { + sendMessage(player, "You find nothing on the stand.") + } else { + sendMessage(player, "You find another artifact.") + addItemOrDrop(player, Items.OGRE_ARTEFACT_4818) + } + } else { + // At no point should this be reached since you need to start the quest anyway. + sendMessage(player, "You find nothing on the stand.") + } + return@on true + } + } +} diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogrePotionAndFletchingListeners.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogrePotionAndFletchingListeners.kt new file mode 100644 index 000000000..1db9325ff --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogrePotionAndFletchingListeners.kt @@ -0,0 +1,109 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import kotlin.math.min + +public enum class BrutalArrows(val nailItem: Int, val level: Int, val product: Int, val exp: Double) { + BRONZE_BRUTAL(Items.BRONZE_NAILS_4819, 7, Items.BRONZE_BRUTAL_4773, 8.4), + IRON_BRUTAL(Items.IRON_NAILS_4820, 18, Items.IRON_BRUTAL_4778, 15.6), + STEEL_BRUTAL(Items.STEEL_NAILS_1539, 33, Items.STEEL_BRUTAL_4783, 30.6), + BLACK_BRUTAL(Items.BLACK_NAILS_4821, 38, Items.BLACK_BRUTAL_4788, 39.0), + MITHRIL_BRUTAL(Items.MITHRIL_NAILS_4822, 49, Items.MITHRIL_BRUTAL_4793, 45.0), + ADAMANT_BRUTAL(Items.ADAMANTITE_NAILS_4823, 62, Items.ADAMANT_BRUTAL_4798, 61.2), + RUNE_BRUTAL(Items.RUNE_NAILS_4824, 77, Items.RUNE_BRUTAL_4803, 75.0) + ; + + companion object { + @JvmField + val nailItemMap = values().associateBy { it.nailItem } + val nailItemArray = nailItemMap.values.map { it.nailItem }.toIntArray() + } +} +/** + * This handles potions and fletching related to zogre flesh eaters. + * + * Relicym's Balm is unique to zogre flesh eaters. + * Maybe move this to herblore when it can handle quest requirements better. + */ +class ZogrePotionAndFletchingListeners : InteractionListener { + + override fun defineListeners() { + // ROGUES_PURSE_POTIONUNF_4840 is already in UnfinishedPotion.java + onUseWith(IntType.ITEM, Items.ROGUES_PURSE_POTIONUNF_4840, Items.CLEAN_SNAKE_WEED_1526) { player, used, with -> + if (!hasLevelStat(player, Skills.HERBLORE, 8)) { + sendMessage(player, "You need a herblore level of 8 to make this mix.") + return@onUseWith true + } + if (getQuestStage(player, ZogreFleshEaters.questName) < 7) { + sendMessage(player, "You need to have partially completed Zogre Flesh Eaters to make this mix.") + return@onUseWith true + } + + if(removeItem(player, used) && removeItem(player, with)) { + sendMessage(player, "You add the snake weed to the rogues purse solution and make Relicyms Balm.") + addItem(player, Items.RELICYMS_BALM3_4844) + rewardXP(player, Skills.HERBLORE, 40.0) + } + return@onUseWith true + } + + // FletchingListeners.kt + // ACHEY_TREE_LOGS_2862 -> UNSTRUNG_COMP_BOW_4825 -> COMP_OGRE_BOW_4827 + // Requirement to wield comp ogre bow. + onEquip(Items.COMP_OGRE_BOW_4827) { player, _ -> + if (getQuestStage(player, ZogreFleshEaters.questName) >= 8){ + return@onEquip true + } + sendMessage(player, "You need to complete part of Zogre Flesh Eaters to equip this.") + return@onEquip false + } + + + onUseWith(IntType.ITEM, BrutalArrows.nailItemArray, Items.FLIGHTED_OGRE_ARROW_2865) { player, used, with -> + fun getMaxAmount(_unused: Int = 0): Int { + val tips = amountInInventory(player, used.id) + val shafts = amountInInventory(player, with.id) + return min(tips, shafts) + } + + fun process() { + val amountThisIter = min(6, getMaxAmount()) + if (removeItem(player, Item(used.id, amountThisIter)) && removeItem(player, Item(with.id, amountThisIter))) { + addItem(player, BrutalArrows.nailItemMap[used.id]!!.product, amountThisIter) + sendMessage(player, "You make $amountThisIter brutal arrows.") + rewardXP(player, Skills.FLETCHING, BrutalArrows.nailItemMap[used.id]!!.exp) + } + } + + if (getQuestStage(player, ZogreFleshEaters.questName) < 7) { + sendMessage(player, "You need to complete part of Zogre Flesh Eaters to make these.") + return@onUseWith true + } + + if (getStatLevel(player, Skills.FLETCHING) < BrutalArrows.nailItemMap[used.id]!!.level) { + sendMessage(player, "You need a Fletching level of " + BrutalArrows.nailItemMap[used.id]!!.level + " to make these.") + return@onUseWith true + } + + sendSkillDialogue(player) { + create { id, amount -> + runTask( + player, + delay = 2, + repeatTimes = min(amount, getMaxAmount() / 6 + 1), + task = ::process + ) + } + calculateMaxAmount(::getMaxAmount) + withItems(Item(BrutalArrows.nailItemMap[used.id]!!.product, 5)) + } + return@onUseWith true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZombieBrentleVahnBehavior.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZombieBrentleVahnBehavior.kt new file mode 100644 index 000000000..d24485447 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZombieBrentleVahnBehavior.kt @@ -0,0 +1,40 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.system.timer.impl.Disease +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** The zombie you have to fight when you click on the skeleton. */ +class ZombieBrentleVahnBehavior : NPCBehavior(NPCs.ZOMBIE_1826) { + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + val disease = getOrStartTimer(victim, 10) + disease.hitsLeft = 10 + } + + override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { + super.onDropTableRolled(self, killer, drops) + // Drops backpack when killed. + if (killer is Player && getQuestStage(killer, ZogreFleshEaters.questName) in 2..4) { + drops.add(Item(Items.RUINED_BACKPACK_4810)) + setAttribute(killer, ZogreFleshEaters.attributeFoughtZombie, true) + } + } + + var clearTime = 0 + override fun tick(self: NPC): Boolean { + // You have 400 ticks to kill this guy + if (clearTime++ > 400) { + clearTime = 0 + poofClear(self) + } + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeBarmanDialogue.kt b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeBarmanDialogue.kt new file mode 100644 index 000000000..40b59d721 --- /dev/null +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeBarmanDialogue.kt @@ -0,0 +1,110 @@ +package content.region.kandarin.gnomestronghold.dialogue + +import core.api.addItem +import core.api.inInventory +import core.api.openDialogue +import core.api.openNpcShop +import core.api.removeItem +import core.api.sendMessage +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +private const val INGREDIENT_COST = 20 +private const val NOT_ENOUGH_MONEY_MESSAGE = "You do not have enough money to buy that." + +/** + * Handles the dialogue for the Gnome Barman NPCs in the Tree Gnome Stronghold. + * @author Broseki + */ +@Initializable +class GnomeBarmanDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return GnomeBarmanDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GnomeBarmanDialogueStart(), npc) + return false + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BARMAN_849) + } +} + +class GnomeBarmanDialogueStart : DialogueLabeller() { + override fun addConversation() { + npc(ChatAnim.OLD_HAPPY, "Good day to you. What can I get you to drink?") + options( + DialogueOption("store", "What do you have?", expression = ChatAnim.FRIENDLY, spokenText = "What do you have."), + DialogueOption("nothing", "Nothing thanks.", expression = ChatAnim.FRIENDLY), + DialogueOption( + "ingredients", "Can I buy some ingredients?", + spokenText = "I was just wanting to buy a cocktail ingredient actually.", + expression = ChatAnim.FRIENDLY + ), + ) + + label("store") + npc(ChatAnim.OLD_HAPPY, "Here, take a look at our menu.") + exec { player, npc -> + openNpcShop(player, npc.id) + } + + label("nothing") + npc(ChatAnim.OLD_HAPPY, "Okay, take it easy.") + + label("ingredients") + npc(ChatAnim.OLD_HAPPY, "Sure thing, what did you want?") + options( + DialogueOption("lemon", "A lemon.", expression = ChatAnim.FRIENDLY), + DialogueOption("orange", "An orange.", expression = ChatAnim.FRIENDLY), + DialogueOption("shaker", "A cocktail shaker.", expression = ChatAnim.FRIENDLY), + DialogueOption( + "buynothing", "Nothing thanks.", + spokenText = "Actually nothing thanks.", + expression = ChatAnim.FRIENDLY + ), + ) + + label("lemon") + npc(ChatAnim.OLD_HAPPY, "$INGREDIENT_COST coins please.") + exec { player, _ -> + if (removeItem(player, Item(Items.COINS_995, INGREDIENT_COST))) { + addItem(player, Items.LEMON_2102) + } else { + sendMessage(player, NOT_ENOUGH_MONEY_MESSAGE) + } + } + + label("orange") + npc(ChatAnim.OLD_HAPPY, "$INGREDIENT_COST coins please.") + exec { player, _ -> + if (removeItem(player, Item(Items.COINS_995, INGREDIENT_COST))) { + addItem(player, Items.ORANGE_2108) + } else { + sendMessage(player, NOT_ENOUGH_MONEY_MESSAGE) + } + } + + label("shaker") + npc(ChatAnim.OLD_HAPPY, "$INGREDIENT_COST coins please.") + exec { player, _ -> + if (removeItem(player, Item(Items.COINS_995, INGREDIENT_COST))) { + addItem(player, Items.COCKTAIL_SHAKER_2025) + } else { + sendMessage(player, NOT_ENOUGH_MONEY_MESSAGE) + } + } + + label("buynothing") + // Just end the conversation here without any further dialogue + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeWomanDialogue.kt b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeWomanDialogue.kt new file mode 100644 index 000000000..c6f7fd2fd --- /dev/null +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeWomanDialogue.kt @@ -0,0 +1,104 @@ +package content.region.kandarin.gnomestronghold.dialogue + +import core.api.addItemOrDrop +import core.api.openDialogue +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Handled the dialogue for the Gnome Woman NPCs in the Tree Gnome Stronghold. + * @author Broseki + */ +@Initializable +class GnomeWomanDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return GnomeWomanDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Pick a random number between 0 - 5 to determine the dialogue tree to use + val randomDialogue = (0..5).random() + + when (randomDialogue) { + 0 -> openDialogue(player, GnomeWomanDialogueFile0(), npc) + 1 -> openDialogue(player, GnomeWomanDialogueFile1(), npc) + 2 -> openDialogue(player, GnomeWomanDialogueFile2(), npc) + 3 -> openDialogue(player, GnomeWomanDialogueFile3(), npc) + 4 -> openDialogue(player, GnomeWomanDialogueFile4(), npc) + else -> openDialogue(player, GnomeWomanDialogueFile5(), npc) + } + return false + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GNOME_WOMAN_168, NPCs.GNOME_WOMAN_169) + } +} + +class GnomeWomanDialogueFile0 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_HAPPY, "Hello adventurer. Here are some wise words:") + player(ChatAnim.FRIENDLY, "OK.") + npc(ChatAnim.OLD_CALM_TALK1, "Happiness is inward and not outward. So it does not depend on what we have but on what we are!") + } +} + +class GnomeWomanDialogueFile1 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_HAPPY, "Hi. I've never seen so many humans in my life.") + player(ChatAnim.LAUGH, "I've never seen so many gnomes!") + npc(ChatAnim.OLD_LAUGH1, "So we're both learning.") + } +} + +class GnomeWomanDialogueFile2 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_HAPPY, "Hello traveller. Are you eating properly? You look tired.") + player(ChatAnim.FRIENDLY, "I think so.") + npc(ChatAnim.OLD_CALM_TALK1, "Here, get this worm down you. It'll do you the world of good.") + item(Item(Items.KING_WORM_2162), "The gnome gives you a worm.") + exec { player, npc -> + addItemOrDrop(player, Items.KING_WORM_2162) + } + player(ChatAnim.HAPPY, "Thanks!") + } +} + +class GnomeWomanDialogueFile3 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_HAPPY, "Well good day to you kind sir. Are you new to these parts?") + player(ChatAnim.FRIENDLY + , "Kind of.") + npc(ChatAnim.OLD_HAPPY, "Well if you're looking for a good night out: Blurberry's cocktail bar is great!") + } +} + +class GnomeWomanDialogueFile4 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_CALM_TALK1, "Some people grumble because roses have thorns. I'm thankful that thorns have roses!") + player(ChatAnim.HAPPY, "Good attitude!") + } +} + +class GnomeWomanDialogueFile5 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + player(ChatAnim.FRIENDLY, "How are you?") + npc(ChatAnim.OLD_HAPPY, "Not bad, a little worn out.") + player(ChatAnim.FRIENDLY, "Maybe you should have a lie down.") + npc(ChatAnim.OLD_HAPPY, "With three kids to feed I've no time for naps!") + player(ChatAnim.HAPPY, "Sounds like hard work!") + npc(ChatAnim.OLD_HAPPY, "It is but they're worth it.") + } +} diff --git a/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java b/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java index 7cec187ec..1322bf19c 100644 --- a/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java +++ b/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java @@ -17,6 +17,7 @@ import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; import content.global.travel.EssenceTeleport; +import content.data.Quests; /** * Represents the wizard guild plugin. @@ -35,7 +36,6 @@ public final class WizardGuildPlugin extends OptionHandler { SceneryDefinition.forId(2155).getHandlers().put("option:open", this); SceneryDefinition.forId(1722).getHandlers().put("option:climb-up", this); new WizardDistentorDialogue().init(); - new ZavisticRarveDialogue().init(); new ProfessorImblewynDialogue().init(); new WizardFrumsconeDialogue().init(); new RobeStoreDialogue().init(); @@ -74,7 +74,7 @@ public final class WizardGuildPlugin extends OptionHandler { } break; case "teleport": - if (!player.getQuestRepository().isComplete("Rune Mysteries")) { + if (!player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES)) { player.getPacketDispatch().sendMessage("You need to have completed the Rune Mysteries Quest to use this feature."); return true; } @@ -133,7 +133,7 @@ public final class WizardGuildPlugin extends OptionHandler { stage = 2; break; case 2: - if (!player.getQuestRepository().isComplete("Rune Mysteries")) { + if (!player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES)) { player("Nothing thanks, I'm just looking around."); stage = 4; return true; @@ -175,68 +175,6 @@ public final class WizardGuildPlugin extends OptionHandler { } - /** - * Represents the dialogue used for zavistic rarve. - * @author 'Vexia - * @version 1.0 - */ - public final class ZavisticRarveDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ZavisticRarveDialogue} {@code Object}. - */ - public ZavisticRarveDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ZavisticRarveDialogue} {@code Object}. - * @param player the player. - */ - public ZavisticRarveDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ZavisticRarveDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("What are you doing...Oh, it's you...sorry...didn't", "realise... what can I do for you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player("Thanks for your help with all of this."); - stage = 1; - break; - case 1: - npc("Ooohh, no thanks required. It's I who should be", "thanking you my friend...your investigative mind has", "shown how vigilant we really should be for this type of", "evil use of the magical arts."); - stage = 2; - break; - case 2: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2059 }; - } - - } - /** * Represents the wizard distentor dialogue. * @author 'Vexia diff --git a/Server/src/main/content/region/kandarin/handlers/GnomeTortoiseNPC.kt b/Server/src/main/content/region/kandarin/handlers/GnomeTortoiseNPC.kt new file mode 100644 index 000000000..84d32fdbc --- /dev/null +++ b/Server/src/main/content/region/kandarin/handlers/GnomeTortoiseNPC.kt @@ -0,0 +1,284 @@ +package content.region.kandarin.handlers + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.combat.CombatSwingHandler +import core.game.node.entity.combat.MultiSwingHandler +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.impl.Animator.Priority +import core.game.node.entity.impl.Projectile +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.npc.agg.AggressiveBehavior +import core.game.node.entity.npc.agg.AggressiveHandler +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import org.rs09.consts.NPCs + +/* + * Behavior is based on the following sources: + * https://www.youtube.com/watch?v=u2A-_ihV_2w (november 2008) + * https://www.youtube.com/watch?v=O0EIZu7-iys (august 2009) + * https://runescape.wiki/w/Tortoise?oldid=815524 https://web.archive.org/web/20090308094532/http://runescape.wikia.com/wiki/Tortoise + * + * todo fix att, str, def? I'm not sure what is the typical way to calculate these when they are unknown. + * level 79: HP 101, max hit 12, "low attack but good defence" + * level 92: HP 121, max hit 12, "low attack but good defence" + * + * https://runescape.wiki/w/Gnome_Archer?oldid=949978 https://web.archive.org/web/20090929124104/http://runescape.wikia.com/wiki/Gnome_archer + * https://runescape.wiki/w/Gnome_Driver?oldid=1235409 https://web.archive.org/web/20090929124109/http://runescape.wikia.com:80/wiki/Gnome_driver + * https://runescape.wiki/w/Gnome_Mage?oldid=1425756 https://web.archive.org/web/20090928131013/http://runescape.wikia.com:80/wiki/Gnome_mage + */ + +@Initializable +class GnomeTortoiseNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GnomeTortoiseNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TORTOISE_3808) + } + + fun spawnGnomes(location: Location, direction: Direction) { + + //todo: sometimes the child spawns or direction is wrong on death. does a move trigger right before it dies? + + var archerLoc = location + var driverLoc = location + var mageLoc = location + + // X D X 3x3 turtle, C center + // M C A D driver, M mage, A archer + // X X X + + // If I was smart I could probably do this with vectors, but I'm dumb so just doing the possibilities by hand. + if(direction == Direction.NORTH) { + archerLoc = location.transform(1,0,0) + driverLoc = location.transform(0,1,0) + mageLoc = location.transform(-1,0,0) + }else if(direction == Direction.NORTH_EAST) { + archerLoc = location.transform(1,-1,0) + driverLoc = location.transform(1,1,0) + mageLoc = location.transform(-1,1,0) + }else if(direction == Direction.EAST) { + archerLoc = location.transform(0,-1,0) + driverLoc = location.transform(1,0,0) + mageLoc = location.transform(0,1,0) + }else if(direction == Direction.SOUTH_EAST) { + archerLoc = location.transform(-1,-1,0) + driverLoc = location.transform(1,-1,0) + mageLoc = location.transform(1,1,0) + }else if(direction == Direction.SOUTH) { + archerLoc = location.transform(-1,0,0) + driverLoc = location.transform(0,-1,0) + mageLoc = location.transform(1,0,0) + }else if(direction == Direction.SOUTH_WEST) { + archerLoc = location.transform(-1,1,0) + driverLoc = location.transform(-1,-1,0) + mageLoc = location.transform(1,-1,0) + }else if(direction == Direction.WEST) { + archerLoc = location.transform(0,1,0) + driverLoc = location.transform(-1,0,0) + mageLoc = location.transform(0,-1,0) + }else if(direction == Direction.NORTH_WEST) { + archerLoc = location.transform(1,1,0) + driverLoc = location.transform(-1,1,0) + mageLoc = location.transform(-1,-1,0) + } + + val npcArcher = GnomeArcherNPC(NPCs.GNOME_ARCHER_3814, archerLoc) + npcArcher.sendChat("Argh!") + npcArcher.init() + + val npcDriver = GnomeDriverNPC(NPCs.GNOME_DRIVER_3815, driverLoc) + npcDriver.sendChat("Nooooo! Dobbie's dead!") + npcDriver.init() + + val npcMage = GnomeMageNPC(NPCs.GNOME_MAGE_3816, mageLoc) + npcMage.sendChat("Kill the infidel!") + npcMage.init() + } + + override fun finalizeDeath(killer: Entity?) { + val turtleLoc = this.centerLocation + val turtleDir = this.direction + spawnGnomes(turtleLoc, turtleDir) + super.finalizeDeath(killer) + // todo remove this debug if not needed. It's just telling me the "direction" the tortoise dies in so I can verify the direction that the child NPCs should spawn. + if (killer is Player) { + killer.debug(direction.toString()) + } + } +} + +//handles the attack switching +class GnomeTortoiseBehavior : NPCBehavior(NPCs.TORTOISE_3808) { + + private val combatHandler = MultiSwingHandler( + true, + // per wiki source, melee has a max hit of 12 + SwitchAttack( + CombatStyle.MELEE.swingHandler, + Animation(3953, Priority.HIGH) + ), + // todo correct the projectile locations (they should originate from the range or mage gnome, not the center). not sure how to do this. + SwitchAttack( + CombatStyle.RANGE.swingHandler, + Animation(3954, Priority.HIGH), + null, + null, + Projectile.create( + null as Entity?, + null, + 10, //bronze arrow + 35, + 30, + 10, + 50, + 14, + 255 + ) + ), + // per wiki source above, spell should be water strike with the sounds of ice barrage + SwitchAttack( + CombatStyle.MAGIC.swingHandler, + Animation(3955, Priority.HIGH), + null, + null, + Projectile.create( + null as Entity?, + null, + 94, //water strike + 35, + 30, + 10, + 50, + 14, + 255 + ) + ) + ) + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + return combatHandler + } +} + +// Handles Gnome Archer behavior +class GnomeArcherNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GnomeArcherNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GNOME_ARCHER_3814) + } + + override fun init() { + super.init() + this.isRespawn = false + this.isAggressive = true + this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() { + override fun ignoreCombatLevelDifference(): Boolean { + return true + } + }) + } +} + +class GnomeArcherBehavior : NPCBehavior(NPCs.GNOME_ARCHER_3814) { + override fun onCreation(self: NPC) { + // stops the entity from instantly moving. + delayEntity(self, 1) + setAttribute(self, "despawn-time", GameWorld.ticks + 25) + } + + override fun tick(self: NPC): Boolean { + if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks)) + self.clear() + return true + } +} + +// Handles Gnome Mage behavior +class GnomeMageNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GnomeMageNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GNOME_MAGE_3816) + } + + override fun init() { + super.init() + this.isRespawn = false + this.isAggressive = true + this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() { + override fun ignoreCombatLevelDifference(): Boolean { + return true + } + }) + } +} + +class GnomeMageBehavior : NPCBehavior(NPCs.GNOME_MAGE_3816) { + override fun onCreation(self: NPC) { + // stops the entity from instantly moving. + delayEntity(self, 1) + setAttribute(self, "despawn-time", GameWorld.ticks + 25) + } + + override fun tick(self: NPC): Boolean { + if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks)) + self.clear() + return true + } +} + +// Handles Gnome Driver behavior +class GnomeDriverNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GnomeDriverNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GNOME_DRIVER_3815) + } + + override fun init() { + super.init() + this.isRespawn = false + this.isAggressive = true + this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() { + override fun ignoreCombatLevelDifference(): Boolean { + return true + } + }) + } +} + +class GnomeDriverBehavior : NPCBehavior(NPCs.GNOME_DRIVER_3815) { + override fun onCreation(self: NPC) { + // stops the entity from instantly moving. + delayEntity(self, 1) + setAttribute(self, "despawn-time", GameWorld.ticks + 25) + } + + override fun tick(self: NPC): Boolean { + if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks)) + self.clear() + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/handlers/RasoloNPC.java b/Server/src/main/content/region/kandarin/handlers/RasoloNPC.java deleted file mode 100644 index b75407700..000000000 --- a/Server/src/main/content/region/kandarin/handlers/RasoloNPC.java +++ /dev/null @@ -1,84 +0,0 @@ -package content.region.kandarin.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -@Initializable -public class RasoloNPC extends OptionHandler { - @Override - public Plugin newInstance(Object arg) throws Throwable { - new RosaloDialouge().init(); - NPCDefinition.forId(1972).getHandlers().put("option:talk-to",this); - NPCDefinition.forId(1972).getHandlers().put("option:trade",this); - return null; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if(option.equals("trade")){ - new NPC(1972).openShop(player); - } else { - player.getDialogueInterpreter().open(1972); - } - return false; - } - - /** - * Rasolo npc - * @author ceik - */ - - public class RosaloDialouge extends DialoguePlugin{ - public RosaloDialouge(){ - /** - * - */ - } - public RosaloDialouge(Player player){super(player);} - - @Override - public DialoguePlugin newInstance(Player player) { - return new RosaloDialouge(player); - } - - @Override - public boolean open(Object... args){ - npc("Hello, would you like to see my wares?"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch(stage){ - case 0: - player.getDialogueInterpreter().sendOptions("Select one", "Yes, please", "No, thanks"); - stage++; - break; - case 1: - switch(buttonId){ - case 1: - end(); - new NPC(1972).openShop(player); - break; - case 2: - player("No, thanks"); - stage++; - break; - } - break; - case 2: - end(); - break; - } - return true; - } - @Override - public int[] getIds() {return new int[] {1972};} - } -} diff --git a/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt b/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt index 1fd390535..b1233b698 100644 --- a/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt +++ b/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt @@ -1,115 +1,101 @@ package content.region.kandarin.pisc.dialogue -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player +import core.ServerConstants +import core.api.addItemOrDrop +import core.api.hasActivatedSecondaryBankAccount +import core.api.hasIronmanRestriction +import core.api.isUsingSecondaryBankAccount +import core.api.openBankAccount +import core.api.openBankPinSettings +import core.api.openGrandExchangeCollectionBox +import core.api.openNpcShop +import core.api.toggleBankAccount +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable +import core.game.node.item.Item import org.rs09.consts.Items import org.rs09.consts.NPCs -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE -/** - * Provides the regular dialogue for Arnold Lydspor. - * TODO: Swan Song quest will need a special case handling. - * - * @author vddCore - */ -@Initializable -class ArnoldLydsporDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - START_DIALOGUE -> npcl( - FacialExpression.FRIENDLY, - "Ah, you come back! What you want from Arnold, heh?" - ).also { stage++ } - - 1 -> showTopics( - IfTopic( - FacialExpression.ASKING, - "Can you open my bank account, please?", - 2, - !hasIronmanRestriction(player, IronmanMode.ULTIMATE) - ), - - IfTopic( - FacialExpression.NEUTRAL, - "I'd like to check my bank PIN settings.", - 3, - !hasIronmanRestriction(player, IronmanMode.ULTIMATE) - ), - IfTopic( - FacialExpression.NEUTRAL, - "I'd like to collect items.", - 4, - !hasIronmanRestriction(player, IronmanMode.ULTIMATE) - ), - Topic(FacialExpression.ASKING, "Would you like to trade?", 5), - Topic(FacialExpression.FRIENDLY, "Nothing, I just came to chat.", 7) - ) - - 2 -> { - openBankAccount(player) - end() - } - - 3 -> { - openBankPinSettings(player) - end() - } - - 4 -> { - openGrandExchangeCollectionBox(player) - end() - } - - 5 -> npcl( - FacialExpression.FRIENDLY, - "Ja, I have wide range of stock..." - ).also { stage++ } - - 6 -> { - openNpcShop(player, NPCs.ARNOLD_LYDSPOR_3824) - end() - } - - 7 -> npcl(FacialExpression.FRIENDLY, - "Ah, that is nice - always I like to chat, but " + - "Herr Caranos tell me to get back to work! " + - "Here, you been nice, so have a present." - ).also { stage++ } - - 8 -> sendItemDialogue( - player, - Items.CABBAGE_1965, - "Arnold gives you a cabbage." - ).also { - addItemOrDrop(player, Items.CABBAGE_1965) - stage++ - } - - 9 -> playerl( - FacialExpression.HALF_THINKING, - "A cabbage?" - ).also { stage++ } - - 10 -> npcl( - FacialExpression.HAPPY, - "Ja, cabbage is good for you!" - ).also { stage++ } - - 11 -> playerl( - FacialExpression.NEUTRAL, - "Um... Thanks!" - ).also { stage = END_DIALOGUE } +class ArnoldLydsporDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.ARNOLD_LYDSPOR_3824, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, ArnoldLydsporLabellerFile(), node as NPC) + return@on true + } + on(NPCs.ARNOLD_LYDSPOR_3824, IntType.NPC, "bank") { player, _ -> + openBankAccount(player) + return@on true + } + on(NPCs.ARNOLD_LYDSPOR_3824, IntType.NPC, "collect") { player, _ -> + openGrandExchangeCollectionBox(player) + return@on true } - - return true } - override fun getIds(): IntArray = intArrayOf(NPCs.ARNOLD_LYDSPOR_3824) -} \ No newline at end of file + class ArnoldLydsporLabellerFile : DialogueLabeller() { + override fun addConversation() { + npc(ChatAnim.FRIENDLY, "Ah, you come back! What you want from Arnold, heh?") + goto("main options") + + label("main options") + options( + DialogueOption("access", "Can you open my bank account please?", expression = ChatAnim.ASKING) { player, _ -> !hasIronmanRestriction(player, IronmanMode.ULTIMATE) }, + DialogueOption("buy second bank", "I'd like to open a secondary bank account.") { player, _ -> return@DialogueOption !hasIronmanRestriction(player, IronmanMode.ULTIMATE) && 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.", expression = ChatAnim.NEUTRAL) { player, _ -> !hasIronmanRestriction(player, IronmanMode.ULTIMATE) }, + DialogueOption("collect", "I'd like to collect items.", expression = ChatAnim.NEUTRAL) { player, _ -> !hasIronmanRestriction(player, IronmanMode.STANDARD) }, + DialogueOption("trade", "Would you like to trade?", expression = ChatAnim.ASKING), + DialogueOption("chat", "Nothing, I just came to chat.", expression = ChatAnim.FRIENDLY) { player, _ -> return@DialogueOption hasIronmanRestriction(player, IronmanMode.STANDARD) || !ServerConstants.SECOND_BANK } + ) + + label("access") + exec { player, _ -> openBankAccount(player) } + goto("nowhere") + + label("buy second bank") + npc(ChatAnim.GUILTY, "I'm so sorry! My little shop does not have the fibre-optic connections to be able to open a second account. Try asking again in a brick-and-mortar building in a big city!") + player(ChatAnim.FRIENDLY, "That's okay, I understand. I will ask again in a big bank like Varrock's.") + goto("nowhere") + + 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("trade") + npc(ChatAnim.FRIENDLY, "Ja, I have wide range of stock...") + exec { player, _ -> openNpcShop(player, NPCs.ARNOLD_LYDSPOR_3824) } + goto("nowhere") + + label("chat") + npc("Ah, that is nice - always I like to chat, but Herr Caranos tell me to get back to work! Here, you been nice, so have a present.") + exec { player, _ -> addItemOrDrop(player, Items.CABBAGE_1965) } + item(Item(Items.CABBAGE_1965), "Arnold gives you a cabbage.") + player(ChatAnim.HALF_THINKING, "A cabbage?") + npc(ChatAnim.HAPPY, "Ja, cabbage is good for you!") + player(ChatAnim.NEUTRAL, "Um... Thanks!") + goto("nowhere") + } + } +} diff --git a/Server/src/main/content/region/kandarin/pisc/handlers/SeaweedNetHandler.kt b/Server/src/main/content/region/kandarin/pisc/handlers/SeaweedNetHandler.kt index b33c40068..4ae4df149 100644 --- a/Server/src/main/content/region/kandarin/pisc/handlers/SeaweedNetHandler.kt +++ b/Server/src/main/content/region/kandarin/pisc/handlers/SeaweedNetHandler.kt @@ -9,12 +9,13 @@ import org.rs09.consts.Animations import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType +import content.data.Quests class SeaweedNetHandler : InteractionListener { override fun defineListeners() { on(NET, IntType.SCENERY, "Take-from"){ player, node -> - if (!isQuestComplete(player, "Swan Song")) + if (!isQuestComplete(player, Quests.SWAN_SONG)) { sendMessage(player, "You must complete Swan Song first.") } diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofDialogue.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofDialogue.java index a43f229ad..ae586a3f3 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofDialogue.java @@ -1,5 +1,6 @@ package content.region.kandarin.quest.dwarfcannon; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; @@ -44,7 +45,7 @@ public class CaptainLawgofDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(DwarfCannon.NAME); + quest = player.getQuestRepository().getQuest(Quests.DWARF_CANNON); switch (quest.getStage(player)) { case 80: player("Hi."); diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java index 37af05ec3..8268ced31 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java @@ -8,6 +8,7 @@ import core.plugin.ClassScanner; import core.game.node.entity.skill.Skills; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents the dwarf cannon quest. @@ -15,12 +16,6 @@ import static core.api.ContentAPIKt.*; */ @Initializable public class DwarfCannon extends Quest { - - /** - * The name of this quest. - */ - public static final String NAME = "Dwarf Cannon"; - /** * The dwarf remain item. */ @@ -40,13 +35,13 @@ public class DwarfCannon extends Quest { * The mould item. */ public static final Item MOULD = new Item(4); - public static int[] railVarbits = new int[] { 2240, 2241, 2242, 2243, 2244, 2245 }; + public static int[] railVarbits = new int[] { 2240, 2241, 2242, 2243, 2244, 2245 }; /** * Constructs a new {@Code DwarfCannon} {@Code Object} */ public DwarfCannon() { - super(NAME, 49, 48, 1); + super(Quests.DWARF_CANNON, 49, 48, 1); } @Override diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannonPlugin.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannonPlugin.java index 7739d88cf..309895c76 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannonPlugin.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannonPlugin.java @@ -1,5 +1,6 @@ package content.region.kandarin.quest.dwarfcannon; +import content.data.Quests; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; import core.game.component.ComponentDefinition; @@ -17,7 +18,6 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.tools.Log; -import core.tools.SystemLogger; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; @@ -61,14 +61,14 @@ public class DwarfCannonPlugin extends OptionHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - final Quest quest = player.getQuestRepository().getQuest(DwarfCannon.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.DWARF_CANNON); if (quest.getStage(player) > 50) { player.getDialogueInterpreter().sendDialogues(player, null, "This should work nicely now that I've fixed it."); return true; } //setVarp(player, 1, 2041, true); setVarp(player, 0, 8, true); - player.getQuestRepository().getQuest(DwarfCannon.NAME).setStage(player, 60); + player.getQuestRepository().getQuest(Quests.DWARF_CANNON).setStage(player, 60); player.sendMessage("Well done! You've fixed the cannon! Better go and tell Captain Lawgof."); /* * Component component = new Component(409); @@ -85,7 +85,7 @@ public class DwarfCannonPlugin extends OptionHandler { @Override public boolean handle(final Player player, final Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest(DwarfCannon.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.DWARF_CANNON); switch (node.getId()) { case 3: if (!node.getLocation().equals(new Location(3015, 3453, 0))) { @@ -309,7 +309,7 @@ public class DwarfCannonPlugin extends OptionHandler { //setVarp(player, 1, 2041, true); //setVarp(player, 0, 8, true); - player.getQuestRepository().getQuest(DwarfCannon.NAME).setStage(player, 60); + player.getQuestRepository().getQuest(Quests.DWARF_CANNON).setStage(player, 60); player.sendMessage("Well done! You've fixed the cannon! Better go and tell Captain Lawgof."); GameWorld.getPulser().submit(new Pulse(5, player) { @Override diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/LollkDialogue.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/LollkDialogue.java index 11cc1886d..08cedded9 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/LollkDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/LollkDialogue.java @@ -1,5 +1,6 @@ package content.region.kandarin.quest.dwarfcannon; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -41,7 +42,7 @@ public class LollkDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(DwarfCannon.NAME); + quest = player.getQuestRepository().getQuest(Quests.DWARF_CANNON); switch (quest.getStage(player)) { case 40: npc("Thank the heavens, you saved me!", "I thought I'd be goblin lunch for sure!"); diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionDialogue.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionDialogue.java index be7fa1847..05ac29b60 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionDialogue.java @@ -1,5 +1,6 @@ package content.region.kandarin.quest.dwarfcannon; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -49,7 +50,7 @@ public class NulodionDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(DwarfCannon.NAME); + quest = player.getQuestRepository().getQuest(Quests.DWARF_CANNON); switch (quest.getStage(player)) { case 70: player("Hello there."); diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DwarfMultiCannonPlugin.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DwarfMultiCannonPlugin.java index 736bb57eb..0fbe40125 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DwarfMultiCannonPlugin.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DwarfMultiCannonPlugin.java @@ -12,6 +12,7 @@ import core.game.node.item.Item; import core.plugin.Plugin; import core.plugin.Initializable; import core.plugin.ClassScanner; +import content.data.Quests; /** * Handles the Dwarf multi-cannon. @@ -73,7 +74,7 @@ public final class DwarfMultiCannonPlugin extends OptionHandler { player.getPacketDispatch().sendMessage("You don't have all the cannon components!"); return true; } - if (!player.getQuestRepository().isComplete("Dwarf Cannon") && player.getDetails().getRights() != Rights.ADMINISTRATOR) { + if (!player.getQuestRepository().isComplete(Quests.DWARF_CANNON) && player.getDetails().getRights() != Rights.ADMINISTRATOR) { player.getPacketDispatch().sendMessage("You have to complete the Dwarf Cannon to know how to use this."); return true; } diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/BonzoDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/BonzoDialogue.java index 9a62eb53b..8ed55ae9e 100644 --- a/Server/src/main/content/region/kandarin/quest/fishingcontest/BonzoDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/BonzoDialogue.java @@ -6,6 +6,7 @@ import core.plugin.Initializable; import core.game.node.entity.player.Player; import core.game.activity.ActivityManager; import core.game.dialogue.DialoguePlugin; +import content.data.Quests; @Initializable @@ -126,7 +127,7 @@ public final class BonzoDialogue extends DialoguePlugin { player.getDialogueInterpreter().sendDialogue("You are given the Hemenester fishing trophy!"); player.getInventory().add(FishingContest.FISHING_TROPHY); player.getInventory().remove(FishingContest.RAW_GIANT_CARP); - player.getQuestRepository().setStage(QuestRepository.getQuests().get("Fishing Contest"),20); + player.getQuestRepository().setStage(QuestRepository.getQuests().get(Quests.FISHING_CONTEST),20); stage = 100; break; } diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/DwarfDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/DwarfDialogue.java index a48682faa..6e56d58e5 100644 --- a/Server/src/main/content/region/kandarin/quest/fishingcontest/DwarfDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/DwarfDialogue.java @@ -6,6 +6,7 @@ import core.game.node.item.GroundItemManager; import core.plugin.Initializable; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; +import content.data.Quests; @Initializable public class DwarfDialogue extends DialoguePlugin { @@ -22,7 +23,12 @@ public class DwarfDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - int questStage = player.getQuestRepository().getStage("Fishing Contest"); + int questStage = player.getQuestRepository().getStage(Quests.FISHING_CONTEST); + if(player.getQuestRepository().getStage(Quests.FISHING_CONTEST) == 100){ + npc(FacialExpression.OLD_NORMAL,"Welcome, oh great fishing champion!","Feel free to pop by and use","our tunnel any time!"); + stage = 2500; + return true; + } if((questStage < 20 && questStage > 0) && !player.getInventory().containsItem(FishingContest.FISHING_PASS)){ player("I lost my fishing pass..."); stage = 1000; @@ -33,16 +39,11 @@ public class DwarfDialogue extends DialoguePlugin { stage = 2000; return true; } - if(player.getQuestRepository().getStage("Fishing Contest") >= 10 && !player.getAttribute("fishing_contest:won",false)){ + if(player.getQuestRepository().getStage(Quests.FISHING_CONTEST) >= 10 && !player.getAttribute("fishing_contest:won",false)){ npc(FacialExpression.OLD_NORMAL,"Have you won yet?"); stage = 1500; return true; } - if(player.getQuestRepository().getStage("Fishing Contest") == 100){ - npc(FacialExpression.OLD_NORMAL,"Welcome, oh great fishing champion!","Feel free to pop by and use","our tunnel any time!"); - stage = 2500; - return true; - } npc(FacialExpression.OLD_NORMAL,"Hmph! What do you want?"); stage = 0; return true; @@ -172,7 +173,7 @@ public class DwarfDialogue extends DialoguePlugin { if(!player.getInventory().add(FishingContest.FISHING_PASS)){ GroundItemManager.create(FishingContest.FISHING_PASS,player.getLocation()); } - player.getQuestRepository().getQuest("Fishing Contest").start(player); + player.getQuestRepository().getQuest(Quests.FISHING_CONTEST).start(player); stage++; break; case 58: @@ -220,7 +221,7 @@ public class DwarfDialogue extends DialoguePlugin { stage++; break; case 2004: - player.getQuestRepository().getQuest("Fishing Contest").finish(player); + player.getQuestRepository().getQuest(Quests.FISHING_CONTEST).finish(player); player.getInventory().remove(FishingContest.FISHING_TROPHY); end(); break; diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContest.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContest.java index 1e87b8df7..9e01f552e 100644 --- a/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContest.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContest.java @@ -5,10 +5,11 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; +import content.data.Quests; @Initializable public class FishingContest extends Quest { - public FishingContest(){super("Fishing Contest",62,61,1,11,0,1,5);} + public FishingContest(){super(Quests.FISHING_CONTEST,62,61,1,11,0,1,5);} public static final Item FISHING_ROD = new Item(307); public static final Item FISHING_PASS = new Item(27); public static final Item RED_VINE_WORM = new Item(25); @@ -18,27 +19,50 @@ public class FishingContest extends Quest { public static final Item GARLIC = new Item(1550); public static final Item SPADE = new Item(952); + // Winner is stranger in black if you did not do the garlic. + // https://www.youtube.com/watch?v=6zL7M8mCL30 @Override public void drawJournal(Player player, int stage) { - int line = 11; + int line = 12; super.drawJournal(player, stage); - if(stage < 10) { - line(player, "I can start this quest by trying to take the !!shortcut??", line++); - line(player, "near !!White Wolf Mountain??", line++); - line(player,"I need level !!10 Fishing?? to start this quest.",line++,player.getSkills().getLevel(Skills.FISHING) >= 10); - } else if (stage >= 10){ - line(player,"The !!mountain Dwarves' home?? would be an ideal way to get across ",line++,stage >= 20); - line(player,"White Wolf Mountain safely. However, the Dwarves aren't too",line++, stage >= 20); - line(player,"fond of strangers. They will let you through if you can !!bring ",line++, stage >= 20); - line(player,"!!them a trophy.?? The trophy is the prize for the annual Hemenster",line++,stage >= 20); - line(player,"!!fishing competition.??",line++,stage >= 20); - if(stage == 20) - line(player,"I should return to !!Austri?? or !!Vestri??.",line++); - if(stage >= 100){ - line(player,"%%QUEST COMPLETE!&&",line++); + if (stage == 0) { + line(player, "I can start this quest by speaking to the !!Dwarves?? at the", line++); + line(player, "tunnel entrances on either side of !!White Wolf Mountain??.", line++); + line(player,"!!I must have level 10 fishing.??", line++, player.getSkills().getLevel(Skills.FISHING) >= 10); + } else if ( stage < 100 ) { + line(player, "The Dwarves will let me use the tunnel through White Wolf", line++, true); + line(player, "Mountain if I can will the Hemenster Fishing Competition.", line++, true); + + if (stage >= 20) { + line(player,"I easily won the contest by catching some Giant Carp.", line++, false); + } else if (stage >= 10) { + // https://youtu.be/z4MfANC2KqI + line(player, "They gave me a !!Fishing Contest Pass?? to enter the contest.", line++, false); + line(player, "I need to bring them back the !!Hemenster Fishing Trophy??.", line++, false); } + + if (stage >= 100) { + + } else if (stage >= 20) { + // https://youtu.be/rysJl-DRihE + line(player, "I should take back the !!Trophy?? back to the !!Dwarf?? at the side of", line++, false); + line(player, "!!White Wolf Mountain?? and claim my !!reward??.", line++, false); + } + + } else { + // https://youtu.be/u5Osw_jas4A + line(player, "The Dwarves' wanted me to earn their friendship by winning", line++, true); + line(player, "the Hemenster Fishing Competition.", line++, true); + line(player, "I scared away a vampyre with some garlic and easily won the", line++, true); + line(player, "contest by catching some Giant Carp.", line++, true); + line++; + line(player,"%%QUEST COMPLETE!&&", line++); + line++; + line(player, "As a reward for getting the Fishing Competition Trophy the", line++, false); + line(player, "Dwarves will let me use their tunnel to travel quickly and", line++, false); + line(player, "safely under White Wolf Mountain anytime I wish.", line++, false); } } @@ -47,9 +71,10 @@ public class FishingContest extends Quest { int ln = 10; super.finish(player); player.getPacketDispatch().sendItemZoomOnInterface(FISHING_TROPHY.getId(), 230, 277, 5); + // https://youtu.be/8dK362LbYdE drawReward(player,"1 Quest Point",ln++); - drawReward(player,"2437 Fishing XP.",ln++); - drawReward(player,"Access to the White Wolf Mountain shortcut.",ln); + drawReward(player,"2437 Fishing XP",ln++); + drawReward(player,"Access to Tunnel shortcut",ln); player.removeAttribute("fishing_contest:garlic"); player.removeAttribute("fishing_contest:won"); player.removeAttribute("fishing_contest:pass-shown"); diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/GarlicPipeInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/GarlicPipeInteraction.java index ec13a470a..5771f2dab 100644 --- a/Server/src/main/content/region/kandarin/quest/fishingcontest/GarlicPipeInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/GarlicPipeInteraction.java @@ -13,6 +13,7 @@ import core.plugin.Plugin; import org.rs09.consts.Items; import core.game.interaction.PluginInteraction; import core.game.interaction.PluginInteractionManager; +import content.data.Quests; @Initializable public class GarlicPipeInteraction extends PluginInteraction { @@ -31,7 +32,7 @@ public class GarlicPipeInteraction extends PluginInteraction { Scenery usedWith = event.getUsedWith().asScenery(); Item used = event.getUsedItem(); - if(used.getId() == Items.GARLIC_1550 && usedWith.getId() == 41 && usedWith.getLocation().equals(Location.create(2638, 3446, 0)) && player.getQuestRepository().getStage("Fishing Contest") > 0){ + if(used.getId() == Items.GARLIC_1550 && usedWith.getId() == 41 && usedWith.getLocation().equals(Location.create(2638, 3446, 0)) && player.getQuestRepository().getStage(Quests.FISHING_CONTEST) > 0){ player.getPulseManager().run(new MovementPulse(player, usedWith.getLocation().transform(0, -1, 0)) { @Override public boolean pulse() { diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/GateInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/GateInteraction.java index 96829b11d..7a79ca976 100644 --- a/Server/src/main/content/region/kandarin/quest/fishingcontest/GateInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/GateInteraction.java @@ -9,6 +9,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.game.interaction.PluginInteraction; import core.game.interaction.PluginInteractionManager; +import content.data.Quests; @Initializable public class GateInteraction extends PluginInteraction { @@ -27,11 +28,11 @@ public class GateInteraction extends PluginInteraction { } public boolean handleGate(Player player, Node node){ - if(!player.getAttribute("fishing_contest:pass-shown",false) || player.getQuestRepository().getStage("Fishing Contest") < 10) { + if(!player.getAttribute("fishing_contest:pass-shown",false) || player.getQuestRepository().getStage(Quests.FISHING_CONTEST) < 10) { player.getPulseManager().run(new MovementPulse(player, node.asScenery().getLocation().transform(1, 0, 0)) { @Override public boolean pulse() { - if(player.getQuestRepository().getStage("Fishing Contest") >= 10){ + if(player.getQuestRepository().getStage(Quests.FISHING_CONTEST) >= 10){ player.sendMessage("You should give your pass to Morris."); } else { player.sendMessage("You need a fishing pass to fish here."); diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java index c4ca0001b..97daa72b1 100644 --- a/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java @@ -11,12 +11,13 @@ import core.plugin.Plugin; import core.game.interaction.PluginInteraction; import core.game.interaction.PluginInteractionManager; import core.game.world.repository.Repository; +import content.data.Quests; @Initializable public class StairInteraction extends PluginInteraction { @Override public boolean handle(Player player, Node node) { - if(!player.getQuestRepository().isComplete("Fishing Contest")) { + if(!player.getQuestRepository().isComplete(Quests.FISHING_CONTEST)) { Scenery object = node.asScenery(); switch (object.getId()) { case 57: diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/VineInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/VineInteraction.java index 50d7e4a53..17ceb597a 100644 --- a/Server/src/main/content/region/kandarin/quest/fishingcontest/VineInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/VineInteraction.java @@ -10,6 +10,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.game.interaction.PluginInteraction; import core.game.interaction.PluginInteractionManager; +import content.data.Quests; @Initializable public class VineInteraction extends PluginInteraction { @@ -24,7 +25,7 @@ public class VineInteraction extends PluginInteraction { @Override public boolean handle(Player player, Node node) { if(node instanceof Scenery){ - if(player.getQuestRepository().getStage("Fishing Contest") > 0 && player.getQuestRepository().getStage("Fishing Contest") < 100){ + if(player.getQuestRepository().getStage(Quests.FISHING_CONTEST) > 0 && player.getQuestRepository().getStage(Quests.FISHING_CONTEST) < 100){ player.getPulseManager().run(new MovementPulse(player, node.asScenery().getLocation().transform(0, 0, 0)) { @Override public boolean pulse() { diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/AnitaDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/AnitaDialogue.kt index 00aa223ea..4d9c58b1c 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/AnitaDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/AnitaDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.grandtree +import content.data.Quests import core.api.addItemOrDrop import core.api.getQuestStage import core.api.sendDialogue @@ -10,7 +11,7 @@ import org.rs09.consts.Items class AnitaDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - when(getQuestStage(player!!, TheGrandTree.questName)){ + when(getQuestStage(player!!, Quests.THE_GRAND_TREE)){ 60 -> { if(player!!.hasItem(Item(Items.GLOUGHS_KEY_788)) && stage < 12){ when(stage){ diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonCutscene.kt b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonCutscene.kt index 2afe9f1cf..186d85616 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonCutscene.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonCutscene.kt @@ -1,20 +1,16 @@ package content.region.kandarin.quest.grandtree -import content.region.misthalin.dorgeshuun.quest.thelosttribe.LostTribeCutscene import core.ServerConstants -import core.api.openDialogue import core.api.sendChat -import core.api.sendDialogue -import core.api.sendMessage import core.game.activity.Cutscene import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.game.world.map.Direction import core.game.world.map.Location -import core.game.global.action.DoorActionHandler -import core.game.node.entity.npc.NPC +import core.game.world.update.flag.context.Animation import org.rs09.consts.NPCs +// Source video https://www.youtube.com/watch?v=LS1Xtuz0sLA class BlackDemonCutscene(player: Player) : Cutscene(player) { override fun setup() { setExit(Location.create(2491, 9864, 0)) @@ -23,7 +19,6 @@ class BlackDemonCutscene(player: Player) : Cutscene(player) { } loadRegion(9882) addNPC(NPCs.GLOUGH_671, 48, 8, Direction.WEST) - addNPC(NPCs.BLACK_DEMON_677, 43, 9, Direction.EAST) } override fun runStage(stage: Int) { @@ -40,6 +35,8 @@ class BlackDemonCutscene(player: Player) : Cutscene(player) { rotateCamera(0, 0) sendChat(player, "Hello?") player.face(getNPC(NPCs.GLOUGH_671)!!) + addNPC(NPCs.BLACK_DEMON_677, 39, 8, Direction.EAST) + move(getNPC(NPCs.BLACK_DEMON_677)!!, 41, 8) timedUpdate(3) } 2 -> { @@ -53,7 +50,7 @@ class BlackDemonCutscene(player: Player) : Cutscene(player) { timedUpdate(10) } 4 -> { - moveCamera(55, 4,2000) + moveCamera(55, 4, 2000) rotateCamera(55, 6) timedUpdate(4) } @@ -62,24 +59,27 @@ class BlackDemonCutscene(player: Player) : Cutscene(player) { playerDialogueUpdate(FacialExpression.SCARED, "Glough?") } 6 -> { - dialogueUpdate(NPCs.GLOUGH_671, FacialExpression.ANGRY, "You really are becoming a headache! Well, at least now you can die knowing you were right, it will save me having to hunt you down like all the other human filth of " + ServerConstants.SERVER_NAME + "!") + dialogueUpdate(NPCs.GLOUGH_671, FacialExpression.OLD_ANGRY1, "You really are becoming a headache! Well, at least now you can die knowing you were right, it will save me having to hunt you down like all the other human filth of " + ServerConstants.SERVER_NAME + "!") } 7 -> { playerDialogueUpdate(FacialExpression.SCARED, "You're crazy, Glough!") } 8 -> { - dialogueUpdate(NPCs.GLOUGH_671, FacialExpression.ANGRY, "Bah! Well, soon you'll see, the gnomes are ready to fight. In three weeks this tree will be dead wood, in ten weeks it will be 30 battleships! Finally we will rid the world of the disease called humanity!") + dialogueUpdate(NPCs.GLOUGH_671, FacialExpression.OLD_ANGRY1, "Bah! Well, soon you'll see, the gnomes are ready to fight. In three weeks this tree will be dead wood, in ten weeks it will be 30 battleships! Finally we will rid the world of the disease called humanity!") } 9 -> { playerDialogueUpdate(FacialExpression.SCARED, "What makes you think I'll let you get away with it?") } 10 -> { - moveCamera(47,9) - rotateCamera(40, 9) - dialogueUpdate(NPCs.GLOUGH_671, FacialExpression.ANGRY, "Fool...meet my little friend!") + moveCamera(55, 8, 700) + rotateCamera(48, 8) + val demon = getNPC(NPCs.BLACK_DEMON_677)!! + move(demon, 46, 8) + demon.animate(Animation(64, 140)) + dialogueUpdate(NPCs.GLOUGH_671, FacialExpression.OLD_ANGRY1, "Fool...meet my little friend!") } 11 -> { - end{ + end(fade = false){ BlackDemonNPC(NPCs.BLACK_DEMON_677, Location.create(2485, 9864, 0)).init() } } diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonNPC.kt b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonNPC.kt index cdefc97de..112321405 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonNPC.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonNPC.kt @@ -1,13 +1,11 @@ package content.region.kandarin.quest.grandtree -import content.region.kandarin.quest.grandtree.TheGrandTree.Companion.questName +import content.data.Quests import core.api.* import core.game.node.entity.Entity import core.game.node.entity.npc.AbstractNPC -import core.game.node.entity.player.Player import core.game.world.map.Location import core.plugin.Initializable -import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.interaction.InteractionListener @@ -28,7 +26,7 @@ class BlackDemonNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,lo override fun finalizeDeath(killer: Entity?) { // In the event that this npcID is used somewhere else... if(killer!!.asPlayer().location.regionId == 9882) { - setQuestStage(killer!!.asPlayer(), questName, 98) + setQuestStage(killer!!.asPlayer(), Quests.THE_GRAND_TREE, 98) this.isRespawn = false } super.finalizeDeath(killer) diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/CaptainErrdoDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/CaptainErrdoDialogue.kt index 1460a7399..2b225bb47 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/CaptainErrdoDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/CaptainErrdoDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.grandtree +import content.data.Quests import content.global.travel.glider.Gliders import core.api.getQuestStage import core.api.teleport @@ -10,7 +11,7 @@ import core.tools.END_DIALOGUE class CaptainErrdoDialogue: DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - when(getQuestStage(player!!, TheGrandTree.questName)){ + when(getQuestStage(player!!, Quests.THE_GRAND_TREE)){ 55 -> { if(player!!.location.regionId == 11567){ when(stage){ diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/CharlieDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/CharlieDialogue.kt index 1a70f14f6..d4af65825 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/CharlieDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/CharlieDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.quest.grandtree -import content.region.kandarin.quest.grandtree.TheGrandTree.Companion.questName +import content.data.Quests import core.ServerConstants import core.api.* import core.game.dialogue.DialogueFile @@ -18,7 +18,7 @@ import org.rs09.consts.NPCs class CharlieDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, questName)) { + when (getQuestStage(player!!, Quests.THE_GRAND_TREE)) { 46 -> { when (stage) { 0 -> playerl("Tell me. Why would you want to kill the Grand Tree?").also { stage++ } @@ -32,7 +32,7 @@ class CharlieDialogue : DialogueFile() { 8 -> npcl("I don't know what he's up to. If you want to find out, you'd better search his home.").also { stage++ } 9 -> playerl("OK. Thanks Charlie.").also { stage++ } 10 -> npcl("Good luck!").also { - setQuestStage(player!!, questName, 47) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 47) stage = END_DIALOGUE } } @@ -79,7 +79,7 @@ class CharlieDialogue : DialogueFile() { 8 -> { unlock(player!!) npc.clear() - setQuestStage(player!!, questName, 55) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 55) return true } } @@ -96,13 +96,13 @@ class CharlieDialogue : DialogueFile() { 0 -> playerl("How are you doing, Charlie?").also { stage++ } 1 -> npcl("I've been better.").also { stage++ } 2 -> playerl("Glough has some plan to rule ${ServerConstants.SERVER_NAME}!").also { stage++ } - 3 -> npcl("I wouldn't put it past him, the Gnome's crazy!").also { stage++ } + 3 -> npcl("I wouldn't put it past him, the gnome's crazy!").also { stage++ } 4 -> playerl("I need some proof to convince the King.").also { stage++ } 5 -> npcl("Hmm...you could be in luck! Before Glough had me locked up I heard him mention that he'd left his chest keys at his girlfriend's.").also { stage++ } 6 -> playerl("Where does she live?").also { stage++ } 7 -> npcl("Just west of the toad swamp.").also { stage++ } 8 -> playerl("OK, I'll see what I can find.").also { - setQuestStage(player!!, questName, 60) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 60) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/ForemanNPC.kt b/Server/src/main/content/region/kandarin/quest/grandtree/ForemanNPC.kt index 56381b09e..1977a47b8 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/ForemanNPC.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/ForemanNPC.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.grandtree +import content.data.Quests import core.ServerConstants import core.api.* import core.game.component.Component @@ -38,7 +39,7 @@ class ForemanNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,locat } override fun finalizeDeath(killer: Entity?) { - if(getQuestStage(killer as Player, TheGrandTree.questName) == 55) { + if(getQuestStage(killer as Player, Quests.THE_GRAND_TREE) == 55) { sendMessage(killer,"The foreman drops a piece of paper as he dies.") produceGroundItem(killer, Items.LUMBER_ORDER_787, 1, this.location) } diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/GloughDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/GloughDialogue.kt index 5b538b8d4..496184c70 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/GloughDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/GloughDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.quest.grandtree -import content.region.kandarin.quest.grandtree.TheGrandTree.Companion.questName +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -15,7 +15,7 @@ import core.tools.END_DIALOGUE class GloughDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, questName)) { + when (getQuestStage(player!!, Quests.THE_GRAND_TREE)) { 40 -> { when (stage) { 0 -> playerl("Hello.").also { stage++ } @@ -28,7 +28,7 @@ class GloughDialogue : DialogueFile() { 7 -> npcl("I should've known! The humans are going to invade!").also { stage++ } 8 -> playerl("Never!").also { stage++ } 9 -> npcl("Your type can't be trusted! I'll take care of this! Go back to the King.").also { - setQuestStage(player!!, questName, 45) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 45) stage = END_DIALOGUE } } @@ -70,7 +70,7 @@ class GloughDialogue : DialogueFile() { } 8 -> { npc.clear() - setQuestStage(player!!, questName, 50) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 50) teleport(player!!, cell) player!!.unlock() return true diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/GrandTreeListeners.kt b/Server/src/main/content/region/kandarin/quest/grandtree/GrandTreeListeners.kt index 4c969c234..8548efe96 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/GrandTreeListeners.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/GrandTreeListeners.kt @@ -1,6 +1,5 @@ package content.region.kandarin.quest.grandtree -import content.region.kandarin.quest.grandtree.TheGrandTree.Companion.questName import core.api.* import core.game.interaction.IntType import core.game.interaction.InteractionListener @@ -15,6 +14,7 @@ import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.NPCs import org.rs09.consts.Sounds +import content.data.Quests class GrandTreeListeners: InteractionListener { @@ -90,7 +90,7 @@ class GrandTreeListeners: InteractionListener { } on(2444, IntType.SCENERY, "open"){ player, node -> - if(node.location == Location(2487,3464,2) && !isQuestComplete(player, questName)){ + if(node.location == Location(2487,3464,2) && !isQuestComplete(player, Quests.THE_GRAND_TREE)){ if(getAttribute(player, "/save:grandtree:twig1", false) && getAttribute(player, "/save:grandtree:twig2", false) && getAttribute(player, "/save:grandtree:twig3", false) && @@ -103,7 +103,10 @@ class GrandTreeListeners: InteractionListener { } on(2446, IntType.SCENERY, "open"){ player, node -> - if(node.location == Location(2463, 3497, 0) && isQuestComplete(player!!, questName)){ + if(node.location == Location(2463, 3497, 0) && isQuestComplete( + player!!, + Quests.THE_GRAND_TREE + )){ player.animator.animate(Animation(828)) // Go to tunnels teleport(player, Location(2464, 9897, 0)) @@ -115,8 +118,8 @@ class GrandTreeListeners: InteractionListener { SceneryBuilder.replace(Scenery(2436, Location(2482,3462,1)),Scenery(2437, Location(2482,3462,1)),2) sendDialogue(player,"You found a scroll!") addItemOrDrop(player, Items.INVASION_PLANS_794) - if(getQuestStage(player!!, questName) < 60) - setQuestStage(player!!, questName, 60) + if(getQuestStage(player!!, Quests.THE_GRAND_TREE) < 60) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 60) return@onUseWith true } onUseWith(IntType.SCENERY, Items.TWIGS_789, 2440){ player, used, with -> @@ -165,7 +168,7 @@ class GrandTreeListeners: InteractionListener { return@on true } on(2435, IntType.SCENERY, "search"){ player, _ -> - if(getQuestStage(player, questName) == 47){ + if(getQuestStage(player, Quests.THE_GRAND_TREE) == 47){ sendItemDialogue(player, Items.GLOUGHS_JOURNAL_785,"You've found Glough's Journal!") addItemOrDrop(player, Items.GLOUGHS_JOURNAL_785) } @@ -174,7 +177,7 @@ class GrandTreeListeners: InteractionListener { // Roots for Daconia rock on(32319, IntType.SCENERY, "search"){ player, node -> - if(getQuestStage(player, questName) < 99 || player.hasItem(Item(Items.DACONIA_ROCK_793))){ return@on true; } + if(getQuestStage(player, Quests.THE_GRAND_TREE) < 99 || player.hasItem(Item(Items.DACONIA_ROCK_793))){ return@on true; } // RNG for which root the rock is under if(node.location == roots[getAttribute(player,"grandtree:rock",1)]){ sendItemDialogue(player, Item(Items.DACONIA_ROCK_793), "You've found a Daconia rock!") @@ -192,7 +195,7 @@ class GrandTreeListeners: InteractionListener { return@on true } on(2451, IntType.SCENERY, "push"){ player, roots -> - if (hasRequirement(player, "The Grand Tree")) { + if (hasRequirement(player, Quests.THE_GRAND_TREE)) { val outsideMine = player.location == Location.create(2467, 9903, 0) || player.location == Location.create(2468, 9903, 0) if(outsideMine) { forceMove(player, player.location, player.location.transform(0, 2, 0), 25, 60, null, 819) diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/HazelmereDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/HazelmereDialogue.kt index 9edb39423..ab59a6ba6 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/HazelmereDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/HazelmereDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.quest.grandtree -import content.region.kandarin.quest.grandtree.TheGrandTree.Companion.questName +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.node.item.Item @@ -10,7 +10,7 @@ import org.rs09.consts.Items class HazelmereDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, questName)) { + when (getQuestStage(player!!, Quests.THE_GRAND_TREE)) { 10 -> { if(player!!.hasItem(Item(Items.BARK_SAMPLE_783))){ when (stage) { @@ -24,7 +24,7 @@ class HazelmereDialogue : DialogueFile() { if(removeItem(player!!, Items.BARK_SAMPLE_783)){ addItemOrDrop(player!!, Items.HAZELMERES_SCROLL_786) } - setQuestStage(player!!, questName, 20) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 20) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/KingNarnodeDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/KingNarnodeDialogue.kt index 333022cbd..5cbf67861 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/KingNarnodeDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/KingNarnodeDialogue.kt @@ -1,6 +1,6 @@ package content.region.kandarin.quest.grandtree -import content.region.kandarin.quest.grandtree.TheGrandTree.Companion.questName +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.interaction.MovementPulse @@ -67,7 +67,7 @@ class KingNarnodeDialogue : DialogueFile() { }) } override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, questName)) { + when (getQuestStage(player!!, Quests.THE_GRAND_TREE)) { 0 -> { when (stage) { 0 -> npcl("Welcome Traveller. I am King Narnode. It's nice to see an outsider.").also { stage++ } @@ -222,7 +222,7 @@ class KingNarnodeDialogue : DialogueFile() { 64 -> npcl("First, I must warn the tree guardians. Please, could you tell the chief tree guardian, Glough. He lives in a tree house just in front of the Grand Tree.").also { stage++ } 65 -> npcl("If he's not there he will be at his girlfriend Anita's place. Meet me back here once you've told him.").also { stage++ } 66 -> playerl("Ok! I'll be back soon.").also { - setQuestStage(player!!, questName, 40) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 40) stage = END_DIALOGUE } } @@ -231,7 +231,7 @@ class KingNarnodeDialogue : DialogueFile() { when(stage) { 0 -> npcl("Hello Traveller, did you speak to Glough?").also { stage++ } 1 -> playerl("Not yet.").also { stage++ } - 2 -> npcl("OK. He lives just in front of the Grand Tree. Let me know when you’ve talked to him.").also{ stage = END_DIALOGUE } + 2 -> npcl("OK. He lives just in front of the Grand Tree. Let me know when you've talked to him.").also{ stage = END_DIALOGUE } } } 45 -> { @@ -242,7 +242,7 @@ class KingNarnodeDialogue : DialogueFile() { 3 -> npcl("Yes Glough really knows what he's doing. The human has been detained until we know who else is involved. Maybe Glough was right, maybe humans are invading!").also { stage++ } 4 -> playerl("I doubt it, can I speak to the prisoner?").also { stage++ } 5 -> npcl("Certainly. He's on the top level of the tree. Be careful, it's a long way down!").also { - setQuestStage(player!!, questName, 46) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 46) stage = END_DIALOGUE } } @@ -255,7 +255,7 @@ class KingNarnodeDialogue : DialogueFile() { } 55 -> { when(stage){ - 0 -> playerl("King Nardone, I need to talk!").also { stage++ } + 0 -> playerl("King Narnode, I need to talk!").also { stage++ } 1 -> npcl("Traveller, what are you doing here? The stronghold has been put on full alert! It's not safe for you here!").also { stage++ } 2 -> playerl("Your Highness, I believe Glough is killing the trees in order to make a mass fleet of warships!").also { stage++ } 3 -> npcl("That's an absurd accusation!").also { stage++ } @@ -281,7 +281,7 @@ class KingNarnodeDialogue : DialogueFile() { } 7 -> npcl("On the other hand, if Glough's right about the humans we will need an army of gnomes to protect ourselves. ").also { stage++ } 8 -> npcl("So I've decided to allow Glough to raise a mighty gnome army. The Grand Tree's still slowly dying. If it is human sabotage we must respond!").also{ - setQuestStage(player!!, questName, 70) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 70) removeItem(player!!, Item(Items.INVASION_PLANS_794), Container.INVENTORY) stage = END_DIALOGUE } @@ -383,7 +383,7 @@ class KingNarnodeUnderGroundDialogue : DialogueFile() { }) } override fun handle(componentID: Int, buttonID: Int) { - when(getQuestStage(player!!, questName)) { + when(getQuestStage(player!!, Quests.THE_GRAND_TREE)) { 98 -> when (stage) { 0 -> npcl("Traveller, you're wounded! What happened?").also { stage++ } 1 -> playerl("It's Glough! He set a demon on me!").also { stage++ } @@ -398,7 +398,7 @@ class KingNarnodeUnderGroundDialogue : DialogueFile() { 10 -> sendNPCDialogue(player!!, NPCs.GNOME_GUARD_163,"Yes sir!").also{ stage++ } 11 -> npcl("You have my full apologies Traveller! And my gratitude! A reward will have to wait though, the tree is still dying!").also {stage++} 12 -> npcl("The guards are clearing Glough's rock supply now but there must be more Daconia hidden somewhere in the roots! Help us search, we have little time!").also { - setQuestStage(player!!, questName, 99) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 99) // position of the daconia rock if(getAttribute(player!!,"treegnome:rock",0) == 0){ val answer = (1..5).random() @@ -425,7 +425,7 @@ class KingNarnodeUnderGroundDialogue : DialogueFile() { 11 -> playerl("Strange!").also { stage++ } 12 -> npcl("That's magic trees for you! All the best Traveller and thanks again!").also { stage++ } 13 -> playerl("You too, Your Highness!").also { - finishQuest(player!!, questName) + finishQuest(player!!, Quests.THE_GRAND_TREE) removeItem(player!!, Items.DACONIA_ROCK_793) stage = END_DIALOGUE } @@ -494,7 +494,7 @@ class KingNarnodeUnderGroundDialogue : DialogueFile() { 31 -> if (player!!.inventory.freeSlots() >= 2) { npcl("Up here.") stage = END_DIALOGUE - setQuestStage(player!!, questName, 10) + setQuestStage(player!!, Quests.THE_GRAND_TREE, 10) addItemOrDrop(player!!, Items.BARK_SAMPLE_783) addItemOrDrop(player!!, Items.TRANSLATION_BOOK_784) leadUpLadder() @@ -504,4 +504,4 @@ class KingNarnodeUnderGroundDialogue : DialogueFile() { } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/ShipyardWorkerDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/ShipyardWorkerDialogue.kt index a2a1fa0ea..1487b306c 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/ShipyardWorkerDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/ShipyardWorkerDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.grandtree +import content.data.Quests import core.api.getAttribute import core.api.getQuestStage import core.api.setAttribute @@ -13,7 +14,7 @@ class ShipyardWorkerDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { when(stage){ 0 -> npcl("Hey you! What are you up to?").also { - if(getQuestStage(player!!, TheGrandTree.questName) == 55) { + if(getQuestStage(player!!, Quests.THE_GRAND_TREE) == 55) { setAttribute(player!!, "/save:grandtree:opt1", false) setAttribute(player!!, "/save:grandtree:opt2", false) setAttribute(player!!, "/save:grandtree:opt3", false) diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/TheGrandTree.kt b/Server/src/main/content/region/kandarin/quest/grandtree/TheGrandTree.kt index e8bff2b27..c1b74008f 100644 --- a/Server/src/main/content/region/kandarin/quest/grandtree/TheGrandTree.kt +++ b/Server/src/main/content/region/kandarin/quest/grandtree/TheGrandTree.kt @@ -1,15 +1,15 @@ package content.region.kandarin.quest.grandtree -import core.api.addItemOrDrop import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable -class TheGrandTree: Quest("The Grand Tree", 71, 70, 5, 150, 0, 1, 160) { +class TheGrandTree: Quest(Quests.THE_GRAND_TREE, 71, 70, 5, 150, 0, 1, 160) { override fun newInstance(`object`: Any?): Quest { return this } @@ -96,7 +96,4 @@ class TheGrandTree: Quest("The Grand Tree", 71, 70, 5, 150, 0, 1, 160) { } } - companion object { - const val questName = "The Grand Tree" - } } diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/AnnaDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/AnnaDialogue.kt new file mode 100644 index 000000000..7998584f2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/AnnaDialogue.kt @@ -0,0 +1,60 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonAnna +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class AnnaDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, AnnaDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return AnnaDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ANNA_814, NPCs.ANNA_6192) + } +} + +class AnnaDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "Oh really? What do you want to know then?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 6), + IfTopic("Do you recognise this thread?", 7, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 10, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("It was clearly an intruder.").also { stage++ } + 4 -> playerl("Well, I don't think it was.").also { stage++ } + 5 -> npcl("It was one of our lazy servants then.").also { stage = END_DIALOGUE } + + 6 -> npcl("In the library. No one else was there so you'll just have to take my word for it.").also { stage = END_DIALOGUE } + + 7 -> sendDialogue(player!!, "You show Anna the thread from the study.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1809)) stage++ else stage = 9 } + 8 -> npcl("It's some Green thread. It's not exactly uncommon is it? My trousers are made of the same material.").also { stage = END_DIALOGUE } + + 9 -> npcl("Not really, no. Thread is fairly common.").also { stage = END_DIALOGUE } + + 10 -> npcl(FacialExpression.ANNOYED, "That useless Gardener Stanford let his compost heap fester. It's an eyesore to the garden! So I bought some poison from a travelling salesman so that I could kill off some of the wildlife living in it.") + .also { setAttribute(player!!, attributeAskPoisonAnna, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/BobDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/BobDialogue.kt new file mode 100644 index 000000000..75455b51d --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/BobDialogue.kt @@ -0,0 +1,61 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonBob +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class BobDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, BobDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return BobDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.BOB_815, NPCs.BOB_6193) + } +} + +class BobDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("I suppose I had better talk to you then.").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 4), + IfTopic(FacialExpression.THINKING, "Do you recognise this thread?", 7, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 10, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("I don't really care as long as no one thinks it's me. Maybe it was that strange poison seller who headed towards the seers village.").also { stage = END_DIALOGUE } + + 4 -> npcl("I was walking by myself in the garden.").also { stage++ } + 5 -> playerl("And can anyone vouch for that?").also { stage++ } + 6 -> npcl("No. But I was.").also { stage = END_DIALOGUE } + + 7 -> sendDialogue(player!!, "You show him the thread you discovered.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1808)) stage++ else stage = 9 } + 8 -> npcl("It's some red thread. I suppose you think that's some kind of clue? It looks like the material my trousers are made of.").also { stage = END_DIALOGUE } + + 9 -> npcl(FacialExpression.THINKING, "It's some thread. Great clue. No, really.").also { stage = END_DIALOGUE } + + 10 -> npcl(FacialExpression.THINKING, "What's it to you anyway?").also { stage++ } + 11 -> npcl(FacialExpression.ANGRY, "If you absolutely must know, we had a problem with the beehive in the garden, and as all of our servants are so pathetically useless, I decided I would deal with it myself. So I did.") + .also { setAttribute(player!!, attributeAskPoisonBob, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/CarolDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/CarolDialogue.kt new file mode 100644 index 000000000..66b283ccf --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/CarolDialogue.kt @@ -0,0 +1,59 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonCarol +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class CarolDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, CarolDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return CarolDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.CAROL_816, NPCs.CAROL_6194) + } +} + +class CarolDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("Well, ask what you want to know then.").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 4), + IfTopic("Do you recognise this thread?", 5, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 8, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("I don't know. I think it's very convenient that you have arrived here so soon after it happened. Maybe it was you.").also { stage = END_DIALOGUE } + + 4 -> npcl("Why? Are you accusing me of something? You seem to have a very high opinion of yourself. I was in my room if you must know, alone.").also { stage = END_DIALOGUE } + + 5 -> sendDialogue(player!!, "You show Carol the thread found at the crime scene.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1808)) stage++ else stage = 7 } + 6 -> npcl("It's some red thread... it kind of looks like the Same material as my trousers. But obviously it's not.").also { stage = END_DIALOGUE } + + 7 -> npcl("It's some thread. Sorry, do you have a point here? Or do you just enjoy wasting peoples time?").also { stage = END_DIALOGUE } + + 8 -> npcl(FacialExpression.THINKING, "I don't see what on earth it has to do with you, but the drain outside was").also { stage++ } + 9 -> npcl(FacialExpression.ANNOYED, "blocked, and as nobody else here has the intelligence to even unblock a simple drain I felt I had to do it myself.") + .also { setAttribute(player!!, attributeAskPoisonCarol, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/DavidDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/DavidDialogue.kt new file mode 100644 index 000000000..b25862064 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/DavidDialogue.kt @@ -0,0 +1,60 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonDavid +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class DavidDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DavidDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return DavidDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DAVID_817, NPCs.DAVID_6195) + } +} + +class DavidDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("And? Make this quick. I have better things to do than be interrogated by halfwits all day.").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 5), + IfTopic("Do you recognise this thread?", 6, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 9, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("I don't really know or care. Frankly, the old man deserved to die.").also { stage++ } + 4 -> npcl("There was a suspicious red headed man who came to the house the other day selling poison now I think about it. Last I saw he was headed towards the tavern in the Seers village.").also { stage = END_DIALOGUE } + + 5 -> npcl("That is none of your business. Are we finished now, or are you just going to stand there irritating me with your idiotic questions all day?").also { stage = END_DIALOGUE } + + 6 -> sendDialogue(player!!, "You show him the thread you found on the study window.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1809)) stage++ else stage = 8 } + 7 -> npcl("It's some Green thread, like my trousers are made of. Are you finished? I'm not sure which I dislike more bout you, your face or your general bad odour.").also { stage = END_DIALOGUE } + + 8 -> npcl("No. Can I go yet? Your face irritates me.").also { stage = END_DIALOGUE } + + 9 -> npcl(FacialExpression.ANGRY, "There was a nest of spiders upstairs between the two servants' quarters. Obviously I had to kill them before our pathetic servants whined at my father some more.").also { stage++ } + 10 -> npcl(FacialExpression.THINKING, "Honestly, it's like they expect to be treated like royalty! If I had my way I would fire the whole workshy lot of them!") + .also { setAttribute(player!!, attributeAskPoisonDavid, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/DonovanDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/DonovanDialogue.kt new file mode 100644 index 000000000..28fae9bbf --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/DonovanDialogue.kt @@ -0,0 +1,57 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class DonovanDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DonovanDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return DonovanDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DONOVAN_THE_FAMILY_HANDYMAN_806) + } +} + +class DonovanDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("I have no interest in talking to gawkers.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl("I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 5), + IfTopic("Did you hear any suspicious noises at all?", 6, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 9, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("Oh... I really couldn't say. I wouldn't really want to point any fingers at anybody. If I had to make a guess I'd have to say it was probably Bob though.").also { stage++ } + 4 -> npcl("I saw him arguing with Lord Sinclair about some missing silverware from the Kitchen. It was a very heated argument.").also { stage = END_DIALOGUE } + + 5 -> npcl("Me? I was sound asleep here in the servants Quarters. It's very hard work as a handyman around here. There's always something to do!").also { stage = END_DIALOGUE } + + 6 -> npcl("Hmmm... No, I didn't, but I sleep very soundly at night.").also { stage++ } + 7 -> playerl("So you didn't hear any sounds of a struggle or any barking from the guard dog next to his study window?").also { stage++ } + 8 -> npcl("Now you mention it, no. It is odd I didn't hear anything like that. But I do sleep very soundly as I said and wouldn't necessarily have heard it if there was any such noise.").also { stage = END_DIALOGUE } + + 9 -> npcl("Well, I do know Frank bought some poison recently to clean the family crest that's outside.").also { stage++ } + 10 -> npcl("It's very old and rusty, and I couldn't clean it myself, so he said he would buy some cleaner and clean it himself. He probably just got some from that Poison Salesman who came to the door the other day...").also { stage++ } + 11 -> npcl("You'd really have to ask him though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} + diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/ElizabethDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/ElizabethDialogue.kt new file mode 100644 index 000000000..faacbbe69 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/ElizabethDialogue.kt @@ -0,0 +1,64 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonElizabeth +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ElizabethDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ElizabethDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return ElizabethDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ELIZABETH_818, NPCs.ELIZABETH_6196) + } +} + +class ElizabethDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("What's so important you need to bother me with then?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 4), + IfTopic("Do you recognise this thread?", 7, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 12, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("Could have been anyone. The old man was an idiot. He's been asking for it for years.").also { stage = END_DIALOGUE } + + 4 -> npcl("I was out.").also { stage++ } + 5 -> playerl("Care to be any more specific?").also { stage++ } + 6 -> npcl("Not really. I don't have to justify myself to the likes of you, you know. I know the King personally you know. Now are we finished here?").also { stage = END_DIALOGUE } + + 7 -> sendDialogue(player!!, "You show her the thread from the study window.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1810)) stage++ else stage = 11 } + 8 -> npcl(" Looks like Blue thread to me. If you can't work that out for yourself I don't hold much hope of you solving this crime.").also { stage++ } + 9 -> playerl("It looks a lot like the material your trousers are made of doesn't it?").also { stage++ } + 10 -> npcl("I suppose it does. So what?").also { stage = END_DIALOGUE } + + 11 -> npcl(" It's some thread. You're not very good at this whole investigation thing are you?").also { stage = END_DIALOGUE } + + 12 -> npcl("There was a nest of mosquitos under the fountain in the garden, which I killed with poison the other day. You can see for yourself if you're capable of managing that, which I somehow doubt.").also { stage++ } + 13 -> playerl(FacialExpression.ANNOYED, "I hate mosquitos!").also { stage++ } + 14 -> npcl("Doesn't everyone?") + .also { setAttribute(player!!, attributeAskPoisonElizabeth, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/FlypaperDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/FlypaperDialogue.kt new file mode 100644 index 000000000..952be8a03 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/FlypaperDialogue.kt @@ -0,0 +1,26 @@ +package content.region.kandarin.quest.murdermystery + +import core.api.* +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE +import org.rs09.consts.Items + +class FlypaperDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogue(player!!, "There's some flypaper in there. Should I take it?").also { stage++ } + 1 -> options("Yes, it might be useful.", "No, I don't see any need for it.").also { stage++ } + 2 -> when(buttonID) { + 1 -> if (addItem(player!!, Items.FLYPAPER_1811)){ + sendDialogue(player!!, "You take a piece of fly paper. There is still plenty of fly paper left.") + .also { stage = END_DIALOGUE } + } + else { + sendDialogue(player!!, "You don't have enough space in your inventory.") + .also { stage = END_DIALOGUE } + } + 2 -> sendDialogue(player!!, "You leave the paper in the sack.").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/FountainDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/FountainDialogue.kt new file mode 100644 index 000000000..95ef1d79e --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/FountainDialogue.kt @@ -0,0 +1,15 @@ +package content.region.kandarin.quest.murdermystery + +import core.api.* +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE + +class FountainDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogue(player!!, "The fountain is swarming with mosquitos. There's a nest of them underneath the fountain.").also { stage++ } + 1 -> playerl("I hate mosquitos, they're so annoying!").also { stage++ } + 2 -> sendDialogue(player!!, "It's certainly clear nobody's used poison here.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/FrankDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/FrankDialogue.kt new file mode 100644 index 000000000..15e8af9bb --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/FrankDialogue.kt @@ -0,0 +1,60 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonFrank +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class FrankDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, FrankDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return FrankDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.FRANK_819, NPCs.FRANK_6197) + } +} + +class FrankDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("Good for you. Now what do you want?").also { stage++ } + 2 -> npcl(FacialExpression.SAD, "...And can you spare me any money? I'm a little short...").also { stage++ } + 3 -> showTopics( + Topic("Who do you think is responsible?", 4), + Topic( "Where were you when the murder happened?", 5), + IfTopic("Do you recognise this thread?", 6, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 9, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 4 -> npcl("I don't know. You don't know how long it takes an inheritance to come through do you? I could really use that money pretty soon...").also { stage = END_DIALOGUE } + + 5 -> npcl("I don't know, somewhere around here probably. Could you spare me a few coins? I'll be able to pay you back double tomorrow it's just there's this poker night tonight in town...").also { stage = END_DIALOGUE } + + 6 -> sendDialogue(player!!, "Frank examines the thread from the crime scene.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1810)) stage++ else stage = 8 } + 7 -> npcl("It kind of looks like the same material as my trousers are made of... same colour anyway. Think it's worth anything? Can I have it? Or just some money?").also { stage = END_DIALOGUE } + + 8 -> npcl("It looks like thread to me, but I'm not exactly an expert. Is it worth something? Can I have it? Actually, can you spare me a few gold?").also { stage = END_DIALOGUE } + + 9 -> npcl("Would you like to buy some? I'm kind of strapped for cash right now. I'll sell it to you cheap. It's hardly been used at all.").also { stage++ } + 10 -> npcl("I just used a bit to clean that family crest outside up a bit. Do you think I could get much money for the family crest, actually? It's cleaned up a bit now.") + .also { setAttribute(player!!, attributeAskPoisonFrank, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/GossipDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/GossipDialogue.kt new file mode 100644 index 000000000..095fd175d --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/GossipDialogue.kt @@ -0,0 +1,133 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +@Initializable +class GossipDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GossipDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return GossipDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GOSSIP_813) + } +} + +class GossipDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("There's some kind of commotion up at the Sinclair place I hear. Not surprising all things considered.").also { stage = END_DIALOGUE} + 1 -> when (stage) { + 0 -> playerl("I'm investigating the murder up at the Sinclair place.").also { stage++ } + 1 -> npcl("Murder is it? Well, I'm not really surprised...").also { stage++ } + 2 -> options("What can you tell me about the Sinclairs?", "Who do you think was responsible?", "I think the butler did it.", "I am so confused about who did it.").also { stage++ } + 3 -> when(buttonID) { + 1 -> playerl("What can you tell me about the Sinclairs?").also { stage = 4 } + 2 -> playerl("Who do you think was responsible?").also { stage = 40 } + 3 -> playerl("I think the butler did it.").also { stage = 42 } + 4 -> playerl("I am so confused about who did it.").also { stage = 43 } + } + 4 -> npcl("Well, what do you want to know?").also { stage++ } + 5 -> options("Tell me about Lord Sinclair.", "Why do the Sinclairs live so far from town?", "What can you tell me about his sons?", "What can you tell me about his daughters?").also { stage++ } + 6 -> when(buttonID) { + 1 -> playerl("Tell me about Lord Sinclair.").also { stage = 7 } + 2 -> playerl("Why do the Sinclairs live so far from town?").also { stage = 10 } + 3 -> playerl("What can you tell me about his sons?").also { stage = 12 } + 4 -> playerl("What can you tell me about his daughters?").also { stage = 15 } + } + 7 -> npcl("Old Lord Sinclair was a great man with a lot of respect around these parts. More than his worthless children have anyway.").also { stage++ } + 8 -> playerl("His children? They have something to gain by his death?").also { stage++ } + 9 -> npcl("Yes. You could say that. Not that I am one to gossip.").also { stage = END_DIALOGUE } + + 10 -> npcl("Well, they used to live in the big castle, but old Lord Sinclair gave it up so that those strange knights could live there instead. So the king built him a new house to the North.").also { stage++ } + 11 -> npcl("It's more cramped than his old place, but he seemed to like it. His children were furious at him for doing it though!").also { stage = END_DIALOGUE } + + 12 -> npcl("His sons eh? They all have their own skeletons in their cupboards. You'll have to be more specific. Who are you interested in exactly?").also { stage++ } + 13 -> options("Tell me about Bob.", "Tell me about David.", "Tell me about Frank.").also { stage++ } + 14 -> when(buttonID) { + 1 -> playerl("Tell me about Bob.").also { stage = 18 } + 2 -> playerl("Tell me about David.").also { stage = 23 } + 3 -> playerl("Tell me about Frank.").also { stage = 27 } + } + + 15 -> npcl("His daughters eh? They're all nasty pieces of work, which of them specifically did you want to know about?").also { stage++ } + 16 -> options("Tell me about Anna.", "Tell me about Carol.", "Tell me about Elizabeth.").also { stage++ } + 17 -> when(buttonID) { + 1 -> playerl("Tell me about Anna.").also { stage = 30 } + 2 -> playerl("Tell me about Carol.").also { stage = 33 } + 3 -> playerl("Tell me about Elizabeth.").also { stage = 36 } + } + + 18 -> npcl("Bob is an odd character indeed... I'm not one to gossip, but I heard Bob is addicted to Tea. He can't make").also { stage++ } + 19 -> npcl("it through the day without having at least 20 cups!").also { stage++ } + 20 -> npcl("You might not think that's such a big thing, but he has spent thousands of gold to feed his habit!").also { stage++ } + 21 -> npcl("At one point he stole a lot of silverware from the kitchen and pawned it just so he could afford to buy his daily tea allowance.").also { stage++ } + 22 -> npcl("If his father ever found out, he would be in so much trouble... he might even get disowned!").also { stage = END_DIALOGUE } + + 23 -> npcl("David... oh David... not many people know this, but David really has an anger problem. He's always screaming and shouting").also { stage++ } + 24 -> npcl("at the household servants when he's angry, and they live in a state of fear, always walking on eggshells around him, but none of them have the courage").also { stage++ } + 25 -> npcl("to talk to his father about his behaviour. If they did, Lord Sinclair would almost certainly").also { stage++ } + 26 -> npcl("kick him out of the house, as some of the servants have been there longer than he has, and he definitely has no right to treat them like he does... but I'm not one to gossip about people.").also { stage = END_DIALOGUE } + + 27 -> npcl("I'm not one to talk ill of people behind their back, but Frank is a real piece of work. He is an absolutely terrible gambler...he can't pass 2 dogs in the street without putting a bet on which one will bark first!").also { stage++ } + 28 -> npcl("He has already squandered all of his allowance, and I heard he had stolen a number of paintings of his fathers to sell to try and cover his debts, but he still owes a lot of people a lot of").also { stage++ } + 29 -> npcl("money. If his father ever found out, he would stop his income, and then he would be in serious trouble!").also { stage = END_DIALOGUE } + + 30 -> npcl("Anna... ah yes... Anna has 2 great loves:").also { stage++ } + 31 -> npcl("Sewing and gardening. But one thing she has kept secret is that she once had an affair with Stanford the gardener, and tried to get him fired when they broke up,").also { stage++ } + 32 -> npcl("by killing all of the flowers in the garden. If her father ever found out she had done that he would be so furious he would probably disown her.").also { stage = END_DIALOGUE } + + 33 -> npcl("Oh Carol... she is such a fool. You didn't hear this from me, but I heard a while ago she was conned out of a lot of money by a travelling salesman who sold her a box full").also { stage++ } + 34 -> npcl("of beans by telling her they were magic. But they weren't. She sold some rare books from the library to cover her debts, but").also { stage++ } + 35 -> npcl("her father would be incredibly annoyed if he ever found out - he might even throw her out of the house!").also { stage = END_DIALOGUE } + + 36 -> npcl("Elizabeth? Elizabeth has a strange problem... She cannot help herself, but is always stealing small objects - it's pretty sad that she is rich enough to afford to buy things, but would rather steal them instead.").also { stage++ } + 37 -> npcl("Now, I don't want to spread stories, but I heard she even stole a silver needle from her father that had great sentimental value for him.").also { stage++ } + 38 -> npcl("He was devastated when it was lost, and cried for a week thinking he had lost it!").also { stage++ } + 39 -> npcl("If he ever found out that it was her who had stolen it he would go absolutely mental, maybe even disowning her!").also { stage = END_DIALOGUE } + + 40 -> npcl("Well, I guess it could have been an intruder, but with that big guard dog of theirs I seriously doubt it. I suspect it was someone closer to home...").also { stage ++ } + 41 -> npcl("Especially as I heard that the poison salesman in the Seers' village made a big sale to one of the family the other day.") + .also { setAttribute(player!!, attributeNoiseClue, true)} + .also { stage = END_DIALOGUE } + + 42 -> npcl("And I think you've been reading too many cheap detective novels. Hobbes is kind of uptight, but his loyalty to old Lord Sinclair is beyond question.").also { stage = END_DIALOGUE } + + 43 -> playerl("Think you could give me any hints?") + .also { when (RandomFunction.random(4)) { //RS3 wiki seems to indicate this is fully random, not based off of progression + 0 -> stage = 44 + 1 -> stage = 46 + 2 -> stage = 47 + 3 -> stage = 48 + 4 -> stage = 51 + } + } + 44 -> npcl("Well, I don't know if it's related, but I heard from that Poison Salesman in town that he sold some poison to one of the Sinclair family").also { stage++ } + 45 -> npcl("the other day. I don't think he has any stock left now though...").also { stage = END_DIALOGUE } + + 46 -> npcl("Well, I don't know how much help this is, but I heard their guard dog will bark loudly at anyone it doesn't recognise. Maybe you should find out if anyone heard anything suspicious?") + .also { stage = END_DIALOGUE } + + 47 -> npcl("Well, this might be of some help to you. My father was in the guards when he was younger and he always said that there isn't a crime that can't be solved through careful examination of the crime scene and all surrounding areas.").also { stage = END_DIALOGUE } + + 48 -> npcl("I don't know how much help this is to you, but my dad was in the guard once and he told me the marks on your hands are totally unique. He calls them 'finger prints'.").also { stage++ } + 49 -> npcl("He said you can find them easily on any shiny metallic surface, by using a fine powder to mark out where the marks are and then using some sticky paper to lift the print from the object.").also { stage++ } + 50 -> npcl("I bet if you could find a way to get everyone's 'finger prints' you could solve the crime pretty easily!").also { stage = END_DIALOGUE } + + 51 -> npcl("My father used to be in the guard, he always wrote himself notes on a piece of paper so he could keep track of information easily.").also { stage++ } + 52 -> npcl("Maybe you should try that? Don't forget to thank me if I help you solve the case!").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/GuardDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/GuardDialogue.kt new file mode 100644 index 000000000..d661a6aae --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/GuardDialogue.kt @@ -0,0 +1,220 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeRandomMurderer +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.clueCount +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.solvedMystery +import core.ServerConstants.Companion.SERVER_NAME +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class GuardDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GuardDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return GuardDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUARD_812, NPCs.GUARD_6191) + } +} + +class GuardDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + val KILLER = when (getAttribute(player!!, attributeRandomMurderer, 0)) { + 0 -> "Anna" + 1 -> "Bob" + 2 -> "Carol" + 3 -> "David" + 4 -> "Elizabeth" + 5 -> "Frank" + else -> "Anna" + } + val POISONSPOT = when (getAttribute(player!!, attributeRandomMurderer, 0)) { + 0 -> "compost heap" + 1 -> "beehive" + 2 -> "drain" + 3 -> "spiders nest" + 4 -> "fountain" + 5 -> "Sinclair Crest" + else -> "compost heap" + } + val KILLERMALE = when (getAttribute(player!!, attributeRandomMurderer, 0)) { + 1, 3, 5 -> true + else -> false + } + val EVIDENCE = (1796..1822).toList().toIntArray() //All Murder Mystery related items are between these item ids + + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "What's going on here?").also { stage++ } + 1 -> npcl(FacialExpression.SAD, "Oh, it's terrible! Lord Sinclair has been murdered and we don't have any clues as to who or why. We're totally baffled!").also { stage++ } + 2 -> npcl(FacialExpression.SAD, "If you can help us we will be very grateful.").also { stage++ } + 3 -> options("Sure, I'll help.", "You should do your own dirty work.").also { stage++ } + 4 -> when (buttonID) { + 1 -> playerl(FacialExpression.HAPPY, "Sure, I'll help.").also { stage++ } + 2 -> playerl("You should do your own dirty work.").also { stage = 8 } + } + 5 -> npcl(FacialExpression.HAPPY, "Thanks a lot!").also { stage++ } + 6 -> playerl(FacialExpression.NEUTRAL, "What should I be doing to help?").also { stage++ } + 7 -> npcl(FacialExpression.NEUTRAL, "Look around and investigate who might be responsible. The Sarge said every murder leaves clues to who done it, but frankly we're out of our depth here.") + .also { setAttribute(player!!, attributeRandomMurderer, RandomFunction.random(5))} + .also { setQuestStage(player!!, Quests.MURDER_MYSTERY, 1) } + .also { stage = END_DIALOGUE } + + 8 -> npcl("Get lost then, this is private property! ...Unless you'd like to be taken in for questioning yourself?").also { stage = END_DIALOGUE } + } + 1 -> when (stage) { + 0 -> options("What should I be doing to help again?", "How did Lord Sinclair die?", "I know who did it!").also { stage++ } + 1 -> when(buttonID) { + 1 -> playerl("What should I be doing to help?").also { stage = 2 } + 2 -> playerl("How did Lord Sinclair die?").also { stage = 3 } + 3 -> playerl(FacialExpression.HAPPY, "I know who did it!") + .also { + stage = if (solvedMystery(player!!)) 46 + else if (clueCount(player!!) == 1) { + if (inInventory(player!!, Items.KILLERS_PRINT_1815)) { + 41 + } + else if (getAttribute(player!!, attributePoisonClue,0) == 2) { + 37 + } + else 33 + } + else if (clueCount(player!!) > 1) 32 + else 6 + } + } + 2 -> npcl("Look around and investigate who might be responsible. The Sarge said every murder leaves clues to who done it, but frankly we're out of our depth here.").also { stage = END_DIALOGUE } + + 3 -> npcl("Well, it's all very mysterious. Mary, the maid, found the body in the study next to his bedroom on the east wing of the ground floor.").also { stage++ } + 4 -> npcl("The door was found locked from the inside, and he seemed to have been stabbed, but there was an odd smell in the room. Frankly, I'm stumped.").also { stage = END_DIALOGUE } + + 5 -> npcl("Really? That was quick work! Who?").also { stage++ } + 6 -> options("It was an intruder!", "The butler did it!", "It was one of the servants!", "It was one of his family!").also { stage++ } + 7 -> when (buttonID) { + 1 -> playerl("It was an intruder!").also { stage = 10 } + 2 -> playerl("The butler did it!").also { stage = 8 } + 3 -> playerl("It was one of the servants!").also { stage = 13 } + 4 -> playerl("It was one of his family!").also { stage = 20 } + } + 8 -> npcl("I hope you have proof to that effect. We have to arrest someone for this and it seems to me that only the actual murderer would gain by falsely accusing someone.").also { stage++ } + 9 -> npcl("Although having said that the butler is kind of shifty looking...").also { stage = END_DIALOGUE } + + 10 -> npcl("That's what we were thinking too. That someone broke in to steal something, was discovered by Lord Sinclair, stabbed him and ran.").also { stage++ } + 11 -> npcl("It's odd that apparently nothing was stolen though... Find out something has been stolen,").also { stage++ } + 12 -> npcl("and the case is closed, but the murdered man was a friend of the King and it's more than my job's worth not to investigate fully.").also { stage = END_DIALOGUE } + + 13 -> npcl("Oh really? Which one?").also { stage++ } + 14 -> options("It was one of the women.", "It was one of the men.").also { stage++ } + 15 -> when (buttonID) { + 1 -> playerl("It was one of the women.").also { stage = 16 } + 2 -> playerl("It was one of the men.").also { stage = 18 } + } + 16 -> options("It was so obviously Louisa The Cook.", "It must have been Mary The Maid.").also { stage++ } + 17 -> when (buttonID) { + 1 -> playerl("It was so obviously Louisa The Cook.").also { stage = 27 } + 2 -> playerl("It must have been Mary The Maid.").also { stage = 27 } + } + + 18 -> options("It can only be Donovan the Handyman.", "Pierre the Dog Handler. No question", "Hobbes the Butler. The butler *always* did it.", "You must know it was Stanford The Gardener.").also { stage++ } + 19-> when (buttonID) { + 1 -> playerl("It can only be Donovan the Handyman.").also { stage = 27 } + 2 -> playerl("Pierre the Dog Handler. No question.").also { stage = 27 } + 3 -> playerl("Hobbes the Butler. The butler *always* did it.").also { stage = 8 } + 4 -> playerl("You must know it was Stanford The Gardener.").also { stage = 27 } + } + + 20 -> npcl("Oh really? Which one?").also { stage++ } + 21 -> options("It was one of the women.", "It was one of the men.").also { stage++ } + 22 -> when (buttonID) { + 1 -> playerl("It was one of the women.").also { stage = 23 } + 2 -> playerl("It was one of the men.").also { stage = 25 } + } + 23 -> options("I know it was Anna.", "I am so sure it was Carol.", "I'll bet you anything it was Elizabeth.").also { stage++ } + 24 -> when (buttonID) { + 1 -> playerl("I know it was Anna.").also { stage = 27 } + 2 -> playerl("I am so sure it was Carol.").also { stage = 27 } + 3 -> playerl("I'll bet you anything it was Elizabeth.").also { stage = 27 } + } + 25 -> options("I'm certain it was Bob.", "It was David. No doubt about it.", "If it wasn't Frank I'll eat my shoes.").also { stage++ } + 26 -> when (buttonID) { + 1 -> playerl("I'm certain it was Bob.").also { stage = 27 } + 2 -> playerl("It was David. No doubt about it.").also { stage = 27 } + 3 -> playerl("If it wasn't Frank I'll eat my shoes.").also { stage = 27 } + } + 27 -> sendDialogue(player!!, "You tell the guard who you suspect of the crime.").also { stage++ } + 28 -> npcl("Great work, show me the evidence and we'll take them to the dungeons.").also { stage++ } + 29 -> npcl("You *DO* have evidence of their crime, right?").also { stage++ } + 30 -> playerl("Uh...").also { stage++ } + 31 -> npcl("Tch. You wouldn't last a day in the guards with sloppy thinking like that. Come see me when you have some proof of your accusations.").also { stage = END_DIALOGUE } + + 32 -> showTopics( + IfTopic("I have proof that it wasn't any of the servants.", 33, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810), true), + IfTopic("I have proof one of the family lied about the poison.", 37, getAttribute(player!!, attributePoisonClue, 0) == 2, true), + IfTopic("I have the finger prints of the culprit.", 41, inInventory(player!!, Items.KILLERS_PRINT_1815), true) + ) + 33 -> playerl("I have proof that it wasn't any of the servants!").also { stage++ } + 34 -> sendDialogue(player!!, "You show the guard the thread you found on the window.").also { stage ++ } + 35 -> playerl("All the servants dress in black so it couldn't have been one of them.").also { stage++ } + 36 -> npcl("That's some good work there. I guess it wasn't a servant. You still haven't proved who did do it though.").also { stage = END_DIALOGUE } + + 37 -> playerl("I have proof that $KILLER is lying about the poison.").also { stage++ } + 38 -> npcl("Oh really? How did you get that?").also { stage++ } + 39 -> sendDialogue(player!!, "You tell the guard about the " + + when (getAttribute(player!!, attributeRandomMurderer, 0)) { + 4 -> "mosquitos at the fountain." + 5 -> "tarnished family crest." + else -> "$POISONSPOT." + }).also { stage++ } + 40 -> npcl("Hmm. That's some good detective work there, we need more evidence before we can close the case though. Keep up the good work.").also { stage = END_DIALOGUE } + + 41 -> playerl("I have the fingerprints of the culprit!").also { stage++ } + 42 -> playerl("I have $KILLER's fingerprints here, you can see for yourself they match the fingerprints on the murder weapon exactly.").also { stage++ } + 43 -> sendDialogue(player!!,"You show the guard the finger prints evidence.").also { stage++ } + 44 -> npcl("... I'm impressed. How on earth did you think of something like that? I've never heard of such a technique for finding criminals before!").also { stage++ } + 45 -> npcl("This will come in very handy in the future but we can't arrest someone on just this. I'm afraid you'll still need to find more evidence before we can close this case completely.").also { stage = END_DIALOGUE } + + 46 -> playerl(FacialExpression.HAPPY, "I have conclusive proof who the killer was.").also { stage++ } + 47 -> npcl(FacialExpression.HAPPY, "You do? That's excellent work. Let's hear it then.").also { stage++ } + 48 -> playerl("I don't think it was an intruder, and I don't think Lord Sinclair was killed by being stabbed.").also { stage++ } + 49 -> npcl("Hmmm? Really? Why not?").also { stage++ } + 50 -> playerl(FacialExpression.HAPPY, "Nobody heard the guard dog barking, which it would have if it had been an intruder who was responsible.").also { stage++ } + 51 -> playerl("Nobody heard any signs of a struggle either. I think the knife was there to throw suspicion away from the real culprit.").also { stage++ } + 52 -> npcl("Yes, that makes sense. But who did do it then?").also { stage++ } + 53 -> sendDialogue(player!!, "You prove to the guard the thread matches $KILLER's clothes.").also { stage++ } + 54 -> npcl("Yes, I'd have to agree with that... but we need more evidence!").also { stage++ } + 55 -> sendDialogue(player!!, "You prove to the guard $KILLER did not use the poison on the $POISONSPOT.").also { stage++ } + 56 -> npcl("Excellent work - have you considered a career as a detective? But I'm afraid it's still not quite enough...").also { stage++ } + 57 -> sendDialogue(player!!, "You match $KILLER's finger prints with those on the dagger found in the body of Lord Sinclair.").also { stage++ } + 58 -> npcl(FacialExpression.HAPPY, "Yes. There's no doubt about it. It must have been $KILLER who killed " + if (KILLERMALE) {"his"} else {"her"} + " father. All of the guards must congratulate you on your excellent work in helping us to solve this case.").also { stage++ } + 59 -> npcl("We don't have many murders here in $SERVER_NAME and I'm afraid we wouldn't have been able to solve it by ourselves. We will hold " + if (KILLERMALE) {"him"} else {"her"} + " here under house arrest until such time as we bring " + if (KILLERMALE) {"him"} else {"her"} + " to trial.").also { stage++ } + 60 -> npcl("You have our gratitude, and I'm sure the rest of the family's as well, in helping to apprehend the murderer. I'll just take the evidence from you now.").also { stage++ } + 61 -> sendDialogue(player!!, "You hand over all the evidence.").also { stage++ } + 62 -> npcl(FacialExpression.HAPPY, "Please accept this reward from the family!").also { stage++ } + 63 -> finishQuest(player!!, Quests.MURDER_MYSTERY) + .also { + player!!.inventory.removeAll(EVIDENCE) + player!!.bank.removeAll(EVIDENCE) + player!!.equipment.removeAll(EVIDENCE) + } + .also { stage = END_DIALOGUE } + } + 100 -> when (stage) { + 0 -> npcl("Excellent work on solving the murder! All of the guards I know are very impressed, and don't worry, we have the murderer under guard until they can be taken to trial.").also { stage++ } + 1 -> playerl("Is there anything else I can do? Seems awfully quiet up at the house, considering their sibling has just been arrested.").also { stage++ } + 2 -> npcl("Nothing right now, we have it all under control.").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/HobbesDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/HobbesDialogue.kt new file mode 100644 index 000000000..75e67d2d6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/HobbesDialogue.kt @@ -0,0 +1,62 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class HobbesDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, HobbesDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return HobbesDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.HOBBES_808) + } +} + +class HobbesDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("This is private property! Please leave!").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 6), + IfTopic("Did you hear any suspicious noises at all?", 7, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 12, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("Well, in my considered opinion it must be David. The man is nothing more than a bully And I happen to know that poor Lord Sinclair and David had a massive argument in the living").also { stage++ } + 4 -> npcl("room about the way he treats the staff, the other day. I did not intend to overhear their conversation, but they were shouting so loudly I could not help but Overhear it. David definitely used the words").also { stage++ } + 5 -> npcl("'I am going to kill you!' as well. I think he should be the prime suspect. He has a nasty temper that one.").also { stage = END_DIALOGUE } + + 6 -> npcl("I was assisting the cook with the evening meal. I gave Mary His Lordships' dinner, and sent her to take it to him, then heard the scream as she found the body.").also { stage = END_DIALOGUE } + + 7 -> npcl("How do you mean 'suspicious'?").also { stage++ } + 8 -> playerl("Any sounds of a struggle with Lord Sinclair?").also { stage++ } + 9 -> npcl("No, I definitely didn't hear anything like that.").also { stage++ } + 10 -> playerl("How about the guard dog barking at all?").also { stage++ } + 11 -> npcl("You know, now you come to mention it I don't believe I did. I suppose that is Proof enough that it could not have been an intruder who is responsible.").also { stage = END_DIALOGUE } + + 12 -> npcl("Well, I do know that Elizabeth was extremely annoyed by the mosquito nest under the fountain in the garden, and was going to do something about it. I suspect any poison she bought would have been").also { stage++ } + 13 -> npcl ("enough to get rid of it. A Good job, too. I hate mosquitos.").also { stage++ } + 14 -> playerl("Yeah, so do I.").also { stage++ } + 15 -> npcl("You'd really have to ask her though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} + diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/LouisaDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/LouisaDialogue.kt new file mode 100644 index 000000000..f8c7306a7 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/LouisaDialogue.kt @@ -0,0 +1,59 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class LouisaDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, LouisaDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return LouisaDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.LOUISA_809) + } +} + +class LouisaDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("I'm far too upset to talk to random people right now.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 6), + IfTopic("Did you hear any suspicious noises at all?", 7, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 12, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("Elizabeth.").also { stage ++ } + 4 -> npcl("Her father confronted her about her constant petty thieving, and was devastated to find she had stolen a silver needle which had meant a lot to him.").also { stage++ } + 5 -> npcl("You could hear their argument from Lumbridge!").also { stage = END_DIALOGUE } + + 6 -> npcl("I was right here with Hobbes and Mary. You can't suspect me surely!").also { stage = END_DIALOGUE } + + 7 -> npcl("Suspicious? What do you mean suspicious?").also { stage++ } + 8 -> playerl("Any sounds of a struggle with an intruder for example?").also { stage++ } + 9 -> npcl("No, I'm sure I don't recall any such thing.").also { stage++ } + 10 -> playerl("How about the guard dog barking at an intruder?").also { stage++ } + 11 -> npcl("No, I didn't. If you don't have anything else to ask can You go and leave me alone now? I have a lot of cooking to do for this evening.").also { stage = END_DIALOGUE } + + 12 -> npcl("I told Carol to buy some from that strange poison salesman and clean the drains before they began to smell any worse. She was the one who blocked them in the first place with a load").also { stage++ } + 13 -> npcl("of beans that she bought for some reason. There were far too many to eat, and they were almost rotten when she bought them anyway! You'd really have to ask her though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/MaryDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/MaryDialogue.kt new file mode 100644 index 000000000..07388707c --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/MaryDialogue.kt @@ -0,0 +1,59 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class MaryDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MaryDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return MaryDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.MARY_810) + } +} + +class MaryDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 5), + IfTopic("Did you hear any suspicious noises at all?", 7, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 12, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("Oh I don't know... Frank was acting kind of funny... After that big argument him and the Lord had the other day by the beehive... so").also { stage ++ } + 4 -> npcl("I guess maybe him... but it's really scary to think someone here might have been responsible. I actually hope it was a burglar...").also { stage = END_DIALOGUE } + + 5 -> npcl("I was with Hobbes and Louisa in the Kitchen helping to prepare Lord Sinclair's meal, and then when I took it to his study... I saw... oh, it was horrible... he was...").also { stage++ } + 6 -> sendDialogue(player!!, "She seems to be on the verge of crying. You decide not to push her anymore for details.").also { stage = END_DIALOGUE} + + 7 -> npcl("I don't really remember hearing anything out of the ordinary.").also { stage++ } + 8 -> playerl("No sounds of a struggle then?").also { stage++ } + 9 -> npcl("No, I don't remember hearing anything like that.").also { stage++ } + 10 -> playerl("How about the guard dog barking?").also { stage++ } + 11 -> npcl("Oh that horrible dog is always barking at noting but I don't think I did...").also { stage = END_DIALOGUE } + + 12 -> npcl("I overheard Anna saying to Stanford that if he didn't do something about the state of his compost heap, she was going to.").also { stage++ } + 13 -> npcl("She really doesn't get on well with Stanford, I really have no idea why. You'd really have to ask her though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMystery.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMystery.kt new file mode 100644 index 000000000..23ec627b1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMystery.kt @@ -0,0 +1,156 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import core.api.* +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +// https://www.youtube.com/watch?v=f1Eou5i5hBo at 2:29 +// https://www.youtube.com/watch?v=BwO4JhZdQKo at 25:20 +// https://www.youtube.com/watch?v=u5Osw_jas4A at 26:35 +// https://www.youtube.com/watch?v=f1Eou5i5hBo +// https://www.youtube.com/watch?v=9Q2aLUg44SQ at 10:02 +// https://www.youtube.com/watch?v=_lTUessdfOM 6:18 + +/** + * Murder Mystery Quest + */ +@Initializable +class MurderMystery : Quest(Quests.MURDER_MYSTERY, 93, 92, 3, 192, 0, 1, 2) { + + companion object { + const val questName = "Murder Mystery" + + const val attributeRandomMurderer = "/save:quest:murdermystery-randommurderer" //Alphabetical, 0 for Anna, 1 for Bob, 2 for Carol, 3 for David, 4 for Elizabeth, 5 for Frank + const val attributeTakenThread = "/save:quest:murdermystery-takenthread" //true after taking thread for the first time from window + const val attributeNoiseClue = "/save:quest:murdermystery-noiseclue" //true on learning of the barking dog for the first time + const val attributePoisonClue = "/save:quest:murdermystery-poisonclue" //1 after learning poison was bought, 2 after finding liar + const val attributeAskPoisonAnna = "/save:quest:murdermystery-askpoisonanna" //true after asking Anna about poison + const val attributeAskPoisonBob = "/save:quest:murdermystery-askpoisonbob" //true after asking Bob about poison + const val attributeAskPoisonCarol = "/save:quest:murdermystery-askpoisoncarol" //true after asking Carol about poison + const val attributeAskPoisonDavid = "/save:quest:murdermystery-askpoisondavid" //true after asking David about poison + const val attributeAskPoisonElizabeth = "/save:quest:murdermystery-askpoisonelizabeth" //true after asking Elizabeth about poison + const val attributeAskPoisonFrank = "/save:quest:murdermystery-askpoisonfrank" //true after asking Anna about poison + + fun clueCount(player: Player) : Int { + var count = 0 + if (inInventory(player, Items.CRIMINALS_THREAD_1808) || inInventory(player, Items.CRIMINALS_THREAD_1809) || inInventory(player, Items.CRIMINALS_THREAD_1810)) { + count++ + } + if (inInventory(player, Items.KILLERS_PRINT_1815)) { + count ++ + } + if (getAttribute(player, attributePoisonClue,0) == 2) { + count ++ + } + return count + } + + fun solvedMystery(player: Player) : Boolean { + return ( + ( inInventory(player, Items.CRIMINALS_THREAD_1808) || inInventory(player, Items.CRIMINALS_THREAD_1809) || inInventory(player, Items.CRIMINALS_THREAD_1810) ) + && inInventory(player, Items.KILLERS_PRINT_1815) + && getAttribute(player, attributePoisonClue,0) == 2 + && getAttribute(player, attributeNoiseClue, false) + ) + } + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, Quests.MURDER_MYSTERY) > 0 + + if (!started) { + line(player, "I can start this quest by speaking to one of the !!Guards?? at", line++, false) + line(player, "the !!Sinclair Mansion??, North of the !!Seer's Village??.", line++, false) + } else if (stage < 100) { + line(player, "Lord Sinclair, a prominent nobleman, had been horribly", line++, true) + line(player, "murdered at his mansion. The guards had been sent to", line++, true) + line(player, "investigate his murder, but have been completely stuck.", line++, true) + + if (solvedMystery(player)) { + line(player, "One of the guards asked me for my help in solving the", line++, true) + line(player, "murder. After careful examination of the crime scene and", line++, true) + line(player, "interrogating all suspects, I worked out who was guilty.", line++, true) + } else { + line(player, "One of the !!guards?? has asked me for my help in solving the", line++, false) + line(player, "murder. I should !!examine the crime scene?? very closely for", line++, false) + line(player, "evidence, and !!interrogate everybody?? in the area carefully.", line++, false) + } + + // This may not be a stage but an attribute when all the evidence is collected. + if (solvedMystery(player)) { + line(player, "I have !!indisputable evidence?? of who the murderer must be.", line++, false) + line(player, "I should take it to one of the !!Guards?? immediately.", line++, false) + } else { + line++ + if (inInventory(player, Items.CRIMINALS_THREAD_1808) || inInventory(player, Items.CRIMINALS_THREAD_1809) || inInventory(player, Items.CRIMINALS_THREAD_1810)) { + line(player, "I have found some !!coloured thread??. It might be useful.", line++, false) + } + if (inInventory(player, Items.CRIMINALS_DAGGER_1813) || inInventory(player, Items.CRIMINALS_DAGGER_1814)) { + line(player, "I have taken the !!murder weapon??. I think it might help me.", line++, false) + } + if (inInventory(player, Items.PUNGENT_POT_1812)) { + line(player, "I have a !!strange smelling pot??. It seems like a clue.", line++, false) + } + } + } else { + line(player, "One of the guards asked me for my help in solving the", line++, true) + line(player, "murder. After careful examination of the crime scene and", line++, true) + line(player, "interrogating all suspects, I worked out who was guilty.", line++, true) + line(player, "I took the evidence I had collected to the Guards and", line++, true) + line(player, "explained how it could identify the killer. Impressed", line++, true) + line(player, "with my deductions, the killer was arrested and I was", line++, true) + line(player, "given a fair reward for my help in solving the crime.", line++, true) + line++ + line(player,"QUEST COMPLETE!", line) + } + + } + + override fun reset(player: Player) { + removeAttribute(player, attributeRandomMurderer) + removeAttribute(player, attributeNoiseClue) + removeAttribute(player, attributePoisonClue) + removeAttribute(player, attributeTakenThread) + removeAttribute(player, attributeAskPoisonAnna) + removeAttribute(player, attributeAskPoisonBob) + removeAttribute(player, attributeAskPoisonCarol) + removeAttribute(player, attributeAskPoisonDavid) + removeAttribute(player, attributeAskPoisonElizabeth) + removeAttribute(player, attributeAskPoisonFrank) + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Murder Mystery quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.COINS_617, 230, 277, 5) + + drawReward(player, "3 Quest Points", ln++) + drawReward(player, "2000 coins", ln++) + drawReward(player, "1406 Crafting XP", ln++) + + addItem(player, Items.COINS_995, 2000) + rewardXP(player, Skills.CRAFTING, 1406.0) + + removeAttribute(player, attributeNoiseClue) + removeAttribute(player, attributePoisonClue) + removeAttribute(player, attributeTakenThread) + removeAttribute(player, attributeAskPoisonAnna) + removeAttribute(player, attributeAskPoisonBob) + removeAttribute(player, attributeAskPoisonCarol) + removeAttribute(player, attributeAskPoisonDavid) + removeAttribute(player, attributeAskPoisonElizabeth) + removeAttribute(player, attributeAskPoisonFrank) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMysteryListeners.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMysteryListeners.kt new file mode 100644 index 000000000..bf9184584 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMysteryListeners.kt @@ -0,0 +1,442 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonAnna +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonBob +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonCarol +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonDavid +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonElizabeth +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonFrank +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeRandomMurderer +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeTakenThread +import core.api.* +import core.game.interaction.InteractionListener +import core.game.node.item.GroundItem +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class MurderMysteryListeners : InteractionListener { + companion object { + val UNDUSTEDEVIDENCE = intArrayOf( + Items.CRIMINALS_DAGGER_1813, + Items.PUNGENT_POT_1812, + Items.SILVER_NECKLACE_1796, + Items.SILVER_CUP_1798, + Items.SILVER_BOTTLE_1800, + Items.SILVER_BOOK_1802, + Items.SILVER_NEEDLE_1804, + Items.SILVER_POT_1806 + ) + val DUSTEDEVIDENCE = intArrayOf( + Items.CRIMINALS_DAGGER_1814, + Items.SILVER_NECKLACE_1797, + Items.SILVER_CUP_1799, + Items.SILVER_BOTTLE_1801, + Items.SILVER_BOOK_1803, + Items.SILVER_NEEDLE_1805, + Items.SILVER_POT_1807 + ) + val SILVERBARRELS = intArrayOf( + Scenery.ANNA_S_BARREL_2656, + Scenery.BOB_S_BARREL_2657, + Scenery.CAROL_S_BARREL_2658, + Scenery.DAVID_S_BARREL_2659, + Scenery.ELIZABETH_S_BARREL_2660, + Scenery.FRANK_S_BARREL_2661 + ) + val POISONSPOTS = intArrayOf( + Scenery.SINCLAIR_FAMILY_COMPOST_HEAP_26120, + Scenery.SINCLAIR_FAMILY_BEEHIVE_26121, + Scenery.SINCLAIR_MANSION_DRAIN_2843, + Scenery.SPIDERS__NEST_26109, + Scenery.SINCLAIR_FAMILY_FOUNTAIN_2654, + Scenery.SINCLAIR_FAMILY_CREST_2655 + ) + val SUSPECTPRINTS = intArrayOf( + Items.ANNAS_PRINT_1816, + Items.BOBS_PRINT_1817, + Items.CAROLS_PRINT_1818, + Items.DAVIDS_PRINT_1819, + Items.ELIZABETHS_PRINT_1820, + Items.FRANKS_PRINT_1821 + ) + } + + override fun defineListeners() { + on(Items.CRIMINALS_DAGGER_1813, GROUNDITEM, "take") { player, groundItem -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + if (inInventory(player, Items.CRIMINALS_DAGGER_1813) || inInventory(player, Items.CRIMINALS_DAGGER_1814)) { + sendDialogue(player, "I already have the murder weapon.") + } + else if (addItem(player, Items.CRIMINALS_DAGGER_1813)) { + removeGroundItem(groundItem as GroundItem) + sendDialogue(player, "This knife doesn't seem sturdy enough to have killed Lord Sinclair.") + } + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(Items.PUNGENT_POT_1812, GROUNDITEM, "take") { player, groundItem -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + if (inInventory(player, Items.PUNGENT_POT_1812)) { + sendDialogue(player, "I already have the poisoned pot.") + } else if (addItem(player, Items.PUNGENT_POT_1812)) { + removeGroundItem(groundItem as GroundItem) + sendDialogue(player, "It seems like Lord Sinclair was drinking from this before he died.") + } + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(intArrayOf(Scenery.STURDY_WOODEN_GATE_2664, Scenery.STURDY_WOODEN_GATE_2665), SCENERY, "investigate") { player, _ -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + sendDialogue(player, "As you approach the gate the guard dog starts barking loudly at you. There is no way an intruder could have committed the murder. It must have been someone the dog knew to get past it quietly.") + setAttribute(player, attributeNoiseClue, true) + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(Scenery.SMASHED_WINDOW_26110, SCENERY, "investigate") { player, _ -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + sendDialogue(player, "Some thread seems to have been caught on a loose nail on the window.") + if (inInventory(player, Items.CRIMINALS_THREAD_1808) || inInventory(player, Items.CRIMINALS_THREAD_1809) || inInventory(player, Items.CRIMINALS_THREAD_1810)) { + sendMessage(player, "You have already taken the thread.") + } + else if (getAttribute(player, attributeTakenThread, false)) { + if (hasSpaceFor(player, Item(Items.CRIMINALS_THREAD_1808))) { + when (getAttribute(player, attributeRandomMurderer, 0)) { + 1, 2 -> addItem(player, Items.CRIMINALS_THREAD_1808) + 0, 3 -> addItem(player, Items.CRIMINALS_THREAD_1809) + else -> addItem(player, Items.CRIMINALS_THREAD_1810) + } + sendMessage(player, "Lucky for you there's some thread left. You should be less careless in the future.") + } + } + else { + if (hasSpaceFor(player, Item(Items.CRIMINALS_THREAD_1808))) { + when (getAttribute(player, attributeRandomMurderer, 0)) { + 1, 2 -> addItem(player, Items.CRIMINALS_THREAD_1808) + 0, 3 -> addItem(player, Items.CRIMINALS_THREAD_1809) + else -> addItem(player, Items.CRIMINALS_THREAD_1810) + } + sendMessage(player, "You take the thread.") + setAttribute(player, attributeTakenThread, true) + } + } + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(Scenery.SMASHED_WINDOW_26110, SCENERY, "break") { player, _ -> + sendDialogue(player, "You don't want to damage evidence!") + return@on true + } + on(Scenery.SACKS_2663, SCENERY, "investigate") { player, _ -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + openDialogue(player, FlypaperDialogue()) + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(SILVERBARRELS, SCENERY, "search") { player, node -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + when (node.id) { + Scenery.ANNA_S_BARREL_2656 -> { + if (inInventory(player, Items.SILVER_NECKLACE_1796) || inInventory(player, Items.SILVER_NECKLACE_1797)) { + sendDialogue(player, "I already have Anna's Necklace.") + } + else if (addItem(player, Items.SILVER_NECKLACE_1796)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Anna's Silver Necklace.") + } + } + Scenery.BOB_S_BARREL_2657 -> { + if (inInventory(player, Items.SILVER_CUP_1798) || inInventory(player, Items.SILVER_CUP_1799)) { + sendDialogue(player, "I already have Bob's cup.") + } + else if (addItem(player, Items.SILVER_CUP_1798)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Bob's silver cup.") + } + } + Scenery.CAROL_S_BARREL_2658 -> { + if (inInventory(player, Items.SILVER_BOTTLE_1800) || inInventory(player, Items.SILVER_BOTTLE_1801)) { + sendDialogue(player, "I already have Carol's bottle.") + } + else if (addItem(player, Items.SILVER_BOTTLE_1800)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Carol's silver bottle.") + } + } + Scenery.DAVID_S_BARREL_2659 -> { + if (inInventory(player, Items.SILVER_BOOK_1802) || inInventory(player, Items.SILVER_BOOK_1803)) { + sendDialogue(player, "I already have David's book.") + } + else if (addItem(player, Items.SILVER_BOOK_1802)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take David's silver book.") + } + } + Scenery.ELIZABETH_S_BARREL_2660 -> { + if (inInventory(player, Items.SILVER_NEEDLE_1804) || inInventory(player, Items.SILVER_NEEDLE_1805)) { + sendDialogue(player, "I already have Elizabeth's Needle.") + } + else if (addItem(player, Items.SILVER_NEEDLE_1804)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Elizabeth's silver needle.") + } + } + Scenery.FRANK_S_BARREL_2661 -> { + if (inInventory(player, Items.SILVER_POT_1806) || inInventory(player, Items.SILVER_POT_1807)) { + sendDialogue(player, "I already have Frank's pot.") + } + else if (addItem(player, Items.SILVER_POT_1806)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Frank's silver pot.") + } + } + } + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(POISONSPOTS, SCENERY, "investigate") { player, node -> + when (node.id) { + Scenery.SINCLAIR_FAMILY_COMPOST_HEAP_26120 -> { + if (!getAttribute(player, attributeAskPoisonAnna, false)) { + sendDialogue(player, "It's a heap of compost.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 0) { + sendDialogue(player, "The compost is teeming with maggots. Somebody should really do something about it. It's certainly clear nobody's used poison here.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "There is a faint smell of poison behind the smell of the compost.") + } + } + Scenery.SINCLAIR_FAMILY_BEEHIVE_26121 -> { + if (!getAttribute(player, attributeAskPoisonBob, false)) { + sendDialogue(player, "It's a very old beehive.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 1) { + sendDialogue(player, "The beehive buzzes with activity. These bees definitely don't seem poisoned at all.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "The hive is empty. There are a few dead bees and a faint smell of poison.") + } + } + Scenery.SINCLAIR_MANSION_DRAIN_2843 -> { + if (!getAttribute(player, attributeAskPoisonCarol, false)) { + sendDialogue(player, "It's the drains from the kitchen.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 2) { + sendDialogue(player, "The drain is totally blocked. It really stinks. No, it REALLY smells bad. It's certainly clear nobody's cleaned it recently.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "The drain seems to have been recently cleaned. You can still smell the faint aroma of poison.") + } + } + Scenery.SPIDERS__NEST_26109 -> { + if (!getAttribute(player, attributeAskPoisonDavid, false)) { + sendDialogue(player, "It looks like a spiders' nest of some kind...") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 3) { + sendDialogue(player, "There is a spiders' nest here. You estimate there must be at least a few hundred spiders ready to hatch. It's certainly clear nobody's used poison here.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "A faint smell of poison and a few dead spiders is all that remains of the spiders nest.") + } + } + Scenery.SINCLAIR_FAMILY_FOUNTAIN_2654 -> { + if (!getAttribute(player, attributeAskPoisonElizabeth, false)) { + sendDialogue(player, "A fountain with large numbers of insects around the base.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 4) { + openDialogue(player, FountainDialogue()) + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "There are a lot of dead mosquitos around the base of the fountain. A faint smell of poison is in the air, but the water seems clean.") + } + } + Scenery.SINCLAIR_FAMILY_CREST_2655 -> { + if (!getAttribute(player, attributeAskPoisonFrank, false)) { + sendDialogue(player, "The Sinclair Family Crest is hung up here.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 5) { + sendDialogue(player, "It looks like the Sinclair family crest but it is very dirty. You can barely make it out under all of the grime. It's certainly clear nobody's cleaned it recently.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "The Sinclair family crest. It's shiny and freshly polished and has a slight smell of poison.") + } + } + + } + return@on true + } + onUseWith(ITEM, UNDUSTEDEVIDENCE, Items.POT_OF_FLOUR_1933) { player, used, _ -> + when (used.id) { + Items.CRIMINALS_DAGGER_1813 -> { + removeItem(player, used) + addItem(player, Items.CRIMINALS_DAGGER_1814) + sendMessage(player, "You sprinkle a small amount of flour on the murder weapon.") + sendMessage(player, "The murder weapon is now coated with a thin layer of flour.") + } + Items.PUNGENT_POT_1812 -> { + sendMessage(player, "You sprinkle a small amount of flour on the strange smelling pot.") + sendMessage(player, "The surface isn't shiny enough to take a fingerprint from.") + } + Items.SILVER_NECKLACE_1796 -> { + removeItem(player, used) + addItem(player, Items.SILVER_NECKLACE_1797) + sendMessage(player, "You sprinkle the flour on Anna's necklace.") + sendMessage(player, "The necklace is now coated with a thin layer of flour.") + } + Items.SILVER_CUP_1798 -> { + removeItem(player, used) + addItem(player, Items.SILVER_CUP_1799) + sendMessage(player, "You sprinkle the flour on Bob's cup.") + sendMessage(player, "The cup is now coated with a thin layer of flour.") + } + Items.SILVER_BOTTLE_1800 -> { + removeItem(player, used) + addItem(player, Items.SILVER_BOTTLE_1801) + sendMessage(player, "You sprinkle the flour on Carol's bottle.") + sendMessage(player, "The bottle is now coated with a thin layer of flour.") + } + Items.SILVER_BOOK_1802 -> { + removeItem(player, used) + addItem(player, Items.SILVER_BOOK_1803) + sendMessage(player, "You sprinkle the flour on David's book.") + sendMessage(player, "The Book is now coated with a thin layer of flour.") + } + Items.SILVER_NEEDLE_1804 -> { + removeItem(player, used) + addItem(player, Items.SILVER_NEEDLE_1805) + sendMessage(player, "You sprinkle the flour on Elizabeth's needle.") + sendMessage(player, "The Needle is now coated with a thin layer of flour.") + } + Items.SILVER_POT_1806 -> { + removeItem(player, used) + addItem(player, Items.SILVER_POT_1807) + sendMessage(player, "You sprinkle the flour on Frank's pot") + sendMessage(player, "The Pot is now coated with a thin layer of flour.") + } + } + removeItem(player, Items.POT_OF_FLOUR_1933) + addItem(player, Items.EMPTY_POT_1931) + return@onUseWith true + } + onUseWith(ITEM, DUSTEDEVIDENCE, Items.FLYPAPER_1811) { player, used, _ -> + removeItem(player, Items.FLYPAPER_1811) + when (used.id) { + Items.CRIMINALS_DAGGER_1814 -> { + removeItem(player, used) + addItem(player, Items.CRIMINALS_DAGGER_1813) + addItem(player, Items.UNKNOWN_PRINT_1822) + sendMessage(player, "You use the flypaper on the floury dagger.") + sendMessage(player, "You have a clean impression of the murderer's finger prints.") + } + Items.SILVER_NECKLACE_1797 -> { + removeItem(player, used) + addItem(player, Items.SILVER_NECKLACE_1796) + addItem(player, Items.ANNAS_PRINT_1816) + sendMessage(player, "You use the flypaper on the flour covered necklace.") + sendMessage(player, "You have a clean impression of Anna's finger prints.") + } + Items.SILVER_CUP_1799 -> { + removeItem(player, used) + addItem(player, Items.SILVER_CUP_1798) + addItem(player, Items.BOBS_PRINT_1817) + sendMessage(player, "You use the flypaper on the flour covered cup.") + sendMessage(player, "You have a clean impression of Bob's finger prints.") + } + Items.SILVER_BOTTLE_1801 -> { + removeItem(player, used) + addItem(player, Items.SILVER_BOTTLE_1800) + addItem(player, Items.CAROLS_PRINT_1818) + sendMessage(player, "You use the flypaper on the flour covered bottle.") + sendMessage(player, "You have a clean impression of Carol's finger prints.") + } + Items.SILVER_BOOK_1803 -> { + removeItem(player, used) + addItem(player, Items.SILVER_BOOK_1802) + addItem(player, Items.DAVIDS_PRINT_1819) + sendMessage(player, "You use the flypaper on the flour covered book.") + sendMessage(player, "You have a clean impression of David's finger prints.") + } + Items.SILVER_NEEDLE_1805 -> { + removeItem(player, used) + addItem(player, Items.SILVER_NEEDLE_1804) + addItem(player, Items.ELIZABETHS_PRINT_1820) + sendMessage(player, "You use the flypaper on the flour covered needle.") + sendMessage(player, "You have a clean impression of Elizabeth's finger prints.") + } + Items.SILVER_POT_1807 -> { + removeItem(player, used) + addItem(player, Items.SILVER_POT_1806) + addItem(player, Items.FRANKS_PRINT_1821) + sendMessage(player, "You use the flypaper on the flour covered pot.") + sendMessage(player, "You have a clean impression of Frank's finger prints.") + } + } + return@onUseWith true + } + onUseWith(ITEM, SUSPECTPRINTS, Items.UNKNOWN_PRINT_1822) { player, used, with -> + val SUSPECT = when (used.id) { + Items.ANNAS_PRINT_1816 -> "Anna" + Items.BOBS_PRINT_1817 -> "Bob" + Items.CAROLS_PRINT_1818 -> "Carol" + Items.DAVIDS_PRINT_1819 -> "David" + Items.ELIZABETHS_PRINT_1820 -> "Elizabeth" + Items.FRANKS_PRINT_1821 -> "Frank" + else -> "Anna" + } + if (used.id == getAttribute(player, attributeRandomMurderer, 0) + Items.ANNAS_PRINT_1816) { + sendDialogue(player, "The fingerprints are an exact match to $SUSPECT's.") + removeItem(player, with.id) + addItem(player, Items.KILLERS_PRINT_1815) + } + else { + sendDialogue(player, "They don't seem to be the same. I guess that clears $SUSPECT of the crime. You destroy the useless fingerprint.") + removeItem(player, used.id) + } + return@onUseWith true + } + onUseWith(SCENERY, intArrayOf(Items.SILVER_POT_1806, Items.PUNGENT_POT_1812), Scenery.BARREL_OF_FLOUR_26122) { player, _, _ -> + sendDialogue(player, "You probably shouldn't use evidence from a crime scene to keep flour in.") + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/PierreDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/PierreDialogue.kt new file mode 100644 index 000000000..7d94c0677 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/PierreDialogue.kt @@ -0,0 +1,58 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class PierreDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PierreDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return PierreDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.PIERRE_807) + } +} + +class PierreDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("The Guards told me not to talk to anyone.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 5), + IfTopic("Did you hear any suspicious noises at all?", 6, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 11, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("Honestly? I think it was Carol.").also { stage++ } + 4 -> npcl("I saw her in a huge argument with Lord Sinclair in the library the other day. It was something to do with stolen books. She definitely seemed upset enough to have done it afterwards.").also { stage = END_DIALOGUE } + + 5 -> npcl("I was in town at the inn. When I got back the house was swarming with guards who told me what had happened. Sorry.").also { stage = END_DIALOGUE } + + 6 -> npcl("Well, like what?").also { stage++ } + 7 -> playerl("Any sounds of a struggle with Lord Sinclair?").also { stage++ } + 8 -> npcl("No, I don't remember hearing anything like that.").also { stage++ } + 9 -> playerl("How about the guard dog barking at all?").also { stage++ } + 10 -> npcl("I hear him bark all the time. It's one of his favourite things to do. I can't say I did the night of the murder though as I wasn't close enough to hear either way.").also { stage = END_DIALOGUE } + + 11 -> npcl("Well, I know David said that he was going to do something about the spiders' nest that's between the two servants' quarters upstairs.").also { stage++ } + 12 -> npcl("He made a big deal about it to Mary the Maid, calling her useless and incompetent. I felt quite sorry for her actually. You'd really have to ask him though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/StanfordDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/StanfordDialogue.kt new file mode 100644 index 000000000..ea70f1a92 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/StanfordDialogue.kt @@ -0,0 +1,58 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class StanfordDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, StanfordDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return StanfordDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.STANFORD_811) + } +} + +class StanfordDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("Have you no shame? We are all grieving at the moment.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 5), + IfTopic("Did you hear any suspicious noises at all?", 6, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 11, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("It was Anna. She is seriously unbalanced. She trashed the garden once then tried to blame it on me! I bet it was her. It's just the kind of thing she'd do!").also { stage++ } + 4 -> npcl("She really hates me and was arguing with Lord Sinclair about trashing the garden a few days ago.").also { stage = END_DIALOGUE } + + 5 -> npcl("Right here, by my little shed. It's very cosy to sit and think in.").also { stage = END_DIALOGUE } + + 6 -> npcl("Not that I remember.").also { stage++ } + 7 -> playerl("So no sounds of a struggle between Lord Sinclair and an intruder?").also { stage++ } + 8 -> npcl("Not to the best of my recollection.").also { stage++ } + 9 -> playerl("How about the guard dog barking?").also { stage++ } + 10 -> npcl("Not that I can recall.").also { stage = END_DIALOGUE } + + 11 -> npcl("Well, Bob mentioned to me the other day he wanted to get rid of the bees in that hive over there. I think I saw him buying poison").also { stage++ } + 12 -> npcl("from that poison salesman the other day. I assume it was to sort out those bees. You'd really have to ask him though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/AstronomyBook.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/AstronomyBook.kt new file mode 100644 index 000000000..f228e0864 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/AstronomyBook.kt @@ -0,0 +1,169 @@ +package content.region.kandarin.quest.observatoryquest + +import content.global.handlers.iface.* +import core.ServerConstants +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +/** + * Astronomy Book + */ +class AstronomyBook : InteractionListener { + companion object { + private const val TITLE = "THE TALE OF SCORPIUS" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("THE TALE OF", 55), + BookLine("SCORPIUS: ", 56), + BookLine("", 57), + BookLine("A History of Astronomy", 58), + BookLine("in ${ServerConstants.SERVER_NAME}.", 59), + BookLine("", 60), + BookLine("At the start of the 4th", 61), + BookLine("Age, a learned man by", 62), + BookLine("the name of Scorpius,", 63), + BookLine("known well for his powers", 64), + BookLine("of vision and magic,", 65), + ), + Page( + BookLine("sought communion with", 66), + BookLine("the gods of the world. So", 67), + BookLine("many unanswered", 68), + BookLine("questions had he that he", 69), + BookLine("devoted his entire lif to", 70), + BookLine("the cause.", 71), + BookLine("", 72), + BookLine("After many years of", 73), + BookLine("study, seeking knowledge", 74), + BookLine("from the wise of that", 75), + BookLine("time, he developed a", 76), + ) + ), + PageSet( + Page( + BookLine("machine infused with", 55), + BookLine("magical power, infused", 56), + BookLine("with the ability to pierce", 57), + BookLine("into the very heavens - a", 58), + BookLine("huge eye that gave the", 59), + BookLine("user incredible sight, like", 60), + BookLine("never before.", 61), + BookLine("", 62), + BookLine("", 63), + BookLine("", 64), + BookLine("", 65), + ), + Page( + BookLine("As time passed, Scorpius", 66), + BookLine("grew adept at using his", 67), + BookLine("specialized skills, and", 68), + BookLine("followed the movements of", 69), + BookLine("the stars in ${ServerConstants.SERVER_NAME}", 70), + BookLine("which he mapped and", 71), + BookLine("named, and are still used", 72), + BookLine("to this very day.", 73), + BookLine("", 74), + BookLine("Before long, Scorpius", 75), + BookLine("used his knowledge for", 76), + ) + ), + PageSet( + Page( + BookLine("predicting the future,", 55), + BookLine("and, in turn, he called", 56), + BookLine("upon the dark knowledge", 57), + BookLine("of Zamorakian", 58), + BookLine("worshippers to further his", 59), + BookLine("cause. Living below", 60), + BookLine("ground, the followers of", 61), + BookLine("the dark god remained", 62), + BookLine("until the civilisation of", 63), + BookLine("Ardougne grew in", 64), + BookLine("strength and control.", 65), + ), + Page( + BookLine("", 66), + BookLine("", 67), + BookLine("", 68), + BookLine("", 69), + BookLine("", 70), + BookLine("", 71), + BookLine("The kings of that time", 72), + BookLine("worked to banish the", 73), + BookLine("Zamorakian followers in", 74), + BookLine("the area, hiding all", 75), + BookLine("references to Scorpius's", 76), + ) + ), + PageSet( + Page( + BookLine("invention, due to its 'evil", 55), + BookLine("nature'.", 56), + BookLine("", 57), + BookLine("Years after, when the", 58), + BookLine("minds of the kings lent", 59), + BookLine("more towards the", 60), + BookLine("research of the unknown,", 61), + BookLine("the plans of Scorpius", 62), + BookLine("were uncovered and the", 63), + BookLine("heavenly eye constructed", 64), + BookLine("again.", 65), + ), + Page( + BookLine("Since then, many have", 66), + BookLine("studied the ways of the", 67), + BookLine("astronomer, Scorpius, and", 68), + BookLine("in his memory a grave", 69), + BookLine("was constructed near the", 70), + BookLine("Observatory.", 71), + BookLine("", 72), + BookLine("", 73), + BookLine("", 74), + BookLine("", 75), + BookLine("", 76), + ) + ), + + PageSet( + Page(), + Page( + BookLine("Some claim his ghost still", 66), + BookLine("wanders nearby, in", 67), + BookLine("torment as he seeks the", 68), + BookLine("secrets of the heavens", 69), + BookLine("that can never be solved.", 70), + BookLine("Tales tell that he will", 71), + BookLine("grant those adept in the", 72), + BookLine("arts of the astronomer a", 73), + BookLine("blessing of unusual", 74), + BookLine("power.", 75), + BookLine("", 76), + ) + ), + PageSet( + Page( + BookLine("Here ends the tale of how", 55), + BookLine("astronomy entered the", 56), + BookLine("known world.", 57), + ), + Page() + ), + ) + private fun display(player:Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) + BookInterface.setModelOnPage(player,2, 27071, BookInterface.FANCY_BOOK_3_49, 33, 34, 1020, 0, 0) + BookInterface.setModelOnPage(player,4, 27069, BookInterface.FANCY_BOOK_3_49, 17, 18, 1224, 0, 0) + return true + } + } + + override fun defineListeners() { + on(Items.ASTRONOMY_BOOK_600, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinDialogues.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinDialogues.kt new file mode 100644 index 000000000..5d676d5de --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinDialogues.kt @@ -0,0 +1,152 @@ +package content.region.kandarin.quest.observatoryquest + +import core.api.* +import core.game.dialogue.* +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +class GoblinDialogues : InteractionListener { + override fun defineListeners() { + on(NPCs.GREASYCHEEKS_6127, NPC, "talk-to") { player, node -> + openDialogue(player, GreasycheeksDialogueFile(), node as NPC) + return@on true + } + on(NPCs.SMELLYTOES_6128, NPC, "talk-to") { player, node -> + openDialogue(player, SmellytoesDialogueFile(), node as NPC) + return@on true + } + on(NPCs.CREAKYKNEES_6129, NPC, "talk-to") { player, node -> + openDialogue(player, CreakykneesDialogueFile(), node as NPC) + return@on true + } + } +} + +class GreasycheeksDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.GREASYCHEEKS_6127) + player("Hello.") + npc(ChatAnim.OLD_NORMAL, "Shush! I'm concentrating.") + player("Oh, sorry.") + } +} + +class SmellytoesDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SMELLYTOES_6128) + player("Hi there.") + npc(ChatAnim.OLD_NORMAL, "Hey, ids me matesh!") + player("Sorry, have we met?") + npc(ChatAnim.OLD_NORMAL, "Yeah! you wazsh wiv me in dat pub overy by hill!") + player("I have no idea what you're going on about.") + npc(ChatAnim.OLD_NORMAL, "Glad yeeash remembers.") + } +} + +class CreakykneesDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.CREAKYKNEES_6129) + player("Where did you get that lens?") + npc(ChatAnim.OLD_NORMAL, "From that strange metal thing up on the hill.") + player(ChatAnim.ANGRY, "You should give that back!") + npc(ChatAnim.OLD_NORMAL, "Even if it's cracked?") + player("Ah, well, I suppose it's of no use. But, still.") + } +} + +@Initializable +class GreasycheeksNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return GreasycheeksNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GREASYCHEEKS_6127) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + sendChat(this, arrayOf( + "Cook, cook, cook!", + "I'm so hungry!", + "This is gonna taste sooo good!", + ).random()) + } + } +} + +@Initializable +class SmellytoesNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return SmellytoesNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SMELLYTOES_6128) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + sendChat(this, arrayOf( + "Doh ray meeee laa doh faaa!", + "La la la! Do di dum dii!", + ).random()) + } + } +} + +@Initializable +class CreakykneesNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return CreakykneesNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CREAKYKNEES_6129) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + sendChat(this, arrayOf( + "Was that a spark?", + "Come on! Please light!", + ).random()) + } + } +} +@Initializable +class LostGoblinNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return LostGoblinNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GOBLIN_6125) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + sendChat(this, arrayOf( + "Which way should I go?", + "These dungeons are such a maze.", + "Where's the exit?!?", + "This is the fifth time this week. I'm lost!", + "I've been wandering around down here for hours.", + "How do you get back to the village?", + "I hate being so lost!", + "How could I be so disoriented?", + "Where am I? I'm so lost.", + "I know the exit's around here, somewhere." + ).random()) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinGuardNPC.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinGuardNPC.kt new file mode 100644 index 000000000..963438628 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinGuardNPC.kt @@ -0,0 +1,31 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class GoblinGuardNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GoblinGuardNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SLEEPING_GUARD_6122, NPCs.GOBLIN_GUARD_489) + } + + override fun finalizeDeath(entity: Entity) { + if (entity is Player) { + val player = entity.asPlayer() + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) >= 4) { + setAttribute(player, ObservatoryQuest.attributeKilledGuard, true) + } + super.finalizeDeath(player) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryAssistantDialogue.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryAssistantDialogue.kt new file mode 100644 index 000000000..5c7f1f7eb --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryAssistantDialogue.kt @@ -0,0 +1,238 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeReceivedWine +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ObservatoryAssistantDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return ObservatoryAssistantDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ObservatoryAssistantDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.OBSERVATORY_ASSISTANT_6118, 10022) + } +} + +class ObservatoryAssistantDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.OBSERVATORY_ASSISTANT_6118) + + exec { player, npc -> + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 0) { + loadLabel(player, "observatoryQuest0") + } else if (getAttribute(player, attributeReceivedWine, false)) { + loadLabel(player, "receivedwine") + } else if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 100) { + loadLabel(player, "observatoryqueststage100") + } else { + loadLabel(player, "randomStart" + (1..4).random()) + } + } + + label("observatoryQuest0") + player(FacialExpression.ASKING, "Hi, are you busy?") + npc("Me? I'm always busy.") + npc("See that man there? That's the professor. If he had his way, I think he'd never let me sleep!") + npc("Anyway, how might I help you?") + options( + DialogueOption("wonderingwhat", "I was wondering what you do here."), + DialogueOption("justlooking", "Just looking around, thanks."), + DialogueOption("looktelescope", "Can I have a look through that telescope?", expression = FacialExpression.ASKING) + ) + + label("wonderingwhat") + npc("Glad you ask. This is the Observatory reception. Up on the cliff is the Observatory dome, from which you can view the heavens. Usually...") + player(FacialExpression.ASKING, "What do you mean, 'usually'?") + npc("*Ahem*. Back to work, please.") + npc("I'd speak with the professor. He'll explain.") + + label("justlooking") + npc("Okay, just don't break anything. If you need any help, let me know.") + line("The assistant continues with his work.") + + label("looktelescope") + npc("You can. You won't see much though.") + player(FacialExpression.ASKING, "And that's because?") + npc("Just talk to the professor. He'll fill you in.") + + label("randomStart1") + player(FacialExpression.ASKING, "Can I interrupt you?") + npc("I suppose so. Please be quick though.") + goto("afterRandomStart") + + label("randomStart2") + player(FacialExpression.ASKING, "Might I have a word?") + npc("Sure, how can I help you?") + goto("afterRandomStart") + + label("randomStart3") + player("Hello there.") + npc("Yes?") + goto("afterRandomStart") + + label("randomStart4") + player(FacialExpression.ASKING, "Can I speak with you?") + npc("Why, of course. What is it?") + goto("afterRandomStart") + + + label("afterRandomStart") + exec { player, npc -> + loadLabel(player, "observatoryqueststage" + getQuestStage(player, Quests.OBSERVATORY_QUEST)) + } + + label("observatoryqueststage1") + exec { player, _ -> + if (inInventory(player, Items.PLANK_960, 3)) { + loadLabel(player, "enoughplanks") + } + else if (inInventory(player, Items.PLANK_960)) { + loadLabel(player, "someplanks") + } + else { + loadLabel(player, "notenoughplanks") + } + } + + label("notenoughplanks") + player(FacialExpression.ASKING,"Where can I find planks of wood? I need some for the telescope's base.") + npc("I understand planks can be found at Port Khazard, to the east of here. There are some at the Barbarian Outpost, too.") + npc("Failing that, you could always ask the Sawmill Operator. He's to the north-east of Varrock, by the Lumber Yard.") + + label("enoughplanks") + player("I've got some planks for the telescope's base.") + npc("Good work! The professor has been eagerly awaiting them.") + + label("someplanks") + player("I've got a plank.") + npc("That's nice.") + player("You know, for the telescope's base.") + npc("Well done. Remember that you'll need three in total.") + + label("observatoryqueststage2") + exec { player, _ -> + if(inInventory(player, Items.BRONZE_BAR_2349)) { + loadLabel(player, "hasbronzebar") + } else { + loadLabel(player, "nohasbronzebar") + } + } + + label("nohasbronzebar") + player(FacialExpression.ASKING, "Can you help me? How do I go about getting a bronze bar?") + npc("You'll need to use tin and copper ore on a furnace to produce this metal.") + player("Right you are.") + + label("hasbronzebar") + player("The bronze bar is ready, and waiting for the professor.") + npc("He'll surely be pleased. Go ahead and give it to him.") + + label("observatoryqueststage3") + exec { player, _ -> + if(inInventory(player, Items.MOLTEN_GLASS_1775)) { + loadLabel(player, "hasmoltenglass") + } else { + loadLabel(player, "nohasmoltenglass") + } + } + + label("nohasmoltenglass") + player(FacialExpression.ASKING, "What's the best way for me to get molten glass?") + npc("There are many ways, but I'd suggest making it yourself. Get yourself a bucket of sand and some soda ash, which you can get from using seaweed with a furnace. Use the soda ash and sand together in a") + npc("furnace and bang - molten glass is all yours. There's a book about it on the table if you want to know more.") + player("Thank you!") + + label("hasmoltenglass") + player("I've managed to get hold of some molten glass.") + npc("I suggest you have a word with the professor, in that case.") + + label("observatoryqueststage4") + exec { player, _ -> + if(inInventory(player, Items.LENS_MOULD_602)) { + loadLabel(player, "haslensmould") + } else { + loadLabel(player, "nohaslensmould") + } + } + + label("nohaslensmould") + player(FacialExpression.ASKING, "Where can I find this lens mould you mentioned?") + npc("I'm sure I heard one of those goblins talking about it. I bet they've hidden it somewhere. Probably using it for some strange purpose, I'm sure.") + player(FacialExpression.ASKING, "What makes you say that?") + npc("I had a nice new star chart, until recently. I went out to do an errand for the professor the other day, only to see a goblin using it...") + npc("...as some kind of makeshift hankey to blow his nose!") + player("Oh dear.") + npc("You may want to look through the dungeon they have under their little village.") + player("Thanks for the advice.") + + label("haslensmould") + player("I have the lens mould.") + npc("Well done on finding that! I am honestly quite impressed. Make sure you take it straight to the professor.") + player("Will do.") + + label("observatoryqueststage5") + exec { player, _ -> + if(inInventory(player, Items.OBSERVATORY_LENS_603)) { + loadLabel(player, "haslens") + } else { + loadLabel(player, "nohaslens") + } + } + + label("nohaslens") + player(FacialExpression.ASKING, "How should I make this lens?") + npc("Just use the molten glass with the mould. Simple.") + player("Thanks!") + + label("haslens") + player("Do you like this lens? Good, huh?") + npc("Nice. What's that scratch?") + player("Oh, erm, that's a feature.") + player("Yes, that's it! Indubitably, it facilitates the triangulation of photonic illumination to the correct...") + npc("Stop! You can't confuse me with big words. Just pray the professor doesn't notice.") + + label("observatoryqueststage6") + player("Hello again.") + npc("Ah, it's the telescope repairman! The professor is waiting for you in the Observatory.") + player("How can I get to the Observatory?") + npc("Well, since the bridge was ruined, you will have to travel through the dungeon under the goblins' settlement.") + + label("observatoryqueststage100") + player("Hi assistant.") + player("Wait a minute.") + player(FacialExpression.ASKING, "What's your real name?") + npc(FacialExpression.ASKING, "My real name?") + player(FacialExpression.ASKING, "I only know you as the professor's assistant. What's your actual name?") + npc("Patrick.") + player("Hi Patrick, I'm @name.") + npc("Well, hello again, and thanks for helping out the professor.") + npc(" Oh, and, believe it or not, his name is Mambo-duna-roona, but don't tell him I told you.") + player(FacialExpression.AMAZED, "You made that up!") + npc("No, honest! Anyways, you've made my life much easier. Have a drink on me!") + item(Item(Items.JUG_OF_WINE_1993), "The assistant gives you some wine.") + exec { player, _ -> + addItemOrDrop(player, Items.JUG_OF_WINE_1993) + setAttribute(player, attributeReceivedWine, true) + } + player("Thanks very much.") + npc("No problem. Scorpius would be proud.") + player(FacialExpression.ASKING, "Sorry?") + npc("You may want to check out that book on the table, and perhaps look around for a grave...") + + label("receivedwine") + npc(FacialExpression.ASKING, "How was the wine?") + player(FacialExpression.DRUNK, "That was good stuff, *hic*! Wheresh the professher?") + npc(FacialExpression.ASKING, "The professor? He's up in the Observatory. Since the bridge was ruined, you will have to travel through the dungeon under the goblins' settlement.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryCutscene.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryCutscene.kt new file mode 100644 index 000000000..0eae2b1ab --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryCutscene.kt @@ -0,0 +1,199 @@ +package content.region.kandarin.quest.observatoryquest + +import core.api.* +import core.game.activity.Cutscene +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import org.rs09.consts.Animations +import org.rs09.consts.NPCs +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeFinishedCutscene +import core.game.world.map.Location + +class ObservatoryCutscene(player: Player) : Cutscene(player) { + override fun setup() { + setExit(Location(2438, 3163)) + loadRegion(9777) + addNPC(PROFESSOR, 7, 25, Direction.SOUTH) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(5) + } + 1 -> { + teleport(player, 6, 27) + face(player, getNPC(PROFESSOR)!!.location) + rotateCamera(9, 25, 300) + moveCamera(25, 22, 1800) + timedUpdate(5) + } + 2 -> { + fadeFromBlack() + dialogueLinesUpdate("~ The Observatory ~", "The great eye into the heavens.") + } + 3 -> { + moveCamera(14, 10, 1800, 10) + timedUpdate(3) + } + 4 -> { + moveCamera(6, 10, 1800, 10) + timedUpdate(3) + } + 5 -> { + moveCamera(0, 32, 1800, 10) + timedUpdate(3) + } + 6 -> { + moveCamera(4, 36, 1600, 10) + timedUpdate(3) + } + 7 -> { + rotateCamera(7, 25, 300, 5) + moveCamera(13, 40, 1800, 10) + timedUpdate(3) + } + 8 -> { + moveCamera(12, 36, 1800, 5) + timedUpdate(5) + } + 9 -> { + fadeToBlack() + timedUpdate(5) + } + 10 -> { + moveCamera(5, 21, 500) + rotateCamera(6, 27, 100) + timedUpdate(3) + } + 11 -> { + fadeFromBlack() + timedUpdate(5) + } + 12 -> { + playerDialogueUpdate(FacialExpression.NEUTRAL, "Hi professor!") + } + 13 -> { + face(getNPC(PROFESSOR)!!, player.location) + timedUpdate(3) + } + 14 -> { + animate(getNPC(PROFESSOR)!!, WAVE) + dialogueUpdate(PROFESSOR, FacialExpression.HAPPY, "Oh, hi there.") + } + 15 -> { + dialogueUpdate(PROFESSOR, FacialExpression.HAPPY, "I'm just adding the finishing touches.") + } + 16 -> { + playerDialogueUpdate(FacialExpression.HAPPY, "Okay, don't let me interrupt.") + } + 17 -> { + dialogueUpdate(PROFESSOR, FacialExpression.NEUTRAL, "Thank you.") + } + 18 -> { + face(getNPC(PROFESSOR)!!, Location(136, 25)) + dialogueUpdate(PROFESSOR, FacialExpression.NEUTRAL, "Right, let's get this finished.") + } + 19 -> { + animate(getNPC(PROFESSOR)!!, THINKING) + sendChat(getNPC(PROFESSOR)!!, "Hmmmm...") + timedUpdate(5) + } + 20 -> { + move(getNPC(PROFESSOR)!!, 7, 23) + rotateCamera(9, 23, 100) + moveCamera(12, 30, 300) + timedUpdate(3) + } + 21 -> { + move(getNPC(PROFESSOR)!!, 10, 26) + timedUpdate(10) + } + 22 -> { + face(getNPC(PROFESSOR)!!, Location(135, 25)) + animate(getNPC(PROFESSOR)!!, FIX) + sendChat(getNPC(PROFESSOR)!!, "Bit of a tap here...") + timedUpdate(3) + } + 23 -> { + move(getNPC(PROFESSOR)!!, 11, 26) + timedUpdate(5) + } + 24 -> { + face(getNPC(PROFESSOR)!!, player.location) + dialogueUpdate(PROFESSOR, FacialExpression.HAPPY, "@name, I'm just going upstairs to finish off.") + } + 25 -> { + playerDialogueUpdate(FacialExpression.HAPPY, "Right-oh.") + } + 26 -> { + face(getNPC(PROFESSOR)!!, Location(140, 25)) + timedUpdate(3) + } + 27 -> { + teleport(getNPC(PROFESSOR)!!, 11, 22, 1) + teleport(player, 14,29,1) + face(getNPC(PROFESSOR)!!, Location(2442, 3158, 1)) + moveCamera(6, 20, 400) + rotateCamera(10, 24, 400) + timedUpdate(5) + } + 28 -> { + move(getNPC(PROFESSOR)!!, 7, 27) + moveCamera(10, 20, 400, 1) + rotateCamera(7, 27, 50, 20) + timedUpdate(10) + } + 29 -> { + sendChat(getNPC(PROFESSOR)!!, "In goes the lens.") + timedUpdate(3) + } + 30 -> { + animate(getNPC(PROFESSOR)!!, TUNEUP) + timedUpdate(20) + } + 31 -> { + sendChat(getNPC(PROFESSOR)!!, "And one final adjustment.") + move(getNPC(PROFESSOR)!!, 10, 29) + timedUpdate(6) + } + 32 -> { + move(getNPC(PROFESSOR)!!, 10, 25) + face(getNPC(PROFESSOR)!!, Location(137, 26)) + timedUpdate(6) + } + 33 -> { + face(getNPC(PROFESSOR)!!, Location(137, 26)) + animate(getNPC(PROFESSOR)!!, CRANK) + sendChat(getNPC(PROFESSOR)!!, "And all our work pays off.") + timedUpdate(6) + } + 34 -> { + animate(getNPC(PROFESSOR)!!, CHEER) + timedUpdate(10) + } + 35 -> { + moveCamera(6, 27, 500, 5) + rotateCamera(10, 25, 50, 5) + timedUpdate(3) + } + 36 -> { + end { + setAttribute(player, attributeFinishedCutscene, true) + } + } + } + } + + companion object { + val PROFESSOR = NPCs.OBSERVATORY_PROFESSOR_6121 + val WAVE = Animations.HUMAN_WAVE_863 + val THINKING = 6844 + val FIX = 6847 + val TUNEUP = 6848 + val CRANK = 6845 + val CHEER = Animations.HUMAN_CHEER_862 + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryProfessorDialogue.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryProfessorDialogue.kt new file mode 100644 index 000000000..380334049 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryProfessorDialogue.kt @@ -0,0 +1,676 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.* +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeReceivedWine +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ObservatoryProfessorDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return ObservatoryProfessorDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ObservatoryProfessorDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.OBSERVATORY_ASSISTANT_6119, NPCs.OBSERVATORY_ASSISTANT_6120) + } +} + +class ObservatoryProfessorDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.OBSERVATORY_PROFESSOR_488) + + npc("What would you like to talk about?") + options( + DialogueOption("observatoryquest", "Talk about the Observatory Quest.", skipPlayer = true), + DialogueOption("treasuretrails", "Talk about the Treasure Trails.", skipPlayer = true), + ) + + label("observatoryquest") + exec { player, npc -> + loadLabel(player, "observatoryqueststage" + getQuestStage(player, Quests.OBSERVATORY_QUEST)) + } + + label("observatoryqueststage0") + player("Hi, I was...") + npc(FacialExpression.FRIENDLY, "Welcome to the magnificent wonder of the Observatory, where wonder is all around you, where the stars can be clutched from the heavens!") + player("Wow, nice intro.") + npc(FacialExpression.FRIENDLY, "Why, thanks! How might I help you?") + options( + DialogueOption("totallylost", "I'm totally lost."), + DialogueOption("whatwhat", "An Observatory?", expression = FacialExpression.THINKING), + DialogueOption("nah", "I'm just passing through."), + ) + + label("totallylost") + npc(FacialExpression.THINKING, "Lost? It must have been those pesky goblins that led you astray.") + npc("Head north-east to find the city of Ardougne.") + player("I'm sure I'll find the way.") + player("Thanks for all your help.") + npc(FacialExpression.FRIENDLY, "No problem at all. Come and visit again!") + line("The professor carries on with his studies.") + + label("nah") + npc("Fair enough. Not everyone is interested in this place, I suppose.") + + label("whatwhat") + npc(FacialExpression.FRIENDLY, "Of course. We have a superb telescope up in the Observatory, on the hill.") + npc(FacialExpression.FRIENDLY, "A truly marvellous invention, the likes of which you'll never behold again.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Well, it would be if it worked.") + npc(FacialExpression.ANGRY, "Don't interrupt!") + player(FacialExpression.THINKING, "What? It doesn't work?") + npc("Oh, no, no, no. Don't listen to him, he's joking. Aren't you, my FAITHFUL assistant?") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Nope, dead serious. Hasn't been working for a long time.") + npc(FacialExpression.ANGRY, "Arghhh! Get back to work and stop sticking your nose in!") + player("So, it's broken. How come?") + npc(FacialExpression.SAD, "Oh, I suppose there's no use keeping it secret. Did you see those houses outside?") + player("Up on the hill? Yes, I've seen them.") + npc("It's a horde of goblins.") + npc(FacialExpression.SAD, "Since they moved here they have caused nothing but trouble.") + npc("Last week, my telescope was tampered with.") + npc(FacialExpression.ANGRY, "Now, parts need replacing before it can be used again. They've even been messing around in the dungeons under this area. Something needs to be done.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Strikes me that this visitor could help us.") + npc(FacialExpression.ANGRY, "Stop being so rude.") + npc("...") + npc("Although, he has a point. What do you say?") + player(FacialExpression.THINKING, "What, me?") + options( + DialogueOption("yestoquest", "Sounds interesting. How can I help?", "Sounds interesting, what can I do for you?", FacialExpression.FRIENDLY), + DialogueOption("notoquest", "Oh, sorry, I don't have time for that."), + ) + + label("notoquest") + npc("Oh dear. I really do need some help.") + npc("If you see anyone who can help then please send them my way.") + + label("yestoquest") + exec { player, _ -> + if(getQuestStage(player, Quests.OBSERVATORY_QUEST) == 0) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 1) + } + } + npc(FacialExpression.FRIENDLY, "Oh, thanks so much.") + npc(FacialExpression.FRIENDLY, "I shall need some materials for the telescope, so it can be used again.") + npc("Let's start with three planks of wood for the telescope base. My assistant will help with obtaining these, won't you?") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "As if I don't have enough work to do. Seems I don't have a choice.") + npc(FacialExpression.FRIENDLY, "Go talk to him if you need some advice.") + player(FacialExpression.FRIENDLY, "Okay, I'll be right back.") + + label("observatoryqueststage1") + player(FacialExpression.FRIENDLY, "Hi again!") + npc(FacialExpression.FRIENDLY, "It's my helping hand, back again.") + npc(FacialExpression.THINKING, "Do you have the planks yet?") + exec { player, _ -> + if(inInventory(player, Items.PLANK_960, 3)) { + loadLabel(player, "enoughplanks") + } else { + loadLabel(player, "notenoughplanks") + } + } + + label("notenoughplanks") + player("Sorry, not yet. Three planks was it?") + npc("It was indeed.") + + label("enoughplanks") + player("Yes, I've got them. Here they are.") + exec { player, _ -> + if(removeItem(player, Item(Items.PLANK_960, 3))) { + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 1) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 2) + } + } + } + npc(FacialExpression.FRIENDLY, "Well done. This will make a big difference.") + npc(FacialExpression.FRIENDLY, "Now, the bronze for the tube. Oh, assistant!") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Okay, okay, ask me if you need any help, @name.") + + label("observatoryqueststage2") + player("Hi.") + npc("The traveller returns!") + player("Still working hard?") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Some of us are.") + npc(FacialExpression.ANGRY, "What did I tell you about speaking when spoken to?") + npc(FacialExpression.THINKING, "So, @name, you have the bronze bar?") + exec { player, _ -> + if(inInventory(player, Items.BRONZE_BAR_2349)) { + loadLabel(player, "hasbronzebar") + } else { + loadLabel(player, "nohasbronzebar") + } + } + + label("nohasbronzebar") + player("Not yet.") + npc("Please bring me one, then.") + + label("hasbronzebar") + player(FacialExpression.FRIENDLY, "I certainly do. Here you go.") + exec { player, _ -> + if(removeItem(player, Item(Items.BRONZE_BAR_2349))) { + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 2) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 3) + } + } + } + npc(FacialExpression.FRIENDLY, "Great. Now all I need is the lens made.") + npc(FacialExpression.FRIENDLY, "Please get me some molten glass.") + npc("Oi! Lazy bones!") + player(FacialExpression.AMAZED, "What? I'm not lazy.") + npc("Not you! I'm talking to my assistant.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Calm down old man, I heard. @name, I'm here if you need any help.") + npc("Thank you. Wait a minute, who are you calling 'old'?") + + label("observatoryqueststage3") + npc("How are you getting on finding me some molten glass?") + exec { player, _ -> + if(inInventory(player, Items.MOLTEN_GLASS_1775)) { + loadLabel(player, "hasmoltenglass") + } else { + loadLabel(player, "nohasmoltenglass") + } + } + + label("nohasmoltenglass") + player("Still working on it.") + npc("I really need it. Please hurry.") + + label("hasmoltenglass") + player(FacialExpression.FRIENDLY, "Here it is.") + exec { player, _ -> + if(removeItem(player, Item(Items.MOLTEN_GLASS_1775))) { + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 3) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 4) + } + } + } + npc(FacialExpression.FRIENDLY, "Excellent work, let's make the lens.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "It'll need to be made to an exact shape and size.") + npc("Well, obviously, hence why we have a lens mould.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Not any more. One of those goblins took it.") + npc(FacialExpression.SAD, "Great, just what I need. @name, I don't suppose you could find it?") + player(FacialExpression.FRIENDLY, "I'll have a look - where should I start?") + npc(FacialExpression.FRIENDLY, "No idea. You could ask my USELESS assistant if you want.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "What have I done to deserve this?") + + label("observatoryqueststage4") + npc(FacialExpression.THINKING, "Did you bring me the mould?") + exec { player, _ -> + if(inInventory(player, Items.LENS_MOULD_602)) { + loadLabel(player, "haslensmould") + } else { + loadLabel(player, "nohaslensmould") + } + } + + label("nohaslensmould") + player("Still looking for it.") + npc("Please try and find it; my assistant may be able to help.") + + label("haslensmould") + player(FacialExpression.HAPPY, "I certainly have. You'll never guess what they were doing with it.") + npc("Well, from the smell I'd guess cooking some vile concoction.") + player(FacialExpression.HAPPY, "Wow, good guess. Well, here you go.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Please don't give that to him. Last time he tried any Crafting, I had to spend a week cleaning up after the explosion.") + player("Explosion?") + npc(FacialExpression.SUSPICIOUS, "Erm, yes. I think in this instance you had probably better do it.") + player("I suppose it's better I don't ask.") + npc(FacialExpression.HAPPY,"You can use the mould with molten glass to make a new lens.") + exec { player, _ -> + if(inInventory(player, Items.LENS_MOULD_602)) { + addItemOrDrop(player, Items.MOLTEN_GLASS_1775) + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 4) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 5) + } + } + } + item(Item(Items.MOLTEN_GLASS_1775), "The professor gives you back the molten glass.") + + + label("observatoryqueststage5") + npc("Is the lens finished?") + exec { player, _ -> + if(inInventory(player, Items.OBSERVATORY_LENS_603)) { + loadLabel(player, "haslens") + } else { + loadLabel(player, "nohaslens") + } + } + + label("nohaslens") + player("How do I make it again?") + npc("Use the molten glass with the mould.") + player("Huh. Simple.") + + label("haslens") + player("Yes, here it is. You may as well take this mould too.") //No source if no mould present + line("The player hands the observatory professor the observatory lens.") + npc("Wonderful, at last I can fix the telescope.") + npc("Would you accompany me to the Observatory? You simply must see the telescope in operation.") + player("Sounds interesting. Count me in.") + npc("Superb. You'll have to go via the dungeon under the goblin settlement, seeing as the bridge is broken. You'll find stairs up to the Observatory from there.") + player("Okay. See you there.") + exec { player, _ -> + setVarbit(player, ObservatoryQuest.telescopeVarbit, 1, true) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + openOverlay(player, Components.FADE_TO_BLACK_120) + return@queueScript delayScript(player, 4) + } + 1 -> { + if(removeItem(player, Item(Items.OBSERVATORY_LENS_603))) { + removeItem(player, Item(Items.LENS_MOULD_602)) + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 5) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 6) + } + } + return@queueScript delayScript(player, 1) + } + 2 -> { + openOverlay(player, Components.FADE_FROM_BLACK_170) + return@queueScript delayScript(player, 2) + } + 3 -> { + sendDialogueLines(player, "The professor has gone ahead to the Observatory dome. Best you", "follow him to see the finished telescope.") + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + + + } + + label("observatoryqueststage6") + npc("Hello, friend.") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) != null) { + loadLabel(player, "hasviewtelescope") + } else { + loadLabel(player, "nohasviewtelescope") + } + } + label("nohasviewtelescope") + // http://youtu.be/cK5suqcw3qU + player("Hi, this really is impressive.") + npc("Certainly is. Please, take a look through the telescope and tell me what you see.") + + label("hasviewtelescope") + player("I've had a look through the telescope.") + npc("What did you see? If you're not sure, you can find out by looking at the star charts dotted around the walls downstairs.") + player("It was...") + goto("page1") + + + label("page1") + options( + DialogueOption("Aquarius", "Aquarius", skipPlayer = true), + DialogueOption("Capricorn", "Capricorn", skipPlayer = true), + DialogueOption("Sagittarius", "Sagittarius", skipPlayer = true), + DialogueOption("Scorpio", "Scorpio", skipPlayer = true), + DialogueOption("page2", "~ next ~", skipPlayer = true), + ) + + label("page2") + options( + DialogueOption("page1", "~ previous ~", skipPlayer = true), + DialogueOption("Libra", "Libra", skipPlayer = true), + DialogueOption("Virgo", "Virgo", skipPlayer = true), + DialogueOption("Leo", "Leo", skipPlayer = true), + DialogueOption("page3", "~ next ~", skipPlayer = true), + ) + + label("page3") + options( + DialogueOption("page2", "~ previous ~", skipPlayer = true), + DialogueOption("Cancer", "Cancer", skipPlayer = true), + DialogueOption("Gemini", "Gemini", skipPlayer = true), + DialogueOption("Taurus", "Taurus", skipPlayer = true), + DialogueOption("page4", "~ next ~", skipPlayer = true), + ) + + label("page4") + options( + DialogueOption("page3", "~ previous ~", skipPlayer = true), + DialogueOption("Aries", "Aries", skipPlayer = true), + DialogueOption("Pisces", "Pisces", skipPlayer = true), + ) + + label("Aquarius") + player("Aquarius!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 19) { + loadLabel(player, "Aquariuscorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Aquariuscorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Aquarius, the water-bearer.") + npc("It seems suitable, then, to award you with water runes!") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.WATER_RUNE_555, 25) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Aries") + player("Aries!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 20) { + loadLabel(player, "Ariescorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Ariescorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Aries, the ram.") + npc("A fierce fighter. I'm sure he'll look down on you and improve your Attack for such insight.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + rewardXP(player, Skills.ATTACK, 875.0) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Cancer") + player("Cancer!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 21) { + loadLabel(player, "Cancercorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Cancercorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Cancer, the crab.") + npc("An armoured creature - I think I shall reward you with an amulet of protection.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.AMULET_OF_DEFENCE_1729) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Capricorn") + player("Capricorn!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 22) { + loadLabel(player, "Capricorncorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Capricorncorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Capricorn, the goat.") + npc("Capricorn will surely reward your insight with an increase to your Strength.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + rewardXP(player, Skills.STRENGTH, 875.0) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Gemini") + player("Gemini!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 23) { + loadLabel(player, "Geminicorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Geminicorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Gemini, the twins.") + npc("With the double nature of Gemini, I can't offer you anything more suitable than a two-handed weapon.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.BLACK_2H_SWORD_1313) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Leo") + player("Leo!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 24) { + loadLabel(player, "Leocorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Leocorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Leo, the lion.") + npc("I think the majestic power of the lion will raise your Hitpoints.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + rewardXP(player, Skills.HITPOINTS, 875.0) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Libra") + player("Libra!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 25) { + loadLabel(player, "Libracorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Libracorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Libra, the scales.") + npc("Hmmm, balance, law, order - I shall award you with law runes!") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.LAW_RUNE_563, 25) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Pisces") + player("Pisces!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 26) { + loadLabel(player, "Piscescorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Piscescorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Pisces, the fish.") + npc("What's more suitable as a reward than some tuna?") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.TUNA_361, 3) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Sagittarius") + player("Sagittarius!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 27) { + loadLabel(player, "Sagittariuscorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Sagittariuscorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Sagittarius, the centaur.") + npc("As you've spotted the archer, I shall reward you with a maple longbow.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.MAPLE_LONGBOW_851) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Scorpio") + player("Scorpio!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 28) { + loadLabel(player, "Scorpiocorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Scorpiocorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Scorpio, the scorpion.") + npc("I think weapon poison would make a suitable reward.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.WEAPON_POISON_187) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Taurus") + player("Taurus!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 29) { + loadLabel(player, "Tauruscorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Tauruscorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Taurus, the bull.") + npc("This Strength potion should be a suitable reward.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.SUPER_STRENGTH1_161) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Virgo") + player("Virgo!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 30) { + loadLabel(player, "Virgocorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Virgocorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Virgo, the virtuous.") + npc("Virgo will surely provide you with an increase to Defence.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + rewardXP(player, Skills.DEFENCE, 875.0) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("wrongstar") + npc("I'm afraid not. Have another look. Remember, you can check the star charts on the walls for reference.") + + // http://youtu.be/Z5RRnZl2vTg + label("observatoryqueststage100") + npc("Thanks for all your help with the telescope. What can I do for you?") + options( + DialogueOption("needmorehelp", "Do you need any more help with the telescope?", expression = FacialExpression.ASKING), + DialogueOption("mambodunaroona", "Is it true your name is Mambo-duna-roona?", expression = FacialExpression.ASKING) { player, _ -> + return@DialogueOption getAttribute(player, attributeReceivedWine, false) + }, + DialogueOption("nevermind", "Nothing, thanks."), + ) + + label("needmorehelp") + npc("Not right now,") + npc("but the stars may hold a secret for you.") + + label("nevermind") + npc("Okay, no problem. See you again.") + + label("treasuretrails") + npc("Welcome back! How can I help you today?") + player("Can you teach me to solve treasure trail clues?") + npc("Ah, I get asked about treasure trails all the time! Listen carefully and I shall tell you what I know...") + npc("Lots of clues have degrees and minutes written on them. These are the coordinates of the place where the treasure is buried.") + npc("You have to walk to the correct spot, so that your coordinates are exactly the same as the values written on the clue scroll.") + npc("To do this, you must use a sextant, a watch and a chart to find your own coordinates.") + npc("Once you know the coordinates of the place where you are, you know which way you have to walk to get to the place where the treasure is!") + player("Riiight. So where do I get those items from?") + npc("I think Murphy, the owner of the fishing trawler moored south of Ardougne, might be able to spare you a sextant. Then the nearest Clock Tower is south of Ardougne - you could probably get a watch there. I've") + npc("got plenty of charts myself, here have one.") + + label("mambodunaroona") + npc(FacialExpression.AMAZED, "How do you know tha-") + npc(FacialExpression.SUSPICIOUS, "I mean, of course not, what a silly idea.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuest.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuest.kt new file mode 100644 index 000000000..ce6ba2513 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuest.kt @@ -0,0 +1,159 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import core.api.* +import core.api.setVarp +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +// http://youtu.be/TWIkCRRea8A The initial quest log. +// http://youtu.be/onHNm9Z5L-o The best full quest log. +// http://youtu.be/cq-jGTXXHuE +// http://youtu.be/ekYr30h43ag Scorpius. +/** + * Observatory Quest + */ +@Initializable +class ObservatoryQuest : Quest(Quests.OBSERVATORY_QUEST, 96, 95, 2,112, 0, 1, 7) { + + companion object { + val questName = Quests.OBSERVATORY_QUEST + const val observatoryVarp = 112 + const val goblinStoveVarbit = 3837 + const val telescopeVarbit = 3838 + + const val starChartsInterface = 104 + const val telescopeInterface = 552 + const val dungeonWarning = 560 + + const val attributeKilledGuard = "/save:quest:observatoryquest-killedguard" + const val attributeUnlockedGate = "/save:quest:observatoryquest-unlockedgate" + const val attributeTelescopeStar = "/save:quest:observatoryquest-telescopestar" // NULL - not seen telescope, 19 - 30 one of the random star patterns + const val attributeReceivedWine = "/save:quest:observatoryquest-receivedwine" + const val attributeReceivedMould = "/save:quest:observatoryquest-receivedmould" + const val attributeRandomChest = "/save:quest:observatoryquest-randomchest" + const val attributeFinishedCutscene = "/save:quest:observatoryquest-finishedcutscene" + + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, questName) > 0 + + if (!started) { + line(player, "I can start this quest by speaking to the !!professor?? in the", line++, false) + line(player, "!!Observatory reception, south-west of Ardougne.??", line++, false) + } else { + line(player, "I can start this quest by speaking to the professor in the", line++, true) + line(player, "Observatory reception, south-west of Ardougne.", line++, true) + + if (stage >= 5) { + line(player, "I should take the letter the Examiner has given me to the", line++, true) + line(player, "Curator of Varrock Museum, for his approval.", line++, true) + line++ + } else if (stage >= 1) { + line(player, "Seems the observatory telescope needs repairing, due to", line++) + line(player, "the nearby goblins. The !!professor?? wants me to help by", line++) + line(player, "getting the following, with the help of his !!assistant??:", line++) + line++ + } + if (stage >= 2) { + line(player, "!!3 plain wooden planks??", line++, true) + } else if (stage >= 1) { + line(player, "!!3 plain wooden planks??", line++, inInventory(player, Items.PLANK_960, 3)) + } + if (stage >= 3) { + line(player, "!!1 bronze bar??", line++, true) + } else if (stage >= 2) { + line(player, "!!1 bronze bar??", line++, inInventory(player, Items.BRONZE_BAR_2349)) + } + if (stage >= 4) { + line(player, "!!1 molten glass??", line++, true) + } else if (stage >= 3) { + line(player, "!!1 molten glass??", line++, inInventory(player, Items.MOLTEN_GLASS_1775)) + } + if (stage >= 5) { + line(player, "!!1 lens mould??", line++, true) + } else if (stage >= 4) { + line(player, "!!1 lens mould??", line++) + } + + if (stage >= 6) { + line(player, "The professor was pleased to have all the pieces needed", line++, true) + line(player, "to fix the telescope. Apparently, the professor's last", line++, true) + line(player, "attempt at Crafting ended in disaster. So, he wants me to", line++, true) + line(player, "create the lens by using the molten glass with the mould.", line++, true) + line(player, "Fine by me!", line++, true) + } else if (stage >= 5) { + line(player, "The !!professor?? was pleased to have all the pieces needed", line++) + line(player, "to fix the telescope. Apparently, the professor's last", line++) + line(player, "attempt at Crafting ended in disaster. So, he wants me to", line++) + line(player, "create the !!lens?? by using the !!molten glass?? with the !!mould??.", line++) + line(player, "Fine by me!", line++) + } + + if (stage >= 100) { + line(player, "The professor has gone ahead to the Observatory. He", line++, true) + line(player, "wants me to meet him there by travelling through the", line++, true) + line(player, "dungeon below it.", line++, true) + } else if (stage >= 6) { + line(player, "The !!professor?? has gone ahead to the !!Observatory??. He", line++) + line(player, "wants me to meet him there by travelling through the", line++) + line(player, "!!dungeon?? below it.", line++) + } + if (stage >= 100) { + line++ + line(player,"QUEST COMPLETE!", line) + } + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Observatory Quest!", 277, 4) + // This image is special since it isn't an item, but a standalone model. + player.packetDispatch.sendModelOnInterface(1174, 277, 5, 0) // Scenery 2210, Model 1174 + player.packetDispatch.sendAngleOnInterface(277, 5, 2040, 0, 1836) + + drawReward(player, "2 Quest Points", ln++) + drawReward(player, "2,250 Crafting XP", ln++) + drawReward(player, "A payment depending on", ln++) + drawReward(player, "which constellation you", ln++) + drawReward(player, "observed", ln++) + + rewardXP(player, Skills.CRAFTING, 2250.0) + } + + override fun updateVarps(player: Player) { + setVarp(player, observatoryVarp, getQuestStage(player, questName), true) + if(getQuestStage(player, questName) >= 6) { + setVarbit(player, telescopeVarbit, 1, true) + } else { + setVarbit(player, telescopeVarbit, 0, true) + } + if(getQuestStage(player, questName) >= 7) { + setVarp(player, observatoryVarp, 7, true) + } + } + + override fun reset(player : Player) { + removeAttribute(player, attributeKilledGuard) + removeAttribute(player, attributeUnlockedGate) + removeAttribute(player, attributeTelescopeStar) + removeAttribute(player, attributeReceivedWine) + removeAttribute(player, attributeReceivedMould) + removeAttribute(player, attributeRandomChest) + removeAttribute(player, attributeFinishedCutscene) + setVarbit(player, 3837, 0, true) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestInterfaces.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestInterfaces.kt new file mode 100644 index 000000000..a6627b98a --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestInterfaces.kt @@ -0,0 +1,59 @@ +package content.region.kandarin.quest.observatoryquest + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.world.map.Location + +class ObservatoryQuestInterfaces : InterfaceListener { + + companion object { + + val buttonToNameMapping = mapOf( + 19 to "Aquarius", + 20 to "Aries", + 21 to "Cancer", + 22 to "Capricorn", + 23 to "Gemini", + 24 to "Leo", + 25 to "Libra", + 26 to "Pisces", + 27 to "Sagittarius", + 28 to "Scorpio", + 29 to "Taurus", + 30 to "Virgo", + ) + + val buttonToStarObjectMapping = mapOf( + 19 to 27064, + 20 to 27066, + 21 to 27067, + 22 to 27061, + 23 to 27068, + 24 to 27058, + 25 to 27057, + 26 to 27062, + 27 to 27056, + 28 to 27055, + 29 to 27059, + 30 to 27060, + ) + } + + override fun defineInterfaceListeners() { + on(ObservatoryQuest.starChartsInterface) { player, component, opcode, buttonID, slot, itemID -> + if(buttonToStarObjectMapping.contains(buttonID)){ + // 55 57 + player.packetDispatch.sendModelOnInterface(buttonToStarObjectMapping[buttonID]!!, ObservatoryQuest.starChartsInterface, 55, 0) + setInterfaceText(player, buttonToNameMapping[buttonID]!!, ObservatoryQuest.starChartsInterface, 57) + } + return@on true + } + on(ObservatoryQuest.dungeonWarning) { player, _, _, buttonID, _, _ -> + when (buttonID) { + 17 -> teleport(player, Location(2355, 9394)).also { closeInterface(player) } + 18 -> closeInterface(player) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestListeners.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestListeners.kt new file mode 100644 index 000000000..c718f9a10 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestListeners.kt @@ -0,0 +1,294 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeFinishedCutscene +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeRandomChest +import core.ServerConstants +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class ObservatoryQuestListeners : InteractionListener { + + companion object { + val chestMap = mapOf( + Scenery.CHEST_2191 to Scenery.CHEST_2194, + Scenery.CHEST_25385 to Scenery.CHEST_25386, + Scenery.CHEST_25387 to Scenery.CHEST_25388, + Scenery.CHEST_25389 to Scenery.CHEST_25390, + Scenery.CHEST_25391 to Scenery.CHEST_25392 + ) + + val reverseChestMap = chestMap.map { (k, v) -> v to k }.toMap() + + /* Comments show only the default order BEFORE randomization. A randomized + number (0-10) is calculated the first time the player goes down the stairs, + and is then added to the map value modulo 11, rotating the map. + */ + + val chestLocations = mapOf( + Location(2333, 9405) to 0, // key + Location(2312, 9400) to 1, // spider + Location(2310, 9374) to 2, // spider + Location(2348, 9383) to 3, // empty + Location(2356, 9380) to 4, // spider + Location(2359, 9376) to 5, // empty + Location(2360, 9366) to 6, // empty + Location(2351, 9361) to 7, // spider + Location(2364, 9355) to 8, // antipoison + Location(2335, 9374) to 9, // spider + Location(2326, 9360) to 10) // empty + } + + override fun defineListeners() { + + on(Scenery.SIGNPOST_25397, SCENERY, "read") { player, _ -> + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + val servername = ServerConstants.SERVER_NAME + when(stage){ + 0 -> sendDialogueLines(player, "~ The Observatory ~", "Step ahead to the reception if you wish to explore $servername's most", "magnificent invention.").also { + stage++ + } + 1 -> player(FacialExpression.NEUTRAL, "Magnificent invention? I've seen some pretty magnificent", "things in my time. It'll have to be pretty impressive.").also { stage = END_DIALOGUE } + } + } + }) + return@on true + } + + on(Scenery.ORRERY_25401, SCENERY, "view") { player, _ -> + sendChat(player, "Oooooh, bizarre!") + return@on true + } + + on(Scenery.STAIRS_25432, SCENERY, "climb-down") { player, _ -> + openInterface(player, 560) + if (getAttribute(player, attributeRandomChest, null) == null) { + setAttribute(player, attributeRandomChest, RandomFunction.random(11)) + } + return@on true + } + // http://youtu.be/Kg84MYXgo-M -> You can always go to the observatory, no matter which stage. + on(Scenery.STAIRS_25429, SCENERY, "climb up") { player, node -> + if (node.location == Location(2335, 9351)) { + if (!getAttribute(player, attributeFinishedCutscene, false) && getQuestStage(player, Quests.OBSERVATORY_QUEST) == 6) { + ObservatoryCutscene(player).start() + } + else teleport(player, Location(2439, 3164)) + } else { + teleport(player, Location(2457, 3186)) + } + return@on true + } + + on(Scenery.STAIRS_25434, SCENERY, "climb-down") { player, _ -> + teleport(player, Location(2335, 9350)) + return@on true + } + + on(Scenery.STAIRS_25431, SCENERY, "climb-up") { player, _ -> + teleport(player, Location(2443, 3160, 1)) + return@on true + } + + on(Scenery.STAIRS_25437, SCENERY, "climb-down") { player, _ -> + teleport(player, Location(2444, 3162, 0)) + return@on true + } + + // All chests + on(chestMap.keys.toIntArray(), SCENERY, "open") { player, node -> + animate(player, 536) + sendMessage(player, "You open the chest.") + chestMap[node.id]?.let { replaceScenery(node as core.game.node.scenery.Scenery, it, 240) } + return@on true + } + on(chestMap.values.toIntArray(), SCENERY, "close") { player, node -> + animate(player, 535) + reverseChestMap[node.id]?.let { replaceScenery(node as core.game.node.scenery.Scenery, it, -1) } + return@on true + } + + on(chestMap.values.toIntArray(), SCENERY, "search") { player, node -> + val chest = chestLocations[node.location]?.plus(getAttribute(player, attributeRandomChest, 0))?.mod(11) + when (chest) { + 0 -> { + if (inInventory(player, Items.GOBLIN_KITCHEN_KEY_601) || getQuestStage(player, Quests.OBSERVATORY_QUEST) != 4 || getAttribute(player, ObservatoryQuest.attributeUnlockedGate, false)) { + sendMessage(player, "You search the chest.") + sendMessage(player, "The chest is empty.") + } else { + lock(player,2) + queueScript(player, 2, QueueStrength.STRONG) { + if (inInventory(player, Items.GOBLIN_KITCHEN_KEY_601)) { + return@queueScript stopExecuting(player) + } + addItemOrDrop(player, Items.GOBLIN_KITCHEN_KEY_601) + sendItemDialogue(player, Items.GOBLIN_KITCHEN_KEY_601,"You find a kitchen key.") + return@queueScript stopExecuting(player) + } + } + animate(player, 537) + return@on true + } + 1, 2, 4, 7, 9 -> { + sendMessage(player, "You search the chest.") + if (findLocalNPC(player, NPCs.POISON_SPIDER_1009) != null) { + sendMessage(player, "The chest is empty.") + animate(player, 537) + return@on true + } + sendMessage(player, "The chest contains a poisonous spider.") + val npc = NPC(NPCs.POISON_SPIDER_1009) + npc.location = player.location + npc.init() + npc.isRespawn = false + npc.moveStep() + npc.face(player) + animate(player, 537) + return@on true + } + 8 -> { + sendMessage(player, "You search the chest.") + lock(player,2) + queueScript(player, 2, QueueStrength.STRONG) { + addItemOrDrop(player, Items.ANTIPOISON1_179) + sendMessage(player,"This chest contains some antipoison.") + return@queueScript stopExecuting(player) + } + animate(player, 537) + return@on true + } + else -> { + sendMessage(player, "You search the chest.") + sendMessage(player, "The chest is empty.") + animate(player, 537) + return@on true + } + } + } + + on(intArrayOf(Scenery.KITCHEN_GATE_2199, Scenery.KITCHEN_GATE_2200), SCENERY, "open") { player, node -> + if (getAttribute(player, ObservatoryQuest.attributeUnlockedGate, false)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else if (getAttribute(player, ObservatoryQuest.attributeKilledGuard, false)) { + if (removeItem(player, Items.GOBLIN_KITCHEN_KEY_601)) { + sendMessage(player, "The gate unlocks.") + sendMessage(player, "The key is useless now. You discard it.") + setAttribute(player, ObservatoryQuest.attributeUnlockedGate, true) + sendPlayerDialogue(player, "I had better be quick, there may be more guards about.") + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + // http://youtu.be/ZkUF-0eonls + sendMessage(player, "The gate is locked.") + } + } else if (getQuestStage(player, Quests.OBSERVATORY_QUEST) >= 4){ + sendMessage(player, "If you open the gate, the guard will hear you. You need to get rid of him.") + } else sendMessage(player, "The gate is locked.") + return@on true + } + + on(NPCs.SLEEPING_GUARD_6122, NPC, "prod") { player, node -> + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) < 4) { + return@on true + } + sendChat(node as NPC, "Oi, how dare you wake me up!") + transformNpc(node, NPCs.GOBLIN_GUARD_489, 400) + node.attack(player) + return@on true + } + + on(Scenery.GOBLIN_STOVE_25440, SCENERY, "inspect") { player, _ -> + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> sendDialogueLines(player, "The goblins appear to have been using the lens mould to cook their", "stew!").also { stage++ } + 1 -> sendDialogueLines(player, "You shake out its contents and take it with you.").also { animate(player, 537); stage++ } + 2 -> end().also { + sendChat(player, "Euuuw, that smells awful!") + setVarbit(player, ObservatoryQuest.goblinStoveVarbit ,1) + addItemOrDrop(player, Items.LENS_MOULD_602) + } + } + } + }) + return@on true + } + + on(Scenery.GOBLIN_STOVE_25441, SCENERY, "inspect") { player, _ -> + if (!inInventory(player, Items.LENS_MOULD_602)) addItemOrDrop(player, Items.LENS_MOULD_602) + return@on true + } + + onUseWith(ITEM, Items.LENS_MOULD_602, Items.MOLTEN_GLASS_1775) { player, _, with -> + if (getStatLevel(player, Skills.CRAFTING) < 10) { + sendMessage(player, "You need a crafting level of 10 to do this.") + return@onUseWith true + } + + sendMessage(player, "You pour the molten glass into the mould.") + sendMessage(player, "You clasp it together.") + sendItemDialogue(player, Items.OBSERVATORY_LENS_603, "It has produced a small, convex glass disc.") + if (removeItem(player, with)) { + addItemOrDrop(player, Items.OBSERVATORY_LENS_603) + } + return@onUseWith true + } + + on(intArrayOf(Scenery.STAR_CHART_25578, Scenery.STAR_CHART_25579, Scenery.STAR_CHART_25580, Scenery.STAR_CHART_25581, Scenery.STAR_CHART_25582, Scenery.STAR_CHART_25583), SCENERY, "look-at") { player, _ -> + openInterface(player, ObservatoryQuest.starChartsInterface) + return@on true + } + + on(intArrayOf(Scenery.TELESCOPE_25438, Scenery.TELESCOPE_25439), SCENERY, "view") { player, _ -> + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) >= 100) { + openDialogue(player, TelescopeDialogue(), NPC(NPCs.OBSERVATORY_PROFESSOR_488)) + } + else if (getQuestStage(player, Quests.OBSERVATORY_QUEST) >= 6) { + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == null) { + setAttribute(player, ObservatoryQuest.attributeTelescopeStar, (19..30).random()) + } + val randomStar = getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) + openInterface(player, ObservatoryQuest.telescopeInterface) + player.packetDispatch.sendModelOnInterface(ObservatoryQuestInterfaces.buttonToStarObjectMapping[randomStar]!!, ObservatoryQuest.telescopeInterface, 7, 0) + } else { + sendMessage(player, "The telescope is broken.") + } + return@on true + } + + on(intArrayOf(Scenery.DOOR_25526, Scenery.DOOR_25527), SCENERY, "open") { player, _ -> + sendMessage(player, "The door is locked.") + return@on true + } + + on(Scenery.GRAVE_OF_SCORPIUS_2211, SCENERY, "read") { player, _ -> + sendMessage(player, "Here lies Scorpius: Only those who have seen beyond the stars may seek his counsel.") + return@on true + } + } +} + +class TelescopeDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npcl(FacialExpression.ASKING, "What do you see now?").also { stage++ } + 1 -> playerl("I can see a constellation through the telescope. It looks like Scorpio.").also { stage++ } + 2 -> npcl("Scorpio? Interesting. How very fitting.").also { stage++ } + 3 -> playerl(FacialExpression.ASKING, " What do you mean?").also { stage++ } + 4 -> npcl("Scorpius, the founder of all we know relating to astronomy. There's a book about him in the reception. Perhaps you should check it out, you might learn something.").also { stage++ } + 5 -> playerl("Then perhaps I shall.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/PoisonSpiderBehavior.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/PoisonSpiderBehavior.kt new file mode 100644 index 000000000..dc4f0b74d --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/PoisonSpiderBehavior.kt @@ -0,0 +1,20 @@ +package content.region.kandarin.quest.observatoryquest + +import core.api.getAttribute +import core.api.setAttribute +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.world.GameWorld +import org.rs09.consts.NPCs + +class PoisonSpiderBehavior : NPCBehavior(NPCs.POISON_SPIDER_1009) { + override fun onCreation(self: NPC) { + setAttribute(self, "despawn-time", GameWorld.ticks + 100) + } + + override fun tick(self: NPC): Boolean { + if (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks && !self.inCombat()) + self.clear() + return true + } +} diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/SpiritOfScorpiusDialogue.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/SpiritOfScorpiusDialogue.kt new file mode 100644 index 000000000..6d8fd5815 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/SpiritOfScorpiusDialogue.kt @@ -0,0 +1,110 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeReceivedMould +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class SpiritOfScorpiusDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return SpiritOfScorpiusDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, SpiritOfScorpiusDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.SPIRIT_OF_SCORPIUS_492) + } +} + +class SpiritOfScorpiusDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SPIRIT_OF_SCORPIUS_492) + + exec { player, npc -> + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 100) { + if (!inEquipment(player, Items.GHOSTSPEAK_AMULET_552)) loadLabel(player, "noghostspeak") + else loadLabel(player, "observatoryqueststage100") + } else loadLabel(player, "observatoryquestincomplete") + } + + label("observatoryquestincomplete") + line("They seem to be ignoring you.") + + label("noghostspeak") + line("This powerful spirit seems capable of speaking to you", "even though you are not wearing an Amulet of Ghostspeak.") + exec { player, _ -> + loadLabel(player, "observatoryqueststage100") + } + + label("observatoryqueststage100") + npc("Who treads upon my grave?") + options( + DialogueOption("wisdom", "I seek your wisdom.") { player, _ -> + return@DialogueOption !getAttribute(player, attributeReceivedMould, false) + }, + DialogueOption("anothermould", "I need another unholy symbol mould.", "I need another mould for the unholy symbol.") { player, _ -> + return@DialogueOption getAttribute(player, attributeReceivedMould, false) + }, + DialogueOption("blessing", "I have come to seek your blessing."), + DialogueOption("killyou", "I have come to kill you.") + ) + + label("wisdom") + npc("Indeed, I feel you have beheld the far places in the heavens. My Lord instructs me to help you.") + item(Item(Items.UNHOLY_MOULD_1594), "An unholy mould appears in your inventory.") + exec { player, _ -> + addItemOrDrop(player, Items.UNHOLY_MOULD_1594) + setAttribute(player, attributeReceivedMould, true) + } + npc("Here is a mould to make a token for our Lord; a mould for the unholy symbol of Zamorak. Return to me when you desire my blessing.") + + label("anothermould") + exec { player, _ -> + if (inInventory(player, Items.UNHOLY_MOULD_1594)) { + loadLabel(player, "hasmould") + } + else { + addItemOrDrop(player, Items.UNHOLY_MOULD_1594) + loadLabel(player, "lostmould") + } + } + + label("hasmould") + npc("One you already have, another is not needed. Leave me be.") + + label("lostmould") + npc("A lost object is easy to replace. The loss of the affections of our Lord is impossible to forgive.") + + label("blessing") + exec { player, _ -> + if (inInventory(player, Items.UNPOWERED_SYMBOL_1722)) { + loadLabel(player, "canbless") + } + else loadLabel(player, "cannotbless") + } + + label("canbless") + npc("I see you have the unholy symbol of our Lord. I will bless it for you.") + line("The ghost mutters in a strange voice.", "The unholy symbol throbs with power.") + exec { player, _ -> + removeItem(player, Items.UNPOWERED_SYMBOL_1722) + addItemOrDrop(player, Items.UNHOLY_SYMBOL_1724) + } + npc("The symbol of our Lord has been blessed with power! My master calls.") + + + label("cannotbless") + npc("No blessing will be given to those who have no symbol of our Lord's love.") + + label("killyou") + npc("The might of mortals, to me, is as the dust to the sea.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCPeksaDialogue.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCPeksaDialogue.kt new file mode 100644 index 000000000..79df6ceac --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCPeksaDialogue.kt @@ -0,0 +1,41 @@ +package content.region.kandarin.quest.scorpioncatcher + +import core.api.setQuestStage +import core.game.dialogue.DialogueFile +import core.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import content.data.Quests + +class SCPeksaDialogue(val questStage: Int) : DialogueFile() { + + companion object { + const val ASK_ABOUT_SCORPION = START_DIALOGUE + const val ASK_FOR_HELP = 20 + const val THANK_YOU = 30 + + } + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + // Word wrap issues + ASK_ABOUT_SCORPION -> npc("Now how could you know about that, I wonder?", "Mind you, I don't have it anymore.").also { stage++ } + ASK_ABOUT_SCORPION + 1 -> npcl(" I gave it as a present to my brother Ivor when I visited our outpost in the west.").also { stage++ } + ASK_ABOUT_SCORPION + 2 -> npcl("Well, actually I hid it in his bed so it would nip him. It was a bit of a surprise gift.").also { stage++ } + + ASK_ABOUT_SCORPION + 3 -> showTopics( + Topic("So where is this outpost?", ASK_FOR_HELP), + Topic("Thanks for the information", THANK_YOU, true) + ) + + ASK_FOR_HELP -> npcl("Its a fair old trek to the west, across the White Wolf Mountains. Then head west, north-west until you see the axes and horned helmets.").also { stage= + THANK_YOU } + + THANK_YOU -> { + playerl("Thanks for the information").also { stage++ } + setQuestStage(player!!, Quests.SCORPION_CATCHER, ScorpionCatcher.QUEST_STATE_PEKSA_HELP) + } + THANK_YOU + 1 -> npcl ("No problems! Tell Ivor I said hi!").also { stage = END_DIALOGUE } + } + } +} diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCSeerDialogue.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCSeerDialogue.kt new file mode 100644 index 000000000..a15b267c2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCSeerDialogue.kt @@ -0,0 +1,79 @@ +package content.region.kandarin.quest.scorpioncatcher + +import content.region.kandarin.seers.dialogue.SeerDialogue +import core.api.sendDialogue +import core.api.setQuestStage +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import content.data.Quests + +class SCSeerDialogue(val questStage: Int, private val dialogueEntry: Int) : DialogueFile() { + + companion object { + const val FIRST_SCORPION_HELP = 20 + const val FIRST_SCORPION_THORMAC = 30 + const val FIRST_SCORPION_GUIDE = 40 + const val OTHER_SCORPIONS = 50 + + + // There are many ways that this dialogue can be entered + const val ENTRY_HELP = SeerDialogue.SC_QUEST_HELP + const val ENTRY_FRIEND = SeerDialogue.SC_QUEST_FRIEND + const val ENTRY_OTHERS = SeerDialogue.SC_QUEST_OTHER_SCORPIONS + + } + + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> when (dialogueEntry){ + ENTRY_HELP -> npcl("Well you have come to the right place. I am a master of animal detection.").also { stage = FIRST_SCORPION_HELP } + ENTRY_FRIEND -> npcl(FacialExpression.NEUTRAL, "What does the old fellow want?").also { stage = FIRST_SCORPION_THORMAC } + // word wrap doesn't work again + ENTRY_OTHERS -> npc(FacialExpression.NEUTRAL, "Well, I've checked my looking glass. There seems to be", + "a kharid scorpion in a little village to the east,", + "surrounded by lots of uncivilized-looking warriors.", + "Some kind of merchant there seems to have picked it up.").also { stage = OTHER_SCORPIONS } + else -> { + println("Invalid entry to SCSeerDialogue $dialogueEntry") + end() + } + } + + FIRST_SCORPION_THORMAC -> playerl( + FacialExpression.NEUTRAL, + "He's lost his valuable lesser Kharid scorpions." + ).also { stage++ } + FIRST_SCORPION_THORMAC + 1 -> npcl( + FacialExpression.HAPPY, + "Well you have come to the right place. I am a master of animal detection." + ).also { stage = FIRST_SCORPION_GUIDE } + + FIRST_SCORPION_HELP -> npcl("Do you need to locate any particular scorpion? Scorpions are a creature somewhat in abundance.").also { stage++ } + FIRST_SCORPION_HELP + 1 -> playerl("I'm looking for some lesser Kharid scorpions. They belong to Thormac the Sorcerer.").also { + stage = FIRST_SCORPION_GUIDE + } + + FIRST_SCORPION_GUIDE -> npcl(FacialExpression.HAPPY, "Let me look into my looking glass.").also { stage++ } + FIRST_SCORPION_GUIDE + 1 -> sendDialogue(player!!, "The Seer produces a small mirror.").also { stage++ } + FIRST_SCORPION_GUIDE + 2 -> sendDialogue(player!!, "The Seer gazes into the mirror.").also { stage++ } + FIRST_SCORPION_GUIDE + 3 -> sendDialogue(player!!, "The Seer smooths his hair with his hand.").also { stage++ } + FIRST_SCORPION_GUIDE + 4 -> npcl("I can see a scorpion that you seek. It would appear to be near some nasty spiders. I can see two coffins there as well.").also { stage++ } + FIRST_SCORPION_GUIDE + 5 -> npcl("The scorpion seems to be going through some crack in the wall. Its gone into some sort of secret room.").also { stage++ } + FIRST_SCORPION_GUIDE + 6 -> npcl("Well see if you can find the scorpion then, and I'll try and get you some information on the others.").also { + setQuestStage(player!!, Quests.SCORPION_CATCHER, ScorpionCatcher.QUEST_STATE_DARK_PLACE) + stage = END_DIALOGUE + } + + OTHER_SCORPIONS -> npcl ("That's all I can tell about that scorpion.").also { stage++ } + OTHER_SCORPIONS + 1 -> playerl("Any more scorpions?").also { stage++ } + OTHER_SCORPIONS + 2 -> npcl ("It's good that you should ask. I have information on the last scorpion for you.").also { stage++ } + OTHER_SCORPIONS + 3 -> npcl ("It seems to be in some sort of upstairs room. There seems to be some sort of brown clothing lying on a table.").also { + setQuestStage(player!!, Quests.SCORPION_CATCHER, ScorpionCatcher.QUEST_STATE_OTHER_SCORPIONS) + stage = END_DIALOGUE + } + } + } +} diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCThormacDialogue.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCThormacDialogue.kt new file mode 100644 index 000000000..9422c9d06 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCThormacDialogue.kt @@ -0,0 +1,135 @@ +package content.region.kandarin.quest.scorpioncatcher + +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import core.ServerConstants +import core.api.* +import core.game.dialogue.Topic +import core.game.node.item.Item +import org.rs09.consts.Items +import content.data.Quests + +class SCThormacDialogue(val questStage: Int) : DialogueFile() { + + companion object { + const val ASK_FOR_HELP = 10 + const val HOW_TO_CATCH = 30 + const val WHY_SHOULD_I_START = 50 + + const val WAITING_FOR_SCORPIONS = 100 + const val GIVE_ANOTHER_CAGE = 110 + + const val GOT_THEM_ALL = 200 + const val FULL_INVENTORY = 300 + } + + + override fun handle(componentID: Int, buttonID: Int) { + when (stage){ + START_DIALOGUE -> if (questStage == 0) { + npcl(FacialExpression.FRIENDLY, "Hello I am Thormac the Sorcerer, " + + "I don't suppose you could be of assistance to me?").also { stage = ASK_FOR_HELP } + } + else { + npcl(FacialExpression.NEUTRAL, "How goes your quest?").also { + stage = if (hasAnItem(player!!, Items.SCORPION_CAGE_463).exists()){ + // Player has all the scorpions caught + GOT_THEM_ALL + } else { + WAITING_FOR_SCORPIONS + } + } + } + + ASK_FOR_HELP -> showTopics( + Topic(FacialExpression.HAPPY,"What do you need assistance with?", ASK_FOR_HELP+1), + Topic(FacialExpression.NEUTRAL, "I'm a little busy.", END_DIALOGUE) + ) + ASK_FOR_HELP+1 -> npcl(FacialExpression.WORRIED, " I've lost my pet scorpions. " + + "They're lesser Kharid scorpions, a very rare breed.").also { stage++ } + ASK_FOR_HELP+2 -> npcl(FacialExpression.WORRIED, "I left their cage door open, now I don't know where they've gone.").also { stage++ } + ASK_FOR_HELP+3 -> npcl(FacialExpression.WORRIED, "There's three of them, and they're quick little beasties. " + + "They're all over " + ServerConstants.SERVER_NAME + ".").also { stage++ } + ASK_FOR_HELP+4 -> showTopics( + Topic(FacialExpression.ASKING, "So how would I go about catching them then?", HOW_TO_CATCH), + Topic(FacialExpression.ASKING, "What's in it for me?", WHY_SHOULD_I_START), + Topic(FacialExpression.NEUTRAL, "I'm not interested then.", END_DIALOGUE) + ) + + WHY_SHOULD_I_START -> npcl(FacialExpression.WORRIED, "Well I suppose I can aid you with my skills as a staff sorcerer. " + + "Most battlestaffs around here are a bit puny. I can beef them up for you a bit.").also { + // Need to recheck the quest stage since it may have been changed in this dialogue + if (getQuestStage(player!!, Quests.SCORPION_CATCHER) == 0) { + stage++ + } else { + stage = END_DIALOGUE + } + } + WHY_SHOULD_I_START+1 -> showTopics( + Topic(FacialExpression.ASKING, "So how would I go about catching them then?", HOW_TO_CATCH), + Topic(FacialExpression.NEUTRAL, "I'm not interested then.", END_DIALOGUE) + ) + + HOW_TO_CATCH -> npcl(FacialExpression.WORRIED, "Well I have a scorpion cage here which you can " + + "use to catch them in.").also { + if (hasSpaceFor(player!!,Item(Items.SCORPION_CAGE_456))) stage++ + else stage = FULL_INVENTORY + } + HOW_TO_CATCH+1 -> { + sendItemDialogue(player!!, Items.SCORPION_CAGE_456, "Thormac gives you a cage.").also { stage++ } + startQuest(player!!, Quests.SCORPION_CATCHER) + addItemOrDrop(player!!, Items.SCORPION_CAGE_456) + } + HOW_TO_CATCH+2 -> npcl(FacialExpression.WORRIED, "If you go up to the village of Seers, to the North of " + + "here, one of them will be able to tell you where the scorpions are now.").also { stage++ } + HOW_TO_CATCH+3 -> showTopics( + Topic(FacialExpression.ASKING, "What's in it for me?", WHY_SHOULD_I_START), + Topic(FacialExpression.NEUTRAL, "Ok, I will do it then.", END_DIALOGUE ) + ) + + + WAITING_FOR_SCORPIONS -> { + if (!hasAnItem(player!!, arrayOf(Items.SCORPION_CAGE_456, Items.SCORPION_CAGE_457, Items.SCORPION_CAGE_458, + Items.SCORPION_CAGE_459, Items.SCORPION_CAGE_460, Items.SCORPION_CAGE_461, + Items.SCORPION_CAGE_462), false).exists()){ + playerl(FacialExpression.SAD, "I've lost my cage.").also { stage = GIVE_ANOTHER_CAGE } + } else { + playerl(FacialExpression.NEUTRAL, "I've not caught all the scorpions yet.").also { stage++ } + } + } + WAITING_FOR_SCORPIONS+1 -> npcl(FacialExpression.WORRIED, "Well remember to go speak to the Seers" + + " North of here, if you need any help.").also { stage = END_DIALOGUE } + + GIVE_ANOTHER_CAGE -> { + player!!.inventory.add(Item(Items.SCORPION_CAGE_456)) + // Clear all attributes keeping track of the scorpions + player!!.removeAttribute("scorpion_catcher:caught_taverly") + player!!.removeAttribute("scorpion_catcher:caught_barb") + player!!.removeAttribute("scorpion_catcher:caught_monk") + npcl(FacialExpression.NEUTRAL, "Ok, here's another cage. You're almost as bad" + + " at losing things as me.").also { stage = END_DIALOGUE } + } + + GOT_THEM_ALL -> { + playerl ("I have retrieved all your scorpions.").also { stage++ } + } + GOT_THEM_ALL+1 ->{ + npcl(FacialExpression.HAPPY, "Aha, my little scorpions home at last!").also { stage++ } + player!!.inventory.remove(Item(Items.SCORPION_CAGE_463)) + + // Don't need to keep track of which scorpions have been caught anymore + player!!.removeAttribute("scorpion_catcher:caught_taverly") + player!!.removeAttribute("scorpion_catcher:caught_barb") + player!!.removeAttribute("scorpion_catcher:caught_monk") + } + GOT_THEM_ALL+2 ->{ + end().also { finishQuest(player!!, Quests.SCORPION_CATCHER) } + } + + + FULL_INVENTORY -> npcl("You don't have space to hold a cage").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCWallListener.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCWallListener.kt new file mode 100644 index 000000000..864f2ce08 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCWallListener.kt @@ -0,0 +1,37 @@ +package content.region.kandarin.quest.scorpioncatcher + +import core.api.getQuestStage +import core.api.sendMessage +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.Scenery +import content.data.Quests + +class SCWallListener : InteractionListener { + + override fun defineListeners() { + on(Scenery.OLD_WALL_2117, IntType.SCENERY, "search"){ player, node -> + // You can only go through if you are doing the quest + //https://youtu.be/crc-47rwjvE?feature=shared&t=841 + // Otherwise the crack reverts back to normal + // Doesn't make any sense but that's authentic... + if ((ScorpionCatcher.QUEST_STATE_DARK_PLACE .. 99).contains(getQuestStage(player, Quests.SCORPION_CATCHER))) { + // Check what side the player is on and teleport them to the other + if (player.location == Location(2875, 9799, 0)){ + sendMessage(player, "You've found a secret door") + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + else{ + // We're leaving the room + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + } + else{ + sendMessage(player, "You search the wall but find nothing.") + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcher.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcher.kt new file mode 100644 index 000000000..2c314f0d6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcher.kt @@ -0,0 +1,148 @@ +package content.region.kandarin.quest.scorpioncatcher + +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +@Initializable +class ScorpionCatcher : Quest(Quests.SCORPION_CATCHER, 108, 107, 1, 76, 0, 1, 6) { + companion object { + const val QUEST_STATE_NOT_STARTED = 0 + const val QUEST_STATE_TALK_SEERS = 10 + const val QUEST_STATE_DARK_PLACE = 20 + const val QUEST_STATE_OTHER_SCORPIONS = 30 + const val QUEST_STATE_PEKSA_HELP = 40 + const val QUEST_STATE_DONE = 100 + + const val ATTRIBUTE_TAVERLEY = "scorpion_catcher:caught_taverly" + const val ATTRIBUTE_BARB = "scorpion_catcher:caught_barb" + const val ATTRIBUTE_MONK = "scorpion_catcher:caught_monk" + + } + + override fun newInstance(`object`: Any?): Quest { + return this + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + + var ln = 12 + + val caughtTaverly = player!!.getAttribute(ATTRIBUTE_TAVERLEY, false) + val caughtBarb = player.getAttribute(ATTRIBUTE_BARB, false) + val caughtMonk = player.getAttribute(ATTRIBUTE_MONK, false) + + if (stage == QUEST_STATE_NOT_STARTED){ + line(player, "I can start this quest by speaking to !!Thormac?? who is in the", ln++) + line(player, "!!Sorcerer's Tower?? south-west of !!Catherby??", ln++) + ln++ //blank line + line(player, "Requirements:", ln++) + line(player, "Level 31 Prayer", ln, player.skills.staticLevels[Skills.PRAYER] >= 31) + } else { + line(player, "I've spoken to Thormac in the Sorcerer's Tower south-west", ln++, true) + line(player, "of Catherby. He's lost his pet Kharid Scorpions and needs", ln++, true) + line(player, "my help to find them.", ln++, true) + + // 10 -> 20 + if (stage >= QUEST_STATE_DARK_PLACE) { + ln++ + line(player, "I've spoken to a Seer and been given the location of one", ln++, true) + line(player, "of the Kharid Scorpions.", ln++, true) + } else if (stage >= QUEST_STATE_TALK_SEERS) { + ln++ + line(player, "I need to go to the !!Seers' Village?? and talk to the !!Seer??", ln++) + line(player, "about the lost !!Kharid Scorpions??.", ln++) + } + + // 20 -> 20 + 1st Scorpion + if (stage >= QUEST_STATE_DARK_PLACE && caughtTaverly || stage >= QUEST_STATE_OTHER_SCORPIONS) { + ln++ + line(player, "The first Kharid Scorpion is in a secret room near some", ln++, true) + line(player, "nasty spiders with two coffins nearby.", ln++, true) + } else if (stage >= QUEST_STATE_DARK_PLACE) { + ln++ + line(player, "The first !!Kharid Scorpion?? is in a secret room near some", ln++) + line(player, "!!nasty spiders?? with two !!coffins?? nearby.", ln++) + // Jan 21, 2010 version has a slightly updated but similar location. +// line(player, "The first !!Kharid Scorpion?? is in a !!dark place between a lake??", ln++) +// line(player, "and a !!holy island??. It will be close when you enter.", ln++) + ln++ + line(player, "I'll need to talk to a !!Seer?? again one I've caught the first", ln++) + line(player, "!!Kharid Scorpion??.", ln++) + } + + // 20 + 1st Scorpion -> 30 + if (stage >= QUEST_STATE_OTHER_SCORPIONS) { + // This line disappears when the you talk to the Seer again. + } else if (stage >= QUEST_STATE_DARK_PLACE && caughtTaverly){ + // Todo check this line + ln++ + line(player, "I should go back to the Seer and ask about the other", ln++) + line(player, "scorpions.", ln++) + } + + // 30 -> 40 + if (stage >= QUEST_STATE_OTHER_SCORPIONS){ + ln++ + val barb_strike = caughtBarb || (stage == QUEST_STATE_PEKSA_HELP) + line(player, "The second !!Kharid Scorpion?? has been in a !!village of??", ln++, barb_strike || stage == QUEST_STATE_DONE) + line(player, "!!uncivilised-looking warriors in the east??. It's been picked up", ln++, barb_strike || stage == QUEST_STATE_DONE) + line(player, "by some sort of !!merchant??.", ln++, barb_strike || stage == QUEST_STATE_DONE) + // Jan 21, 2010 version has a slightly updated but similar location. +// line(player, "The second !!Kharid Scorpion?? was once in a !!village two??", ln++, barb_strike || stage == QUEST_STATE_DONE) +// line(player, "!!canoe trips from lumbridge??. A !!shopkeeper?? there picked it up.", ln++, barb_strike || stage == QUEST_STATE_DONE) + if (stage == QUEST_STATE_PEKSA_HELP){ + // todo check this block + ln++ + line(player, "I spoke with !!Peksa?? who said he sent it to his brother", ln++, caughtBarb) + line(player, "at the !!Barbarian outpost.??", ln++, caughtBarb) + } + ln++ + line(player, "The third !!Kharid Scorpion?? is in some sort of !!upstairs room??", ln++, caughtMonk || stage == QUEST_STATE_DONE) + line(player, "with !!brown clothing on a table??.", ln++, caughtMonk || stage == QUEST_STATE_DONE) + // Jan 21, 2010 version has a slightly updated but similar location. +// line(player, "The third !!Kharid Scorpion?? is in an !!upstairs room?? with !!brown??", ln++, caughtMonk || stage == QUEST_STATE_DONE) +// line(player, "!!clothing on a table?? The clothing is adorned with a golden", ln++, caughtMonk || stage == QUEST_STATE_DONE) +// line(player, "four-pointed star. You should start looking where monks", ln++, caughtMonk || stage == QUEST_STATE_DONE) +// line(player, "reside.", ln++, caughtMonk || stage == QUEST_STATE_DONE) + } + + // 40 -> 100 + if (stage == QUEST_STATE_DONE) { + // This line disappears when you complete the quest. + } else if (caughtBarb && caughtTaverly && caughtMonk && stage >= QUEST_STATE_OTHER_SCORPIONS){ + ln++ + line(player, "I need to take the !!Kharid Scorpions?? to !!Thormac??.", ln) + } + + // 100 + if (stage == QUEST_STATE_DONE) { + ln++ + line(player, "I've spoken to Thormac and he thanked me for finding his", ln++, true) + line(player, "pet Kharid Scorpions.", ln++, true) + ln++ + ln++ + line(player, "QUEST COMPLETE!", ln) + return + } + + } + } + + override fun finish(player: Player?) { + var ln = 10 + super.finish(player) + player!!.packetDispatch.sendString("You have completed the Scorpion Catcher Quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.SCORPION_CAGE_463, 240, 277, 5) + + drawReward(player, "1 Quest Point", ln++) + drawReward(player, "6625 Strength XP", ln) + + player.skills.addExperience(Skills.STRENGTH, 6625.0) + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcherUseListener.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcherUseListener.kt new file mode 100644 index 000000000..3b95da47b --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcherUseListener.kt @@ -0,0 +1,88 @@ +package content.region.kandarin.quest.scorpioncatcher + +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_TAVERLEY +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_BARB +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_MONK +import core.api.* +import core.game.node.item.Item +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.system.config.NPCConfigParser +import core.game.world.GameWorld +import core.tools.Log +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class ScorpionCatcherUseListener : InteractionListener { + override fun defineListeners() { + val scorpToAttr = mapOf( + /* 385 - Barbarian + * 386 - Taverley + * 387 - Monastery + */ + NPCs.KHARID_SCORPION_385 to ATTRIBUTE_BARB, + NPCs.KHARID_SCORPION_386 to ATTRIBUTE_TAVERLEY, + NPCs.KHARID_SCORPION_387 to ATTRIBUTE_MONK + ) + val cageToScorps = mapOf( + /* Taverley(386) Barbarian(385) Monastery(387) + * TBM + * 456 --- + * 457 O-- + * 458 00- + * 459 -0- + * 460 -00 + * 461 --0 + * 462 0-0 + * 463 000 + */ + Items.SCORPION_CAGE_456 to setOf(), + Items.SCORPION_CAGE_457 to setOf(NPCs.KHARID_SCORPION_386), + Items.SCORPION_CAGE_458 to setOf(NPCs.KHARID_SCORPION_386, NPCs.KHARID_SCORPION_385), + Items.SCORPION_CAGE_459 to setOf(NPCs.KHARID_SCORPION_385), + Items.SCORPION_CAGE_460 to setOf(NPCs.KHARID_SCORPION_385, NPCs.KHARID_SCORPION_387), + Items.SCORPION_CAGE_461 to setOf(NPCs.KHARID_SCORPION_387), + Items.SCORPION_CAGE_462 to setOf(NPCs.KHARID_SCORPION_386, NPCs.KHARID_SCORPION_387), + Items.SCORPION_CAGE_463 to setOf(NPCs.KHARID_SCORPION_386, NPCs.KHARID_SCORPION_385, NPCs.KHARID_SCORPION_387) + ) + + fun catchScorpion(player: Player, item: Node, scorpion: Node): Boolean { + val haveInCage = cageToScorps[item.id] ?: return false + if (scorpion.id in haveInCage) { + sendMessage(player, "You already have this scorpion in this cage.") //TODO check this message + return true + } + val newScorpionSet = haveInCage + setOf(scorpion.id) + var newItem: Int? = null + for ((cage, scorps) in cageToScorps) { + if (scorps == newScorpionSet) { + newItem = cage + } + } + if (newItem == null) { + log(this::class.java, Log.ERR, "Error looking up new scorpion cage item - this isn't possible") + return false + } + val attribute = scorpToAttr[scorpion.id] + if (removeItem(player, Item(item.id, 1)) && addItem(player, newItem)) { + sendMessage(player, "You catch a scorpion!") + setAttribute(player, "/save:$attribute", true) + runTask(player, 2) { + scorpion.asNpc().respawnTick = GameWorld.ticks + scorpion.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return true + } + return false + } + + for (scorp in scorpToAttr.keys) { + for (cage in cageToScorps.keys) { + onUseWith(IntType.NPC, cage, scorp) { player, usedCage, usedScorp -> + return@onUseWith catchScorpion(player, usedCage, usedScorp) + } + } + } + } +} diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusNPC.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusNPC.kt index 0b700a2d3..5dbcbb4e5 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusNPC.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusNPC.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.templeofikov +import content.data.Quests import core.api.* import core.game.dialogue.FacialExpression import core.game.node.entity.Entity @@ -7,7 +8,6 @@ import core.game.node.entity.combat.BattleState import core.game.node.entity.combat.CombatStyle import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.player.Player -import core.game.system.task.Pulse import core.game.world.map.Location import org.rs09.consts.NPCs @@ -64,8 +64,8 @@ class FireWarriorOfLesarkusNPC(id: Int = 0, val player: Player?, location: Locat if (entity is Player) { val player = entity.asPlayer() removeAttribute(player, TempleOfIkov.attributeWarriorInstance) - if(getQuestStage(player, TempleOfIkov.questName) == 3) { - setQuestStage(player, TempleOfIkov.questName, 4) + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 3) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 4) } super.finalizeDeath(player) } diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylBehavior.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylBehavior.kt index 6101484c1..846c5204f 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylBehavior.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylBehavior.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.templeofikov +import content.data.Quests import core.api.isQuestComplete import core.game.node.entity.Entity import core.game.node.entity.npc.NPC @@ -21,7 +22,7 @@ class GuardianOfArmadylBehavior : NPCBehavior(*guardianOfArmadylIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops Pendant of Armadyl after quest complete when killed. - if (killer is Player && isQuestComplete(killer, TempleOfIkov.questName)) { + if (killer is Player && isQuestComplete(killer, Quests.TEMPLE_OF_IKOV)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.ARMADYL_PENDANT_87)) } diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylDialogue.kt index 50a9293e4..c8900621f 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.templeofikov +import content.data.Quests import core.api.* import core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile @@ -143,8 +144,8 @@ class GuardianOfArmadylDialogueFile : DialogueBuilderFile() { .item(Items.ARMADYL_PENDANT_87, "The guardian has given you a pendant.") .endWith { _, player -> setAttribute(player, TempleOfIkov.attributeChosenEnding, 1) - if (getQuestStage(player, TempleOfIkov.questName) == 5) { - setQuestStage(player, TempleOfIkov.questName, 6) + if (getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 5) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 6) } addItemOrDrop(player, Items.ARMADYL_PENDANT_87) } diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienDialogue.kt index c6b3c93d2..c093d134e 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.templeofikov +import content.data.Quests import core.api.* import core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile @@ -26,11 +27,25 @@ class LucienDialogue (player: Player? = null) : DialoguePlugin(player) { class LucienDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages(TempleOfIkov.questName, 100) - .endWith { _, player -> - sendMessage(player, "You have completed the Temple of Ikov quest.") + b.onQuestStages(Quests.TEMPLE_OF_IKOV, 100) + .playerl("I thought I killed you?!") + .npcl("Ha! Ha! Ha!") + .npcl("You can not kill me human!") + .branch { player -> + return@branch if (inInventory(player, Items.PENDANT_OF_LUCIEN_86)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .end() + branch.onValue(0) + .playerl("I've lost the pendant you gave me.") + .npcl("Have another, it will remind you of my power!") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.PENDANT_OF_LUCIEN_86) + } + .item(Items.PENDANT_OF_LUCIEN_86, "Lucien has given you another pendant!") + .end() } - b.onQuestStages(TempleOfIkov.questName, 1,2,3,4,5,6,7) + b.onQuestStages(Quests.TEMPLE_OF_IKOV, 1, 2, 3, 4, 5, 6, 7) .npcl("I told you not to meet me here again!") .branch { player -> return@branch if (inInventory(player, Items.PENDANT_OF_LUCIEN_86)) { 1 } else { 0 } @@ -51,7 +66,7 @@ class LucienDialogueFile : DialogueBuilderFile() { .end() } - b.onQuestStages(TempleOfIkov.questName, 0) + b.onQuestStages(Quests.TEMPLE_OF_IKOV, 0) .npcl("I seek a hero to go on an important mission!") .options().let { optionBuilder -> val returnJoin = b.placeholder() @@ -86,8 +101,8 @@ class LucienDialogueFile : DialogueBuilderFile() { .npcl("I cannot stay here much longer. ") .npcl("I will be west of the Grand Exchange in Varrock. I have a small holding up there.") .endWith { _, player -> - if(getQuestStage(player, TempleOfIkov.questName) == 0) { - setQuestStage(player, TempleOfIkov.questName, 1) + if (getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 0) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 1) } } optionBuilder.option_playerl("Oh no! Sounds far too dangerous!") diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingDialogue.kt index 48cf0b890..2ba1dd6b7 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.templeofikov +import content.data.Quests import core.api.* import core.game.dialogue.* import core.game.node.entity.player.Player @@ -23,11 +24,13 @@ class LucienEndingDialogue (player: Player? = null) : DialoguePlugin(player) { class LucienEndingDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages(TempleOfIkov.questName, 100) + b.onQuestStages(Quests.TEMPLE_OF_IKOV, 100) .endWith { _, player -> + // After quest is over: https://www.youtube.com/watch?v=81DXjfsFcMM + sendMessage(player, "You feel that fighting this individual will be of little practical use.") sendMessage(player, "You have completed the Temple of Ikov quest.") } - b.onQuestStages(TempleOfIkov.questName, 1,2,3,4,5,6,7) + b.onQuestStages(Quests.TEMPLE_OF_IKOV, 1, 2, 3, 4, 5, 6, 7) .npcl(FacialExpression.FRIENDLY, "Have you got the Staff of Armadyl yet?") .branch { player -> return@branch if (inInventory(player, Items.STAFF_OF_ARMADYL_84)) { 1 } else { 0 } @@ -44,8 +47,8 @@ class LucienEndingDialogueFile : DialogueBuilderFile() { .npcl(FacialExpression.FRIENDLY, "I can feel the power of the staff running through me! I will be more powerful and they shall bow down to me!") .npcl(FacialExpression.FRIENDLY, "I suppose you want your reward? I shall grant you much power!") .endWith { _, player -> - if(getQuestStage(player, TempleOfIkov.questName) == 6) { - finishQuest(player, TempleOfIkov.questName) + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 6) { + finishQuest(player, Quests.TEMPLE_OF_IKOV) } } optionBuilder.option_playerl("No, not yet.") diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingNPC.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingNPC.kt index c4d7a4319..18e8e429b 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingNPC.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingNPC.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.templeofikov +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.node.entity.Entity @@ -40,8 +41,8 @@ class LucienEndingNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, when(stage){ 0 -> npcl("You have defeated me for now! I shall reappear in the North!").also { stage++ } 1 -> end().also { - if(getQuestStage(player, TempleOfIkov.questName) == 6) { - finishQuest(player, TempleOfIkov.questName) + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 6) { + finishQuest(player, Quests.TEMPLE_OF_IKOV) } } } diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkov.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkov.kt index 0edb793e8..ebfc4773b 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkov.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkov.kt @@ -1,11 +1,12 @@ package content.region.kandarin.quest.templeofikov import core.api.* -import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * Temple of Ikov Quest @@ -28,10 +29,9 @@ import org.rs09.consts.Items * D - Found ice arrows */ @Initializable -class TempleOfIkov : Quest("Temple of Ikov", 121, 120, 1,26, 0, 1, 80 /* 80 or 90 since there's 2 endings */) { +class TempleOfIkov : Quest(Quests.TEMPLE_OF_IKOV, 121, 120, 1,26, 0, 1, 80 /* 80 or 90 since there's 2 endings */) { companion object { - const val questName = "Temple of Ikov" const val attributeChosenEnding = "/save:quest:templeofikov-chosenending" const val attributeDisabledTrap = "/save:quest:templeofikov-disabledtrap" @@ -49,7 +49,7 @@ class TempleOfIkov : Quest("Temple of Ikov", 121, 120, 1,26, 0, 1, 80 /* 80 or 9 var line = 12 var stage = getStage(player) - var started = getQuestStage(player, questName) > 0 + var started = getQuestStage(player, Quests.TEMPLE_OF_IKOV) > 0 if (!started) { line(player, "I can start this quest at the !!Flying Horse Inn?? in !!Ardougne??", line++, false) @@ -59,6 +59,7 @@ class TempleOfIkov : Quest("Temple of Ikov", 121, 120, 1,26, 0, 1, 80 /* 80 or 9 line(player, "Level 42 !!Thieving??", line++, hasLevelStat(player, Skills.THIEVING, 42)) line(player, "Level 40 !!Ranged??", line++, hasLevelStat(player, Skills.RANGE, 40)) line(player, "Ability to defeat a level 84 enemy with Ranged.", line++, false) + limitScrolling(player, line, true) } else { if (stage >= 2) { line(player, "Lucien has asked me to retrieve the !!Staff of Armadyl?? from", line++, true) @@ -165,8 +166,8 @@ class TempleOfIkov : Quest("Temple of Ikov", 121, 120, 1,26, 0, 1, 80 /* 80 or 9 line++ line(player,"QUEST COMPLETE!", line) } + limitScrolling(player, line, false) } - } override fun reset(player: Player) { diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkovListeners.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkovListeners.kt index 6a832b230..135e3dc92 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkovListeners.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkovListeners.kt @@ -1,15 +1,15 @@ package content.region.kandarin.quest.templeofikov -import content.global.ame.events.drilldemon.DrillDemonUtils -import content.global.ame.events.drilldemon.SeargentDamienDialogue +import content.data.LightSource +import content.data.Quests import content.global.skill.agility.AgilityHandler +import content.global.skill.skillcapeperks.SkillcapePerks import core.api.* import core.game.global.action.DoorActionHandler import core.game.global.action.PickupHandler import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.interaction.QueueStrength -import core.game.node.entity.Entity import core.game.node.entity.skill.Skills import core.game.node.item.GroundItem import core.game.node.item.Item @@ -20,7 +20,6 @@ import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.NPCs import org.rs09.consts.Scenery -import org.rs09.consts.Sounds class TempleOfIkovListeners : InteractionListener { @@ -63,8 +62,8 @@ class TempleOfIkovListeners : InteractionListener { // 1 - 2 Walk past the gate. You must always wear the pendant to get past this gate. on(intArrayOf(Scenery.GATE_94, Scenery.GATE_95), SCENERY, "open") { player, node -> if (inEquipment(player, Items.PENDANT_OF_LUCIEN_86)){ - if(getQuestStage(player, TempleOfIkov.questName) == 1) { - setQuestStage(player, TempleOfIkov.questName, 2) + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 1) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 2) } DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } else { @@ -87,8 +86,8 @@ class TempleOfIkovListeners : InteractionListener { replaceScenery(node.asScenery(), 88, 3) animate(player, Animation(2140)) if (getAttribute(player, TempleOfIkov.attributeDisabledTrap, false)) { - if(getQuestStage(player, TempleOfIkov.questName) == 2) { - setQuestStage(player, TempleOfIkov.questName, 3) + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 2) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 3) } } else { AgilityHandler.fail(player, 2, Location.create(2682, 9855, 0), Animation(770), 20, "You slip and fall to the pit below.") @@ -117,6 +116,40 @@ class TempleOfIkovListeners : InteractionListener { // https://www.youtube.com/watch?v=6ZGpJNeGLJ0 // sendDialogue("Hmm...bit dark down here! I'm not going to venture far!") + // Ikov dungeon top stairs to either boots of lightness or the dark room + on(Scenery.STAIRS_35121, SCENERY, "Climb-down") { player, node -> + if (LightSource.hasActiveLightSource(player) || SkillcapePerks.isActive(SkillcapePerks.CONSTANT_GLOW, player)) { // has light source + teleport(player, Location.create(2641, 9763, 0)) + } else { // doesn't have light source + teleport(player, Location.create(2641, 9740, 0)) + sendDialogueLines(player, "Hmm...bit dark down here! I'm not going to venture far!") + } + true + } + + // Ikov dungeon bottom stairs (lit) to top stairs + on(Scenery.STAIRS_96, SCENERY, "Climb-up") { player, node -> + teleport(player, Location.create(2649, 9804, 0)) + } + + // Ikov dungeon bottom stairs (dark) to top stairs + on(Scenery.STAIRS_33232, SCENERY, "Climb-up") { player, node -> + teleport(player, Location.create(2649, 9804, 0)) + } + + // don't let light extinguish or cape take off + val lightSourceProducts = LightSource.values().map { it.product.id }.toIntArray() + + on(lightSourceProducts, ITEM, "drop") { player, light -> + val active = LightSource.getActiveLightSource(player).product.id + if (player.location.isInRegion(10648) && light.id == active) { + sendMessage(player, "Dropping the " + LightSource.getActiveLightSource(player).product.name.lowercase() + " would leave you without a light source.") + return@on false + } + val removed = removeItem(player, light.id) + if (removed) produceGroundItem(player, light.id) + return@on true + } // B: Attach lever, authentic if you log out, the lever is lost, and you have to do that bridge again onUseWith(SCENERY, Items.LEVER_83, Scenery.LEVER_BRACKET_86) { player, used, with -> @@ -142,7 +175,10 @@ class TempleOfIkovListeners : InteractionListener { // C: Gate opens after attached lever on(intArrayOf(Scenery.GATE_89, Scenery.GATE_90), SCENERY, "open") { player, node -> - if (getAttribute(player, TempleOfIkov.attributeIceChamberAccess, false) || getQuestStage(player, TempleOfIkov.questName) >= 4){ + if (getAttribute(player, TempleOfIkov.attributeIceChamberAccess, false) || getQuestStage( + player, + Quests.TEMPLE_OF_IKOV + ) >= 4){ // To be nice, you can "reset" the chest location by opening the gate. // This is a failsafe if the attribute gets "stuck", although I doubt it will happen. setAttribute(player, TempleOfIkov.attributeRandomChest, chestLocations.random()) @@ -189,7 +225,7 @@ class TempleOfIkovListeners : InteractionListener { // 3: Allow access to Fire Warrior Door after pulling the lever on(Scenery.DOOR_92, SCENERY, "open") { player, node -> removeAttribute(player, TempleOfIkov.attributeWarriorInstance) - if (getQuestStage(player, TempleOfIkov.questName) >= 3){ + if (getQuestStage(player, Quests.TEMPLE_OF_IKOV) >= 3){ DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } else { sendMessage(player, "The door won't open.") @@ -200,7 +236,7 @@ class TempleOfIkovListeners : InteractionListener { // 3 - 4: Calls for the Fire Warrior, allows passing when Fire Warrior is defeated. on(Scenery.DOOR_93, SCENERY, "open") { player, node -> - if (getQuestStage(player, TempleOfIkov.questName) >= 4){ + if (getQuestStage(player, Quests.TEMPLE_OF_IKOV) >= 4){ DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } else { if (getAttribute(player, TempleOfIkov.attributeWarriorInstance, null) == null) { @@ -240,7 +276,7 @@ class TempleOfIkovListeners : InteractionListener { } on(Items.STAFF_OF_ARMADYL_84, IntType.GROUNDITEM,"take") { player, node -> - if (getQuestStage(player, TempleOfIkov.questName) >= 6 && getAttribute(player, TempleOfIkov.attributeChosenEnding, 0) == 1){ + if (getQuestStage(player, Quests.TEMPLE_OF_IKOV) >= 6 && getAttribute(player, TempleOfIkov.attributeChosenEnding, 0) == 1){ sendMessage(player, "You decide not to steal the staff as you have agreed to help the Guardians") } val npcs = findLocalNPCs(player, intArrayOf(NPCs.GUARDIAN_OF_ARMADYL_274, NPCs.GUARDIAN_OF_ARMADYL_275), 4) @@ -248,8 +284,8 @@ class TempleOfIkovListeners : InteractionListener { sendChat(npcs[0], "That is not thine to take!") npcs[0].attack(player) } else { - if(getQuestStage(player, TempleOfIkov.questName) == 5) { - setQuestStage(player, TempleOfIkov.questName, 6) + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 5) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 6) setAttribute(player, TempleOfIkov.attributeChosenEnding, 2) } PickupHandler.take(player, node as GroundItem) diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/WineldaDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/WineldaDialogue.kt index d525d1eb3..aa34d7d76 100644 --- a/Server/src/main/content/region/kandarin/quest/templeofikov/WineldaDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/WineldaDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.templeofikov +import content.data.Quests import core.api.* import core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile @@ -28,7 +29,7 @@ class WineldaDialogue (player: Player? = null) : DialoguePlugin(player) { class WineldaDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages(TempleOfIkov.questName, 5,6,7,100) + b.onQuestStages(Quests.TEMPLE_OF_IKOV, 5, 6, 7, 100) .playerl(FacialExpression.FRIENDLY, "Hi again. Could you do the honours again please?") .npcl(FacialExpression.FRIENDLY, "Certainly! We helps those that helps poor Winelda!") .endWith { _, player -> @@ -56,8 +57,8 @@ class WineldaDialogueFile : DialogueBuilderFile() { .npcl(FacialExpression.FRIENDLY, "Good! Good! My potion is nearly ready! Bubble, bubble, toil and trouble!") .npcl(FacialExpression.FRIENDLY, "Now we shows them ours magic! Hold on tight!") .endWith { _, player -> - if(getQuestStage(player, TempleOfIkov.questName) == 4) { - setQuestStage(player, TempleOfIkov.questName, 5) + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 4) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 5) } // There's a cutscene, but I'm lazy man. teleport(player, Location(2664, 9876, 0)) diff --git a/Server/src/main/content/region/kandarin/quest/tree/BallistaDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/BallistaDialogue.kt index eb707dbf5..afc028e55 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/BallistaDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/BallistaDialogue.kt @@ -1,12 +1,13 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE class BallistaDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, TreeGnomeVillage.questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) if (questStage > 30) { when (stage) { 0 -> sendDialogue(player!!, "The Khazard stronghold has already been breached.").also { stage = END_DIALOGUE } @@ -28,7 +29,7 @@ class BallistaDialogue : DialogueFile(){ when (buttonID) { answer -> { sendDialogue(player!!, "The huge spear flies through the air and screams down directly into the Khazard stronghold. A deafening crash echoes over the battlefield as the front entrance is reduced to rubble.") - setQuestStage(player!!, TreeGnomeVillage.questName, 31) + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 31) } else -> sendDialogue(player!!, "The huge spear completely misses the Khazard stronghold!") } diff --git a/Server/src/main/content/region/kandarin/quest/tree/CommanderMontaiDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/CommanderMontaiDialogue.kt index 083066d20..e758479a2 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/CommanderMontaiDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/CommanderMontaiDialogue.kt @@ -1,30 +1,32 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* import org.rs09.consts.Items import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE class CommanderMontaiDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, TreeGnomeVillage.questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) if (questStage == 10) { when(stage) { 0 -> playerl("Hello.").also { stage++ } - 1 -> npcl("Hello traveller, are you here to help or just to watch?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hello traveller, are you here to help or just to watch?").also { stage++ } 2 -> playerl("I've been sent by King Bolren to retrieve the orb of protection.").also { stage++ } - 3 -> npcl("Excellent we need all the help we can get.").also { stage++ } - 4 -> npcl("I'm Commander Montai. The orb is in the Khazard stronghold to the north, but until we weaken their defences we can't get close.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Excellent we need all the help we can get.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "I'm Commander Montai. The orb is in the Khazard stronghold to the north, but until we weaken their defences we can't get close.").also { stage++ } 5 -> playerl("What can I do?").also { stage++ } - 6 -> npcl("Firstly we need to strengthen our own defences. We desperately need wood to make more battlements, once the battlements are gone it's all over. Six loads of normal logs should do it.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_NORMAL, "Firstly we need to strengthen our own defences. We desperately need wood to make more battlements, once the battlements are gone it's all over. Six loads of normal logs should do it.").also { stage++ } 7 -> options("Ok, I'll gather some wood.", "Sorry, I no longer want to be involved.").also { stage++ } 8 -> when (buttonID) { 1 -> playerl("Ok, I'll gather some wood.").also { stage = 10 } 2 -> playerl("Sorry, I no longer want to be involved.").also { stage = 9 } } - 9 -> npcl("That's a shame, we could have done with your help.").also { stage = END_DIALOGUE } - 10 -> npcl("Please be as quick as you can, I don't know how much longer we can hold out.").also { - setQuestStage(player!!, TreeGnomeVillage.questName, 20) + 9 -> npcl(FacialExpression.OLD_NORMAL, "That's a shame, we could have done with your help.").also { stage = END_DIALOGUE } + 10 -> npcl(FacialExpression.OLD_NORMAL, "Please be as quick as you can, I don't know how much longer we can hold out.").also { + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 20) stage = END_DIALOGUE } } @@ -32,73 +34,73 @@ class CommanderMontaiDialogue : DialogueFile(){ if(inInventory(player!!, Items.LOGS_1511,6)){ when(stage) { 0 -> playerl("Hello.").also { stage++ } - 1 -> npcl("Hello again, we're still desperate for wood soldier.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hello again, we're still desperate for wood soldier.").also { stage++ } 2 -> playerl("I have some here. (You give six loads of logs to the commander.)").also{ stage++ } 3 -> { // Remove the 6 normal logs for(i in 1..6) { removeItem(player!!,Items.LOGS_1511) } - setQuestStage(player!!, TreeGnomeVillage.questName, 25) - npcl("That's excellent, now we can make more defensive battlements. Give me a moment to organize the troops and then come speak to me. I'll inform you of our next phase of attack.") + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 25) + npcl(FacialExpression.OLD_NORMAL, "That's excellent, now we can make more defensive battlements. Give me a moment to organize the troops and then come speak to me. I'll inform you of our next phase of attack.") stage = END_DIALOGUE } } } else { when(stage) { 0 -> playerl("Hello.").also { stage++ } - 1 -> npcl("Hello again, we're still desperate for wood soldier. We need six loads of normal logs.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hello again, we're still desperate for wood soldier. We need six loads of normal logs.").also { stage++ } 2 -> playerl("I'll see what I can do.").also { stage++ } - 3 -> npcl("Thank you.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Thank you.").also { stage = END_DIALOGUE } } } } else if (questStage == 25) { when(stage) { 0 -> playerl("How are you doing Montai?").also { stage++ } - 1 -> npcl("We're hanging in there soldier. For the next phase of our attack we need to breach their stronghold.").also { stage++ } - 2 -> npcl("The ballista can break through the stronghold wall, and then we can advance and seize back the orb.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "We're hanging in there soldier. For the next phase of our attack we need to breach their stronghold.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "The ballista can break through the stronghold wall, and then we can advance and seize back the orb.").also { stage++ } 3 -> playerl("So what's the problem?").also { stage++ } - 4 -> npcl("From this distance we can't get an accurate enough shot. We need the correct coordinates of the stronghold for a direct hit. I've sent out three tracker gnomes to gather them.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "From this distance we can't get an accurate enough shot. We need the correct coordinates of the stronghold for a direct hit. I've sent out three tracker gnomes to gather them.").also { stage++ } 5 -> playerl("Have they returned?").also { stage++ } - 6 -> npcl("I'm afraid not, and we're running out of time. I need you to go into the heart of the battlefield, find the trackers, and bring back the coordinates.").also { stage++ } - 7 -> npcl("Do you think you can do it?").also { stage++ } + 6 -> npcl(FacialExpression.OLD_NORMAL, "I'm afraid not, and we're running out of time. I need you to go into the heart of the battlefield, find the trackers, and bring back the coordinates.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Do you think you can do it?").also { stage++ } 8 -> options("No, I've had enough of your battle.", "I'll try my best.").also { stage++ } 9 -> when(buttonID) { 1 -> playerl("No, I've had enough of your battle.").also { stage = 10 } 2 -> playerl("I'll try my best.").also { stage = 11 } } - 10 -> npcl("I understand, this isn't your fight.").also { stage = END_DIALOGUE } - 11 -> npcl("Thank you, you're braver than most.").also { stage++ } - 12 -> npcl("I don't know how long I will be able to hold out. Once you have the coordinates come back and fire the ballista right into those monsters.").also { stage++ } - 13 -> npcl("If you can retrieve the orb and bring safety back to my people, none of the blood spilled on this field will be in vain.").also { - setQuestStage(player!!, TreeGnomeVillage.questName, 30) + 10 -> npcl(FacialExpression.OLD_NORMAL, "I understand, this isn't your fight.").also { stage = END_DIALOGUE } + 11 -> npcl(FacialExpression.OLD_NORMAL, "Thank you, you're braver than most.").also { stage++ } + 12 -> npcl(FacialExpression.OLD_NORMAL, "I don't know how long I will be able to hold out. Once you have the coordinates come back and fire the ballista right into those monsters.").also { stage++ } + 13 -> npcl(FacialExpression.OLD_NORMAL, "If you can retrieve the orb and bring safety back to my people, none of the blood spilled on this field will be in vain.").also { + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 30) stage = END_DIALOGUE } } } else if (questStage == 30) { when(stage) { 0 -> playerl("Hello.").also { stage++ } - 1 -> npcl("Hello warrior. We need the coordinates for a direct hit from the ballista.").also { stage++ } - 2 -> npcl("Once you have a direct hit you will be able to enter the stronghold and retrieve the orb.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hello warrior. We need the coordinates for a direct hit from the ballista.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "Once you have a direct hit you will be able to enter the stronghold and retrieve the orb.").also { stage = END_DIALOGUE } } } else if (questStage == 31) { if(inInventory(player!!,Items.ORB_OF_PROTECTION_587)){ when(stage) { 0 -> playerl("I have the orb of protection.").also { stage++ } - 1 -> npcl("Incredible, for a human you really are something.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_HAPPY, "Incredible, for a human you really are something.").also { stage++ } 2 -> playerl("Thanks... I think!").also { stage++ } - 3 -> npcl("I'll stay here with my troops and try and hold Khazard's men back. You return the orb to the gnome village. Go as quick as you can, the village is still unprotected.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.OLD_NORMAL, "I'll stay here with my troops and try and hold Khazard's men back. You return the orb to the gnome village. Go as quick as you can, the village is still unprotected.").also { stage = END_DIALOGUE } } } else { when(stage) { 0 -> playerl("I've breached the stronghold.").also { stage++ } - 1 -> npcl("I saw, that was a beautiful sight. The Khazard troops didn't know what hit them.").also { stage++ } - 2 -> npcl("Now is the time to retrieve the orb. It's all in your hands. I'll be praying for you.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "I saw, that was a beautiful sight. The Khazard troops didn't know what hit them.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "Now is the time to retrieve the orb. It's all in your hands. I'll be praying for you.").also { stage = END_DIALOGUE } } } } else if (questStage != 0){ when(stage) { 0 -> playerl("Hello Montai, how are you?").also { stage++ } - 1 -> npcl("I'm ok, this battle is going to take longer to win than I expected. The Khazard troops won't give up even without the orb.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "I'm ok, this battle is going to take longer to win than I expected. The Khazard troops won't give up even without the orb.").also { stage++ } 2 -> playerl("Hang in there.").also { stage = END_DIALOGUE } } } diff --git a/Server/src/main/content/region/kandarin/quest/tree/ElkoyDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/ElkoyDialogue.kt index 61636b90f..708033a0d 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/ElkoyDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/ElkoyDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* import core.game.component.Component import core.game.node.entity.player.Player @@ -10,7 +11,7 @@ import org.rs09.consts.Items import core.game.dialogue.DialogueFile import content.region.kandarin.quest.tree.TreeGnomeVillage.Companion.mazeEntrance import content.region.kandarin.quest.tree.TreeGnomeVillage.Companion.mazeVillage -import content.region.kandarin.quest.tree.TreeGnomeVillage.Companion.questName +import core.game.dialogue.FacialExpression import core.game.world.GameWorld.Pulser import core.tools.END_DIALOGUE @@ -38,37 +39,37 @@ class ElkoyDialogue : DialogueFile(){ }) } override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) val locY = player!!.location.y val followLocation = if(locY > 3161) "village" else "exit" when { inInventory(player!!, Items.ORBS_OF_PROTECTION_588) && followLocation == "exit" -> { when(stage) { 0 -> playerl("Hello Elkoy. I have the orb.").also { stage++ } - 1 -> npcl("Take it to King Bolren, I'm sure he'll be pleased to see you.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_HAPPY, "Take it to King Bolren, I'm sure he'll be pleased to see you.").also { stage++ } 2 -> options("Alright, I'll do that.", "Can you guide me out of the maze now?").also { stage++ } 3 -> when(buttonID) { 1 -> playerl("Alright, I'll do that.").also { stage = END_DIALOGUE } 2 -> playerl("Can you guide me out of the maze now?").also { stage = 4 } } - 4 -> npcl("If you like, but please take the orb to King Bolren soon.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "If you like, but please take the orb to King Bolren soon.").also { stage++ } 5 -> { travelCutscene(player!!, mazeEntrance) stage++ } - 6 -> npcl("Here we are. Please don't lose the orb!").also { stage = END_DIALOGUE } + 6 -> npcl(FacialExpression.OLD_NORMAL, "Here we are. Please don't lose the orb!").also { stage = END_DIALOGUE } } } inInventory(player!!, Items.ORB_OF_PROTECTION_587) -> { when(stage) { 0 -> playerl("Hello Elkoy.").also { stage++ } - 1 -> npcl("You're back! And the orb?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "You're back! And the orb?").also { stage++ } 2 -> playerl("I have it here.").also { stage++ } 3 -> { if(locY > 3161){ - npcl("You're our saviour. Please return it to the village and we are all saved. Would you like me to show you the way to the village?").also { stage++ } + npcl(FacialExpression.OLD_NORMAL, "You're our saviour. Please return it to the village and we are all saved. Would you like me to show you the way to the village?").also { stage++ } } else { - npcl("Take the orb to King Bolren, I'm sure he'll be pleased to see you.").also { stage = END_DIALOGUE } + npcl(FacialExpression.OLD_NORMAL, "Take the orb to King Bolren, I'm sure he'll be pleased to see you.").also { stage = END_DIALOGUE } } } 4 -> options("Yes please.", "No thanks Elkoy.").also { stage++ } @@ -76,24 +77,24 @@ class ElkoyDialogue : DialogueFile(){ 1 -> playerl("Yes please.").also { stage = 7 } 2 -> playerl("No thanks Elkoy.").also { stage = 6 } } - 6 -> npcl("Ok then, take care.").also { stage = END_DIALOGUE } + 6 -> npcl(FacialExpression.OLD_NORMAL, "Ok then, take care.").also { stage = END_DIALOGUE } 7 -> travelCutscene(player!!, mazeVillage).also { stage++ } - 8 -> npcl("Here we are. Take the orb to King Bolren, I'm sure he'll be pleased to see you.").also { stage = END_DIALOGUE } + 8 -> npcl(FacialExpression.OLD_NORMAL, "Here we are. Take the orb to King Bolren, I'm sure he'll be pleased to see you.").also { stage = END_DIALOGUE } } } inInventory(player!!, Items.ORBS_OF_PROTECTION_588) || questStage == 100 -> { when(stage) { 0 -> playerl("Hello Elkoy.").also { stage++ } - 1 -> npcl("You truly are a hero.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_HAPPY, "You truly are a hero.").also { stage++ } 2 -> playerl("Thanks.").also { stage++ } - 3 -> npcl("You saved us by defeating the warlord. I'm humbled and wish you well.").also { stage++ } - 4 -> npcl("Would you like me to show you the way to the ${followLocation}?").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NORMAL, "You saved us by defeating the warlord. I'm humbled and wish you well.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "Would you like me to show you the way to the ${followLocation}?").also { stage++ } 5 -> options("Yes please.", "No thanks Elkoy.").also { stage++ } 6 -> when(buttonID) { 1 -> playerl("Yes please.").also { stage = 8 } 2 -> playerl("No thanks Elkoy.").also { stage = 7 } } - 7 -> npcl("Ok then, take care.").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Ok then, take care.").also { stage = END_DIALOGUE } 8 -> { if(followLocation == "village") { travelCutscene(player!!, mazeVillage) @@ -104,33 +105,33 @@ class ElkoyDialogue : DialogueFile(){ stage++ } } - 9 -> npcl("Here we are. Have a safe journey.").also { stage = END_DIALOGUE } - 10 -> npcl("Here we are. Feel free to have a look around.").also { stage = END_DIALOGUE } + 9 -> npcl(FacialExpression.OLD_NORMAL, "Here we are. Have a safe journey.").also { stage = END_DIALOGUE } + 10 -> npcl(FacialExpression.OLD_NORMAL, "Here we are. Feel free to have a look around.").also { stage = END_DIALOGUE } } } questStage == 0 -> { when(stage) { 0 -> playerl("Hello there.").also { stage++ } - 1 -> npcl("Hello, welcome to our maze. I'm Elkoy the tree gnome.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hello, welcome to our maze. I'm Elkoy the tree gnome.").also { stage++ } 2 -> playerl("I haven't heard of your sort.").also { stage++ } - 3 -> npcl("There's not many of us left. Once you could find tree gnomes anywhere in the world, now we hide in small groups to avoid capture.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_SAD, "There's not many of us left. Once you could find tree gnomes anywhere in the world, now we hide in small groups to avoid capture.").also { stage++ } 4 -> playerl("Capture from whom?").also { stage++ } - 5 -> npcl("Tree gnomes have been hunted for so called 'fun' since as long as I can remember.").also { stage++ } - 6 -> npcl("Our main threat nowadays are General Khazard's troops. They know no mercy, but are also very dense. They'll never find their way through our maze.").also { stage++ } - 7 -> npcl("Have fun.").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.OLD_NORMAL, "Tree gnomes have been hunted for so called 'fun' since as long as I can remember.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_NORMAL, "Our main threat nowadays are General Khazard's troops. They know no mercy, but are also very dense. They'll never find their way through our maze.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Have fun.").also { stage = END_DIALOGUE } } } questStage in 1..39 -> { when (stage) { - 0 -> npcl("Oh my! The orb, they have the orb. We're doomed.").also { stage++ } + 0 -> npcl(FacialExpression.OLD_DISTRESSED, "Oh my! The orb, they have the orb. We're doomed.").also { stage++ } 1 -> playerl("Perhaps I'll be able to get it back for you.").also { stage++ } - 2 -> npcl("Would you like me to show you the way to the ${followLocation}?").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "Would you like me to show you the way to the ${followLocation}?").also { stage++ } 3 -> options("Yes please.", "No thanks Elkoy.").also { stage++ } 4 -> when (buttonID) { 1 -> playerl("Yes please.").also { stage = 6 } 2 -> playerl("No thanks Elkoy.").also { stage = 5 } } - 5 -> npcl("Ok then, take care.").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.OLD_NORMAL, "Ok then, take care.").also { stage = END_DIALOGUE } 6 -> { if(followLocation == "village") travelCutscene(player!!, mazeVillage) @@ -138,20 +139,20 @@ class ElkoyDialogue : DialogueFile(){ travelCutscene(player!!, mazeEntrance) stage++ } - 7 -> npcl("Here we are. I hope you get the orb back soon.").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Here we are. I hope you get the orb back soon.").also { stage = END_DIALOGUE } } } questStage == 40 -> { when(stage) { 0 -> playerl("Hello Elkoy.").also { stage++ } - 1 -> npcl("Did you hear? Khazard's men have pillaged the village! They slaughtered many, and took the other orbs in an attempt to lead us out of the maze. When will the misery end?").also { stage++ } - 2 -> npcl("Would you like me to show you the way to the ${followLocation}?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Did you hear? Khazard's men have pillaged the village! They slaughtered many, and took the other orbs in an attempt to lead us out of the maze. When will the misery end?").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "Would you like me to show you the way to the ${followLocation}?").also { stage++ } 3 -> options("Yes please.", "No thanks Elkoy.").also { stage++ } 4 -> when(buttonID) { 1 -> playerl("Yes please.").also { stage = 6 } 2 -> playerl("No thanks Elkoy.").also { stage = 5 } } - 5 -> npcl("Ok then, take care.").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.OLD_NORMAL, "Ok then, take care.").also { stage = END_DIALOGUE } 6 -> { if(followLocation == "village") { travelCutscene(player!!, mazeVillage) @@ -161,8 +162,8 @@ class ElkoyDialogue : DialogueFile(){ stage++ } } - 7 -> npcl("Please try to get our orbs back for us, otherwise the village is doomed!").also { stage = END_DIALOGUE } - 8 -> npcl("Here we are. Despite what has happened here, I hope you feel welcome.").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Please try to get our orbs back for us, otherwise the village is doomed!").also { stage = END_DIALOGUE } + 8 -> npcl(FacialExpression.OLD_NORMAL, "Here we are. Despite what has happened here, I hope you feel welcome.").also { stage = END_DIALOGUE } } } } diff --git a/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordDialogue.kt index a4d3df749..b70b6449b 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordDialogue.kt @@ -1,12 +1,13 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.getQuestStage import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE class KhazardWarlordDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, TreeGnomeVillage.questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) if(questStage == 31){ when(stage) { 0 -> playerl("Hello there.").also { stage++ } diff --git a/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordNPC.kt b/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordNPC.kt index f1a246287..736612730 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordNPC.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordNPC.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.addItemOrDrop import core.api.getQuestStage import core.api.sendDialogue @@ -27,7 +28,7 @@ class KhazardWarlordNPC(id: Int = 0, location: Location? = null) : AbstractNPC(i } override fun finalizeDeath(killer: Entity?) { - if(getQuestStage(killer as Player, TreeGnomeVillage.questName) == 40) { + if(getQuestStage(killer as Player, Quests.TREE_GNOME_VILLAGE) == 40) { sendDialogue(killer,"As the warlord falls to the ground, a ghostly vapour floats upwards from his battle-worn armour. You search his satchel and find the orbs of protection.") addItemOrDrop(killer, Items.ORBS_OF_PROTECTION_588) } diff --git a/Server/src/main/content/region/kandarin/quest/tree/KingBolrenDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/KingBolrenDialogue.kt index 169f6b016..89f6d5c11 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/KingBolrenDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/KingBolrenDialogue.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* import core.game.system.task.Pulse import core.game.world.map.Location @@ -9,49 +10,49 @@ import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.dialogue.DialogueFile import content.region.kandarin.quest.tree.TreeGnomeVillage.Companion.mazeEntrance -import content.region.kandarin.quest.tree.TreeGnomeVillage.Companion.questName +import core.game.dialogue.FacialExpression import core.game.world.GameWorld import core.tools.END_DIALOGUE class KingBolrenDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) when { questStage < 10 -> { when (stage) { 0 -> playerl("Hello.").also { stage++ } - 1 -> npcl("Well hello stranger. My name's Bolren, I'm the king of the tree gnomes.").also { stage++ } - 2 -> npcl("I'm surprised you made it in, maybe I made the maze too easy.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Well hello stranger. My name's Bolren, I'm the king of the tree gnomes.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "I'm surprised you made it in, maybe I made the maze too easy.").also { stage++ } 3 -> playerl("Maybe.").also { stage++ } - 4 -> npcl("I'm afraid I have more serious concerns at the moment. Very serious.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "I'm afraid I have more serious concerns at the moment. Very serious.").also { stage++ } 5 -> options("I'll leave you to it then.", "Can I help at all?").also { stage++ } 6 -> when (buttonID) { 1 -> playerl("I'll leave you to it then.").also { stage = 7 } 2 -> playerl("Can I help at all?").also { stage = 8 } } - 7 -> npcl("Ok, take care.").also { stage = END_DIALOGUE } - 8 -> npcl("I'm glad you asked.").also { stage++ } - 9 -> npcl("The truth is my people are in grave danger. We have always been protected by the Spirit Tree. No creature of dark can harm us while its three orbs are in place.").also { stage++ } - 10 -> npcl("We are not a violent race, but we fight when we must. Many gnomes have fallen battling the dark forces of Khazard to the North.").also { stage++ } - 11 -> npcl("We became desperate, so we took one orb of protection to the battlefield. It was a foolish move.").also { stage++ } - 12 -> npcl("Khazard troops seized the orb. Now we are completely defenceless.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Ok, take care.").also { stage = END_DIALOGUE } + 8 -> npcl(FacialExpression.OLD_NORMAL, "I'm glad you asked.").also { stage++ } + 9 -> npcl(FacialExpression.OLD_SAD, "The truth is my people are in grave danger. We have always been protected by the Spirit Tree. No creature of dark can harm us while its three orbs are in place.").also { stage++ } + 10 -> npcl(FacialExpression.OLD_SAD, "We are not a violent race, but we fight when we must. Many gnomes have fallen battling the dark forces of Khazard to the North.").also { stage++ } + 11 -> npcl(FacialExpression.OLD_SAD, "We became desperate, so we took one orb of protection to the battlefield. It was a foolish move.").also { stage++ } + 12 -> npcl(FacialExpression.OLD_NORMAL, "Khazard troops seized the orb. Now we are completely defenceless.").also { stage++ } 13 -> playerl("How can I help?").also { stage++ } - 14 -> npcl("You would be a huge benefit on the battlefield. If you would go there and try to retrieve the orb, my people and I will be forever grateful.").also { stage++ } + 14 -> npcl(FacialExpression.OLD_NORMAL,"You would be a huge benefit on the battlefield. If you would go there and try to retrieve the orb, my people and I will be forever grateful.").also { stage++ } 15 -> options("I would be glad to help.", "I'm sorry but I won't be involved.").also { stage++ } 16 -> when (buttonID) { 1 -> playerl("I would be glad to help.").also { stage = 18 } 2 -> playerl("I'm sorry but I won't be involved.").also { stage = 17 } } - 17 -> npcl("Ok then, travel safe.").also { stage = END_DIALOGUE } - 18 -> npcl("Thank you. The battlefield is to the north of the maze. Commander Montai will inform you of their current situation.").also { stage++ } - 19 -> npcl("That is if he's still alive.").also { stage++ } - 20 -> npcl("My assistant shall guide you out. Good luck friend, try your best to return the orb.").also { + 17 -> npcl(FacialExpression.OLD_NORMAL, "Ok then, travel safe.").also { stage = END_DIALOGUE } + 18 -> npcl(FacialExpression.OLD_NORMAL, "Thank you. The battlefield is to the north of the maze. Commander Montai will inform you of their current situation.").also { stage++ } + 19 -> npcl(FacialExpression.OLD_NORMAL, "That is if he's still alive.").also { stage++ } + 20 -> npcl(FacialExpression.OLD_NORMAL, "My assistant shall guide you out. Good luck friend, try your best to return the orb.").also { stage++ } 21 -> { teleport(player!!, mazeEntrance) - sendNPCDialogue(player!!, NPCs.ELKOY_5179, "We're out of the maze now. Please hurry, we must have the orb if we are to survive.") - setQuestStage(player!!, questName, 10) + sendNPCDialogue(player!!, NPCs.ELKOY_5179, "We're out of the maze now. Please hurry, we must have the orb if we are to survive.", FacialExpression.OLD_NORMAL) + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 10) stage = END_DIALOGUE } } @@ -59,7 +60,7 @@ class KingBolrenDialogue : DialogueFile() { questStage < 31 -> { when (stage) { 0 -> playerl("Hello Bolren.").also { stage++ } - 1 -> npcl("Hello traveller, we must retrieve the orb. It's being held by Khazard troops north of here.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hello traveller, we must retrieve the orb. It's being held by Khazard troops north of here.").also { stage++ } 2 -> playerl("Ok, I'll try my best.").also { stage = END_DIALOGUE } } } @@ -67,32 +68,32 @@ class KingBolrenDialogue : DialogueFile() { if(inInventory(player!!,Items.ORB_OF_PROTECTION_587)){ when(stage) { 0 -> playerl("I have the orb.").also { stage++ } - 1 -> npcl("Oh my... The misery, the horror!").also { stage++ } + 1 -> npcl(FacialExpression.OLD_DISTRESSED, "Oh my... The misery, the horror!").also { stage++ } 2 -> playerl("King Bolren, are you OK?").also { stage++ } - 3 -> npcl("Thank you traveller, but it's too late. We're all doomed.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_DISTRESSED, "Thank you traveller, but it's too late. We're all doomed.").also { stage++ } 4 -> playerl("What happened?").also { stage++ } - 5 -> npcl("They came in the night. I don't know how many, but enough.").also { stage++ } + 5 -> npcl(FacialExpression.OLD_DISTRESSED, "They came in the night. I don't know how many, but enough.").also { stage++ } 6 -> playerl("Who?").also { stage++ } - 7 -> npcl("Khazard troops. They slaughtered anyone who got in their way. Women, children, my wife.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_DISTRESSED, "Khazard troops. They slaughtered anyone who got in their way. Women, children, my wife.").also { stage++ } 8 -> playerl("I'm sorry.").also { stage++ } - 9 -> npcl("They took the other orbs, now we are defenceless.").also { stage++ } + 9 -> npcl(FacialExpression.OLD_BOWS_HEAD_SAD, "They took the other orbs, now we are defenceless.").also { stage++ } 10 -> playerl("Where did they take them?").also { stage++ } - 11 -> npcl("They headed north of the stronghold. A warlord carries the orbs.").also { stage++ } + 11 -> npcl(FacialExpression.OLD_NORMAL, "They headed north of the stronghold. A warlord carries the orbs.").also { stage++ } 12 -> options("I will find the warlord and bring back the orbs.", "I'm sorry but I can't help.").also { stage++ } 13 -> when(buttonID) { 1 -> playerl("I will find the warlord and bring back the orbs.").also { stage = 15 } 2 -> playerl("I'm sorry but I can't help.").also { stage = 14 } } - 14 -> npcl("I understand, this isn't your battle.").also { stage = END_DIALOGUE } - 15 -> npcl("You are brave, but this task will be tough even for you. I wish you the best of luck. Once again you are our only hope.").also { stage++ } - 16 -> npcl("I will safeguard this orb and pray for your safe return. My assistant will guide you out.").also { + 14 -> npcl(FacialExpression.OLD_NORMAL, "I understand, this isn't your battle.").also { stage = END_DIALOGUE } + 15 -> npcl(FacialExpression.OLD_NORMAL, "You are brave, but this task will be tough even for you. I wish you the best of luck. Once again you are our only hope.").also { stage++ } + 16 -> npcl(FacialExpression.OLD_NORMAL, "I will safeguard this orb and pray for your safe return. My assistant will guide you out.").also { stage++ } 17 -> { if(removeItem(player!!,Items.ORB_OF_PROTECTION_587)){ teleport(player!!,mazeEntrance) - setQuestStage(player!!, questName,40) - sendNPCDialogue(player!!, NPCs.ELKOY_5179, "Good luck friend.") + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 40) + sendNPCDialogue(player!!, NPCs.ELKOY_5179, "Good luck friend.", FacialExpression.OLD_NORMAL) } stage = END_DIALOGUE } @@ -100,9 +101,9 @@ class KingBolrenDialogue : DialogueFile() { } else { when(stage) { 0 -> playerl("Hello Bolren.").also { stage++ } - 1 -> npcl("Do you have the orb?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Do you have the orb?").also { stage++ } 2 -> playerl("No, I'm afraid not.").also { stage++ } - 3 -> npcl("Please, we must have the orb if we are to survive.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Please, we must have the orb if we are to survive.").also { stage = END_DIALOGUE } } } } @@ -110,12 +111,12 @@ class KingBolrenDialogue : DialogueFile() { if(inInventory(player!!,Items.ORBS_OF_PROTECTION_588)){ when(stage) { 0 -> playerl("Bolren, I have returned.").also { stage++ } - 1 -> npcl("You made it back! Do you have the orbs?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "You made it back! Do you have the orbs?").also { stage++ } 2 -> playerl("I have them here.").also { stage++ } - 3 -> npcl("Hooray, you're amazing. I didn't think it was possible but you've saved us.").also { stage++ } - 4 -> npcl("Once the orbs are replaced we will be safe once more. We must begin the ceremony immediately.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_HAPPY, "Hooray, you're amazing. I didn't think it was possible but you've saved us.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "Once the orbs are replaced we will be safe once more. We must begin the ceremony immediately.").also { stage++ } 5 -> playerl("What does the ceremony involve?").also { stage++ } - 6 -> npcl("The spirit tree has looked over us for centuries. Now we must pay our respects.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_NORMAL, "The spirit tree has looked over us for centuries. Now we must pay our respects.").also { stage++ } 7 -> sendDialogue(player!!,"The gnomes begin to chant. Meanwhile, King Bolren holds the orbs of protection out in front of him.").also { stage++ } 8 -> { // Orbs fly up, gnomes chant, spirit tree animates @@ -158,7 +159,7 @@ class KingBolrenDialogue : DialogueFile() { }) // This loops back to the start of the handle.. if(removeItem(player!!,Items.ORBS_OF_PROTECTION_588)){ - setQuestStage(player!!,questName,99) + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 99) } stage = 0 } @@ -166,31 +167,36 @@ class KingBolrenDialogue : DialogueFile() { } else { when(stage) { 0 -> playerl("Bolren, I have returned.").also { stage++ } - 1 -> npcl("You made it back! Do you have the orbs?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "You made it back! Do you have the orbs?").also { stage++ } 2 -> playerl("No, I'm afraid not.").also { stage++ } - 3 -> npcl("Please, we must have the orb if we are to survive.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Please, we must have the orb if we are to survive.").also { stage = END_DIALOGUE } } } } questStage == 99 -> { when(stage){ - 0 -> npcl("Now at last my people are safe once more. We can live in peace again.").also { stage++ } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Now at last my people are safe once more. We can live in peace again.").also { stage++ } 1 -> playerl("I'm pleased I could help.").also { stage++ } - 2 -> npcl("You are modest brave traveller.").also { stage++ } - 3 -> npcl("Please, for your efforts take this amulet. It's made from the same sacred stone as the orbs of protection. It will help keep you safe on your journeys.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "You are modest brave traveller.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Please, for your efforts take this amulet. It's made from the same sacred stone as the orbs of protection. It will help keep you safe on your journeys.").also { stage++ } 4 -> playerl("Thank you King Bolren.").also { stage++ } - 5 -> npcl("The tree has many other powers, some of which I cannot reveal. As a friend of the gnome people, I can now allow you to use the tree's magic to teleport to other trees grown from related seeds.").also { - finishQuest(player!!,questName) + 5 -> npcl(FacialExpression.OLD_NORMAL, "The tree has many other powers, some of which I cannot reveal. As a friend of the gnome people, I can now allow you to use the tree's magic to teleport to other trees grown from related seeds.").also { + finishQuest(player!!, Quests.TREE_GNOME_VILLAGE) stage = END_DIALOGUE } } } - isQuestComplete(player!!, questName) -> { + isQuestComplete(player!!, Quests.TREE_GNOME_VILLAGE) -> { when(stage) { - 0 -> playerl("Hello Bolren.").also { stage++ } - 1 -> npcl("Thank you for your help traveler.").also { stage = END_DIALOGUE } + 0 -> playerl("Hello again Bolren.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Well hello, it's good to see you again.").also { stage = if (hasAnItem(player!!, Items.GNOME_AMULET_589).container != null) END_DIALOGUE else 2 } + 2 -> playerl("I've lost my amulet.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Oh dear. Here, take another. We are truly indebted to you.").also { + addItemOrDrop(player!!, Items.GNOME_AMULET_589) + stage = END_DIALOGUE + } } } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/kandarin/quest/tree/LieutenantSchepburDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/LieutenantSchepburDialogue.kt index 7f29582d1..2571454a9 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/LieutenantSchepburDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/LieutenantSchepburDialogue.kt @@ -1,17 +1,18 @@ package content.region.kandarin.quest.tree import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE class LieutenantSchepburDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { when(stage) { - 0 -> npcl("Move into position lads! eh? Who are you and what do you want?").also { stage++ } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Move into position lads! eh? Who are you and what do you want?").also { stage++ } 1 -> playerl("Who are you then?").also { stage++ } - 2 -> npcl("Lieutenant Schepbur, commanding officer of the new Armoured Tortoise Regiment.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "Lieutenant Schepbur, commanding officer of the new Armoured Tortoise Regiment.").also { stage++ } 3 -> playerl("There's only two tortoises here, that's hardly a regiment.").also { stage++ } - 4 -> npcl("This is just the beginning! Gnome breeders and trainers are already working to expand the number of units. Soon we'll have hundreds of these beauties, nay thousands! And they will not only carry mages and").also { stage++ } - 5 -> npcl("archers but other fiendish weapons of destruction of gnome devising. An army of giant tortoises will march upon this battlefield and rain the fire of our wrath upon all our enemies! Nothing will be able to stop us!").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "This is just the beginning! Gnome breeders and trainers are already working to expand the number of units. Soon we'll have hundreds of these beauties, nay thousands! And they will not only carry mages and").also { stage++ } + 5 -> npcl(FacialExpression.OLD_NORMAL, "archers but other fiendish weapons of destruction of gnome devising. An army of giant tortoises will march upon this battlefield and rain the fire of our wrath upon all our enemies! Nothing will be able to stop us!").also { stage++ } 6 -> playerl("Oooookayy...... I'll leave you to it then....").also { stage = END_DIALOGUE } } } diff --git a/Server/src/main/content/region/kandarin/quest/tree/LocalGnomeDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/LocalGnomeDialogue.kt index b78813553..b6a0ff54c 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/LocalGnomeDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/LocalGnomeDialogue.kt @@ -1,13 +1,14 @@ package content.region.kandarin.quest.tree import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE class LocalGnomeDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { when (stage) { 0 -> playerl("Hello little man.").also { stage++ } - 1 -> npcl("Little man stronger than big man. Hee hee, lardi dee, lardi da.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_LAUGH1, "Little man stronger than big man. Hee hee, lardi dee, lardi da.").also { stage = END_DIALOGUE } } } } \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/RemsaiDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/RemsaiDialogue.kt index f8cfde14e..3038fed58 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/RemsaiDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/RemsaiDialogue.kt @@ -1,47 +1,49 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.inInventory import core.api.getQuestStage import org.rs09.consts.Items import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE class RemsaiDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, TreeGnomeVillage.questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) when { inInventory(player!!,Items.ORBS_OF_PROTECTION_588) -> { when(stage) { 0 -> playerl("I've returned.").also { stage++ } - 1 -> npcl("You're back, well done brave adventurer. Now the orbs are safe we can perform the ritual for the spirit tree. We can live in peace once again.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "You're back, well done brave adventurer. Now the orbs are safe we can perform the ritual for the spirit tree. We can live in peace once again.").also { stage = END_DIALOGUE } } } inInventory(player!!, Items.ORB_OF_PROTECTION_587) -> { when(stage) { 0 -> playerl("Hello Remsai.").also { stage++ } - 1 -> npcl("Hello, did you find the orb?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hello, did you find the orb?").also { stage++ } 2 -> playerl("I have it here.").also { stage++ } - 3 -> npcl("You're our saviour.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.OLD_HAPPY, "You're our saviour.").also { stage = END_DIALOGUE } } } questStage < 40 -> { when(stage) { 0 -> playerl("Hello Remsai.").also { stage++ } - 1 -> npcl("Hello, did you find the orb?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hello, did you find the orb?").also { stage++ } 2 -> playerl("No, I'm afraid not.").also { stage++ } - 3 -> npcl("Please, we must have the orb if we are to survive.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Please, we must have the orb if we are to survive.").also { stage = END_DIALOGUE } } } questStage == 40 -> { when(stage) { 0 -> playerl("Are you ok?").also { stage++ } - 1 -> npcl("Khazard's men came. Without the orb we were defenceless. They killed many and then took our last hope, the other orbs. Now surely we're all doomed. Without them the spirit tree is useless.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_DISTRESSED, "Khazard's men came. Without the orb we were defenceless. They killed many and then took our last hope, the other orbs. Now surely we're all doomed. Without them the spirit tree is useless.").also { stage = END_DIALOGUE } } } questStage > 40 -> { when(stage) { 0 -> playerl("I've returned.").also { stage++ } - 1 -> npcl("You're back, well done brave adventurer. Now the orbs are safe we can perform the ritual for the spirit tree. We can live in peace once again.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "You're back, well done brave adventurer. Now the orbs are safe we can perform the ritual for the spirit tree. We can live in peace once again.").also { stage = END_DIALOGUE } } } } diff --git a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeOneDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeOneDialogue.kt index b9b22bd34..d4a4919fb 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeOneDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeOneDialogue.kt @@ -1,43 +1,45 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* import org.rs09.consts.Items import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE class TrackerGnomeOneDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, TreeGnomeVillage.questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) when { questStage >= 40 -> { when (stage) { 0 -> playerl("Hello").also { stage++ } - 1 -> npcl("When will this battle end? I feel like I've been fighting forever.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_BOWS_HEAD_SAD, "When will this battle end? I feel like I've been fighting forever.").also { stage = END_DIALOGUE } } } questStage > 30 -> { if(inInventory(player!!, Items.ORB_OF_PROTECTION_587)){ when(stage) { 0 -> playerl("How are you tracker?").also { stage++ } - 1 -> npcl("Now we have the orb I'm much better. They won't stand a chance without it.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Now we have the orb I'm much better. They won't stand a chance without it.").also { stage = END_DIALOGUE } } } else { when(stage) { 0 -> playerl("Hello again.").also { stage++ } - 1 -> npcl("Well done, you've broken down their defences. This battle must be ours.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Well done, you've broken down their defences. This battle must be ours.").also { stage = END_DIALOGUE } } } } questStage == 30 -> { when (stage) { 0 -> playerl("Do you know the coordinates of the Khazard stronghold?").also { stage++ } - 1 -> npcl("I managed to get one, although it wasn't easy.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "I managed to get one, although it wasn't easy.").also { stage++ } 2 -> sendDialogue(player!!, "The gnome tells you the height coordinate.").also { setAttribute(player!!, "/save:treegnome:tracker1", true) stage++ } 3 -> playerl("Well done.").also { stage++ } - 4 -> npcl("The other two tracker gnomes should have the other coordinates if they're still alive.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "The other two tracker gnomes should have the other coordinates if they're still alive.").also { stage++ } 5 -> playerl("OK, take care.").also { stage = END_DIALOGUE } } } diff --git a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeThreeDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeThreeDialogue.kt index 607e087d5..de00880e0 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeThreeDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeThreeDialogue.kt @@ -1,7 +1,9 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE class TrackerGnomeThreeDialogue : DialogueFile(){ @@ -13,36 +15,36 @@ class TrackerGnomeThreeDialogue : DialogueFile(){ 4 to "My legs and your legs.") override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, TreeGnomeVillage.questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) when { questStage == 30 -> { when(stage) { 0 -> playerl("Are you OK?").also { stage++ } - 1 -> npcl("OK? Who's OK? Not me! Hee hee!").also { stage++ } + 1 -> npcl(FacialExpression.OLD_LAUGH1, "OK? Who's OK? Not me! Hee hee!").also { stage++ } 2 -> playerl("What's wrong?").also { stage++ } - 3 -> npcl("You can't see me, no one can. Monsters, demons, they're all around me!").also { stage++ } + 3 -> npcl(FacialExpression.OLD_LAUGH1, "You can't see me, no one can. Monsters, demons, they're all around me!").also { stage++ } 4 -> playerl("What do you mean?").also { stage++ } - 5 -> npcl("They're dancing, all of them, hee hee.").also { stage++ } + 5 -> npcl(FacialExpression.OLD_LAUGH1, "They're dancing, all of them, hee hee.").also { stage++ } 6 -> sendDialogue(player!!,"He's clearly lost the plot.").also { stage++ } 7 -> playerl("Do you have the coordinate for the Khazard stronghold?").also { stage++ } - 8 -> npcl("Who holds the stronghold?").also { stage++ } + 8 -> npcl(FacialExpression.OLD_NORMAL, "Who holds the stronghold?").also { stage++ } 9 -> playerl("What?").also { stage++ } 10 -> { // Generate the x coordinate answer if(getAttribute(player!!,"treegnome:xcoord",0) == 0){ val answer = (1..4).random() - npcl(xcoordMap[answer]) + npcl(FacialExpression.OLD_NORMAL, xcoordMap[answer]) setAttribute(player!!,"/save:treegnome:xcoord",answer) } else { - npcl(xcoordMap[getAttribute(player!!,"treegnome:xcoord",1)]) + npcl(FacialExpression.OLD_NORMAL, xcoordMap[getAttribute(player!!,"treegnome:xcoord",1)]) } stage++ } 11 -> playerl("You're mad").also { stage++ } - 12 -> npcl("Dance with me, and Khazard's men are beat.").also { stage++ } + 12 -> npcl(FacialExpression.OLD_LAUGH1, "Dance with me, and Khazard's men are beat.").also { stage++ } 13 -> sendDialogue(player!!,"The toll of war has affected his mind.").also { stage++ } 14 -> playerl("I'll pray for you little man.").also { stage++ } - 15 -> npcl("All day we pray in the hay, hee hee.").also { + 15 -> npcl(FacialExpression.OLD_LAUGH1, "All day we pray in the hay, hee hee.").also { setAttribute(player!!, "/save:treegnome:tracker3", true) stage = END_DIALOGUE } @@ -51,13 +53,13 @@ class TrackerGnomeThreeDialogue : DialogueFile(){ questStage == 31 -> { when(stage) { 0 -> playerl("Hello again.").also { stage++ } - 1 -> npcl("Don't talk to me, you can't see me. No one can, just the demons.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_BOWS_HEAD_SAD, "Don't talk to me, you can't see me. No one can, just the demons.").also { stage = END_DIALOGUE } } } questStage > 31 -> { when(stage) { 0 -> playerl("Hello").also { stage++ } - 1 -> npcl("I feel dizzy, where am I? Oh dear, oh dear I need some rest.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "I feel dizzy, where am I? Oh dear, oh dear I need some rest.").also { stage++ } 2 -> playerl("I think you do.").also { stage = END_DIALOGUE } } } diff --git a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeTwoDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeTwoDialogue.kt index f17211fd2..d4bbd63c0 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeTwoDialogue.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeTwoDialogue.kt @@ -1,47 +1,49 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* import org.rs09.consts.Items import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE class TrackerGnomeTwoDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - val questStage = getQuestStage(player!!, TreeGnomeVillage.questName) + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) when { questStage == 30 -> { when (stage) { 0 -> playerl("Are you OK?").also { stage++ } - 1 -> npcl("They caught me spying on the stronghold. They beat and tortured me.").also { stage++ } - 2 -> npcl("But I didn't crack. I told them nothing. They can't break me!").also { stage++ } + 1 -> npcl(FacialExpression.OLD_DISTRESSED, "They caught me spying on the stronghold. They beat and tortured me.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_LAUGH1, "But I didn't crack. I told them nothing. They can't break me!").also { stage++ } 3 -> playerl("I'm sorry little man.").also { stage++ } - 4 -> npcl("Don't be. I have the position of the stronghold!").also { stage++ } + 4 -> npcl(FacialExpression.OLD_LAUGH1, "Don't be. I have the position of the stronghold!").also { stage++ } 5 -> sendDialogue(player!!, "The gnome tells you the y coordinate.").also { setAttribute(player!!, "/save:treegnome:tracker2", true) stage++ } 6 -> playerl("Well done.").also { stage++ } - 7 -> npcl("Now leave before they find you and all is lost.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Now leave before they find you and all is lost.").also { stage++ } 8 -> playerl("Hang in there.").also { stage++ } - 9 -> npcl("Go!").also { stage = END_DIALOGUE } + 9 -> npcl(FacialExpression.OLD_NORMAL, "Go!").also { stage = END_DIALOGUE } } } questStage >= 40 -> { when(stage) { 0 -> playerl("Hello").also { stage++ } - 1 -> npcl("When will this battle end? I feel like I've been locked up my whole life.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_DISTRESSED, "When will this battle end? I feel like I've been locked up my whole life.").also { stage = END_DIALOGUE } } } questStage > 30 -> { if(inInventory(player!!,Items.ORB_OF_PROTECTION_587)){ when(stage) { 0 -> playerl("How are you tracker?").also { stage++ } - 1 -> npcl("Now we have the orb I'm much better. Soon my comrades will come and free me.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Now we have the orb I'm much better. Soon my comrades will come and free me.").also { stage = END_DIALOGUE } } } else { when(stage) { 0 -> playerl("Hello again.").also { stage++ } - 1 -> npcl("Well done, you've broken down their defences. This battle must be ours.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Well done, you've broken down their defences. This battle must be ours.").also { stage = END_DIALOGUE } } } } diff --git a/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillage.kt b/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillage.kt index a87e2ca96..14f04896f 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillage.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillage.kt @@ -8,9 +8,10 @@ import core.game.node.entity.skill.Skills import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable -class TreeGnomeVillage: Quest("Tree Gnome Village", 125, 124, 2, 111, 0, 1, 9) { +class TreeGnomeVillage: Quest(Quests.TREE_GNOME_VILLAGE, 125, 124, 2, 111, 0, 1, 9) { override fun newInstance(`object`: Any?): Quest { return this } @@ -91,6 +92,5 @@ class TreeGnomeVillage: Quest("Tree Gnome Village", 125, 124, 2, 111, 0, 1, 9) companion object { val mazeVillage = Location(2515,3159,0) val mazeEntrance = Location(2504,3192,0) - const val questName = "Tree Gnome Village" } } \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillageListeners.kt b/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillageListeners.kt index f57509046..71471bf43 100644 --- a/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillageListeners.kt +++ b/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillageListeners.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player @@ -92,17 +93,25 @@ class TreeGnomeVillageListeners : InteractionListener { return@on true } on(closedChest, IntType.SCENERY, "open"){ player, node -> - SceneryBuilder.replace(node.asScenery(), Scenery(openedChest, node.location, node.asScenery().rotation),10) + replaceScenery(node.asScenery(), openedChest, -1) val upperGuard: NPC = RegionManager.getNpc(player.location, NPCs.KHAZARD_COMMANDER_478, 6) ?: return@on true upperGuard.sendChat("Oi. You! Get out of there.") upperGuard.attack(player) return@on true } on(openedChest, IntType.SCENERY, "search"){ player, _ -> - if(!inInventory(player,Items.ORB_OF_PROTECTION_587)){ - sendDialogue(player,"You search the chest. Inside you find the gnomes' stolen orb of protection.") - addItemOrDrop(player,Items.ORB_OF_PROTECTION_587) + if (getQuestStage(player, Quests.TREE_GNOME_VILLAGE) >= 31) { + if (!hasAnItem(player, Items.ORB_OF_PROTECTION_587).exists()) { + sendDialogue(player, "You search the chest. Inside you find the gnomes' stolen orb of protection.") + addItemOrDrop(player, Items.ORB_OF_PROTECTION_587) + return@on true + } } + sendMessage(player, "You search the chest but find nothing.") + return@on false + } + on(openedChest, IntType.SCENERY, "close"){ _, node -> + replaceScenery(node.asScenery(), closedChest, -1) return@on true } on(strongholdDoor, IntType.SCENERY, "open"){ player, node -> @@ -118,9 +127,7 @@ class TreeGnomeVillageListeners : InteractionListener { } fun squeezeThrough(player: Player){ - val squeezeAnim = Animation.create(3844) - - var dest = if (player.location.y >= 3161) + val dest = if (player.location.y >= 3161) player.location.transform(Direction.SOUTH, 1) else player.location.transform(Direction.NORTH, 1) @@ -132,7 +139,7 @@ class TreeGnomeVillageListeners : InteractionListener { val climbAnimation = Animation(839) val wallLoc = Location(2509,3253,0) override fun handle(componentID: Int, buttonID: Int) { - if(getQuestStage(player!!, TreeGnomeVillage.questName) > 30){ + if(getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) > 30){ val northSouth = if (player!!.location.y <= wallLoc.y) Direction.NORTH else Direction.SOUTH when(stage){ 0 -> sendDialogue(player!!,"The wall has been reduced to rubble. It should be possible to climb over the remains").also{ stage++ } diff --git a/Server/src/main/content/region/kandarin/quest/waterfall/AlmeraDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/AlmeraDialogue.java index 91285e36d..6d3f2bee6 100644 --- a/Server/src/main/content/region/kandarin/quest/waterfall/AlmeraDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/AlmeraDialogue.java @@ -1,5 +1,6 @@ package content.region.kandarin.quest.waterfall; +import content.data.Quests; import core.game.dialogue.DialogueInterpreter; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -27,7 +28,7 @@ public class AlmeraDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest(WaterFall.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.WATERFALL_QUEST); switch (stage) { /* Main dialogue sequence */ case 0: @@ -136,7 +137,7 @@ public class AlmeraDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - final Quest quest = player.getQuestRepository().getQuest(WaterFall.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.WATERFALL_QUEST); if (quest.getStage(player) == 100) { interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Almera."); stage = 7; @@ -146,4 +147,4 @@ public class AlmeraDialogue extends DialoguePlugin { } return true; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/kandarin/quest/waterfall/BaxtorianBook.kt b/Server/src/main/content/region/kandarin/quest/waterfall/BaxtorianBook.kt index c3f1bf63f..fe92e9136 100644 --- a/Server/src/main/content/region/kandarin/quest/waterfall/BaxtorianBook.kt +++ b/Server/src/main/content/region/kandarin/quest/waterfall/BaxtorianBook.kt @@ -1,5 +1,6 @@ package content.region.kandarin.quest.waterfall +import content.data.Quests import content.global.handlers.iface.BookInterface import content.global.handlers.iface.BookLine import content.global.handlers.iface.Page @@ -143,8 +144,8 @@ class BaxtorianBook : InteractionListener { ) private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) - if (player.questRepository.getQuest(WaterFall.NAME).getStage(player) == 20) { - player.questRepository.getQuest(WaterFall.NAME).setStage(player, 30) + if (player.questRepository.getQuest(Quests.WATERFALL_QUEST).getStage(player) == 20) { + player.questRepository.getQuest(Quests.WATERFALL_QUEST).setStage(player, 30) } return true } diff --git a/Server/src/main/content/region/kandarin/quest/waterfall/HudonDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/HudonDialogue.java index 5e59576fe..e8c3262ad 100644 --- a/Server/src/main/content/region/kandarin/quest/waterfall/HudonDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/HudonDialogue.java @@ -1,5 +1,6 @@ package content.region.kandarin.quest.waterfall; +import content.data.Quests; import core.game.dialogue.DialogueInterpreter; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -27,7 +28,7 @@ public class HudonDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest(WaterFall.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.WATERFALL_QUEST); switch (stage) { case 100: // Generic end to the dlg @@ -109,7 +110,7 @@ public class HudonDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - final Quest quest = player.getQuestRepository().getQuest(WaterFall.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.WATERFALL_QUEST); if (quest.getStage(player) >= 20) { interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So you're still here."); stage = 20; @@ -119,4 +120,4 @@ public class HudonDialogue extends DialoguePlugin { } return true; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/kandarin/quest/waterfall/WaterFall.java b/Server/src/main/content/region/kandarin/quest/waterfall/WaterFall.java index 467bef439..33030e29a 100644 --- a/Server/src/main/content/region/kandarin/quest/waterfall/WaterFall.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/WaterFall.java @@ -1,5 +1,6 @@ package content.region.kandarin.quest.waterfall; +import content.data.Quests; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; @@ -13,17 +14,11 @@ import core.plugin.ClassScanner; */ @Initializable public class WaterFall extends Quest { - - /** - * The name of this quest. - */ - public static final String NAME = "Waterfall"; - /** * Constructs a new {@code WaterFall} {@code Object}. */ public WaterFall() { - super("Waterfall", 65, 64, 1, 65, 0, 1, 10); + super(Quests.WATERFALL_QUEST, 65, 64, 1, 65, 0, 1, 10); } @Override diff --git a/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallPlugin.java b/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallPlugin.java index 5bcac3376..2a10ebe5c 100644 --- a/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallPlugin.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallPlugin.java @@ -3,6 +3,7 @@ package content.region.kandarin.quest.waterfall; import java.util.ArrayList; import java.util.List; +import content.data.Quests; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; @@ -130,7 +131,7 @@ public final class WaterfallPlugin extends OptionHandler { @Override public boolean handle(final Player player, Node node, String option) { final int id = node.getId(); - final Quest quest = player.getQuestRepository().getQuest(WaterFall.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.WATERFALL_QUEST); if (quest == null) { player.sendMessage("Error! Waterfall quest cannot be found."); return true; @@ -141,7 +142,7 @@ public final class WaterfallPlugin extends OptionHandler { player.getPulseManager().run(new Pulse(2, player) { @Override public boolean pulse() { - if ((player.getEquipment().containsAtLeastOneItem(295) || player.getInventory().contains(295, 1)) || player.getQuestRepository().isComplete("Waterfall")) { + if ((player.getEquipment().containsAtLeastOneItem(295) || player.getInventory().contains(295, 1)) || player.getQuestRepository().isComplete(Quests.WATERFALL_QUEST)) { player.getPacketDispatch().sendMessage("You walk through the door."); player.teleport(new Location(2575, 9861)); } else { @@ -425,7 +426,7 @@ public final class WaterfallPlugin extends OptionHandler { public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); Item useditem = event.getUsedItem(); - final Quest quest = player.getQuestRepository().getQuest(WaterFall.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.WATERFALL_QUEST); final Scenery object = (Scenery) event.getUsedWith(); if (useditem.getId() == ROPE.getId() && object.getId() == 1996 || object.getId() == 1997) { diff --git a/Server/src/main/content/region/kandarin/quest/whileguthixsleeps/WhileGuthixSleeps.kt b/Server/src/main/content/region/kandarin/quest/whileguthixsleeps/WhileGuthixSleeps.kt index ca348787d..10ea45b99 100644 --- a/Server/src/main/content/region/kandarin/quest/whileguthixsleeps/WhileGuthixSleeps.kt +++ b/Server/src/main/content/region/kandarin/quest/whileguthixsleeps/WhileGuthixSleeps.kt @@ -3,12 +3,11 @@ package content.region.kandarin.quest.whileguthixsleeps import core.api.getQuestStage import core.api.hasLevelStat import core.api.isQuestComplete -import core.api.rewardXP import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills -import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * While Guthix Sleeps Quest @@ -18,17 +17,14 @@ import org.rs09.consts.Items * */ //@Initializable -class WhileGuthixSleeps : Quest("While Guthix Sleeps", 161, 160, 5,5491, 0, 1, 900) { +class WhileGuthixSleeps : Quest(Quests.WHILE_GUTHIX_SLEEPS, 161, 160, 5, 5491, 0, 1, 900) { - companion object { - const val questName = "While Guthix Sleeps" - } override fun drawJournal(player: Player, stage: Int) { super.drawJournal(player, stage) var line = 12 var stage = getStage(player) - var started = getQuestStage(player, questName) > 0 + var started = getQuestStage(player, Quests.WHILE_GUTHIX_SLEEPS) > 0 if (!started) { line(player, "I can start this quest by speaking to !!Radimus Erkle?? in the", line++, false) @@ -44,18 +40,43 @@ class WhileGuthixSleeps : Quest("While Guthix Sleeps", 161, 160, 5,5491, 0, 1, 9 line(player, "!!Level 65 Farming??", line++, hasLevelStat(player, Skills.FARMING, 65)) line(player, "!!Level 23 Summoning??", line++, hasLevelStat(player, Skills.SUMMONING, 23)) line(player, "I also need to have completed the following quests:", line++, false) - line(player, "!!Recipe for Disaster??", line++, isQuestComplete(player, "Recipe for Disaster")) - line(player, "!!Mourning's Ends Part II - The Temple of Light??", line++, isQuestComplete(player, "Mourning's End Part II")) - line(player, "!!Swan Song??", line++, isQuestComplete(player, "Swan Song")) - line(player, "!!Zogre Flesh Eaters??", line++, isQuestComplete(player, "Zogre Flesh Eaters")) - line(player, "!!Path of Glouphrie??", line++, isQuestComplete(player, "Path of Glouphrie")) - line(player, "!!Summer's End??", line++, isQuestComplete(player, "Summer's End")) - line(player, "!!Legends' Quest??", line++, isQuestComplete(player, "Legends' Quest")) - line(player, "!!Dream Mentor??", line++, isQuestComplete(player, "Dream Mentor")) - line(player, "!!Hand in the Sand??", line++, isQuestComplete(player, "The Hand in the Sand")) - line(player, "!!Tears of Guthix??", line++, isQuestComplete(player, "Tears of Guthix")) - line(player, "!!King's Ransom??", line++, isQuestComplete(player, "King's Ransom")) - line(player, "!!Defender of Varrock??", line++, isQuestComplete(player, "Defender of Varrock")) + line( + player, + "!!Recipe for Disaster??", + line++, + isQuestComplete(player, Quests.RECIPE_FOR_DISASTER) + ) + line( + player, + "!!Mourning's Ends Part II - The Temple of Light??", + line++, + isQuestComplete(player, Quests.MOURNINGS_END_PART_II) + ) + line(player, "!!Swan Song??", line++, isQuestComplete(player, Quests.SWAN_SONG)) + line( + player, + "!!Zogre Flesh Eaters??", + line++, + isQuestComplete(player, Quests.ZOGRE_FLESH_EATERS) + ) + line(player, "!!Path of Glouphrie??", line++, isQuestComplete(player, Quests.THE_PATH_OF_GLOUPHRIE)) + line(player, "!!Summer's End??", line++, isQuestComplete(player, Quests.SUMMERS_END)) + line(player, "!!Legends' Quest??", line++, isQuestComplete(player, Quests.LEGENDS_QUEST)) + line(player, "!!Dream Mentor??", line++, isQuestComplete(player, Quests.DREAM_MENTOR)) + line( + player, + "!!Hand in the Sand??", + line++, + isQuestComplete(player, Quests.THE_HAND_IN_THE_SAND) + ) + line(player, "!!Tears of Guthix??", line++, isQuestComplete(player, Quests.TEARS_OF_GUTHIX)) + line(player, "!!King's Ransom??", line++, isQuestComplete(player, Quests.KINGS_RANSOM)) + line( + player, + "!!Defender of Varrock??", + line++, + isQuestComplete(player, Quests.DEFENDER_OF_VARROCK) + ) line(player, "!!Be eligible for entry to the Warriors' Guild??", line++) line(player, "!!Defeated Bork in the Chaos Tunnels??", line++) line(player, "!!And gain a total of 270 quest points.??", line++) @@ -68,7 +89,7 @@ class WhileGuthixSleeps : Quest("While Guthix Sleeps", 161, 160, 5,5491, 0, 1, 9 var ln = 10 super.finish(player) player.packetDispatch.sendString("You have completed While Guthix Sleeps!", 277, 4) - player.packetDispatch.sendItemZoomOnInterface(Items.LONGBOW_839,230,277,5) + player.packetDispatch.sendItemZoomOnInterface(Items.LONGBOW_839, 230, 277, 5) drawReward(player, "5 Quest Points", ln++) drawReward(player, "Lump of dragon metal.", ln++) diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java b/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java index d8f856276..5cc97311a 100644 --- a/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java @@ -52,6 +52,6 @@ public final class CamelotGuardDialogue extends DialoguePlugin { @Override public int[] getIds() { - return new int[] { 812 }; + return new int[] { 6183, 6184 }; } } diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt b/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt index 87eac97a9..323e82d85 100644 --- a/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt +++ b/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt @@ -1,37 +1,41 @@ package content.region.kandarin.seers.dialogue -import core.Util import core.game.dialogue.DialoguePlugin import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.player.link.diary.AchievementDiary import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import core.ServerStore +import core.ServerStore.Companion.getBoolean +import org.json.simple.JSONObject @Initializable class GeoffreyDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { - val diary = player.achievementDiaryManager.getDiary(DiaryType.SEERS_VILLAGE) - if (diary.levelRewarded.any()) { + //determine reward level that has been claimed + var gotoStage = 0 + //want the highest value, so this is checked hardest->easiest + if (AchievementDiary.hasClaimedLevelRewards(player,DiaryType.SEERS_VILLAGE,2)) { + gotoStage = 102 + } + else if (AchievementDiary.hasClaimedLevelRewards(player,DiaryType.SEERS_VILLAGE,1)) { + gotoStage = 101 + } + else if (AchievementDiary.hasClaimedLevelRewards(player,DiaryType.SEERS_VILLAGE,0)) { + gotoStage = 100 + } + //give reward, or proceed to normal dialogue + if (gotoStage != 0) { player("Hello there. Are you Geoff-erm-Flax? I've been told that", "you'll give me some flax.") - // If 1 day has not passed since last flax reward - if (player.getAttribute("diary:seers:flax-timer", 0) > System.currentTimeMillis()) { - stage = 98 - return true - } - // If player cannot receive flax reward - if (!player.inventory.hasSpaceFor(Item(Items.FLAX_1780, 1))) { - stage = 99 - return true - } - // Determine flax reward by seers diary reward status - when (diary.reward) { - -1 -> stage = 999 - 0 -> stage = 100 - 1 -> stage = 101 - 2 -> stage = 102 - } - } else { + //Already claimed flax else no room else give correct reward + stage = if (getStoreFile().getBoolean(player.username.lowercase())) { 98 } + else if (!player.inventory.hasSpaceFor(Item(Items.FLAX_1780, 1))) { 99 } + else { gotoStage } + } + //If the diary has not been completed + else { player("Hello there. You look busy.") stage = 0 } @@ -46,7 +50,7 @@ class GeoffreyDialogue(player: Player? = null) : DialoguePlugin(player) { when (stage) { 999 -> end() 0 -> npc("Yes, I am very busy. Picking GLORIOUS flax.", "The GLORIOUS flax won't pick itself. So I pick it.", "I pick it all day long.").also { stage++ } - 1 -> player("Wow, all that flax must really mount up. What do you do with it all?").also { stage++ } + 1 -> player("Wow, all that flax must really mount up.", "What do you do with it all?").also { stage++ } 2 -> npc("I give it away! I love picking the GLORIOUS flax,", "but, if I let it all mount up, I wouldn't have any", "room for more GLORIOUS flax.").also { stage++ } 3 -> player("So, you're just picking the flax for fun? You must", "really like flax.").also { stage++ } 4 -> npc("'Like' the flax? I don't just 'like' flax. The", "GLORIOUS flax is my calling, my reason to live.", "I just love the feeling of it in my hands!").also { stage++ } @@ -55,7 +59,7 @@ class GeoffreyDialogue(player: Player? = null) : DialoguePlugin(player) { 7 -> player("I know this area! It's, erm, Seers' Village. There's", "a pub and, er, a bank.").also { stage++ } 8 -> npc("Pah! You call that local knowledge? Perhaps if you", "were wearing some kind of item from one of the", "seers, I might trust you.").also { stage = 999 } - 98 -> npc("I've already given you your GLORIOUS flax", "for the day. Come back tomorrow.").also { stage = 999 } // TODO find accurate dialogue + 98 -> npc("Don't be greedy. Other people want GLORIOUS flax too.", "You can have some more tomorrow.").also { stage = 999 } // TODO find accurate dialogue, no source found yet, so I'm using the modern RS3 dialogue from wiki source (https://runescape.wiki/w/Transcript:Geoffrey) 99 -> npc("Yes, but your inventory is full. Come back", "when you have some space for GLORIOUS flax.").also { stage = 999 } // TODO find accurate dialogue 100 -> {rewardFlax(30, "Yes. The seers have instructed me to give you an", "allowance of 30 GLORIOUS flax a day. I'm not going", "to argue with them, so here you go.")} // TODO find accurate dialogue 101 -> {rewardFlax(60, "Yes. Stankers has instructed me to give you an", "allowance of 60 GLORIOUS flax a day. I'm not going", "to argue with a dwarf, so here you go.")} // TODO find accurate dialogue @@ -67,7 +71,7 @@ class GeoffreyDialogue(player: Player? = null) : DialoguePlugin(player) { fun rewardFlax(n: Int, vararg messages: String): Unit { npc(*messages) player.inventory.add(Item(Items.FLAX_1780, n)) - player.setAttribute("/save:diary:seers:flax-timer", Util.nextMidnight(System.currentTimeMillis())) + getStoreFile()[player.username.toLowerCase()] = true stage = 999 } @@ -75,4 +79,8 @@ class GeoffreyDialogue(player: Player? = null) : DialoguePlugin(player) { return intArrayOf(8590) } -} + fun getStoreFile(): JSONObject { + return ServerStore.getArchive("daily-seers-flax") + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialogue.kt b/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialogue.kt new file mode 100644 index 000000000..e241c34f2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialogue.kt @@ -0,0 +1,108 @@ +package content.region.kandarin.seers.dialogue + +import content.region.kandarin.seers.diary.SeerDiaryDialogue +import content.region.kandarin.quest.scorpioncatcher.SCSeerDialogue +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher +import core.api.getAttribute +import core.api.getQuestStage +import core.api.isQuestInProgress +import core.api.openDialogue +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs +import core.game.dialogue.Topic +import content.data.Quests + + + +@Initializable +class SeerDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SEER_388) + } + + companion object { + const val OTHER_TOPIC = 10 + const val DIARY = 20 + const val SC_QUEST = 30 + + const val SC_QUEST_HELP = 40 + const val SC_QUEST_FRIEND = 50 + const val SC_QUEST_OTHER_SCORPIONS = 60 + + const val MANY_GREETINGS = 70 + const val POWER = 80 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.WORRIED, "Uh, what was that dark force? I've never sensed anything like it...").also { stage = START_DIALOGUE } // https://www.youtube.com/watch?v=mYsxit46rGo May 14 2010 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + val scorpionCatcherQuestStage = getQuestStage(player, Quests.SCORPION_CATCHER) + when (stage) { + START_DIALOGUE -> + npcl(FacialExpression.NEUTRAL, "Anyway, sorry about that.").also { stage++ } + START_DIALOGUE+1 -> { + if (isQuestInProgress(player, Quests.SCORPION_CATCHER, 1, 99)) { + showTopics( + Topic("Talk about Scorpion Catcher.", SC_QUEST, true), + Topic("Talk about Achievement Diary.", DIARY, true) + ) + } + else { + showTopics( + Topic("Talk about something else.", OTHER_TOPIC, true), + Topic("Talk about achievement diary.", DIARY, true) + ) + } + } + + SC_QUEST -> { + if (scorpionCatcherQuestStage == ScorpionCatcher.QUEST_STATE_TALK_SEERS) { + showTopics( + Topic("I need to locate some scorpions.", SC_QUEST_HELP,), + Topic("Your friend Thormac sent me to speak to you.", SC_QUEST_FRIEND,), + Topic("I seek knowledge and power!", POWER) + ) + } + else if ((scorpionCatcherQuestStage == ScorpionCatcher.QUEST_STATE_DARK_PLACE) and + getAttribute(player!!, ScorpionCatcher.ATTRIBUTE_TAVERLEY, false) + ) { + playerl( + FacialExpression.NEUTRAL, + "Hi, I have retrieved the scorpion from near the spiders." + ).also { stage = SC_QUEST_OTHER_SCORPIONS } + } + else { + npcl(FacialExpression.NEUTRAL, "Good luck finding those scorpions.").also { stage = END_DIALOGUE } + } + } + SC_QUEST_HELP, SC_QUEST_FRIEND, SC_QUEST_OTHER_SCORPIONS -> { + // Use the current stage value as the entry point to Seers + openDialogue(player, SCSeerDialogue(scorpionCatcherQuestStage, stage), npc) + } + + OTHER_TOPIC -> showTopics( + Topic("Many greetings.", MANY_GREETINGS), + Topic("I seek knowledge and power!", POWER) + ) + DIARY -> openDialogue(player, SeerDiaryDialogue(), npc) + + MANY_GREETINGS -> npcl(FacialExpression.NEUTRAL, + "Remember, whenever you set out to do something, something else must be done first.").also { stage = END_DIALOGUE } + + POWER -> npcl(FacialExpression.NEUTRAL, "Knowledge comes from experience, power comes from battleaxes.").also { stage = END_DIALOGUE } + + } + return true + } +} diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialoguePlugin.java b/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialoguePlugin.java deleted file mode 100644 index 327e2e4d4..000000000 --- a/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialoguePlugin.java +++ /dev/null @@ -1,154 +0,0 @@ -package content.region.kandarin.seers.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.link.diary.AchievementDiary; -import core.game.node.entity.player.link.diary.DiaryType; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the SeerDialoguePlugin dialogue. - * - * @author 'afaroutdude - */ -@Initializable -public class SeerDialoguePlugin extends DialoguePlugin { - - public SeerDialoguePlugin() { - } - - public SeerDialoguePlugin(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[]{388}; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SeerDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - // npc("Uh, what was that dark force? I've never sensed", "anything like it..."); // https://www.youtube.com/watch?v=mYsxit46rGo May 14 2010 - // npc("Anyway, sorry about that."); - options("Talk about something else.", "Talk about achievement diary."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - int level = 0; - - switch (stage) { - case 999: - end(); - break; - case 0: - switch (buttonId) { - case 1: - npc("Many greetings."); - stage = 1; - break; - case 2: - if (AchievementDiary.canReplaceReward(player, DiaryType.SEERS_VILLAGE, 0)) { - player("I seem to have lost my seers' headband..."); - stage = 80; - break; - } - else if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { - player("Can you remind me what my headband does?"); - stage = 90; - break; - } - else if (AchievementDiary.canClaimLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { - player("Hi. I've completed the Easy tasks in my Achievement", "Diary."); - stage = 200; - break; - } - else { - player("Hi! Can you help me out with the Achievement Diary", "tasks?"); - stage = 101; - break; - } - } - break; - case 80: - AchievementDiary.grantReplacement(player, DiaryType.SEERS_VILLAGE, 0); - npc("Here's your replacement. Please be more careful."); - stage = 999; - break; - case 90: - npc("Your headband marks you as an honourary seer.", "Geoffrey - who works in the field to the", "south - will give you free flax every day."); - stage = 999; - break; - case 100: - npc("I certainly do - we have a set of tasks spanning Seers'", "Village, Catherby, Hemenster and the Sinclair Mansion.", "Just complete the tasks listed in the Achievement Diary", "and they will be ticked off automatically."); - stage = 999; - break; - case 101: - npc("I'm afraid not. It is important that adventurers", "complete the tasks unaided. That way, only the truly", "worthy collect the spoils."); - stage = 999; - break; - case 200: - npc("Well done, adventurer. You are clearly a " + (player.isMale() ? "man" : "woman") + "of", "great wisdom. I have a gift for you."); - stage++; - break; - case 201: - if (!AchievementDiary.flagRewarded(player, DiaryType.SEERS_VILLAGE, 0)) { - npc("Come back when you have two free inventory slots."); - stage = 999; - } else { - interpreter.sendItemMessage(AchievementDiary.getRewards(DiaryType.SEERS_VILLAGE, 0)[0], "The seer hands you a strange-looking headband and a", "rusty lamp."); - stage++; - } - break; - case 202: - npc("You are now an honourary seer and Geoffrey - who", "works in the field to the south - will give you free flax", "every day. Don't call him 'Geoffrey' though: he prefers", "to be known as 'Flax'."); - stage++; - break; - case 203: - player("Flax? What sort of name is that for a person."); - stage++; - break; - case 204: - npc("I know, I know. The poor boy is a simple soul - he just", "really loves picking flax. A little too much, I fear."); - stage=999; - break; - - case 1: - options("Many greetings.", "I seek knowledge and power!"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - player("Many greetings."); - stage = 10; - break; - case 2: - player("I seek knowledge and power!"); - stage = 20; - break; - } - break; - case 10: - npc("Remember, whenever you set out to do something,", "something else must be done first."); - stage = 999; - break; - case 20: - npc("Knowledge comes from experience, power", "comes from battleaxes."); - stage = 999; - break; - } - return true; - } - -} diff --git a/Server/src/main/content/region/kandarin/seers/diary/SeerDiaryDialogue.kt b/Server/src/main/content/region/kandarin/seers/diary/SeerDiaryDialogue.kt new file mode 100644 index 000000000..e0eb61789 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/diary/SeerDiaryDialogue.kt @@ -0,0 +1,87 @@ +package content.region.kandarin.seers.diary + +import core.api.sendItemDialogue +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +class SeerDiaryDialogue : DialogueFile() { + + companion object { + + const val LOST_HEAD_BAND = 20 + const val HEAD_BAND_HELP = 30 + const val CLAIM_HEAD_BAND = 40 + const val ASK_FOR_HELP = 50 + } + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE ->{ + if (AchievementDiary.canReplaceReward(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl(FacialExpression.SAD, "I seem to have lost my seers' headband...").also { + stage = LOST_HEAD_BAND + } + } else if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl(FacialExpression.ASKING, "Can you remind me what my headband does?").also { + stage = HEAD_BAND_HELP + } + } else if (AchievementDiary.canClaimLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl( + FacialExpression.HAPPY, + "Hi. I've completed the Easy tasks in my Achievement Diary." + ).also { + stage = CLAIM_HEAD_BAND + } + } else { + playerl(FacialExpression.ASKING, "Do you have an Achievement Diary for me?").also { + stage = ASK_FOR_HELP + } + } + } + + + LOST_HEAD_BAND -> { + if (AchievementDiary.grantReplacement(player, DiaryType.SEERS_VILLAGE, 0)) + npcl(FacialExpression.ANNOYED, "Here's your replacement. Please be more careful.").also { + stage = END_DIALOGUE + } + else + // This line is just guessed + npcl(FacialExpression.HALF_GUILTY, "It seems your inventory is full").also { stage = END_DIALOGUE } + } + HEAD_BAND_HELP -> npcl(FacialExpression.NEUTRAL, "Your headband marks you as an honourary seer. Geoffrey - who works in the field to the south - will give you free flax every day.").also { + stage = END_DIALOGUE + } + + // This has to be npc otherwise wordwrap goes wrong and extends to 5 lines + ASK_FOR_HELP -> npc(FacialExpression.HAPPY, "I certainly do - we have a set of tasks spanning Seers'", "Village, Catherby, Hemenster and the Sinclair Mansion.", + "Just complete the tasks listed in the Achievement Diary", "and they will be ticked off automatically.").also { stage++ } + ASK_FOR_HELP + 1 -> playerl(FacialExpression.ASKING, "Can you help me out with the Achievement Diary tasks?").also { stage++ } + ASK_FOR_HELP + 2 -> npcl(FacialExpression.SAD, + "I'm afraid not. It is important that adventurers complete the tasks unaided. That way, only the truly worthy collect the spoils.").also { + stage = END_DIALOGUE + } + + CLAIM_HEAD_BAND -> npcl(FacialExpression.HAPPY, "Well done, adventurer. You are clearly a "+(if (player!!.isMale) "man" else "woman")+" of great wisdom. I have a gift for you.").also { stage++ } + CLAIM_HEAD_BAND + 1 -> { + if (!AchievementDiary.flagRewarded(player, DiaryType.SEERS_VILLAGE, 0)) { + npcl(FacialExpression.NEUTRAL, "Come back when you have two free inventory slots.").also { + stage = END_DIALOGUE + } + } else { + sendItemDialogue(player!!, AchievementDiary.getRewards(DiaryType.SEERS_VILLAGE, 0)[0], + "The seer hands you a strange-looking headband and a rusty lamp.").also { stage++ } + } + } + CLAIM_HEAD_BAND + 2 -> npcl(FacialExpression.HAPPY, "You are now an honourary seer and Geoffrey - who works in the field to the south - will give you free flax every day. Don't call him 'Geoffrey' though: he prefers to be known as 'Flax'.").also { stage++ } + CLAIM_HEAD_BAND + 3 -> playerl(FacialExpression.ASKING, "Flax? What sort of name is that for a person?").also { stage++ } + CLAIM_HEAD_BAND + 4 -> npcl(FacialExpression.NEUTRAL, "I know, I know. The poor boy is a simple soul - he just really loves picking flax. A little too much, I fear.").also { + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt index b358cf83b..bd9e90188 100644 --- a/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt +++ b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt @@ -9,11 +9,13 @@ import core.game.diary.DiaryLevel import core.game.event.* import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.player.link.prayer.PrayerType import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items import org.rs09.consts.NPCs +import org.rs09.consts.Scenery class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) { companion object { @@ -29,6 +31,7 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) private val SEERS_VILLAGE_AREA = ZoneBorders(2687, 3455, 2742, 3507) private val SEERS_BANK_AREA = ZoneBorders(2721, 3490, 2730, 3493) private val SEERS_COAL_TRUCKS_AREA = ZoneBorders(2690, 3502, 2699, 3508) + private val SEERS_COURTHOUSE_AREA = ZoneBorders(2732, 3467, 2739, 3471) private val RANGING_GUILD_LOCATION = Location(2657, 3439) @@ -98,6 +101,14 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) } override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { + if (event.source.id == Scenery.OBELISK_OF_WATER_2151 && event.amount >= 5) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.CHARGE_5_WATER_ORBS_AT_ONCE + ) + } + when (player.viewport.region.id) { 10805 -> if (event.itemId == Items.FLAX_1779) { progressIncrementalTask( @@ -288,7 +299,7 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) finishTask( player, DiaryLevel.HARD, - HardTasks.HIGH_ALCH_MAGIC_SHORTBOW_INSIDE_BANK + HardTasks.HIGH_ALCH_MAGIC_SHORTBOW_INSIDE_BANK ) } } @@ -299,7 +310,7 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) finishTask( player, DiaryLevel.HARD, - HardTasks.DIAL_FAIRY_RING_MCGRUBORS_WOOD + HardTasks.DIAL_FAIRY_RING_MCGRUBORS_WOOD ) } } @@ -309,7 +320,47 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) finishTask( player, DiaryLevel.EASY, - EasyTasks.BUY_CANDLE + EasyTasks.BUY_CANDLE + ) + } + + if (event.currency.id == Items.ARCHERY_TICKET_1464) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.RANGING_GUILD_BUY_SOMETHING_FOR_TICKETS + ) + } + } + + override fun onInterfaceOpened(player: Player, event: InterfaceOpenEvent) { + if (event.component.id == 332) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.THORMAC_SORCERER_TALK_ABOUT_MYSTIC_STAVES + ) + } + } + + override fun onPrayerPointsRecharged(player: Player, event: PrayerPointsRechargeEvent) { + if (player.viewport.region.id == 10806) { + if (event.altar.id == Scenery.ALTAR_409 || event.altar.id == Scenery.ALTAR_19145) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.PRAY_AT_ALTAR + ) + } + } + } + + override fun onAreaVisited(player: Player) { + if (inBorders(player, SEERS_COURTHOUSE_AREA) && player.prayer.equals(PrayerType.PIETY)) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.ENTER_SEERS_COURTHOUSE_WITH_PIETY ) } } diff --git a/Server/src/main/content/region/kandarin/seers/handlers/SeersCageUnlockPlugin.java b/Server/src/main/content/region/kandarin/seers/handlers/SeersCageUnlockPlugin.java deleted file mode 100644 index 8f0f09929..000000000 --- a/Server/src/main/content/region/kandarin/seers/handlers/SeersCageUnlockPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package content.region.kandarin.seers.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to unlock the sheers cage. - * @author 'Vexia - * @versio 1.0 - */ -@Initializable -public final class SeersCageUnlockPlugin extends OptionHandler { - - @Override - public boolean handle(Player player, Node node, String option) { - player.getPacketDispatch().sendMessage("You can't unlock the pillory, you'll let all the prisoners out!"); - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(6836).getHandlers().put("option:unlock", this); - return this; - } - -} diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/BatteredBookHandler.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/BatteredBookHandler.kt index 4e3854225..f46fcfd97 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/BatteredBookHandler.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/BatteredBookHandler.kt @@ -1,12 +1,12 @@ package content.region.kandarin.seers.quest.elementalworkshop import content.global.handlers.iface.BookInterface -import core.api.setAttribute import core.api.setQuestStage import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.node.entity.player.Player import org.rs09.consts.Items +import content.data.Quests /** * Battered book handler for the Elemental Workshop I quest @@ -23,7 +23,7 @@ class BatteredBookHandler : InteractionListener { BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) if (BookInterface.isLastPage(pageNum, CONTENTS.size)) { if (EWUtils.currentStage(player) == 0) { - setQuestStage(player, "Elemental Workshop I", 1) + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_I, 1) } } return true diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt index f742d5a21..f43ac6bed 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt @@ -16,6 +16,7 @@ import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.currentStag import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.tools.Log +import content.data.Quests /** * Listeners for the Elemental Workshop I quest @@ -102,7 +103,7 @@ class EWListeners : InteractionListener { } // Player needs to receive a battered book sendItemDialogue(player, Item(Items.BATTERED_BOOK_2886), "You find a book titled 'The Elemental Shield'.") - addItem(player, batteredBook.id) + addItemOrDrop(player, batteredBook.id) return@on true } @@ -119,7 +120,7 @@ class EWListeners : InteractionListener { } sendItemDialogue(player, Item(Items.SLASHED_BOOK_9715), "You find a book titled 'The Elemental Shield'.") - addItem(player, slashedBook.id) + addItemOrDrop(player, slashedBook.id) if (player.inventory.addIfDoesntHave(batteredKey)) { sendItemDialogue(player, Item(Items.BATTERED_KEY_2887),"You also find a key.") } @@ -140,7 +141,7 @@ class EWListeners : InteractionListener { sendMessage(player, "Inside you find a small, old, battered key.") replaceSlot(player, with.asItem().slot, slashedBook) addItemOrDrop(player, Items.BATTERED_KEY_2887) - setQuestStage(player, "Elemental Workshop I", 3) + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_I, 3) return true } } @@ -177,8 +178,8 @@ class EWListeners : InteractionListener { return@on true } // Increment quest stage - if (getQuestStage(player, "Elemental Workshop I") < 5) { - setQuestStage(player, "Elemental Workshop I", 5) + if (getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_I) < 5) { + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_I, 5) } // Allow player through the wall sendMessage(player, "You use the battered key to open the doors.") @@ -200,7 +201,7 @@ class EWListeners : InteractionListener { sendPlayerDialogue(player, "Now to explore this area thoroughly, to find what " + "forgotten secrets it contains.", core.game.dialogue.FacialExpression.NEUTRAL) - setQuestStage(player, "Elemental Workshop I", 7) + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_I, 7) } return@on true } @@ -236,7 +237,7 @@ class EWListeners : InteractionListener { on(Scenery.CRATE_3400, IntType.SCENERY, "search") { player, _ -> if (!getAttribute(player, "/save:ew1:got_needle", false)) { setAttribute(player, "/save:ew1:got_needle", true) - addItem(player, Items.NEEDLE_1733) + addItemOrDrop(player, Items.NEEDLE_1733) sendMessage(player, "You find a needle.") } else { sendMessage(player, "You search the crate but find nothing.") @@ -248,7 +249,7 @@ class EWListeners : InteractionListener { on(Scenery.CRATE_3394, IntType.SCENERY, "search") { player, _ -> if (!getAttribute(player, "/save:ew1:got_leather", false)) { setAttribute(player, "/save:ew1:got_leather", true) - addItem(player, Items.LEATHER_1741) + addItemOrDrop(player, Items.LEATHER_1741) sendMessage(player, "You find some leather.") } else { sendMessage(player, "You search the crate but find nothing.") @@ -289,8 +290,8 @@ class EWListeners : InteractionListener { sendMessage(player, "Following the instructions in the book you make an elemental shield.") } // Check to see if the quest is completed, if not, complete the quest - if (!player.questRepository.getQuest("Elemental Workshop I").isCompleted(player)) { - player.questRepository.getQuest("Elemental Workshop I").finish(player) + if (!player.questRepository.getQuest(Quests.ELEMENTAL_WORKSHOP_I).isCompleted(player)) { + player.questRepository.getQuest(Quests.ELEMENTAL_WORKSHOP_I).finish(player) } return@onUseWith true } diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWUtils.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWUtils.kt index 142f10c69..a2629fcb1 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWUtils.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWUtils.kt @@ -4,8 +4,8 @@ import content.global.handlers.iface.BookLine import content.global.handlers.iface.Page import content.global.handlers.iface.PageSet import core.game.node.entity.player.Player -import org.rs09.consts.Vars import core.api.* +import content.data.Quests /** * Utils for the Elemental Workshop I quest @@ -102,6 +102,6 @@ object EWUtils { } fun currentStage(player: Player): Int { - return player.questRepository.getStage("Elemental Workshop I") + return player.questRepository.getStage(Quests.ELEMENTAL_WORKSHOP_I) } } diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/ElementalWorkshopQuest.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/ElementalWorkshopQuest.kt index 397faad13..cb7ea7803 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/ElementalWorkshopQuest.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/ElementalWorkshopQuest.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.Vars import core.game.system.command.Privilege +import content.data.Quests /** * Elemental Workshop I @@ -29,7 +30,7 @@ import core.game.system.command.Privilege * @author Woah, with love */ @Initializable -class ElementalWorkshopQuest : Quest("Elemental Workshop I", 52, 51, 1), Commands { +class ElementalWorkshopQuest : Quest(Quests.ELEMENTAL_WORKSHOP_I, 52, 51, 1), Commands { override fun newInstance(`object`: Any?): Quest { return this diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ArheinMCDialogue.kt b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ArheinMCDialogue.kt index c59014ec4..1309b550f 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ArheinMCDialogue.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ArheinMCDialogue.kt @@ -4,6 +4,7 @@ import core.game.dialogue.FacialExpression import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests /** * @author lila @@ -19,7 +20,7 @@ class ArheinMCDialogue (val questStage: Int) : DialogueFile() { START_DIALOGUE -> playerl(FacialExpression.NEUTRAL, "Can you drop me off on the way down please?").also { stage++ } 1 -> { npcl(FacialExpression.ANNOYED,"I don't think Sir Mordred would like that. He wants as few outsiders visiting as possible. I wouldn't want to lose his business.") - val quest = player!!.questRepository.getQuest("Merlin's Crystal") + val quest = player!!.questRepository.getQuest(Quests.MERLINS_CRYSTAL) player!!.questRepository.setStage(quest, 40) stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/BeggarDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/BeggarDialogue.java index 0c422017d..286b845f1 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/BeggarDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/BeggarDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for king arthur. @@ -48,7 +49,7 @@ public final class BeggarDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + final Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); switch (stage) { case 1: if (quest.getStage(player) == 60 && player.getAttribute("beggar_npc") != null) { diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CandleMakerDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CandleMakerDialogue.java index 003add8ff..46b402ed3 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CandleMakerDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CandleMakerDialogue.java @@ -11,6 +11,7 @@ import core.game.node.item.Item; import core.plugin.Plugin; import core.game.shops.Shops; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the dialogue plugin used to handle the candle maker npc. @@ -56,7 +57,7 @@ public final class CandleMakerDialogue extends DialoguePlugin { @Override public boolean handle(Player player, Node node, String option) { NPC npc = node.asNpc(); - Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); if (quest.getStage(player) > 60) { Shops.openId(player, 56); } else { @@ -78,7 +79,7 @@ public final class CandleMakerDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + final Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); switch (stage) { case 2: if (quest.getStage(player) == 50 || quest.getStage(player) == 60) {// the player has defeated mordred and learned about the black candles diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/KingArthurDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/KingArthurDialogue.java index 694cc4c04..ea84952e5 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/KingArthurDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/KingArthurDialogue.java @@ -8,6 +8,7 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the dialogue plugin used for king arthur. @@ -56,7 +57,7 @@ public final class KingArthurDialogue extends DialoguePlugin { stage = 80; return true; } else { - Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); if (quest.getStage(player) == 99) { player("I have freed Merlin from his crystal!"); stage = 900; @@ -80,7 +81,7 @@ public final class KingArthurDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); switch (stage) { case 900: end(); diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystal.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystal.java index 1c2a16978..15a6c5ce4 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystal.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystal.java @@ -3,8 +3,8 @@ package content.region.kandarin.seers.quest.merlinsquest; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; -import content.region.kandarin.seers.quest.merlinsquest.TheLadyOfTheLake; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the merlin's crystal quest. @@ -17,7 +17,7 @@ public final class MerlinCrystal extends Quest { * Constructs a new {@code MerlinCrystal} {@code Object}. */ public MerlinCrystal() { - super("Merlin's Crystal", 87, 86, 6, 14, 0, 1, 7); + super(Quests.MERLINS_CRYSTAL, 87, 86, 6, 14, 0, 1, 7); } @Override diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalOptionPlugin.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalOptionPlugin.java index 66a51581b..82a53c48c 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalOptionPlugin.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalOptionPlugin.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.scenery.Scenery; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the quest node plugin handler. @@ -23,7 +24,7 @@ public class MerlinCrystalOptionPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + final Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); int id = node instanceof Scenery ? ((Scenery) node).getId() : ((NPC) node).getId(); switch (id) { case 247: diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalPlugin.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalPlugin.java index ce2695d38..e90ecbfd1 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalPlugin.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalPlugin.java @@ -1,6 +1,5 @@ package content.region.kandarin.seers.quest.merlinsquest; -import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.activity.ActivityManager; import core.game.activity.CutscenePlugin; @@ -9,13 +8,10 @@ import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; import core.game.global.action.ClimbActionHandler; import core.game.global.action.DoorActionHandler; -import core.game.global.action.DropListener; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.impl.ForceMovement; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; @@ -24,12 +20,12 @@ import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; import core.game.world.GameWorld; -import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.ClassScanner; +import content.data.Quests; /** * Handles the Merlin's Crystal Dialogue/Interactions. @@ -73,7 +69,7 @@ public final class MerlinCrystalPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + final Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); final int id = node instanceof Item ? ((Item) node).getId() : node instanceof Scenery ? ((Scenery) node).getId() : ((NPC) node).getId(); switch (id) { case 62: @@ -178,7 +174,7 @@ public final class MerlinCrystalPlugin extends OptionHandler { @Override public boolean open(Object... args) { - final Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + final Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); if (quest.getStage(player) == 99) { npc = (NPC) args[0]; npc("Thank you! Thank you! Thank you!"); @@ -328,8 +324,8 @@ public final class MerlinCrystalPlugin extends OptionHandler { if (p != null) { p.stop(false); } - if (player.getQuestRepository().getQuest("Merlin's Crystal").getStage(player) == 30) { - player.getQuestRepository().getQuest("Merlin's Crystal").setStage(player, 40); + if (player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL).getStage(player) == 30) { + player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL).setStage(player, 40); } player.unlock(); player.getProperties().setTeleportLocation(Location.create(2778, 3401, 0)); @@ -449,7 +445,7 @@ public final class MerlinCrystalPlugin extends OptionHandler { @Override public boolean isHidden(final Player player) { - if (player.getQuestRepository().getQuest("Merlin's Crystal").getStage(player) == 60 && this.getAttribute("beggar_owner", "").equals(player.getUsername())) { + if (player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL).getStage(player) == 60 && this.getAttribute("beggar_owner", "").equals(player.getUsername())) { return false; } return true; diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinListeners.kt b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinListeners.kt index f7a5e87ad..3a24ee325 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinListeners.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinListeners.kt @@ -8,13 +8,14 @@ import core.game.global.action.DropListener import core.game.node.entity.npc.NPC import core.game.node.entity.impl.ForceMovement import org.rs09.consts.Items +import content.data.Quests class MerlinListeners : InteractionListener { private val BONE_DROP_LOCATION = Location(2780, 3515, 0) override fun defineListeners() { on (Items.BAT_BONES_530, IntType.ITEM, "drop") { player, node -> - val merlinStage = getQuestStage(player, "Merlin's Crystal") + val merlinStage = getQuestStage(player, Quests.MERLINS_CRYSTAL) var doingQuest = player.location == BONE_DROP_LOCATION && merlinStage == 80 var hasAuxiliaryRequirements = inInventory(player, Items.LIT_BLACK_CANDLE_32) && getAttribute(player, "thrantax_npc", null) == null diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirGawainDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirGawainDialogue.java index c94ab7d0b..0f3d5b858 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirGawainDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirGawainDialogue.java @@ -4,6 +4,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; +import content.data.Quests; /** * Represents the dialogue plugin used for Sir Gawain. @@ -40,7 +41,7 @@ public final class SirGawainDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); npc("Good day to you " + (player.isMale() ? "sir" : "madam") + "!"); stage = 0; diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirKayDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirKayDialogue.java index 14f4bd3ca..594645e0d 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirKayDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirKayDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; +import content.data.Quests; /** * Represents the dialogue plugin used for Sir Kay. @@ -42,7 +43,7 @@ public final class SirKayDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); options("Hello.", "Talk about achievement diary."); stage = 0; diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLancelotDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLancelotDialogue.java index 09b3eb285..b8d5b8d8a 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLancelotDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLancelotDialogue.java @@ -4,6 +4,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; +import content.data.Quests; /** * Represents the dialogue plugin used for king arthur. @@ -42,7 +43,7 @@ public final class SirLancelotDialogue extends DialoguePlugin { npc = (NPC) args[0]; npc("Greetings! I am Sir Lancelot, the greatest Knight in the", "land! What do you want?"); - quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); stage = 0; return true; } diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLucan.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLucan.java index 9ce08250b..2732d5667 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLucan.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLucan.java @@ -5,6 +5,7 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Handles the dialogue for Sir Lucan @@ -42,7 +43,7 @@ public class SirLucan extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); if (quest.getStage(player) == 100) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Congratulations on freeing Merlin!"); stage = 20; diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirMordredNPC.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirMordredNPC.java index c7ec62b1c..6065a1b2e 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirMordredNPC.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirMordredNPC.java @@ -9,6 +9,7 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Graphics; +import content.data.Quests; /** * Handles Sir Mordred @@ -50,7 +51,7 @@ public class SirMordredNPC extends AbstractNPC { super.getSkills().setLifepoints(50); if (killer != null && killer.isPlayer()) { final Player p = ((Player) killer); - Quest quest = p.getQuestRepository().getQuest("Merlin's Crystal"); + Quest quest = p.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); if (quest.getStage(p) == 40) { quest.setStage(p, 50); p.getQuestRepository().syncronizeTab(p); diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirPalomedes.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirPalomedes.java index 61420baa8..50b4e2de2 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirPalomedes.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirPalomedes.java @@ -5,6 +5,7 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Handles the SirPalomedes dialogue. @@ -41,7 +42,7 @@ public class SirPalomedes extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); if (quest.getStage(player) == 100) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Congratulations on freeing Merlin!"); stage = 20; diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/TheLadyOfTheLake.kt b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/TheLadyOfTheLake.kt index 625bf6a7a..e1ed69c03 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/TheLadyOfTheLake.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/TheLadyOfTheLake.kt @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType import core.game.node.item.Item import org.rs09.consts.Items +import content.data.Quests /** * Handles the LadyOfTheLake dialogue. @@ -35,7 +36,7 @@ class TheLadyOfTheLake(player: Player? = null) : DialoguePlugin(player) { } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - val quest = player.questRepository.getQuest("Merlin's Crystal") + val quest = player.questRepository.getQuest(Quests.MERLINS_CRYSTAL) when (stage) { 0 -> options("Who are you?", "I seek the sword Excalibur.", "Good day.").also { stage = 1 } 1 -> when (buttonId) { diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxDialogue.java index fd9b4a68e..d30d76d33 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxDialogue.java @@ -6,6 +6,7 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Handles the thrantax dialogue. @@ -41,7 +42,7 @@ public class ThrantaxDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); + final Quest quest = player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL); switch (stage) { case 0: interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Now what were those magic words again?"); diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxNPC.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxNPC.java index d96b52f7c..16efc79b6 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxNPC.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxNPC.java @@ -4,6 +4,7 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.node.entity.npc.NPC; import core.game.world.map.Location; +import content.data.Quests; /** * Handles the thrantax npc. @@ -28,7 +29,7 @@ public class ThrantaxNPC extends NPC { @Override public boolean isHidden(final Player player) { - if (player.getQuestRepository().getQuest("Merlin's Crystal").getStage(player) == 80 && this.getAttribute("thrantax_owner", "").equals(player.getUsername())) { + if (player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL).getStage(player) == 80 && this.getAttribute("thrantax_owner", "").equals(player.getUsername())) { return false; } return true; diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/BaileyDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/BaileyDialogue.kt new file mode 100644 index 000000000..67d446af2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/BaileyDialogue.kt @@ -0,0 +1,24 @@ +package content.region.kandarin.witchhaven.dialogue + +import content.region.kandarin.witchhaven.quest.seaslug.BaileyDialogueFile +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class BaileyDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player): DialoguePlugin { + return BaileyDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, BaileyDialogueFile(), npc) + return true + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.BAILEY_695) + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/CarolineDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/CarolineDialogue.kt index 477aa8020..cc3b0ad95 100644 --- a/Server/src/main/content/region/kandarin/witchhaven/dialogue/CarolineDialogue.kt +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/CarolineDialogue.kt @@ -1,41 +1,29 @@ package content.region.kandarin.witchhaven.dialogue +import content.region.kandarin.witchhaven.quest.seaslug.CarolineDialogueFile +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills import core.plugin.Initializable +import org.rs09.consts.Items import org.rs09.consts.NPCs -/** - * @author qmqz - */ - @Initializable class CarolineDialogue(player: Player? = null) : DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(FacialExpression.FRIENDLY,"Hello again.").also { stage = 0 } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> npc(FacialExpression.FRIENDLY, "Hello traveller, how are you?").also { stage++ } - 1 -> player(FacialExpression.FRIENDLY, "Not bad thanks, yourself?").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "I'm good. Busy as always looking after Kent and Kennith but no complaints.").also { stage = 99 } - - 99 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return CarolineDialogue(player) } - + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Sea Slug + openDialogue(player!!, CarolineDialogueFile(), npc) + return true + } override fun getIds(): IntArray { return intArrayOf(NPCs.CAROLINE_696) } -} +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartDialogue.kt index 4d99bdf08..5f7432efa 100644 --- a/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartDialogue.kt +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartDialogue.kt @@ -1,5 +1,7 @@ package content.region.kandarin.witchhaven.dialogue +import content.region.kandarin.witchhaven.quest.seaslug.HolgartDialogueFile +import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -7,37 +9,18 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs -/** - * @author qmqz - */ - @Initializable class HolgartDialogue(player: Player? = null) : DialoguePlugin(player){ - fun gender (male : String = "sir", female : String = "madam") = if (player.isMale) male else female - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> npc(FacialExpression.FRIENDLY, "Well hello " + gender() + ", beautiful day isn't it?").also { stage++ } - 1 -> player(FacialExpression.FRIENDLY, "Not bad I suppose.").also { stage++ } - 2 -> npc(FacialExpression.FRIENDLY, "Just smell that sea air... beautiful.").also { stage++ } - 3 -> player(FacialExpression.FRIENDLY, "Hmm... lovely...").also { stage = 99 } - - 99 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return HolgartDialogue(player) } - + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Sea Slug + openDialogue(player!!, HolgartDialogueFile(), npc) + return true + } override fun getIds(): IntArray { - return intArrayOf(NPCs.HOLGART_4866) + return intArrayOf(NPCs.HOLGART_700) + // return intArrayOf(NPCs.HOLGART_4866) } } diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartIslandDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartIslandDialogue.kt new file mode 100644 index 000000000..883bdd746 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartIslandDialogue.kt @@ -0,0 +1,27 @@ +package content.region.kandarin.witchhaven.dialogue + +import content.region.kandarin.witchhaven.quest.seaslug.HolgartIslandDialogueFile +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +// This is to handle when Holgart is on the fishing platform. +@Initializable +class HolgartIslandDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player): DialoguePlugin { + return HolgartIslandDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Sea Slug + openDialogue(player!!, HolgartIslandDialogueFile(), npc) + return true + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.HOLGART_698) + // return intArrayOf(NPCs.HOLGART_4866) + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartPlatformDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartPlatformDialogue.kt new file mode 100644 index 000000000..3ac3027c9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartPlatformDialogue.kt @@ -0,0 +1,27 @@ +package content.region.kandarin.witchhaven.dialogue + +import content.region.kandarin.witchhaven.quest.seaslug.HolgartPlatformDialogueFile +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +// This is to handle when Holgart is on the fishing platform. +@Initializable +class HolgartPlatformDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player): DialoguePlugin { + return HolgartPlatformDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Sea Slug + openDialogue(player!!, HolgartPlatformDialogueFile(), npc) + return true + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.HOLGART_699, NPCs.FISHERMAN_4871) + // return intArrayOf(NPCs.HOLGART_4866) + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/KennithDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/KennithDialogue.kt new file mode 100644 index 000000000..2ad5de63a --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/KennithDialogue.kt @@ -0,0 +1,45 @@ +package content.region.kandarin.witchhaven.dialogue + +import content.region.kandarin.witchhaven.quest.seaslug.KennithDialogueFile +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.NPCs + +class KennithDialogue : InteractionListener { + + override fun defineListeners() { + on(NPCs.KENNITH_4864, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, KennithDialogueFile(), npc) + return@on true + } + } + + // Because Kennith is behind the counter + override fun defineDestinationOverrides() { + setDest(IntType.NPC, intArrayOf(NPCs.KENNITH_4864),"talk-to"){ _, _ -> + return@setDest Location.create(2765, 3286, 1) + } + } +} + +// INSTEAD OF THIS as Kennith is unreachable. +//@Initializable +//class KennithDialogue(player: Player? = null) : DialoguePlugin(player){ +// override fun newInstance(player: Player): DialoguePlugin { +// return KennithDialogue(player) +// } +// override fun handle(interfaceId: Int, buttonId: Int): Boolean { +// // Fallback to default. Always the start of Sea Slug +// openDialogue(player!!, KennithDialogueFile(), npc) +// return true +// } +// override fun getIds(): IntArray { +// // Base is CAROLINE_697 (Should be named KENNITH_697) +// return intArrayOf(NPCs.CAROLINE_697, NPCs.KENNITH_4864) +// } +//} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/KentDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/KentDialogue.kt new file mode 100644 index 000000000..25b3c447e --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/KentDialogue.kt @@ -0,0 +1,24 @@ +package content.region.kandarin.witchhaven.dialogue + +import content.region.kandarin.witchhaven.quest.seaslug.KentDialogueFile +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class KentDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player): DialoguePlugin { + return KentDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Sea Slug + openDialogue(player!!, KentDialogueFile(), npc) + return true + } + override fun getIds(): IntArray { + // Base is CAROLINE_697 (Should be named KENNITH_697) + return intArrayOf(NPCs.KENT_701) + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/WitchavenVillagerDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/WitchavenVillagerDialogue.kt new file mode 100644 index 000000000..8bb49bd9b --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/WitchavenVillagerDialogue.kt @@ -0,0 +1,83 @@ +package content.region.kandarin.witchhaven.dialogue + +import core.api.openDialogue +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class WitchavenVillagerDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player): DialoguePlugin { + return WitchavenVillagerDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Sea Slug + openDialogue(player!!, WitchavenVillagerDialogueFile(), npc) + return true + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.WITCHAVEN_VILLAGER_4883, NPCs.WITCHAVEN_VILLAGER_4884, + NPCs.WITCHAVEN_VILLAGER_4885, NPCs.WITCHAVEN_VILLAGER_4886, + NPCs.WITCHAVEN_VILLAGER_4887, NPCs.WITCHAVEN_VILLAGER_4888) + } +} + +class WitchavenVillagerDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .player(FacialExpression.FRIENDLY, "Hello there.") + .branch { player -> + return@branch (0 .. 4).random() + }.let { branch -> + branch.onValue(0) + .npcl("What have you got to be so cheerful about?") + .playerl("Well, it's another nice day.") + .npcl("Ha! Try worrying about how you feed a family with no job. Then tell me how nice the day is!") + .playerl("Okay, I guess I've caught you at a bad time. Goodbye.") + .end() + + branch.onValue(1) + .npcl("Hmm? Oh, hello there.") + .playerl("Are you okay? You seem a bit preoccupied.") + .npcl("It's nothing stranger. No need to concern yourself.") + .end() + + branch.onValue(2) + .npcl("Spare a coin mister?") + .playerl("What do you need it for?") + .npcl("For a poor unemployed fisherman what needs to eat.") + .playerl("Why don't you just fish for some food?") + .npcl("Err... Goodbye mister.") + .end() + + branch.onValue(3) + .npcl("Can you believe they did this to us?") + .playerl("Wha...") + .npcl("I mean, what did they think would happen?") + .playerl("Who...") + .npcl("Building that whacking great Fishing Platform just off the coast.") + .playerl("Fish...") + .npcl("Dratted thing stole all of our trade.") + .playerl("Excuse...") + .npcl("I'm sorry, I'm too angry to speak right now. Goodbye") + .end() + + branch.onValue(4) + .npcl("With our nets and gear we're faring,") + .npcl("On the wild and wasteful ocean,") + .npcl("It's there on the deep that we harvest and reap our bread,") + .npcl("As we hunt the bonny shoals of herring.") + .playerl("That's a lovely song.") + .npcl("Aye lad, and sing it every day we did.") + .npcl("'Till the Fishing Platform came and ruined everything.") + .playerl("Oh, I'm sorry.") + .npcl("No need lad, it not be your fault.") + .end() + } + + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/WitchhavenVillageDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/WitchhavenVillageDialogue.kt deleted file mode 100644 index 38b0f00e8..000000000 --- a/Server/src/main/content/region/kandarin/witchhaven/dialogue/WitchhavenVillageDialogue.kt +++ /dev/null @@ -1,78 +0,0 @@ -package content.region.kandarin.witchhaven.dialogue - -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import org.rs09.consts.NPCs - -/** - * @author qmqz - */ - -@Initializable -class WitchhavenVillageDialogue(player: Player? = null) : DialoguePlugin(player){ - - private val conversations = arrayOf (0, 7, 11, 19, 24) - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(FacialExpression.FRIENDLY, "Hello there.").also { stage = conversations.random() } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - - 0 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 1 -> npc(FacialExpression.NEUTRAL, "Ye mariners all, as ye pass by,").also { stage++ } - 2 -> npc(FacialExpression.NEUTRAL, "Come in and drink if you are dry,").also { stage++ } - 3 -> npc(FacialExpression.NEUTRAL, "Come spend, me lads, your money brisk,").also { stage++ } - 4 -> npc(FacialExpression.NEUTRAL, "And pop your nose in a jug of this.").also { stage++ } - 5 -> player(FacialExpression.NEUTRAL, "You're not fooling anyone you know.").also { stage++ } - 6 -> npc(FacialExpression.NEUTRAL, "We fooled you easily enough.").also { stage = 99 } - - 7 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 8 -> npc(FacialExpression.NEUTRAL, "Free. She is free...").also { stage++ } - 9 -> player(FacialExpression.NEUTRAL, "What?").also { stage++ } - 10 -> npc(FacialExpression.NEUTRAL, "The mother is free.").also { stage = 99 } - - 11 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 12 -> npc(FacialExpression.NEUTRAL, "You! You did it!").also { stage++ } - 13 -> player(FacialExpression.NEUTRAL, "I didn't mean to!").also { stage++ } - 14 -> npc(FacialExpression.NEUTRAL, "You killed him!").also { stage++ } - 15 -> player(FacialExpression.NEUTRAL, "It was an accide... Killed who?").also { stage++ } - 16 -> npc(FacialExpression.NEUTRAL, "Our Prince, you killed our Prince.").also { stage++ } - 17 -> player(FacialExpression.NEUTRAL, "Oh that, yes I did.").also { stage++ } - 18 -> npc(FacialExpression.NEUTRAL, "Leave us alone.").also { stage = 99 } - - 19 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 20 -> npc(FacialExpression.NEUTRAL, "Soon now... So soon...").also { stage++ } - 21 -> npc(FacialExpression.NEUTRAL, "The stars are almost right.").also { stage++ } - 22 -> player(FacialExpression.NEUTRAL, "For what?").also { stage++ } - 23 -> npc(FacialExpression.NEUTRAL, ". . .").also { stage = 99 } - - 24 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 25 -> npc(FacialExpression.NEUTRAL, "Ahh, our saviour.").also { stage++ } - 26 -> player(FacialExpression.NEUTRAL, "Please don't remind me.").also { stage++ } - 27 -> npc(FacialExpression.NEUTRAL, "Do not worry, soon your regret will be gone.").also { stage++ } - 28 -> player(FacialExpression.NEUTRAL, "If you think you will get to me...").also { stage++ } - 29 -> npc(FacialExpression.NEUTRAL, "All in good time.").also { stage = 99 } - - - 99 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return WitchhavenVillageDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.WITCHAVEN_VILLAGER_4883, NPCs.WITCHAVEN_VILLAGER_4884, - NPCs.WITCHAVEN_VILLAGER_4885, NPCs.WITCHAVEN_VILLAGER_4886, - NPCs.WITCHAVEN_VILLAGER_4887, NPCs.WITCHAVEN_VILLAGER_4888) - } -} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/BaileyDialogueFile.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/BaileyDialogueFile.kt new file mode 100644 index 000000000..9b6399d99 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/BaileyDialogueFile.kt @@ -0,0 +1,68 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import org.rs09.consts.Items + +class BaileyDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.SEA_SLUG, 0, 1, 2, 3, 4) + .playerl(FacialExpression.FRIENDLY, "Hello there.") + .npcl(FacialExpression.SCARED, "What? Who are you? Come inside quickly!") + .npcl(FacialExpression.SCARED, "What are you doing here?") + .playerl("I'm trying to find out what happened to a boy named Kennith.") + .npcl("Oh you mean Kent's son. He's around somewhere, probably hiding if he knows what's good for him.") + .playerl(FacialExpression.THINKING, "Hiding from what? What's got you so frightened?") + .npcl("Haven't you seen all those things out there?") + .playerl(FacialExpression.THINKING, "The sea slugs?") + .npcl(FacialExpression.SUSPICIOUS, "It all began about a week ago. We pulled up a haul of deep sea flatfish. Mixed in with them we found these slug things, but thought nothing of it.") + .npcl(FacialExpression.SUSPICIOUS, "Not long after that my friends began to change, now they spend all day pulling in hauls of fish, only to throw back the fish and keep those nasty sea slugs.") + .npcl(FacialExpression.SUSPICIOUS, "What am I supposed to do with those? I haven't figured out how to kill one yet. If I put them near the stove they squirm and jump away.") + .playerl(FacialExpression.THINKING, "I doubt they would taste too good.") + .npcl(FacialExpression.ANGRY, "This is no time for humour.") + .playerl("I'm sorry, I didn't mean to upset you.") + .npcl(FacialExpression.SCARED, "That's okay. I just can't shake the feeling that this is the start of something... Terrible.") + .end() + + b.onQuestStages(Quests.SEA_SLUG, 5) + .playerl("Hello.") + .npcl(FacialExpression.EXTREMELY_SHOCKED, "Oh, thank the gods it's you. They've all gone mad I tell you, one of the fishermen tried to throw me into the sea!") + .playerl("They're all being controlled by the sea slugs.") + .npcl("I figured as much.") + .playerl("I need to get Kennith off this platform, but I can't get past the fishermen.") + .npcl("The sea slugs are scared of heat... I figured that out when I tried to cook them.") + .npcl("Here.") + .betweenStage { _, player, _, _ -> + addItemOrDrop(player, Items.UNLIT_TORCH_596) + } + .iteml(Items.UNLIT_TORCH_596, "Bailey gives you a torch.") + .npcl("I doubt the fishermen will come near you if you can get this torch lit. The only problem is all the wood and flint are damp... I can't light a thing!") + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 5) { + setQuestStage(player, Quests.SEA_SLUG, 6) + } + } + + // We aren't going to give you a spare torch. Go get an unlit torch somewhere else. + b.onQuestStages(Quests.SEA_SLUG, 6, 7, 8) + .playerl("Hello.") + .npcl("Oh, thank the gods it's you. They've all gone mad I tell you, one of the fishermen tried to throw me into the sea!") + .playerl("They're all being controlled by the sea slugs.") + .npcl("I figured as much.") + .playerl("I need to get Kennith off this platform, but I can't get past the fishermen.") + .npcl("The sea slugs are scared of heat... I figured that out when I tried to cook them.") + .npcl("I doubt the fishermen will come near you if you can get this torch lit. The only problem is all the wood and flint are damp... I can't light a thing!") + .end() + + b.onQuestStages(Quests.SEA_SLUG, 9, 10, 100) + .playerl("I've managed to light the torch.") + .npcl("Well done traveller, you'd better get Kennith out of here soon. The fishermen are becoming stranger by the minute, and they keep pulling up those blasted sea slugs.") + .playerl("Don't worry I'm working on it.") + .npcl("Just be sure to watch your back. The fishermen seem to have taken notice of you.") + .end() + + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/CarolineDialogueFile.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/CarolineDialogueFile.kt new file mode 100644 index 000000000..bd6892832 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/CarolineDialogueFile.kt @@ -0,0 +1,71 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.skill.Skills + +class CarolineDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.SEA_SLUG, 0) + .playerl(FacialExpression.FRIENDLY, "Hello there.") + .npcl(FacialExpression.SAD, "Is there any chance you could help me?") + .playerl(FacialExpression.THINKING, "What's wrong?") + .npcl("It's my husband, he works on a fishing platform. Once a month he takes our son, Kennith, out with him.") + .npcl(FacialExpression.THINKING, "They usually write to me regularly, but I've heard nothing all week. It's very strange.") + .playerl("Maybe the post was lost!") + .npcl(FacialExpression.THINKING, "Maybe, but no-one's heard from the other fishermen on the platform. Their families are becoming quite concerned.") + .branch { player -> + return@branch if (hasLevelStat(player, Skills.FIREMAKING, 30)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .npcl("However, I don't think you are ready to visit the platform.") + .line("You need Level 30 Firemaking to start the Sea Slug quest.") + .end() + return@let branch.onValue(1) + } + .npcl(FacialExpression.HALF_THINKING, "Is there any chance you could visit the platform and find out what's going on?") + .options().let { optionBuilder -> + optionBuilder.option_playerl("I suppose so, how do I get there?") + .npcl("That's very good of you @name. My friend Holgart will take you there.") + .playerl("Ok, I'll go and see if they're ok.") + .npcl("I'll reward you for your time. It'll give me peace of mind to know Kennith and my husband, Kent, are safe.") + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 0) { + setQuestStage(player, Quests.SEA_SLUG, 1) + } + } + optionBuilder.option_playerl("I'm sorry, I'm too busy.") + .npcl(FacialExpression.SAD, "That's a shame.") + .playerl("Bye.") + .npcl("Bye.") + .end() + } + + b.onQuestStages(Quests.SEA_SLUG, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + .playerl("Hello Caroline.") + .npcl("Brave @name, have you any news about my son and his father?") + .playerl("I'm working on it now Caroline.") + .npcl("Please bring them back safe and sound.") + .playerl("I'll do my best.") + .end() + + b.onQuestStages(Quests.SEA_SLUG, 11) + .playerl("Hello.") + .npcl("Brave @name, you've returned!") + .npcl("Kennith told me about the strange goings-on at the platform. I had no idea it was so serious.") + .npcl("I could have lost my son and my husband if it wasn't for you.") + .playerl("We found Kent stranded on an island.") + .npcl("Yes. Holgart told me and sent a rescue party out. Kent's back home now, resting with Kennith. I don't think he'll be doing any fishing for a while.") + .npcl("Here, take these Oyster pearls as a reward. They're worth quite a bit and can be used to make lethal crossbow bolts.") + .playerl(FacialExpression.FRIENDLY, "Thanks!") + .npcl(FacialExpression.FRIENDLY, "Thank you. Take care of yourself @name.") + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 11) { + finishQuest(player, Quests.SEA_SLUG) + } + } + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/FishermanDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/FishermanDialogue.kt new file mode 100644 index 000000000..ba344e520 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/FishermanDialogue.kt @@ -0,0 +1,102 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class FishermanDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player): DialoguePlugin { + return FishermanDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Sea Slug + openDialogue(player!!, FishermanDialogueFile(), npc) + return true + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.FISHERMAN_702, NPCs.FISHERMAN_703, NPCs.FISHERMAN_704) + } +} + +class FishermanDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .player(FacialExpression.FRIENDLY, "Hello there.") + .line("Their eyes are staring vacantly into space.") + .branch { player -> + return@branch (1 .. 1).random() + }.let { branch -> + branch.onValue(0) + .npc(FacialExpression.AMAZED, "Ye mariners all, as ye pass by,") + .npc(FacialExpression.AMAZED, "Come in and drink if you are dry,") + .npc(FacialExpression.AMAZED, "Come spend, me lads, your money brisk,") + .npc(FacialExpression.AMAZED, "And pop your nose in a jug of this.") + .player("You're not fooling anyone you know.") + .npc(FacialExpression.AMAZED, "We fooled you easily enough.") + .end() + + branch.onValue(1) + .npcl(FacialExpression.AMAZED,"You are not part of our family...") + .playerl("Umm. Not last time I checked.") + .npcl(FacialExpression.AMAZED,"Soon you will be... Soon you will...") + .end() + + branch.onValue(2) + .npcl(FacialExpression.AMAZED,"Keep away human... Leave or face the deep blue...") + .playerl("Pardon?") + .npcl(FacialExpression.AMAZED,"You will all end up in the blue... Deep deep under the blue...") + .end() + + branch.onValue(3) + .npcl(FacialExpression.AMAZED,"Lost to us.. She is Lost to us...") + .playerl("Who is lost?") + .npcl(FacialExpression.AMAZED,"Trapped by the light... Lost and trapped...") + .playerl("Ermm... So you don't want to tell me then?") + .npcl(FacialExpression.AMAZED,"Trapped... In stone and darkness...") + .end() + + branch.onValue(4) + .npcl(FacialExpression.AMAZED,"Must find family...") + .playerl("What?") + .npcl(FacialExpression.AMAZED,"Soon we will all be together...") + .playerl("Are you ok?") + .npcl(FacialExpression.AMAZED,"Must find family... They are all under the blue... Deep deep under the blue...") + .playerl("Ermm... I'll leave you to it then.") + .end() + + branch.onValue(5) + .npcl(FacialExpression.AMAZED,"Free of the deep blue we are...") + .npcl(FacialExpression.AMAZED,"We must find...") + .playerl("Yes?") + .npcl(FacialExpression.AMAZED,"a new home...") + .npcl(FacialExpression.AMAZED,"We must leave this place...") + .playerl("Where will you go?") + .npcl(FacialExpression.AMAZED,"Away.. Away to her...") + .playerl("Riiight.") + .end() + + branch.onValue(6) + .npcl(FacialExpression.AMAZED,"Below the deep, deep blue she waits...") + .playerl("Who waits?") + .npcl(FacialExpression.AMAZED,"They came to her with fire and faith...") + .playerl("Who? Who came to who?") + .npcl(FacialExpression.AMAZED,"Too many... Too many...") + .playerl("Too many what? Make sense!") + .npcl(FacialExpression.AMAZED,"Locked away for all eternity...") + .playerl("You'd better start making sense Sonny Jim or I'll...") + .npcl(FacialExpression.AMAZED,"Free... Soon to be free...") + .end() + + branch.onValue(7) + .npcl(FacialExpression.AMAZED,"Must escape the blue.. Deep deep blue") + .playerl("Pardon?") + .npcl(FacialExpression.AMAZED,"Family... Under the blue... Must escape the blue...") + .end() + } + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartDialogueFile.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartDialogueFile.kt new file mode 100644 index 000000000..559ce7335 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartDialogueFile.kt @@ -0,0 +1,101 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import org.rs09.consts.Items + +class HolgartDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.SEA_SLUG, 0) + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl(FacialExpression.FRIENDLY, "Well hello @g[m'lad,m'laddy]. Beautiful day isn't it?") + .playerl("Not bad I suppose.") + .npcl("Just smell that sea air... beautiful.") + .playerl(FacialExpression.THINKING, "Hmm... lovely...") + .end() + + b.onQuestStages(Quests.SEA_SLUG, 1) + .npcl(FacialExpression.FRIENDLY, "Hello, m'hearty.") + .playerl("I would like a ride on your boat to the fishing platform.") + .npcl(FacialExpression.SAD, "I'm afraid it isn't sea worthy, it's full of holes. To fill the holes I'll need some swamp paste.") + .playerl(FacialExpression.THINKING, "Swamp paste?") + .npcl("Yes, swamp tar mixed with flour and heated over a fire.") + .branch { player -> + return@branch if (inInventory(player, Items.SWAMP_PASTE_1941)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .playerl("Where can I find swamp tar?") + .npcl("Unfortunately the only supply of swamp tar is in the swamps below Lumbridge. It's too far for an old man like me to travel.") + .npcl("If you make me some swamp paste I'll give you a ride in my boat.") + .playerl("I'll see what I can do.") + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 1) { + setQuestStage(player, Quests.SEA_SLUG, 2) + } + } + branch.onValue(1) + .npcl("In fact, unless me nose be mistaken, you've got some in yer pack.") + .playerl("Oh yes, I forgot about that stuff. Can you use it?") + .npcl("Aye @g[lad,lass]. That be perfect.") + .betweenStage { _, player, _, _ -> + removeItem(player, Items.SWAMP_PASTE_1941) + } + .iteml(Items.SWAMP_PASTE_1941, "You give Holgart the swamp paste.") + // Cutscene + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 1) { + setQuestStage(player, Quests.SEA_SLUG, 3) + } + } + } + + b.onQuestStages(Quests.SEA_SLUG, 2) + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl("Hello, m'hearty. Did you manage to make some swamp paste?") + .branch { player -> + return@branch if (inInventory(player, Items.SWAMP_PASTE_1941)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .playerl("I'm afraid not.") + .npcl("It's simply swamp tar mixed with flour heated over a fire. Unfortunately the only supply of swamp tar is in the swamps below Lumbridge.") + .npcl("I can't fix my row boat without it.") + .playerl("Ok, I'll try to find some.") + .end() + branch.onValue(1) + .playerl("Yes, I have some here.") + .betweenStage { _, player, _, _ -> + removeItem(player, Items.SWAMP_PASTE_1941) + } + .iteml(Items.SWAMP_PASTE_1941, "You give Holgart the swamp paste.") + // Cutscene + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 2) { + setQuestStage(player, Quests.SEA_SLUG, 3) + } + } + + } + + + b.onQuestStages(Quests.SEA_SLUG, 3, 4, 5, 6, 7, 8, 9, 10, 11, 100) + .playerl(FacialExpression.FRIENDLY, "Hello, Holgart.") + .npcl("Hello again land lover. There's some strange goings on, on that platform, I tell you.") + .options().let { optionBuilder -> + optionBuilder.option("Will you take me there?") + .playerl(FacialExpression.THINKING, "Will you take me there?") + .npcl("Of course m'hearty. If that's what you want.") + .endWith() { df, player -> + SeaSlugListeners.seaslugBoatTravel(player, 0) + } + + optionBuilder.option_playerl("I'm keeping away from there.") + .npcl("Fair enough m'hearty.") + .end() + } + + + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartIslandDialogueFile.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartIslandDialogueFile.kt new file mode 100644 index 000000000..49ab8c4ab --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartIslandDialogueFile.kt @@ -0,0 +1,22 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression + +class HolgartIslandDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.SEA_SLUG, 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 100) + .playerl("We'd better get back to the platform so we can see what's going on.") + .npcl(FacialExpression.SUSPICIOUS, "You're right. It all sounds pretty creepy.") + .endWith() { df, player -> + SeaSlugListeners.seaslugBoatTravel(player, 3) + } + b.onQuestStages(Quests.SEA_SLUG, 4) + .playerl("Where are we?") + .npc("Someway off mainland still. You'd better see if me old", "matey's okay.") + .end() + + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartPlatformDialogueFile.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartPlatformDialogueFile.kt new file mode 100644 index 000000000..f9678899d --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/HolgartPlatformDialogueFile.kt @@ -0,0 +1,41 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression + +class HolgartPlatformDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.SEA_SLUG, 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 100) + .playerl(FacialExpression.FRIENDLY, "Hey, Holgart.") + .npcl("Have you had enough of this place yet? It's really starting to scare me.") + .options().let { optionBuilder -> + optionBuilder.option_playerl("Okay, let's go back.") + .endWith() { df, player -> + SeaSlugListeners.seaslugBoatTravel(player, 1) + } + optionBuilder.option_playerl("No, I'm going to stay a while.") + .npcl("Okay... you're the boss.") + .end() + } + + b.onQuestStages(Quests.SEA_SLUG, 4) + .playerl("Holgart, something strange is going on here.") + .npcl("You're telling me, none of the sailors seem to remember who I am.") + .playerl("Apparently Kennith's father left for help a couple of days ago.") + .npcl("That's a worry, no-one's heard from him on shore. Come on, we'd better go look for him.") + .endWith() { df, player -> + SeaSlugListeners.seaslugBoatTravel(player, 2) + } + + b.onQuestStages(Quests.SEA_SLUG, 11) + .playerl("Did you get the kid back to shore?") + .npcl("Yes, he's safe and sound with his parents. Your turn to return to land now adventurer.") + .playerl("Looking forward to it.") + .endWith() { df, player -> + SeaSlugListeners.seaslugBoatTravel(player, 1) + } + + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/KennithDialogueFile.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/KennithDialogueFile.kt new file mode 100644 index 000000000..a12f2e9b8 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/KennithDialogueFile.kt @@ -0,0 +1,69 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression + +class KennithDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.SEA_SLUG, 0, 1, 2, 3) + .playerl(FacialExpression.THINKING, "Are you okay young one?") + .npcl(FacialExpression.CHILD_SAD, "No, I want daddy!") + .playerl("Where is your father?") + .npcl(FacialExpression.CHILD_SAD, "He went to get help days ago.") + .npcl(FacialExpression.CHILD_SAD, "The nasty fishermen tried to throw me and daddy into the sea. So he told me to hide here.") + .playerl("That's good advice, you stay here and I'll go try and find your father.") + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 3) { + setQuestStage(player, Quests.SEA_SLUG, 4) + } + } + b.onQuestStages(Quests.SEA_SLUG, 4, 5, 6) + .playerl(FacialExpression.THINKING, "Are you okay?") + .npcl(FacialExpression.CHILD_SAD, "I want to see daddy!") + .playerl("I'm working on it.") + .end() + + b.onQuestStages(Quests.SEA_SLUG, 7) + .playerl("Hello Kennith, are you okay?") + .npcl(FacialExpression.CHILD_SAD, "No, I want my daddy.") + .playerl("You'll be able to see him soon. First we need to get you back to land, come with me to the boat.") + .npcl(FacialExpression.CHILD_SURPRISED, "No!") + .playerl("What, why not?") + .npcl(FacialExpression.CHILD_SURPRISED, "I'm scared of those nasty sea slugs. I won't go near them.") + .playerl("Okay, you wait here and I'll go figure another way to get you out.") + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 7) { + setQuestStage(player, Quests.SEA_SLUG, 8) + } + } + + b.onQuestStages(Quests.SEA_SLUG, 8) + // This stage is unfortunately left out. You can't interact with Kennith authentically. + .end() + + b.onQuestStages(Quests.SEA_SLUG, 9) + .playerl("Kennith, I've made an opening in the wall. You can come out through there.") + .npcl(FacialExpression.CHILD_THINKING, "Are there any sea slugs on the other side?") + .playerl("Not one.") + .npcl(FacialExpression.CHILD_THINKING, "How will I get downstairs?") + .playerl("I'll figure that out in a moment.") + .npcl(FacialExpression.CHILD_NORMAL, "Ok, when you have I'll come out.") + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 9) { + setQuestStage(player, Quests.SEA_SLUG, 10) + } + } + + b.onQuestStages(Quests.SEA_SLUG, 10) + // This stage is also unfortunately left out. You can't interact with Kennith authentically. + .end() + + b.onQuestStages(Quests.SEA_SLUG, 11, 100) + // Kennith is varp swapped out, so is no longer here. + .end() + + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/KentDialogueFile.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/KentDialogueFile.kt new file mode 100644 index 000000000..1e9fee52c --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/KentDialogueFile.kt @@ -0,0 +1,41 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression + +class KentDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(Quests.SEA_SLUG, 0, 1, 2, 3, 4, 5, 6) + .npcl("Oh thank Saradomin! I thought I'd be left out here forever.") + .playerl("Your wife sent me out to find you and your boy. Kennith's fine by the way, he's on the platform.") + .npcl("I knew the row boat wasn't sea worthy. I couldn't risk bringing him along but you must get him off that platform.") + .playerl("What's going on here?") + .npcl("Five days ago we pulled in a huge catch. As well as fish we caught small slug like creatures, hundreds of them.") + .npcl("That's when the fishermen began to act strange.") + .npcl("It was the sea slugs, they attack themselves to your body and somehow take over the mind of the carrier.") + .npcl("I told Kennith to hide until I returned but I was washed up here.") + .npcl("Please go back and get my boy, you can send help for me later.") + .npcl(FacialExpression.EXTREMELY_SHOCKED, "@name wait!") + .betweenStage { _, player, _, _ -> + visualize(npc!!, 4807, 790) + sendMessage(player, "*slooop*") + sendMessage(player, "He pulls a sea slug from under your top.") + } + .npcl("A few more minutes and that thing would have full control of your body.") + .playerl(FacialExpression.EXTREMELY_SHOCKED, "Yuck! Thanks Kent.") + .endWith() { df, player -> + if(getQuestStage(player, Quests.SEA_SLUG) == 4) { + setQuestStage(player, Quests.SEA_SLUG, 5) + } + } + + b.onQuestStages(Quests.SEA_SLUG, 5, 6, 7, 8, 9, 10, 11, 100) + .playerl("Hello.") + .npcl("Oh my, I must get back to shore.") + .end() + + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/SeaSlug.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/SeaSlug.kt new file mode 100644 index 000000000..ef3424233 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/SeaSlug.kt @@ -0,0 +1,186 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.api.* +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +/** + * Sea Slug Quest + * + * Note that the varp 159 controls the quest AND some environments: + * The BADLY_REPAIRED_WALL_18381 disappears after varp 159 is set to 9 + * KENNITH_4864 disappears after varp 159 is set to 11 + * KENNITH_4865 (but ID 4864) appears after varp 159 is set to 11 + * + * https://www.youtube.com/watch?v=lf83SACuIDw (This is amazing) + * https://www.youtube.com/watch?v=VnghpKbUqKw + * https://www.youtube.com/watch?v=thHuATlGYag + * https://www.youtube.com/watch?v=VR91Rbyuou4 (This has many other unvisited paths) + */ +@Initializable +class SeaSlug : Quest(Quests.SEA_SLUG, 109, 108, 1,159, 0, 1, 12) { + + companion object { + const val questVarp = 159 + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, Quests.SEA_SLUG) > 0 + + if (!started) { + line(player, "I can start this quest by speaking to !!Caroline?? who is !!East??", line++, false) + line(player, "!!of Ardougne??.", line++, false) + line++ + line(player, "Requirements:", line++, false) + // I think this is an old line. I saw it being the other line for 30 Firemaking reqs. + line(player, "You'll need level 30 !!Firemaking??", line++, hasLevelStat(player, Skills.FIREMAKING, 30)) + // line(player, "!!Level 30 Firemaking??", line++, hasLevelStat(player, Skills.FIREMAKING, 30)) + } else { + line(player, "I have spoken to Caroline and agreed to help", line++, true) + line++ + + if (stage >= 3) { + line(player, "I gave Holgart the Swamp Paste and his boat is now ready", line++, true) + line(player, "to take me to the Fishing Platform", line++, true) + } else if (stage >= 2) { + // authentic from https://www.youtube.com/watch?v=VnghpKbUqKw + line(player, "I've spoken to !!Holgart?? but his boat is broken", line++, false) + line(player, "He needs me to bring him some !!Swamp Paste??", line++, false) + line++ + line(player, "I can make !!Swamp Paste?? by mixing !!Swamp Tar?? with !!Flour?? and", line++, false) + line(player, "then heating the mixture on a !!Fire??", line++, false) + line(player, "I can find !!Swamp Tar?? in the !!Swamp South of Lumbridge??", line++, false) + line++ + line(player, "I need to get to the !!Fishing Platform?? and find out what's", line++, false) + line(player, "happened to Kent and Kennith", line++, false) + } else if (stage >= 1) { + // derived + line(player, "I need to speak to !!Holgart??.", line++, false) + } + + if (stage >= 5) { + line++ + line(player, "I've found Kennith, he's hiding behind some boxes.", line++, true) + line++ + line(player, "I've found Kent on a small island", line++, true) + } else if (stage >= 4) { + line++ + line(player, "I've found Kennith, he's hiding behind some boxes.", line++, true) + line++ + line(player, "I need to find !!Kent??", line++, false) + } else if (stage >= 3) { + line++ + line(player, "I need to find !!Kent?? and !!Kennith??", line++, false) + } + + if (stage >= 9) { + line++ + line(player, "!!Kent?? has asked me to help !!Kennith?? escape", line++, true) + } else if (stage >= 5) { + line++ + line(player, "!!Kent?? has asked me to help !!Kennith?? escape", line++, false) + } + + if (stage >= 9) { + line(player, "After speaking to Bailey, I found that Sea Slugs are", line++, true) + line(player, "afraid of heat.", line++, true) + line(player, "I should find a way of lighting this damp torch.", line++, true) + } else if (stage >= 6) { + line(player, "After speaking to !!Bailey??, I found that Sea Slugs are", line++, false) + line(player, "afraid of heat.", line++, false) + line(player, "I should find a way of lighting this damp torch.", line++, false) + } + + if (stage >= 8) { + // Disappears + } else if (stage >= 7) { + // Derived + line(player, "I should talk to !!Kennith??", line++, false) + } + + if (stage >= 9) { + line(player, "I've created an opening to let Kennith escape", line++, true) + } else if (stage >= 8) { + // Derived + line(player, "I need to find a way to get !!Kennith?? out", line++, false) + } + + if (stage >= 10) { + line++ + line(player, "Kennith can't get downstairs without some help", line++, true) + } else if (stage >= 9) { + // Derived + line++ + line(player, "I should talk to !!Kennith?? again", line++, false) + } + + + if (stage >= 11) { + line++ + line(player, "I've used the Crane to lower Kennith into the boat", line++, true) + } else if (stage >= 10) { + line++ + line(player, "!!Kennith?? won't go near the !!Sea Slugs??", line++, false) + line(player, "I need to find another way to get him out", line++, false) + } + + if (stage >= 100) { + line++ + line(player, "I've spoken to Caroline and she thanked me for", line++, true) + line(player, "rescuing her family from the Sea Slugs", line++, true) + } else if (stage >= 11) { + line++ + line(player, "I need to take the boat back to shore and talk to !!Caroline??", line++, false) + } + + if (stage >= 100) { + line++ + line(player,"QUEST COMPLETE!", line) + } + } + + } + + override fun reset(player: Player) { + // removeAttribute(player, attributeTalkedToHolgart) + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed Sea Slug!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.SEA_SLUG_1466,230,277,5) + + drawReward(player, "1 Quest Point", ln++) + drawReward(player, "7175 Fishing XP", ln++) + drawReward(player, "Oyster pearls", ln++) + + rewardXP(player, Skills.FISHING, 7175.0) + addItemOrDrop(player, Items.OYSTER_PEARLS_413) + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + this.updateVarps(player) + } + + override fun updateVarps(player: Player) { + // The quest stages are perfectly aligned with the varp since the varp controls npcs and sceneries + if (getQuestStage(player, Quests.SEA_SLUG) >= 12) { + setVarp(player, questVarp, 12, true) // Except for stage 100 which is varp set to 12 obviously. + } else { + setVarp(player, questVarp, getQuestStage(player, Quests.SEA_SLUG), true) + } + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/SeaSlugListeners.kt b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/SeaSlugListeners.kt new file mode 100644 index 000000000..79a1b10ab --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/quest/seaslug/SeaSlugListeners.kt @@ -0,0 +1,196 @@ +package content.region.kandarin.witchhaven.quest.seaslug + +import content.data.Quests +import core.api.* +import core.game.global.action.ClimbActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.combat.ImpactHandler.HitsplatType +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.Location +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class SeaSlugListeners : InteractionListener { + + companion object { + + // The boat travels are animated nicely for you by un-hiding child 10 - 13 + val BOAT_TRAVEL_CHILD = arrayOf(10, 11, 12, 13) + val BOAT_TRAVEL_TICKS = arrayOf(5, 9, 8, 7) + val BOAT_TRAVEL_DESTINATION = arrayOf( + Location(2782, 3273), // From LAND to PLATFORM + Location(2721, 3304), // From PLATFORM to LAND + Location(2800, 3320), // From PLATFORM to ISLAND + Location(2782, 3273), // From ISLAND to PLATFORM + ) + val BOAT_TRAVEL_DIALOGUE = arrayOf( + "You arrive at the fishing platform.", // From LAND to PLATFORM + "The boat arrives at Witchaven.", // From PLATFORM to LAND + "You arrive on a small island.", // From PLATFORM to ISLAND + "You arrive at the fishing platform.", // From ISLAND to PLATFORM + ) + + fun seaslugBoatTravel(player: Player, travelIndex: Int) { + if (travelIndex == 0) { + // Prevent bringing lit torches. + while(removeItem(player, Items.LIT_TORCH_594)) { + sendMessage(player, "Your torch goes out on the crossing.") + addItemOrDrop(player, Items.UNLIT_TORCH_596) + } + } + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when(stage){ + 0 -> { + closeOverlay(player) + openOverlay(player, Components.FADE_TO_BLACK_120) + lock(player, 2) + return@queueScript delayScript(player, 1) + } + 1 -> { + teleport(player, BOAT_TRAVEL_DESTINATION[travelIndex]) + openOverlay(player, Components.SEASLUG_BOAT_TRAVEL_461) + setComponentVisibility(player, Components.SEASLUG_BOAT_TRAVEL_461, BOAT_TRAVEL_CHILD[travelIndex], false) + lock(player, BOAT_TRAVEL_TICKS[travelIndex]) + return@queueScript delayScript(player, BOAT_TRAVEL_TICKS[travelIndex]) + } + 2 -> { + sendDialogue(player, BOAT_TRAVEL_DIALOGUE[travelIndex]) + player.interfaceManager.closeOverlay() + openOverlay(player, Components.FADE_FROM_BLACK_170) + return@queueScript stopExecuting(player) + } + } + return@queueScript stopExecuting(player) + } + } + + } + override fun defineListeners() { + // https://www.youtube.com/watch?v=VR91Rbyuou4 + // Your tinderbox is damp from the sea crossing. It won't light here. + // Your torch goes out on the crossing. + + onUseWith(IntType.ITEM, Items.SWAMP_TAR_1939, Items.POT_OF_FLOUR_1933){ player, used, with -> + val toRemove = Item(used.id, 1, used.asItem().slot) + if(removeItem(player, toRemove) && removeItem(player, with)) { + sendMessage(player, "You mix the flour with the swamp tar.") + sendMessage(player, "It mixes into a paste.") + addItemOrDrop(player, Items.EMPTY_POT_1931) + addItemOrDrop(player, Items.RAW_SWAMP_PASTE_1940) + } + return@onUseWith true + } + + // You can only cook it using firewood. + // sendMessage(player, "You can't cook that in a range.") + onUseWith(SCENERY, Items.RAW_SWAMP_PASTE_1940, Scenery.FIRE_2732) { player, used, with -> + val toRemove = Item(used.id, 1, used.asItem().slot) + if(removeItem(player, toRemove)) { + sendMessage(player, "You warm the paste over the fire. It thickens into a sticky goo.") + addItemOrDrop(player, Items.SWAMP_PASTE_1941) + } + return@onUseWith true + } + + + on(Scenery.LADDER_18324, IntType.SCENERY, "climb-up") { player, _ -> + if (getQuestStage(player, Quests.SEA_SLUG) in 5..6) { + if (getQuestStage(player, Quests.SEA_SLUG) == 6 && inInventory(player, Items.LIT_TORCH_594)) { + setQuestStage(player, Quests.SEA_SLUG, 7) + ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, Location(2784, 3285, 1)) + } else { + animate(player, 4785) + sendMessage(player, "You attempt to climb up the ladder.") + sendMessage(player, "The fisherman approach you...") + sendMessage(player, "and smack you on the head with a fishing rod!") + sendMessage(player, "Ouch!") + sendChat(player, "Ouch!") + player.impactHandler.manualHit(player, 4, ImpactHandler.HitsplatType.NORMAL) + } + } else { + ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, Location(2784, 3285, 1)) + } + return@on true + } + on(Scenery.LADDER_18325, IntType.SCENERY, "climb-down") { player, _ -> + ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_DOWN, Location(2784, 3287, 0)) + return@on true + } + + on(Scenery.BADLY_REPAIRED_WALL_18381, IntType.SCENERY, "kick") { player, _ -> + if(getQuestStage(player, Quests.SEA_SLUG) == 8) { + animate(player, 4804) + sendMessage(player, "You kick the loose panel.") + sendMessage(player, "The wood is rotted and crumbles away...") + sendMessage(player, "... leaving an opening big enough for Kennith to climb through.") + setQuestStage(player, Quests.SEA_SLUG, 9) + } else { + // https://youtu.be/OM-akv7oIZ0 2:41 + sendMessage(player, "You kick the loose panel...") + sendMessage(player, "... but nothing interesting happens.") + } + return@on true + } + + on(Scenery.CRANE_18327, IntType.SCENERY, "rotate") { player, node -> + if(getQuestStage(player, Quests.SEA_SLUG) == 10) { + // This is supposed to be a cutscene, but goddamn do I hate programming cutscenes. + lock(player, 6) + player.dialogueInterpreter.sendPlainMessage(true, "Kennith scrambles through the broken wall...") + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.CRANE_18326, 6) + animateScenery(node as core.game.node.scenery.Scenery, 4798) + setQuestStage(player, Quests.SEA_SLUG, 11) + queueScript(player, 6, QueueStrength.SOFT) { stage: Int -> + sendDialogue(player, "Down below, you see Holgart collect the boy from the crane and lead him away to safety.") + return@queueScript stopExecuting(player) + } + } else { + sendMessage(player, "You rotate the crane around.") + animateScenery(node as core.game.node.scenery.Scenery, 4796) + } + return@on true + } + + onUseWith(IntType.ITEM, Items.DAMP_STICKS_1467, Items.BROKEN_GLASS_1469){ player, used, with -> + if(removeItem(player, used)) { + visualize(player, 4809, 791) + addItemOrDrop(player, Items.DRY_STICKS_1468) + } + return@onUseWith true + } + + onUseWith(IntType.ITEM, Items.DRY_STICKS_1468, Items.UNLIT_TORCH_596){ player, bolt, tip -> + addItemOrDrop(player, Items.LIT_TORCH_594) + return@onUseWith true + } + + on(Items.DRY_STICKS_1468, ITEM, "rub-together") { player, _ -> + sendMessage(player, "You rub together the dry sticks and the sticks catch alight.") + if(removeItem(player, Items.UNLIT_TORCH_596)) { + sendMessage(player, "You place the smouldering twigs to your torch.") + sendMessage(player, "Your torch lights.") + addItemOrDrop(player, Items.LIT_TORCH_594) + } + return@on true + } + + + on(NPCs.SEA_SLUG_1006, IntType.NPC, "take") { player, _ -> + sendMessage(player, "You pick up the sea slug.") + sendMessage(player, "It sinks its teeth deep into your hand.") + sendMessage(player, "You drop the sea slug.") + sendChat(player, "Ouch!") + impact(player, 3, HitsplatType.NORMAL) + return@on true + } + + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/yanille/dialogue/LeonDialogue.java b/Server/src/main/content/region/kandarin/yanille/dialogue/LeonDialogue.java deleted file mode 100644 index 08f548ac6..000000000 --- a/Server/src/main/content/region/kandarin/yanille/dialogue/LeonDialogue.java +++ /dev/null @@ -1,96 +0,0 @@ -package content.region.kandarin.yanille.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used for the leon npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LeonDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code LeonDialogue} {@code Object}. - */ - public LeonDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LeonDialogue} {@code Object}. - * @param player the player. - */ - public LeonDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LeonDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendOptions("Select an Option", "What is this place?", "Can I have a go with your crossbow?", "What are you holding there?"); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.ASKING, "What is this place?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Can I have a go with your crossbow?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.ASKING, "What are you holding there?"); - stage = 30; - break; - - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "This is Aleck's Hunter Emporium. Basically, it's just a", "shop with fancy name; you can buy various weapons", "and traps here."); - stage = 11; - break; - case 11: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm afraid with it being a prototype, I've only got a few", "for my own testing purposes."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "This? This is a prototype for a new type of crossbow", "I've been designing."); - stage = 31; - break; - case 31: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 5111 }; - } -} diff --git a/Server/src/main/content/region/kandarin/yanille/dialogue/LeonDialogue.kt b/Server/src/main/content/region/kandarin/yanille/dialogue/LeonDialogue.kt new file mode 100644 index 000000000..f5e3b2463 --- /dev/null +++ b/Server/src/main/content/region/kandarin/yanille/dialogue/LeonDialogue.kt @@ -0,0 +1,134 @@ +package content.region.kandarin.yanille.dialogue + +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class LeonDialogue (player: Player? = null) : DialoguePlugin(player) { + + companion object { + const val WHAT_IS_THIS_PLACE = 10 + const val BUY_GEAR = 20 + const val ABOUT_CBOW = 30 + const val ABOUT_AMMO = 40 + const val BYE = 50 + const val CRAZY = 60 + const val TRADE = 70 + const val MAKE_OWN_AMMO = 80 + const val CRAFT_AMMO = 90 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (hasAnItem(player, Items.HUNTERS_CROSSBOW_10156).exists()) npcl(FacialExpression.HAPPY, "Oh, hey, you have one of my crossbows! How's it working for you?") + else sendItemDialogue(player, Items.HUNTERS_CROSSBOW_10156,"Leon is gazing intently at the crossbow in his hands.") + return true + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> showTopics( + IfTopic("It's okay, thanks.", 0, hasAnItem(player, Items.HUNTERS_CROSSBOW_10156).exists()), + Topic("What is this place?", WHAT_IS_THIS_PLACE), + Topic("Can you tell me about your crossbow?", ABOUT_CBOW), + Topic("Tell me about the ammo for your crossbow.", ABOUT_AMMO), + Topic("I'll be off now, excuse me.", BYE), + + ) + + WHAT_IS_THIS_PLACE -> npcl(FacialExpression.NEUTRAL, "This is Aleck's Hunter Emporium. Basically, it's just a shop with a fancy name; you can buy various weapons and traps here.").also { stage++ } + WHAT_IS_THIS_PLACE + 1 -> showTopics( + Topic("Can I buy some equipment from the shop then?", BUY_GEAR), + Topic("Can you tell me about your crossbow?", ABOUT_CBOW), + Topic("Tell me about the ammo for your crossbow.", ABOUT_AMMO), + Topic("I'll be off now, excuse me.", BYE) + ) + + BUY_GEAR -> npcl(FacialExpression.NEUTRAL, "Oh, this isn't my shop; the owner is Aleck over there behind the counter.").also { stage++ } + BUY_GEAR + 1 -> npcl(FacialExpression.NEUTRAL, "I experiment with weapon designs. I'm here because I've been trying to convince people to back my research and maybe sell some of my products - like this one I'm holding - in their shops.").also { stage++ } + BUY_GEAR + 2 -> npcl(FacialExpression.SAD, "Aleck doesn't seem to be interested, though.").also { stage++ } + BUY_GEAR + 3 -> showTopics( + Topic("Can you tell me about your crossbow?", ABOUT_CBOW), + Topic("Tell me about the ammo for your crossbow.", ABOUT_AMMO), + Topic("I'll be off now, excuse me.", BYE) + ) + + ABOUT_CBOW -> npcl(FacialExpression.HAPPY, "It's good, isn't it? I designed it to incorporate the bones of various animals in its construction.").also { stage++ } + ABOUT_CBOW + 1 -> npcl(FacialExpression.HAPPY, "It's a fair bit faster than an ordinary crossbow too; it'll take you far less time to reload between shots.").also { stage++ } + ABOUT_CBOW + 2 -> showTopics( + Topic("That's crazy, it'll never work!", CRAZY), + Topic("That sounds good. Let's trade.", TRADE) + ) + + CRAZY -> npcl(FacialExpression.HALF_THINKING, "That's what they said about my wind-powered mouse traps, too.").also { stage++ } + CRAZY + 1 -> playerl(FacialExpression.HALF_WORRIED, "And did they work?").also { stage++ } + CRAZY + 2 -> npcl(FacialExpression.HALF_THINKING, "Well, they only ran into problems because people kept insisting on trying to use them indoors.").also { stage++ } + CRAZY + 3 -> npcl(FacialExpression.HAPPY, "Anyway, I think my crossbow invention is showing a lot more promise.").also { stage = 0 } + + TRADE -> { + end() + openNpcShop(player, npc.id) + } + + ABOUT_AMMO -> npcl(FacialExpression.NEUTRAL, "Ah, I admit as a result of its... er... unique construction, it won't take just any old bolts.").also { stage++ } + ABOUT_AMMO + 1 -> npcl(FacialExpression.NEUTRAL, "If you can supply the materials and a token fee, I'd be happy to make some for you.").also { stage++ } + ABOUT_AMMO + 2 -> npcl(FacialExpression.NEUTRAL, "I need kebbit spikes, lots of 'em. Not all kebbits have spikes, mind you. You'll be wanting prickly kebbits or, even better, razor-backed kebbits to get material hard enough.").also { stage++ } + ABOUT_AMMO + 3 -> showTopics( + Topic("Can't I just make my own?", MAKE_OWN_AMMO), + Topic("Okay, can you make ammo for me?", CRAFT_AMMO) + ) + + MAKE_OWN_AMMO -> npcl(FacialExpression.HALF_THINKING, "Yes, I suppose you could, although you'll need a steady hand with a knife and a chisel.").also { stage++ } + MAKE_OWN_AMMO + 1 -> npcl(FacialExpression.HALF_THINKING, "The bolts have an unusual diameter, but basically you'll just need to be able to carve kebbit spikes into straight shafts.").also { stage++ } + MAKE_OWN_AMMO + 2 -> npcl(FacialExpression.NEUTRAL, "Meanwhile, since you're here, I can make some for you if you have the materials.").also { stage = 0 } + + // OSRS suggests this may be inauthentic and there should be a make x interface. + // No 2009 sources found saying that though + CRAFT_AMMO -> npcl(FacialExpression.NEUTRAL, "Sure what type of bolts do you want?").also { stage++ } + CRAFT_AMMO + 1 -> showTopics( + Topic("Kebbit bolts.", CRAFT_AMMO + 2), + Topic("Long kebbit bolts.", CRAFT_AMMO + 3) + ) + CRAFT_AMMO + 2 -> { + if (hasAnItem(player!!, Items.KEBBIT_SPIKE_10105).exists() && inInventory(player, Items.COINS_995, 20)){ + if(removeItem(player, Items.KEBBIT_SPIKE_10105) && removeItem(player, Item(Items.COINS_995, 20))){ + addItem(player, Items.KEBBIT_BOLTS_10158, 6) + sendItemDialogue(player, Items.KEBBIT_BOLTS_10158, "You hand the weapon designer one spike and 20 coins. In return he presents you with 6 bolts.").also { stage = END_DIALOGUE } + } + } + else{ + sendItemDialogue(player, Items.KEBBIT_SPIKE_10105, "You need 1 kebbit spike and 20 coins to make 6 kebbit bolts.").also { stage = END_DIALOGUE } + } + } + CRAFT_AMMO + 3 -> { + if (hasAnItem(player!!, Items.LONG_KEBBIT_SPIKE_10107).exists() && inInventory(player, Items.COINS_995, 24)){ + if(removeItem(player, Items.LONG_KEBBIT_SPIKE_10107) && removeItem(player, Item(Items.COINS_995, 40))){ + addItem(player, Items.LONG_KEBBIT_BOLTS_10159, 6) + sendItemDialogue(player, Items.LONG_KEBBIT_BOLTS_10159, "You hand the weapon designer one long spike and 40 coins. In return he presents you with 6 long bolts.").also { stage = END_DIALOGUE } + } + } + else{ + sendItemDialogue(player, Items.LONG_KEBBIT_SPIKE_10107, "You need 1 long kebbit spike and 40 coins to make 6 kebbit bolts.").also { stage = END_DIALOGUE } + } + } + + BYE -> npcl(FacialExpression.NEUTRAL, "Well, if you ever find yourself in need of that innovative edge, you can always find me here.").also { stage++ } + BYE + 1 -> playerl(FacialExpression.HALF_ROLLING_EYES, "...thanks").also { stage = END_DIALOGUE } + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LEON_5111) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/yanille/dialogue/ZavisticRarveDialogue.kt b/Server/src/main/content/region/kandarin/yanille/dialogue/ZavisticRarveDialogue.kt new file mode 100644 index 000000000..9ccbedc95 --- /dev/null +++ b/Server/src/main/content/region/kandarin/yanille/dialogue/ZavisticRarveDialogue.kt @@ -0,0 +1,120 @@ +package content.region.kandarin.yanille.dialogue + +import content.region.kandarin.feldip.quest.zogreflesheaters.ZogreFleshEaters +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ZavisticRarveDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return ZavisticRarveDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ZavisticRarveDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ZAVISTIC_RARVE_2059) + } +} +class ZavisticRarveDialogueFile : DialogueBuilderFile() { + + companion object { + fun dialogueInitialTalk(builder: DialogueBuilder) : DialogueBuilder { + return builder + .branch { player -> + return@branch if (getQuestStage(player, ZogreFleshEaters.questName) > 3 /* || hand in the sand quest */) { + 1 + } else { + 0 + } + }.let { branch2 -> + val returnJoin = builder.placeholder() + branch2.onValue(1) + .npcl("What are you doing...Oh, it's you...sorry...didn't realise...what can I do for you?") + // There is a fork here if you are doing hand in the sand. + .goto(returnJoin) + branch2.onValue(0) + .npcl("What are you doing bothering me? Don't you think some of us have work to do?") + .playerl("I thought you were here to help?") + .npcl("Well... I am, I suppose, anyway... we're very busy here, hurry up, what do you want?") + .goto(returnJoin) + return@let returnJoin.builder() + } + } + + fun dialogueInitialTalkViaBell(builder: DialogueBuilder) : DialogueBuilder { + return builder + .branch { player -> + return@branch if (getQuestStage(player, ZogreFleshEaters.questName) > 3 /* || hand in the sand quest */) { + 1 + } else { + 0 + } + }.let { branch2 -> + val returnJoin = builder.placeholder() + branch2.onValue(1) + .npcl("What are you doing...Oh, it's you...sorry...didn't realise...what can I do for you?") + .goto(returnJoin) + branch2.onValue(0) + .npcl("What are you doing ringing that bell?! Don't you think some of us have work to do?") + .playerl("But I was told to ring the bell if I wanted some attention.") + .npcl("Well...anyway...we're very busy here, hurry up what do you want?") + .goto(returnJoin) + return@let returnJoin.builder() + } + } + + fun defaultTalk(continueBuilder: DialogueBuilder) { + continueBuilder.let { builder -> + val returnJoin = builder.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("What is there to do in the Wizards' Guild?") + .npcl("This is the finest wizards' establishment in the land.") + .npcl("We have magic portals to the other towers of wizardry around Gielinor.") + .npcl("We have a particularly wide collection of runes in our rune shop.") + .npcl("We sell some of the finest mage robes in the land and we have a training area full of zombies for you to practice your magic on.") + .goto(returnJoin) + optionBuilder.option_playerl("What are the requirements to get in the Wizards' Guild?") + .npcl("You need a magic level of 66, the high magic energy level is too dangerous for anyone below that level.") + .goto(returnJoin) + optionBuilder.option_playerl("What do you do in the Guild?") + .npcl("I'm the Grand Secretary for the Wizards' Guild, I have lots of correspondence to keep up with, as well as attending to the discipline of the more problematic guild members.") + .goto(returnJoin) + optionBuilder.option_playerl("Ok, thanks.") + .end() + } + builder.goto(returnJoin) + } + } + } + + + override fun create(b: DialogueBuilder) { + + b.onPredicate { player -> isQuestComplete(player, ZogreFleshEaters.questName) } + .let { dialogueInitialTalk(it) } + .let { defaultTalk(it) } + + // This is during the ZogreFleshEaters quest + b.onPredicate { player -> isQuestInProgress(player, ZogreFleshEaters.questName, 2, 99) } + .manualStage() { df, player, _, _ -> + openDialogue(player, content.region.kandarin.feldip.quest.zogreflesheaters.ZavisticRarveDialogueFile(), npc!!) + } + .end() + + b.onPredicate { _ -> true } + .let { dialogueInitialTalk(it) } + .let { defaultTalk(it) } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/brimhaven/dialogue/AlfonseWaiterDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/AlfonseWaiterDialogue.java deleted file mode 100644 index 72c201399..000000000 --- a/Server/src/main/content/region/karamja/brimhaven/dialogue/AlfonseWaiterDialogue.java +++ /dev/null @@ -1,92 +0,0 @@ -package content.region.karamja.brimhaven.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin for the npc alfonse the waiter. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class AlfonseWaiterDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code AlfonseWaiterDialogue} {@code Object}. - */ - public AlfonseWaiterDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code AlfonseWaiterDialogue} {@code Object}. - * @param player the player. - */ - public AlfonseWaiterDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AlfonseWaiterDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to the Shrimp and Parrot.", "Would you like to order, sir?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, please.", "No, thank you.", "Where do you get your Karambwan from?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, please."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thank you."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where do you get your Karambwan from?"); - stage = 30; - break; - } - break; - case 10: - end(); - npc.openShop(player); - break; - case 20: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "We buy directly off Lubufu, a local fisherman. He", "seems to have a monopoly over Karambwan sales."); - stage = 31; - break; - case 31: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 793 }; - } -} diff --git a/Server/src/main/content/region/karamja/brimhaven/dialogue/GarvDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/GarvDialogue.java deleted file mode 100644 index 8f995db1e..000000000 --- a/Server/src/main/content/region/karamja/brimhaven/dialogue/GarvDialogue.java +++ /dev/null @@ -1,88 +0,0 @@ -package content.region.karamja.brimhaven.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the garv dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GarvDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GarvDialogue} {@code Object}. - */ - public GarvDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GarvDialogue} {@code Object}. - * @param player the player. - */ - public GarvDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GarvDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello. What do you want?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Can I go in there?", "I want for nothing!"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I go in there?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I want for nothing!"); - stage = 20; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No. In there is private."); - stage = 11; - break; - case 11: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're one of a very lucky few then."); - stage = 21; - break; - case 21: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 788 }; - } -} diff --git a/Server/src/main/content/region/karamja/brimhaven/dialogue/GruborDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/GruborDialogue.java deleted file mode 100644 index c181f9399..000000000 --- a/Server/src/main/content/region/karamja/brimhaven/dialogue/GruborDialogue.java +++ /dev/null @@ -1,99 +0,0 @@ -package content.region.karamja.brimhaven.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the grubor dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GruborDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GruborDialogue} {@code Object}. - */ - public GruborDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GruborDialogue} {@code Object}. - * @param player the player. - */ - public GruborDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GruborDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes? What do you want?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Would you like your hedges trimming?", "I want to come in.", "Do you want to trade?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Would you like your hedges trimming?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I want to come in."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you want to trade?"); - stage = 30; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Eh? Don't be daft! We don't even HAVE any hehdges!"); - stage = 11; - break; - case 11: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No, go away."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No, I'm busy."); - stage = 31; - break; - case 31: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 789 }; - } -} diff --git a/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenListeners.kt b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenListeners.kt index ddb5c270a..e2b43bc28 100644 --- a/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenListeners.kt +++ b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenListeners.kt @@ -44,21 +44,6 @@ class BrimhavenListeners : InteractionListener { */ private const val RESTAURANT_REAR_DOOR = Scenery.DOOR_1591 - /** - * Represents the door of the Black Arm Gang office used in the Heroes' Quest. - */ - private const val GANG_OFFICE_DOOR = Scenery.DOOR_2626 - - /** - * Represents the door guarded by Garv on ScarFace Pete's mansion used in the Heroes' Quest. - */ - private const val MANSION_DOOR = Scenery.DOOR_2627 - - /** - * Represents the kitchen door in the Shrimp and Parrot restaurant used in the Heroes' Quest. - */ - private const val RESTAURANT_KITCHEN_DOOR = Scenery.DOOR_2628 - /** * Represents Lubufu's karambwan fishing spot unlocked in Tai Bwo Wannai Trio. */ @@ -108,21 +93,6 @@ class BrimhavenListeners : InteractionListener { return@on true } - on(GANG_OFFICE_DOOR, IntType.SCENERY, "open") { player, _ -> - openDialogue(player, 789, Repository.findNPC(789)!!) - return@on true - } - - on(MANSION_DOOR, IntType.SCENERY, "open") { player, _ -> - openDialogue(player, 788, Repository.findNPC(788)!!, true) - return@on true - } - - on(RESTAURANT_KITCHEN_DOOR, IntType.SCENERY, "open") { player, _ -> - sendMessage(player, "The door is securely closed.") - return@on true - } - on(KARAMBWAN_FISHING_SPOT, IntType.NPC, "fish") { player, _ -> sendNPCDialogue( player, diff --git a/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java b/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java index 164df698b..b32c1bfa0 100644 --- a/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java +++ b/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java @@ -10,6 +10,7 @@ import core.plugin.Initializable; import core.game.world.map.Location; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents the dialogue plugin used to handle the customs officer. @@ -54,7 +55,7 @@ public final class CustomsOfficerDialogue extends DialoguePlugin { public boolean open(Object... args) { npc = (NPC) args[0]; if (args.length > 1) { - if (player.getQuestRepository().isComplete("Pirate's Treasure")) { + if (player.getQuestRepository().isComplete(Quests.PIRATES_TREASURE)) { if (player.getInventory().containsItem(RUM)) { interpreter.sendDialogues(npc, null, "Aha, trying to smuggle rum are we?"); stage = 900; diff --git a/Server/src/main/content/region/karamja/handlers/CustomsOfficerPlugin.java b/Server/src/main/content/region/karamja/handlers/CustomsOfficerPlugin.java index 826467b8b..a29ad6b17 100644 --- a/Server/src/main/content/region/karamja/handlers/CustomsOfficerPlugin.java +++ b/Server/src/main/content/region/karamja/handlers/CustomsOfficerPlugin.java @@ -7,6 +7,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the customs officer plugin. @@ -25,7 +26,7 @@ public final class CustomsOfficerPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - if (!player.getQuestRepository().isComplete("Pirate's Treasure")) { + if (!player.getQuestRepository().isComplete(Quests.PIRATES_TREASURE)) { player.getDialogueInterpreter().open(((NPC) node).getId(), ((NPC) node)); player.getPacketDispatch().sendMessage("You may only use the Pay-fare option after completing Pirate's Treasure."); return true; diff --git a/Server/src/main/content/region/karamja/handlers/TribesmanNPC.java b/Server/src/main/content/region/karamja/handlers/TribesmanNPC.java deleted file mode 100644 index 184357c1d..000000000 --- a/Server/src/main/content/region/karamja/handlers/TribesmanNPC.java +++ /dev/null @@ -1,48 +0,0 @@ -package content.region.karamja.handlers; - -import core.game.node.entity.npc.AbstractNPC; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.game.system.config.NPCConfigParser; - -/** - * Represents the tribesamn npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class TribesmanNPC extends AbstractNPC { - - /** - * Represents the npc ids. - */ - private static final int[] IDS = new int[] { 191, 2496, 2497 }; - - /** - * Constructs a new {@code TribesmanNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public TribesmanNPC(int id, Location location) { - super(id, location, true); - getDefinition().getHandlers().put(NPCConfigParser.POISONOUS, true); - } - - /** - * Constructs a new {@code TribesmanNPC} {@code Object}. - */ - public TribesmanNPC() { - super(0, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new TribesmanNPC(id, location); - } - - @Override - public int[] getIds() { - return IDS; - } - -} diff --git a/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java index 9263035b8..5639c3b20 100644 --- a/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java +++ b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java @@ -14,6 +14,7 @@ import core.tools.RandomFunction; import core.tools.StringUtils; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * The main type or the jungle potion quest. @@ -23,16 +24,11 @@ import static core.api.ContentAPIKt.*; @Initializable public final class JunglePotion extends Quest { - /** - * The name of the quest. - */ - public static final String NAME = "Jungle Potion"; - /** * Constructs a new {@code JunglePotion} {@code Object}. */ public JunglePotion() { - super(NAME, 81, 80, 1, 175, 0, 1, 12); + super(Quests.JUNGLE_POTION, 81, 80, 1, 175, 0, 1, 12); } @Override @@ -119,7 +115,7 @@ public final class JunglePotion extends Quest { }); } }, - PALM_TREE(2577, Herbs.ARDRIGAL, 20, "You are looking for Ardrigal. It is related to the palm", "and grows in its brothers shady profusion."), SITO_FOIL(2579, Herbs.SITO_FOIL, 30, "You are looking for Sito Foil, and it grows best where", "the ground has been blackened by the living flame."), VOLENCIA_MOSS(2581, Herbs.VOLENCIA_MOSS, 40, "You are looking for Volencia Moss. It clings to rocks", "for its existence. It is difficult to see, so you must", "search for it well."), ROGUES_PURSE(32106, Herbs.ROGUES_PUSE, 50, "It inhabits the darkness of the underground, and grows", "in the caverns to the north. A secret entrance to the", "caverns is set into the northern cliffs, be careful Bwana.") { + PALM_TREE(2577, Herbs.ARDRIGAL, 20, "You are looking for Ardrigal. It is related to the palm", "and grows in its brothers shady profusion."), SITO_FOIL(2579, Herbs.SITO_FOIL, 30, "You are looking for Sito Foil, and it grows best where", "the ground has been blackened by the living flame."), VOLENCIA_MOSS(2581, Herbs.VOLENCIA_MOSS, 40, "You are looking for Volencia Moss. It clings to rocks", "for its existence. It is difficult to see, so you must", "search for it well."), ROGUES_PURSE(32106, Herbs.ROGUES_PURSE, 50, "It inhabits the darkness of the underground, and grows", "in the caverns to the north. A secret entrance to the", "caverns is set into the northern cliffs, be careful Bwana.") { @Override public void search(final Player player, final Scenery object) { final Animation animation = Animation.create(2097); diff --git a/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotionPlugin.java b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotionPlugin.java index d1ac8fcf9..5828060ba 100644 --- a/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotionPlugin.java +++ b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotionPlugin.java @@ -1,5 +1,6 @@ package content.region.karamja.quest.junglepotion; +import content.data.Quests; import core.cache.def.impl.SceneryDefinition; import core.game.dialogue.DialogueInterpreter; import core.game.dialogue.DialoguePlugin; @@ -33,7 +34,7 @@ public final class JunglePotionPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest(JunglePotion.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.JUNGLE_POTION); switch (node.getId()) { case 2584: player.getDialogueInterpreter().open("jogre_dialogue"); diff --git a/Server/src/main/content/region/karamja/quest/junglepotion/TrufitusDialogue.java b/Server/src/main/content/region/karamja/quest/junglepotion/TrufitusDialogue.java index df5e52885..267bef03c 100644 --- a/Server/src/main/content/region/karamja/quest/junglepotion/TrufitusDialogue.java +++ b/Server/src/main/content/region/karamja/quest/junglepotion/TrufitusDialogue.java @@ -1,5 +1,6 @@ package content.region.karamja.quest.junglepotion; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; @@ -48,7 +49,7 @@ public final class TrufitusDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(JunglePotion.NAME); + quest = player.getQuestRepository().getQuest(Quests.JUNGLE_POTION); switch (quest.getStage(player)) { case 0: npc("Greetings Bwana! I am Trufitus Shakaya of the Tai", "Bwo Wannai village."); diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/CrompertyDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/CrompertyDialogue.kt index a8d6ba8ea..54ec527f3 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/CrompertyDialogue.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/CrompertyDialogue.kt @@ -14,6 +14,7 @@ import core.ServerConstants import core.api.playAudio import core.game.world.GameWorld import org.rs09.consts.Sounds +import content.data.Quests @Initializable class CrompertyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { @@ -32,7 +33,7 @@ class CrompertyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Two jobs? That's got to be tough.").also { stage = 5 } 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So, what have you invented?").also { stage = 10 } 3 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Can you teleport me to the Rune Essence?").also { - if(player.questRepository.isComplete("Rune Mysteries")){ + if(player.questRepository.isComplete(Quests.RUNE_MYSTERIES)){ EssenceTeleport.teleport(npc,player) end() } @@ -73,7 +74,7 @@ class CrompertyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu 28 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As you wish.").also { stage = 1000 } 30 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Okey dokey! Ready?").also { - stage = if(player.questRepository.hasStarted("Tribal Totem") && player.questRepository.getStage("Tribal Totem") < 50) { + stage = if(player.questRepository.hasStarted(Quests.TRIBAL_TOTEM) && player.questRepository.getStage(Quests.TRIBAL_TOTEM) < 50) { 35 } else 31 @@ -110,12 +111,12 @@ class CrompertyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu npc.sendChat("Dipsolum sententa sententi!") GameWorld.Pulser.submit(object : Pulse(1) { var counter = 0 - var delivered = player.questRepository.getStage("Tribal Totem") >= 25 + var delivered = player.questRepository.getStage(Quests.TRIBAL_TOTEM) >= 25 override fun pulse(): Boolean { when(counter++){ 2 -> { if(delivered) { - player.questRepository.getQuest("Tribal Totem").setStage(player,30) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player,30) player.properties.teleportLocation = LOCATIONS[1] } else player.properties.teleportLocation = LOCATIONS[0] diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/HoracioDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/HoracioDialogue.kt index da8cd274e..09cc8aea8 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/HoracioDialogue.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/HoracioDialogue.kt @@ -4,12 +4,13 @@ import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable +import content.data.Quests @Initializable class HoracioDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { - if(player.questRepository.hasStarted("Tribal Totem")){ + if(player.questRepository.hasStarted(Quests.TRIBAL_TOTEM)){ npcl(FacialExpression.HAPPY,"It's a fine day to be out in a garden, isn't it? ") stage = 5 } diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/KangaiMauDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/KangaiMauDialogue.kt index 9f35bc75f..1e72fbd22 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/KangaiMauDialogue.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/KangaiMauDialogue.kt @@ -7,15 +7,16 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable class KangaiMauDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { - if(!player.questRepository.hasStarted("Tribal Totem")){ + if(!player.questRepository.hasStarted(Quests.TRIBAL_TOTEM)){ npcl(FacialExpression.HAPPY,"Hello. I'm Kangai Mau of the Rantuki Tribe.") stage = 0 - } else if(isQuestComplete(player, "Tribal Totem")) { + } else if(isQuestComplete(player, Quests.TRIBAL_TOTEM)) { npcl(FacialExpression.HAPPY, "Many greetings esteemed thief.") stage = 40 } @@ -23,7 +24,7 @@ class KangaiMauDialogue(player: Player? = null) : DialoguePlugin(player) { npcl(FacialExpression.ASKING,"Have you got our totem back?") stage = 35 } - else if(player.questRepository.hasStarted("Tribal Totem")){ + else if(player.questRepository.hasStarted(Quests.TRIBAL_TOTEM)){ npcl(FacialExpression.ASKING,"Have you got our totem back?") stage = 30 } @@ -55,8 +56,8 @@ class KangaiMauDialogue(player: Player? = null) : DialoguePlugin(player) { 20 -> playerl(FacialExpression.THINKING,"How can I find Handlemoret's house? Ardougne IS a big place...").also { stage++ } 21 -> npcl(FacialExpression.ANNOYED,"I don't know Ardougne. You tell me.").also { stage++ } 22 -> playerl(FacialExpression.HAPPY,"Ok, I will get it back.").also { - player.questRepository.getQuest("Tribal Totem").start(player) - player.questRepository.getQuest("Tribal Totem").setStage(player, 10) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).start(player) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player, 10) stage++ } 23 -> npcl(FacialExpression.HAPPY,"Best of luck with that adventurer").also { stage = 1000 } @@ -66,8 +67,8 @@ class KangaiMauDialogue(player: Player? = null) : DialoguePlugin(player) { 35 -> playerl(FacialExpression.HAPPY,"Yes I have.").also { stage++ } 36 -> npcl(FacialExpression.HAPPY,"You have??? Many thanks brave adventurer! Here, have some freshly cooked Karamjan fish, caught specially by my tribe.").also { stage++ } 37 -> sendDialogue("You hand over the totem").also { - if(!isQuestComplete(player, "Tribal Totem") && removeItem(player, Items.TOTEM_1857)) { - player.questRepository.getQuest("Tribal Totem").finish(player) + if(!isQuestComplete(player, Quests.TRIBAL_TOTEM) && removeItem(player, Items.TOTEM_1857)) { + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).finish(player) stage = 1000 } else { stage = 1000 diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/RPDTEmployeeDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/RPDTEmployeeDialogue.kt index 6a4c0cbf2..2bd933011 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/RPDTEmployeeDialogue.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/RPDTEmployeeDialogue.kt @@ -5,12 +5,13 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs +import content.data.Quests @Initializable class RPDTEmployeeDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npcl(FacialExpression.HAPPY,"Welcome to R.P.D.T.!") - stage = if(player.questRepository.getStage("Tribal Totem") == 20){ + stage = if(player.questRepository.getStage(Quests.TRIBAL_TOTEM) == 20){ 5 }else 0 return true @@ -22,7 +23,7 @@ class RPDTEmployeeDialogue(player: Player? = null) : DialoguePlugin(player) { 5 -> playerl(FacialExpression.ASKING,"So, when are you going to deliver this crate?").also { stage++ } 6 -> npcl(FacialExpression.THINKING,"Well... I guess we could do it now...").also { - player.questRepository.getQuest("Tribal Totem").setStage(player,25) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player,25) stage = 1000 } diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt index dbb4c6096..0fdbf616f 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt @@ -8,6 +8,7 @@ import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType +import content.data.Quests class TribalTotemListeners : InteractionListener { @@ -22,19 +23,21 @@ class TribalTotemListeners : InteractionListener { override fun defineListeners() { on(frontDoor, IntType.SCENERY, "Open"){ player, door -> - if(player.questRepository.getStage("Tribal Totem") >= 35){ + if(player.questRepository.getStage(Quests.TRIBAL_TOTEM) >= 35){ core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,door.asScenery()) } - sendMessage(player,"The door is locked shut.") + else { + sendMessage(player,"The door is locked shut.") + } return@on true } on(realCrate, IntType.SCENERY, "Investigate"){ player, node -> - if(player.questRepository.getStage("Tribal Totem") in 1..19 && !player.inventory.containsAtLeastOneItem(Items.ADDRESS_LABEL_1858)){ + if(player.questRepository.getStage(Quests.TRIBAL_TOTEM) in 1..19 && !player.inventory.containsAtLeastOneItem(Items.ADDRESS_LABEL_1858)){ sendDialogue(player,"There is a label on this crate. It says; To Lord Handelmort, Handelmort Mansion Ardogune.You carefully peel it off and take it.") - addItem(player,Items.ADDRESS_LABEL_1858,1) + addItemOrDrop(player,Items.ADDRESS_LABEL_1858,1) } - else if(player.questRepository.getStage("Tribal Totem") in 1..19 && player.inventory.containsAtLeastOneItem(Items.ADDRESS_LABEL_1858)){ + else if(player.questRepository.getStage(Quests.TRIBAL_TOTEM) in 1..19 && player.inventory.containsAtLeastOneItem(Items.ADDRESS_LABEL_1858)){ sendDialogue(player,"There was a label on this crate, but it's gone now since you took it!") } return@on true @@ -48,7 +51,7 @@ class TribalTotemListeners : InteractionListener { onUseWith(IntType.SCENERY,label,wizCrate){ player, used, with -> sendDialogue(player,"You carefully place the delivery address label over the existing label, covering it completely.") removeItem(player,label) - player.questRepository.getQuest("Tribal Totem").setStage(player,20) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player,20) return@onUseWith true } @@ -95,7 +98,8 @@ class TribalTotemListeners : InteractionListener { on(openChest, IntType.SCENERY, "Search"){ player, node -> if(!player.inventory.containsAtLeastOneItem(Items.TOTEM_1857)){ sendDialogue(player,"Inside the chest you find the tribal totem.") - addItem(player,Items.TOTEM_1857) + addItemOrDrop(player,Items.TOTEM_1857) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player,35) } else{ sendDialogue(player,"Inside the chest you don't find anything because you already took the totem!") diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt index ea707ce49..13c07751b 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt @@ -1,7 +1,6 @@ package content.region.karamja.quest.tribaltotem import core.api.rewardXP -import core.game.content.quest.fremtrials.FremennikTrials import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills @@ -9,9 +8,10 @@ import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable -class TribalTotem : Quest("Tribal Totem",126,125,1,200,0,1,5){ +class TribalTotem : Quest(Quests.TRIBAL_TOTEM,126,125,1,200,0,1,5){ class SkillRequirement(val skill: Int?, val level: Int?) val requirements = arrayListOf() @@ -19,7 +19,7 @@ class TribalTotem : Quest("Tribal Totem",126,125,1,200,0,1,5){ override fun drawJournal(player: Player?, stage: Int) { super.drawJournal(player, stage) var line = 11 - val started = player?.questRepository?.getStage("Tribal Totem")!! > 0 + val started = player?.questRepository?.getStage(Quests.TRIBAL_TOTEM)!! > 0 if(!started){ line(player,"I can start this quest by speaking to !!Kangai Mau?? in",line++) diff --git a/Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java b/Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java index 5176ad97b..e06e1ad09 100644 --- a/Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java +++ b/Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java @@ -12,6 +12,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** @@ -41,7 +42,7 @@ public class BrokenCartBypass extends OptionHandler { }); } public final boolean handle(Player player, Node node, String options){ - if (!hasRequirement(player, "Shilo Village")) + if (!hasRequirement(player, Quests.SHILO_VILLAGE)) return true; Location location = new Location(0,0); Location playerloc = new Location(player.getLocation().getX(),player.getLocation().getY()); diff --git a/Server/src/main/content/region/karamja/shilo/handlers/ShiloCart.kt b/Server/src/main/content/region/karamja/shilo/handlers/ShiloCart.kt index b432a0cf9..1b15bbcab 100644 --- a/Server/src/main/content/region/karamja/shilo/handlers/ShiloCart.kt +++ b/Server/src/main/content/region/karamja/shilo/handlers/ShiloCart.kt @@ -14,6 +14,7 @@ import core.tools.END_DIALOGUE import org.rs09.consts.Components import org.rs09.consts.Items import org.rs09.consts.NPCs +import content.data.Quests class ShiloCart : InteractionListener { @@ -67,7 +68,7 @@ class ShiloCart : InteractionListener { class CartQuickPay : DialogueFile(){ override fun handle(interfaceId: Int, buttonId: Int) { - if (!hasRequirement(player!!, "Shilo Village")) return; + if (!hasRequirement(player!!, Quests.SHILO_VILLAGE)) return; val shilo = npc?.id == 510; when (stage) { 0 -> if(inInventory(player!!,Items.COINS_995,10)){ @@ -103,7 +104,7 @@ class CartQuickPay : DialogueFile(){ class CartTravelDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { - if (!hasRequirement(player!!, "Shilo Village")) return; + if (!hasRequirement(player!!, Quests.SHILO_VILLAGE)) return; val shilo = npc?.id == 510; when (stage) { 0 -> npcl("I am offering a cart ride to " + (if (shilo) "Shilo Village" else "Brimhaven") + " if you're interested? It will cost 10 gold coins. Is that Ok?").also { stage++ } @@ -117,4 +118,4 @@ class CartTravelDialogue : DialogueFile(){ 4 -> openDialogue(player!!,CartQuickPay(),npc!!) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarDialogue.kt b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarDialogue.kt new file mode 100644 index 000000000..bac301a6a --- /dev/null +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarDialogue.kt @@ -0,0 +1,49 @@ +package content.region.karamja.tzhaar.handlers + +import core.api.openNpcShop +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class TzHaarDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HALF_GUILTY, "Can I help you JalYt-Ket-${player.username}?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "What do you have to trade?", 10, true), + Topic(FacialExpression.HALF_GUILTY, "What did you call me?", 20), + Topic(FacialExpression.HALF_GUILTY, "No I'm fine thanks.", END_DIALOGUE), + ) + 10 -> end().also { openNpcShop(player, npc.id) } + 20 -> npcl(FacialExpression.HALF_GUILTY, "Are you not JalYt-Ket?").also { stage++ } + 21 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "What's a 'JalYt-Ket'?", 22), + Topic(FacialExpression.HALF_GUILTY, "I guess so...", 25), + Topic(FacialExpression.HALF_GUILTY, "No I'm not!", END_DIALOGUE) + ) + + 22 -> npcl(FacialExpression.HALF_GUILTY, "That what you are... you tough and strong no?").also { stage++ } + 23 -> playerl(FacialExpression.HALF_GUILTY, "Well yes I suppose I am...").also { stage++ } + 24 -> npcl(FacialExpression.HALF_GUILTY, "Then you JalYt-Ket!").also { stage = END_DIALOGUE } + + 25 -> npcl(FacialExpression.HALF_GUILTY, "Well then, no problems.").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TZHAAR_HUR_TEL_2620, NPCs.TZHAAR_HUR_LEK_2622, NPCs.TZHAAR_MEJ_ROH_2623) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJah.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJah.java deleted file mode 100644 index 09653c566..000000000 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJah.java +++ /dev/null @@ -1,313 +0,0 @@ -package content.region.karamja.tzhaar.handlers; - -import core.game.dialogue.DialogueInterpreter; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; -import core.game.world.GameWorld; - -/** - * Handles the TzHaarMejJal dialogue. - * @author 'Vexia - * @author Empathy - * @author Logg - */ -@Initializable -public class TzHaarMejJah extends DialoguePlugin { - private static final Item APPEARANCE_FEE = new Item(6529, 8000); // 8000 tokkul, about the same as you get from failing jad - - public TzHaarMejJah() { - - } - - public TzHaarMejJah(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new TzHaarMejJah(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You want help JalYt-Ket-" + player.getUsername() + "?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (GameWorld.getSettings().getJad_practice_enabled()) { - if (player.getAttribute("fc_practice_jad", false)) { - interpreter.sendOptions("Select an Option", player.getInventory().containItems(6570) ? "I have a fire cape here." : "What is this place?", "What did you call me?", "About my challenge...", "No I'm fine thanks."); - } else { - interpreter.sendOptions("Select an Option", player.getInventory().containItems(6570) ? "I have a fire cape here." : "What is this place?", "What did you call me?", "I want to challenge Jad directly.", "No I'm fine thanks."); - } - } else { - interpreter.sendOptions("Select an Option", player.getInventory().containItems(6570) ? "I have a fire cape here." : "What is this place?", "What did you call me?", "No I'm fine thanks."); - } - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - if (player.getInventory().containItems(6570)) { - interpreter.open(DialogueInterpreter.getDialogueKey("firecape-exchange"), npc); - break; - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What is this place?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What did you call me?"); - stage = 20; - break; - case 3: - if (GameWorld.getSettings().getJad_practice_enabled()) { - if (player.getAttribute("fc_practice_jad", false)) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "About my challenge..."); - stage = 64; - } else { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "The challenge is too long.", "I want to challenge Jad directly."); - stage = 50; - } - } else { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No I'm fine thanks."); - stage = 30; - } - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No I'm fine thanks."); - stage = 30; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "This is the fight caves, TzHaar-Xil made it for practice,", "but many JalYt come here to fight too.", "Just enter the cave and make sure you're prepared."); - stage = 11; - break; - case 11: - interpreter.sendOptions("Select an Option", "Are there any rules?", "Ok thanks."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Are there any rules?"); - stage = 14; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok thanks."); - stage = 13; - break; - } - break; - case 13: - end(); - break; - case 14: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Rules? Survival is the only rule in there."); - stage = 15; - break; - case 15: - interpreter.sendOptions("Select an Option", "Do I win anything?", "Sounds good."); - stage = 16; - break; - case 16: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do I win anything?"); - stage = 17; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sounds good."); - stage = 13; - break; - - } - break; - case 17: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You ask a lot of questions.", "Might give you TokKul if you last long enough."); - stage = 18; - break; - case 18: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "..."); - stage = 19; - break; - case 19: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Before you ask, TokKul is like your Coins."); - stage = 500; - break; - case 500: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Gold is like you JalYt, soft and easily broken, we use", "hard rock forged in fire like TzHaar!"); - stage = 501; - break; - case 501: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Are you not JalYt-Ket?"); - stage = 21; - break; - case 21: - interpreter.sendOptions("Select an Option", "What's a 'JalYt-Ket'?", "I guess so...", "No I'm not!"); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's a 'JalYt-Ket'?"); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I guess so..."); - stage = 200; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No I'm not!"); - stage = 300; - break; - } - break; - case 100: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That what you are... you tough and strong no?"); - stage = 101; - break; - case 101: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well yes I suppose I am..."); - stage = 102; - break; - case 102: - end(); - break; - case 200: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I guess so...."); - stage = 201; - break; - case 201: - end(); - break; - case 300: - end(); - break; - case 30: - end(); - break; - - case 50: - interpreter.sendDialogues(npc, FacialExpression.DISGUSTED_HEAD_SHAKE, "I thought you strong and tough", "but you want skip endurance training?"); - stage = 57; - break; - case 57: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Maybe you not JalYt-Ket afterall."); - stage = 58; - break; - case 58: - interpreter.sendOptions("Select an Option", "I don't have time for it, man.", "No, I'm JalYt-Ket!"); - stage = 51; - break; - case 51: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't have time for it, man."); - stage = 52; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I'm JalYt-Ket! I swear!", "I'll do the training properly."); - stage = 30; - break; - } - break; - case 52: - interpreter.sendDialogues(npc, FacialExpression.DISGUSTED_HEAD_SHAKE, "JalYt, you know you not get reward","if you not do training properly, ok?"); - stage = 56; - break; - case 56: - interpreter.sendOptions("Select an Option", "That's okay, I don't need a reward.", "Oh, nevermind then."); - stage = 53; - break; - case 53: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's okay, I don't need a reward."); - stage = 54; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, nevermind then."); - stage = 30; - break; - } - break; - case 54: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I just wanna fight the big guy."); - stage = 55; - break; - case 55: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Okay JalYt.","TzTok-Jad not show up for just anyone."); - stage = 59; - break; - case 59: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "You give 8000 TokKul, TzTok-Jad know you serious.", "You get it back if you victorious."); - stage = 60; - break; - case 60: - interpreter.sendOptions("Select an Option", "That's fair, here's 8000 TokKul.", "I don't have that much on me, but I'll go get it.", "TzTok-Jad must be old and tired to not just accept my challenge."); - stage = 61; - case 61: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's fair, here's 8000 TokKul."); - if (!player.getInventory().containsItem(APPEARANCE_FEE)) { - stage = 62; - break; - } - if (player.getInventory().remove(APPEARANCE_FEE)) { - stage = 69; - } else { - stage = 62; - } - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't have that much on me, but I'll go get it."); - stage = 30; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "TzTok-Jad must be old and tired", "to not just accept my challenge."); - stage = 63; - break; - } - break; - case 62: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "JalYt, you not have the TokKul.", "You come back when you are serious."); - stage = 30; - break; - case 63: - interpreter.sendDialogues(npc, FacialExpression.ANGRY, "JalYt-Mor, you the old and tired one.", "You the one not want to do proper training."); - stage = 30; - break; - case 64: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "TzTok-Jad is waiting for you.", "Do not make TzTok-Jad wait long."); - stage = 30; - break; - case 69: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Okay JalYt. Enter cave when you are prepared.", "You find TzTok-Jad waiting for JalYt challenger."); - player.setAttribute("fc_practice_jad", true); - stage = 30; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("tzhaar-mej"), 2617 }; - } -} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJal.kt b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJal.kt new file mode 100644 index 000000000..a0b865ca7 --- /dev/null +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJal.kt @@ -0,0 +1,167 @@ +package content.region.karamja.tzhaar.handlers + +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.GameWorld.settings +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Handles the TzHaarMejJal dialogue. + * @author 'Vexia + * @author Empathy + * @author Logg + */ +@Initializable +class TzHaarMejJal(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HALF_GUILTY, "You want help JalYt-Ket-${player.username}?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> showTopics( + if (inInventory(player, Items.FIRE_CAPE_6570)) { + Topic(FacialExpression.HALF_GUILTY, "I have a fire cape here.", 100) + } else { + Topic(FacialExpression.HALF_GUILTY, "What is this place?", 10) + }, + Topic(FacialExpression.HALF_GUILTY, "What did you call me?", 20), + IfTopic(FacialExpression.HALF_GUILTY, "About my challenge...", 50, getAttribute(player, "fc_practice_jad", false) && settings!!.jad_practice_enabled), + IfTopic("I want to challenge Jad directly.", 30, !getAttribute(player, "fc_practice_jad", false) && settings!!.jad_practice_enabled, skipPlayer = true), + Topic(FacialExpression.HALF_GUILTY, "No I'm fine thanks.", END_DIALOGUE) + ) + + 10 -> npc(FacialExpression.HALF_GUILTY, + "This is the fight caves, TzHaar-Xil made it for practice,", + "but many JalYt come here to fight too.", + "Just enter the cave and make sure you're prepared." + ).also { stage++ } + + 11 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "Are there any rules?", 12), + Topic(FacialExpression.HALF_GUILTY, "Ok thanks.", END_DIALOGUE) + ) + + 12 -> npcl(FacialExpression.HALF_GUILTY, "Rules? Survival is the only rule in there.").also { stage++ } + 13 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "Do I win anything?", 14), + Topic(FacialExpression.HALF_GUILTY, "Sounds good.", END_DIALOGUE) + ) + + 14 -> npc(FacialExpression.HALF_GUILTY, + "You ask a lot of questions.", + "Might give you TokKul if you last long enough." + ).also { stage++ } + 15 -> playerl(FacialExpression.HALF_GUILTY, "...").also { stage ++ } + 16 -> npcl(FacialExpression.HALF_GUILTY, "Before you ask, TokKul is like your Coins.").also { stage++ } + 17 -> npc(FacialExpression.HALF_GUILTY, + "Gold is like you JalYt, soft and easily broken, we use", + "hard rock forged in fire like TzHaar!" + ).also{ stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.HALF_GUILTY, "Are you not JalYt-Ket?").also { stage++ } + 21 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "What's a 'JalYt-Ket'?", 22), + Topic(FacialExpression.HALF_GUILTY, "I guess so...", END_DIALOGUE), + Topic(FacialExpression.HALF_GUILTY, "No I'm not!", END_DIALOGUE) + ) + + 22 -> npcl(FacialExpression.HALF_GUILTY, "That what you are... you tough and strong no?").also { stage++ } + 23 -> playerl(FacialExpression.HALF_GUILTY, "Well yes I suppose I am...").also { stage = END_DIALOGUE } + + 30 -> player(FacialExpression.HALF_GUILTY, + "The challenge is too long.", + "I want to challenge Jad directly." + ).also { stage++ } + 31 -> npc(FacialExpression.DISGUSTED_HEAD_SHAKE, + "I thought you strong and tough", + "but you want skip endurance training?" + ).also { stage++ } + 32 -> npcl(FacialExpression.NEUTRAL, "Maybe you not JalYt-Ket afterall.").also { stage++ } + 33 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "I don't have time for it, man.", 35), + Topic("No, I'm JalYt-Ket!", 34, skipPlayer = true) + ) + + 34 -> player(FacialExpression.HALF_GUILTY, + "No, I'm JalYt-Ket! I swear!", + "I'll do the training properly." + ).also { stage = END_DIALOGUE } + + 35 -> npc(FacialExpression.DISGUSTED_HEAD_SHAKE, + "JalYt, you know you not get reward", + "if you not do training properly, ok?" + ).also { stage++ } + 36 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "That's okay, I don't need a reward.", 37), + Topic(FacialExpression.HALF_GUILTY, "Oh, nevermind then.", END_DIALOGUE) + ) + + 37 -> playerl(FacialExpression.NEUTRAL, "I just wanna fight the big guy.").also { stage++ } + 38 -> npc(FacialExpression.NEUTRAL, + "Okay JalYt.", + "TzTok-Jad not show up for just anyone." + ).also { stage++ } + 39 -> npc(FacialExpression.NEUTRAL, + "You give 8000 TokKul, TzTok-Jad know you serious.", + "You get it back if you victorious." + ).also { stage++ } + 40 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "That's fair, here's 8000 TokKul.", 41), + Topic(FacialExpression.HALF_GUILTY, "I don't have that much on me, but I'll go get it.", END_DIALOGUE), + Topic("TzTok-Jad must be old and tired to not just accept my challenge.", 42, skipPlayer = true) + ) + + 41 -> if (removeItem(player, Item(Items.TOKKUL_6529, 8000))) { + npc(FacialExpression.NEUTRAL, + "Okay JalYt. Enter cave when you are prepared.", + "You find TzTok-Jad waiting for JalYt challenger." + ) + .also { setAttribute(player, "fc_practice_jad", true) } + .also { stage = END_DIALOGUE } + } else npc(FacialExpression.NEUTRAL, + "JalYt, you not have the TokKul.", + "You come back when you are serious." + ).also { stage = END_DIALOGUE } + + 42 -> player(FacialExpression.HALF_GUILTY, + "TzTok-Jad must be old and tired", + "to not just accept my challenge." + ).also { stage++ } + 43 -> npc(FacialExpression.ANGRY, + "JalYt-Mor, you the old and tired one.", + "You the one not want to do proper training." + ).also { stage = END_DIALOGUE } + + 50 -> npc(FacialExpression.NEUTRAL, + "TzTok-Jad is waiting for you.", + "Do not make TzTok-Jad wait long." + ).also { stage = END_DIALOGUE } + + 100 -> sendDialogueOptions(player, "Sell your fire cape?", "Yes, sell it for 8,000 TokKul.", "No, keep it.").also { stage++ } + 101 -> when (buttonId) { + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hand your cape here, young JalYte.").also { stage++ } + 2 -> end() + } + 102 -> end().also { if (removeItem(player, Items.FIRE_CAPE_6570)) addItem(player, Items.TOKKUL_6529, 8000) } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return TzHaarMejJal(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TZHAAR_MEJ_JAL_2617) + } +} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzRekJadNPC.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzRekJadNPC.java deleted file mode 100644 index 2c5993906..000000000 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzRekJadNPC.java +++ /dev/null @@ -1,263 +0,0 @@ -package content.region.karamja.tzhaar.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.game.dialogue.DialogueInterpreter; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.repository.Repository; -import core.plugin.Plugin; -import core.plugin.ClassScanner; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Handles everything in regards to jad pet. - * @author Empathy - * - */ -@Initializable -public class TzRekJadNPC extends OptionHandler { - - /** - * The tzhaar mej id. - */ - private static final int TZHAAR_MEJ_ID = 2617; - - /** - * The firecape item. - */ - private static final Item FIRECAPE = new Item(6570); - - /** - * The tokkul item. - */ - private static final Item TOKKUL = new Item(6529); - - /** - * The jad item. - */ - private static final Item JAD_PET = new Item(14828); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(TZHAAR_MEJ_ID).getHandlers().put("option:exchange fire cape", this); - ClassScanner.definePlugins(new TzhaarMejJalDialogue(), new TzRekJadDialogue()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "exchange fire cape": - player.getDialogueInterpreter().open(DialogueInterpreter.getDialogueKey(player.getInventory().containsItem(FIRECAPE) ? "firecape-exchange" : "tzhaar-mej"), node.asNpc()); - break; - } - return true; - } - - /** - * Handles the TzhaarMejJal Dialogue to gamble for jad pet. - * @author Empathy - * - */ - public final class TzhaarMejJalDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TzhaarMejJalDialogue} {@code Object}. - */ - public TzhaarMejJalDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TzhaarMejJalDialogue} {@code Object}. - * - * @param player the player. - */ - public TzhaarMejJalDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TzhaarMejJalDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I have a fire cape here."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Sell your fire cape?", "Yes, sell it for 8,000 TokKul.", "No, keep it.", "Bargain for TzRek-Jad."); - stage = 1; - break; - case 1: - switch(buttonId) { - case 1: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, player.getInventory().containsItems(FIRECAPE) ? "Hand your cape here, young JalYte." : "You not have firecape, JalYt."); - stage = 10; - break; - case 2: - end(); - break; - case 3: - interpreter.sendOptions("Sacrifice your firecape?", "Yes, I know I won't get my cape back.", "No, I like my cape!"); - stage = 20; - break; - } - break; - case 10: - if (player.getInventory().containsItem(FIRECAPE)) { - if (player.getInventory().remove(FIRECAPE)) { - TOKKUL.setAmount(8000); - player.getInventory().add(TOKKUL); - TOKKUL.setAmount(1); - } - } - end(); - break; - case 20: - switch (buttonId) { - case 1: - if (player.hasItem(JAD_PET)) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Best you train one TzRek-Jad only."); - stage = 21; - break; - } - if (player.getFamiliarManager().hasFamiliar()) { - if (player.getFamiliarManager().getFamiliar().getId() == 8650) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Best you train one TzRek-Jad only."); - stage = 21; - break; - } - } - if (player.getInventory().remove(FIRECAPE)) { - int r = RandomFunction.getRandom(200); - if (r == 1) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You lucky. Better train him good else TzTok-Jad find", "you, JalYt."); - if (!player.getFamiliarManager().hasFamiliar()) { - player.getFamiliarManager().summon(JAD_PET, true); - player.sendNotificationMessage("You have a funny feeling like you're being followed."); - } else if (player.getInventory().freeSlots() > 0) { - player.getInventory().add(JAD_PET); - player.sendNotificationMessage("You feel something weird sneaking into your backpack."); - } - Repository.sendNews(player.getUsername() + " now commands a miniature TzTok-Jad!"); - } else { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You not lucky. Maybe next time, JalYt."); - } - } - stage = 21; - break; - case 2: - end(); - break; - } - break; - case 21: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("firecape-exchange") }; - } - } - - /** - * Handles the TzRekJad dialogue. - * @author Empathy - * - */ - public final class TzRekJadDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TzRekJadDialogue} {@code Object}. - */ - public TzRekJadDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TzRekJadDialogue} {@code Object}. - * - * @param player the player. - */ - public TzRekJadDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TzRekJadDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - int i = RandomFunction.getRandom(1); - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, i == 1 ? "Do you miss your people?" : "Are you hungry?"); - stage = (i == 1 ? 0 : 5); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Mej-TzTok-Jad Kot-Kl!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No.. I don't think so."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Jal-Zek Kl?"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, no, I wouldn't hurt you."); - stage = 4; - break; - case 4: - end(); - break; - case 5: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Kl-Kra!"); - stage = 6; - break; - case 6: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ooookay..."); - stage = 4; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8650 }; - } - } - -} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java deleted file mode 100644 index b4b23b029..000000000 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java +++ /dev/null @@ -1,198 +0,0 @@ -package content.region.karamja.tzhaar.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.activity.ActivityManager; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used for tzhaar city. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class TzhaarCityPlugin extends OptionHandler { - - /** - * Represents the locations to use. - */ - private static final Location[] LOCATIONS = new Location[] { Location.create(2480, 5175, 0), Location.create(2866, 9571, 0) }; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(31284).getHandlers().put("option:enter", this);// karamja - // cave. - SceneryDefinition.forId(9359).getHandlers().put("option:enter", this);// tzhaar - // exit - SceneryDefinition.forId(9356).getHandlers().put("option:enter", this); - SceneryDefinition.forId(9369).getHandlers().put("option:pass", this); - new TzhaarDialogue().init(); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - int id = ((Scenery) node).getId(); - switch (option) { - case "enter": - switch (id) { - case 31284: - player.getProperties().setTeleportLocation(LOCATIONS[0]); - break; - case 9359: - player.getProperties().setTeleportLocation(LOCATIONS[1]); - break; - case 9356: - if (player.getFamiliarManager().hasFamiliar()) { - player.getPacketDispatch().sendMessage("You can't enter this with a follower."); - break; - } - ActivityManager.start(player, "fight caves", false); - break; - } - break; - case "pass": - switch (id) { - case 9369: - ActivityManager.start(player, "fight pits", false); - break; - } - break; - } - return true; - } - - /** - * Represents the dialogue plugin used for the tzhaar npcs. - * @author 'Vexia - * @version 1.0 - */ - public static final class TzhaarDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TzhaarDialogue} {@code Object}. - */ - public TzhaarDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TzhaarDialogue} {@code Object}. - * @param player the player. - */ - public TzhaarDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TzhaarDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Can I help you JalYt-Ket-" + player.getUsername() + "?"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("What do you have to trade?", "What did you call me?", "No I'm fine thanks."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - player("What did you call me?"); - stage = 20; - break; - case 3: - player("No I'm fine thanks."); - stage = 30; - break; - } - break; - case 10: - break; - case 20: - npc("Are you not JalYt-Ket?"); - stage = 21; - break; - case 21: - options("What's a 'JalYt-Ket'?", "I guess so...", "No I'm not!"); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - player("What's a 'JalYt-Ket'?"); - stage = 100; - break; - case 2: - player("I guess so..."); - stage = 120; - break; - case 3: - player("No I'm not!"); - stage = 130; - break; - } - break; - case 100: - npc("That what you are... you tough and strong no?"); - stage = 101; - break; - case 101: - player("Well yes I suppose I am..."); - stage = 102; - break; - case 102: - npc("Then you JalYt-Ket!"); - stage = 103; - break; - case 103: - end(); - break; - case 120: - npc("Well then, no problems."); - stage = 121; - break; - case 121: - end(); - break; - case 130: - end(); - break; - case 23: - end(); - break; - case 30: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2620, 2622, 2623 }; - } - - } -} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java index e1baca76a..7559b9362 100644 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java @@ -62,7 +62,7 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { * @param player The player. */ public TzhaarFightCavesPlugin(Player player) { - super("fight caves", true, true, true, ZoneRestriction.CANNON, ZoneRestriction.RANDOM_EVENTS); + super("fight caves", true, true, true, ZoneRestriction.CANNON, ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.FOLLOWERS); super.player = player; } diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarListeners.kt b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarListeners.kt new file mode 100644 index 000000000..f785ecd0b --- /dev/null +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarListeners.kt @@ -0,0 +1,36 @@ +package content.region.karamja.tzhaar.handlers + +import core.api.sendNPCDialogueLines +import core.api.teleport +import core.game.activity.ActivityManager +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.NPCs + +class TzhaarListeners : InteractionListener { + override fun defineListeners() { + on(intArrayOf(31284, 9359, 9356), IntType.SCENERY, "enter") { player, node -> + when (node.id) { + 31284 -> teleport(player, Location.create(2480, 5175, 0)) + 9359 -> teleport(player, Location.create(2866, 9571, 0)) + 9356 -> { + if (player.familiarManager.hasFamiliar()) { + sendNPCDialogueLines(player, NPCs.TZHAAR_MEJ_JAL_2617, FacialExpression.ANGRY, false, "No Kimit-Zil in the cave! This is a fight for YOU,", "not your friends!") + } else ActivityManager.start(player, "fight caves", false) + } + } + return@on true + } + + on(9369, IntType.SCENERY, "pass") { player, _ -> + ActivityManager.start(player, "fight pits", false) + return@on true + } + + on(31292, IntType.SCENERY, "go-through") { _, _ -> + return@on false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/PadulahDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/PadulahDialogue.kt new file mode 100644 index 000000000..2eeed97c8 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/PadulahDialogue.kt @@ -0,0 +1,66 @@ +package content.region.misc.apeatoll.dialogue.marim + +import core.api.openDialogue +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class PadulahDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PadulahDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return PadulahDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.PADULAH_1447) + } +} + +class PadulahDialogueFile: DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.defaultDialogue().npcl( + "What do you want?" + ).playerl( + FacialExpression.NEUTRAL, + "Oh, nothing in particular really." + ).npcl( + "Well stop distracting me then. I'm meant to be guarding this sacred statue from the temple of Marimbo." + ).options().let { optionsBuilder -> optionsBuilder.option("Where is the temple of Marimbo?") + .npcl( + "You're not from around here are you?" + ).playerl( + FacialExpression.NEUTRAL, + "Actually I'm not. I'm a visitor from foreign lands." + ).npcl( + "Very well, the temple is to the east of the village." + ).end() + optionsBuilder.option("What is the statue of? ") + .npcl( + "It's of Marimbo, you cretin!" + ).playerl( + FacialExpression.NEUTRAL, + "Ah yes. How stupid of me not to see the likeness." + ).end() + optionsBuilder.option("I'll be back later.") + .npcl( + "I wouldn't count on it." + ).playerl( + FacialExpression.WORRIED, + "What?!" + ).npcl( + "Oh, nothing." + ).end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/SolihibDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/SolihibDialogue.kt new file mode 100644 index 000000000..381e3e856 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/SolihibDialogue.kt @@ -0,0 +1,42 @@ +package content.region.misc.apeatoll.dialogue.marim + +import content.data.Quests +import core.api.hasRequirement +import core.api.openNpcShop +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +class SolihibDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.SOLIHIB_1433, IntType.NPC, "talk-to") { player, node -> + if (!hasRequirement(player, Quests.MONKEY_MADNESS)) return@on true + DialogueLabeller.open(player, SolihibDialogueLabellerFile(), node as NPC) + return@on true + } + on(NPCs.SOLIHIB_1433, IntType.NPC, "trade") { player, _ -> + if (!hasRequirement(player, Quests.MONKEY_MADNESS)) return@on true + openNpcShop(player, NPCs.SOLIHIB_1433) + return@on true + } + } + + class SolihibDialogueLabellerFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SOLIHIB_1433) + + npc(ChatAnim.FRIENDLY, "Would you like to buy or sell some food?") + options( + DialogueOption("trade", "Yes, please."), + DialogueOption("nowhere", "No, thanks.") + ) + label("trade") + exec { player, _ -> openNpcShop(player, NPCs.SOLIHIB_1433) } + goto("nowhere") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java b/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java index beaaccd03..15e7f8803 100644 --- a/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java +++ b/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java @@ -8,6 +8,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the dialogue plugin used for a cave monk. @@ -52,7 +53,7 @@ public final class CaveMonk extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Lost City"); + quest = player.getQuestRepository().getQuest(Quests.LOST_CITY); switch (quest.getStage(player)) { case 0: case 10: diff --git a/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt b/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt index c650a958d..4f177c848 100644 --- a/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt +++ b/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt @@ -1,59 +1,63 @@ package content.region.misc.entrana.dialogue -import core.api.addItemOrDrop +import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE import org.rs09.consts.Items import org.rs09.consts.NPCs -/** - * @author qmqz - */ - @Initializable class HighPriestEntranaDialogue(player: Player? = null) : DialoguePlugin(player){ - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - - if (!player.questRepository.isComplete("Heroes' Quest")) { - npc(FacialExpression.FRIENDLY, "Many greetings. Welcome to our fair island.").also { stage = 10 } - } else { - options("Have you seen a pair of ice gloves?", "Ask about Entrana").also { stage = 5 } - } - - - return true - } - override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(FacialExpression.FRIENDLY, "and devoted the island to those who wish peace for the world.").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "The inhabitants of this island are mostly monks who spend their time meditating on Saradomin's ways.").also { stage++ } - 3 -> npcl(FacialExpression.FRIENDLY, "Of course, there are now more pilgrims to this holy site, since Saradomin defeated Zamorak in the battle of Lumbridge. It is good that so many see Saradomin's true glory!").also { stage = 99 } - - 5 -> when (buttonId) { - 1 -> npcl(FacialExpression.FRIENDLY, "By which you mean the pair of mythical gloves you stole from the cold dead body of the Queen of the Ice?").also { stage = 20 } - 2 -> npcl(FacialExpression.FRIENDLY,"You are standing on the holy island of Entrana. It was here that Saradomin first stepped upon Gielinor. In homage to Saradomin's first arrival, we have built a great church,").also { stage = 1 } + // Ice gloves were NOT reclaimable till 2017. + START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Many greetings. Welcome to our fair island.").also { + if (inInventory(player, Items.SILVER_POT_4658) || + inInventory(player, Items.SILVER_POT_4660) || + inInventory(player, Items.SILVER_POT_4662) || + inInventory(player, Items.SILVER_POT_4664) || + inInventory(player, Items.SILVER_POT_4666)) { + stage = 6 + } else { + stage = 1 + } } + 1 -> npcl(FacialExpression.FRIENDLY, "You are standing on the holy island of Entrana. It was here that Saradomin first stepped upon Gielinor.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "In homage to Saradomin's first arrival, we have built a great church, and devoted the island to those who wish peace for the world.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "The inhabitants of this island are mostly monks who spend their time meditating on Saradomin's ways.").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, "Of course, there are now more pilgrims to this holy site, since Saradomin defeated Zamorak in the battle of Lumbridge.").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "It is good that so many see Saradomin's true glory!").also { stage = END_DIALOGUE } - 10 -> npc(FacialExpression.FRIENDLY, "Enjoy our stay here. May it be spiritually uplifting!").also { stage = 99 } - - 20 -> player(FacialExpression.SUSPICIOUS, "Er...").also { stage++ } - 21 -> npcl(FacialExpression.ANNOYED, "The gloves that you acquired to aid you in plucking the Entranan firebird? Another victim of your murderous nature, I should add.").also { stage++ } - 22 -> npcl(FacialExpression.ANNOYED, "The ice gloves that, from context, I would assume you have lost, if not carelessly discarded?").also { stage++ } - - 23 -> player(FacialExpression.FRIENDLY, "Those ice gloves, yes.").also { stage++ } - 24 -> sendDialogue("The high priest of Entrana shivers as he hands you your lost ice gloves.").also { - addItemOrDrop(player, Items.ICE_GLOVES_1580, 1) - stage++ + 6 -> playerl(FacialExpression.FRIENDLY, "Hi, I was wondering, can you quickly bless this for me?").also { stage++ } + 7 -> npc(FacialExpression.FRIENDLY, "A somewhat strange request, but I see no harm in it.", "There you go.", "May Saradomin walk with you.").also { + if (inInventory(player, Items.SILVER_POT_4658)) { + if (removeItem(player, Items.SILVER_POT_4658)) { + addItemOrDrop(player, Items.BLESSED_POT_4659) + } + } else if (inInventory(player, Items.SILVER_POT_4660)) { + if (removeItem(player, Items.SILVER_POT_4660)) { + addItemOrDrop(player, Items.BLESSED_POT_4661) + } + } else if (inInventory(player, Items.SILVER_POT_4662)) { + if (removeItem(player, Items.SILVER_POT_4662)) { + addItemOrDrop(player, Items.BLESSED_POT_4663) + } + } else if (inInventory(player, Items.SILVER_POT_4664)) { + if (removeItem(player, Items.SILVER_POT_4664)) { + addItemOrDrop(player, Items.BLESSED_POT_4665) + } + } else if (inInventory(player, Items.SILVER_POT_4666)) { + if (removeItem(player, Items.SILVER_POT_4666)) { + addItemOrDrop(player, Items.BLESSED_POT_4667) + } + } + stage = END_DIALOGUE } - 25 -> npcl(FacialExpression.ANNOYED, "One of my monks found these gloves. If only to spare this world from further carnage, I return them to you.").also { stage = 99 } - - 99 -> end() } return true } @@ -65,4 +69,4 @@ class HighPriestEntranaDialogue(player: Player? = null) : DialoguePlugin(player) override fun getIds(): IntArray { return intArrayOf(NPCs.HIGH_PRIEST_216) } -} +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/entrana/handlers/EntranaListeners.kt b/Server/src/main/content/region/misc/entrana/handlers/EntranaListeners.kt new file mode 100644 index 000000000..105321131 --- /dev/null +++ b/Server/src/main/content/region/misc/entrana/handlers/EntranaListeners.kt @@ -0,0 +1,56 @@ +package content.region.misc.entrana.handlers + +import content.global.travel.ship.Ships +import core.api.* +import core.cache.def.impl.ItemDefinition +import core.game.dialogue.DialogueFile +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * Listeners for the Entrana monks' fast-travel option + * @author Player Name + */ + +fun sail(player: Player?, ship: Ships) { + ship.sail(player) + playJingle(player!!, 172) +} + +class EntranaListeners : InteractionListener { + override fun defineListeners() { + for (npc in arrayOf(NPCs.MONK_OF_ENTRANA_2730, NPCs.MONK_OF_ENTRANA_658, NPCs.MONK_OF_ENTRANA_2731)) { + on(npc, IntType.NPC, "take-boat") { player, _ -> + sail(player, Ships.ENTRANA_TO_PORT_SARIM) + return@on true + } + } + + for (npc in arrayOf(NPCs.MONK_OF_ENTRANA_2728, NPCs.MONK_OF_ENTRANA_657, NPCs.MONK_OF_ENTRANA_2729)) { + on(npc, IntType.NPC, "take-boat") { player, _ -> + if (!ItemDefinition.canEnterEntrana(player)) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + this.npc = NPC(npc) + when (stage) { + 0 -> npc("NO WEAPONS OR ARMOUR are permitted on holy", "Entrana AT ALL. We will not allow you to travel there", "in breach of mighty Saradomin's edict.").also { stage++ } + 1 -> npc("Do not try to deceive us again. Come back when you", "have laid down your Zamorakian instruments of death.").also { stage = END_DIALOGUE } + } + } + }) + return@on true + } + sail(player, Ships.PORT_SARIM_TO_ENTRANA) + if (!player.achievementDiaryManager.getDiary(DiaryType.FALADOR).isComplete(0, 14)) { + player.achievementDiaryManager.getDiary(DiaryType.FALADOR).updateTask(player, 0, 14, true) + } + return@on true + } + } + } +} diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/KjutDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/KjutDialogue.kt index d534f02fb..9cd9bdca0 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/KjutDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/KjutDialogue.kt @@ -1,5 +1,6 @@ package content.region.misc.keldagrim.dialogue +import content.data.Quests import core.api.addItemOrDrop import core.api.inInventory import core.api.isQuestComplete @@ -41,7 +42,7 @@ class KjutDialogue(player: Player? = null) : DialoguePlugin(player) { stage = END_DIALOGUE } 6 -> { - if (isQuestComplete(player, "Forgettable Tale of a Drunken Dwarf")) { + if (isQuestComplete(player, Quests.FORGETTABLE_TALE)) { npcl(FacialExpression.OLD_DEFAULT, "I thought you would know plenty!").also { stage = 14 } } else { npcl(FacialExpression.OLD_DEFAULT, "Just go out in the streets, they can't be hard to find!").also { stage = 7 } diff --git a/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt index 06dcb2ac5..20d4d5771 100644 --- a/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt +++ b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt @@ -9,18 +9,19 @@ import core.game.world.map.Location import org.rs09.consts.Components import core.game.world.GameWorld import core.api.* +import content.data.Quests object KeldagrimCartMethods { @JvmStatic fun goToKeldagrim(player: Player){ - if (!hasRequirement(player, "The Giant Dwarf")) + if (!hasRequirement(player, Quests.THE_GIANT_DWARF)) return GameWorld.Pulser.submit(TravelToKeldagrimPulse(player)) } @JvmStatic fun leaveKeldagrimTo(player: Player, dest: Location){ - if (!hasRequirement(player, "The Giant Dwarf")) + if (!hasRequirement(player, Quests.THE_GIANT_DWARF)) return GameWorld.Pulser.submit(TravelFromKeldagrimPulse(player,dest)) } @@ -68,7 +69,7 @@ class TravelFromKeldagrimPulse(val player: Player, val dest: Location): Pulse(){ class TravelToKeldagrimPulse(val player: Player) : Pulse(){ var counter = 0 - var cartNPC = NPC(1546) + var cartNPC = NPC(1544) override fun pulse(): Boolean { when(counter++){ 0 -> player.lock().also { player.interfaceManager.open(Component(115)) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FishmongerMiscDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FishmongerMiscDialogue.kt index 9f2a60285..e2ece679c 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/FishmongerMiscDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FishmongerMiscDialogue.kt @@ -7,6 +7,7 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs +import content.data.Quests /** * @author qmqz @@ -17,7 +18,7 @@ class FishmongerMiscDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Throne of Miscellania")) { + if (!isQuestComplete(player, Quests.THRONE_OF_MISCELLANIA)) { npcl(FacialExpression.FRIENDLY,"Greetings, Sir. Get your fresh fish here! I've heard that the Etceterian fish is stored in a cow shed.").also { stage = 0 } } else { npcl(FacialExpression.FRIENDLY,"Greetings, Your Highness. Have some fresh fish! I've heard that the Etceterian fish is stored in a cow shed.").also { stage = 0 } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt index c46e2f42e..6320d5ad1 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt @@ -22,7 +22,7 @@ class FlowerGirlDialogue(player: Player? = null) : DialoguePlugin(player){ //issues getting throne of miscellania status /* - when (player.questRepository.getQuest("Throne of Miscellania").isCompleted(player)) { + when (player.questRepository.getQuest(Quests.THRONE_OF_MISCELLANIA).isCompleted(player)) { true -> npc(FacialExpression.HAPPY, "Good day, Your Royal Highness.").also { stage = 1 } false -> npc(FacialExpression.NEUTRAL, "Hello.").also { stage = 1 } } @@ -39,7 +39,7 @@ class FlowerGirlDialogue(player: Player? = null) : DialoguePlugin(player){ 2 -> { /* - when (player.questRepository.getQuest("Throne of Miscellania").isCompleted(player)) { + when (player.questRepository.getQuest(Quests.THRONE_OF_MISCELLANIA).isCompleted(player)) { true -> npc(FacialExpression.HAPPY, "I'm selling flowers, 15gp for three. Would you like some, Your Highness?").also { stage++ } false -> npc(FacialExpression.NEUTRAL, "I'm selling flowers, 15gp for three. Would you like some?").also { stage++ } } diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/BankerTutorDialogue.java b/Server/src/main/content/region/misc/tutisland/dialogue/BankerTutorDialogue.java deleted file mode 100644 index c6a121500..000000000 --- a/Server/src/main/content/region/misc/tutisland/dialogue/BankerTutorDialogue.java +++ /dev/null @@ -1,155 +0,0 @@ -package content.region.misc.tutisland.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the banker tutor dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BankerTutorDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BankerTutorDialogue} {@code Object}. - * @param player the player. - */ - public BankerTutorDialogue(final Player player) { - super(player); - } - - /** - * Constructs a new {@code BankerTutorDialogue} {@code Object}. - */ - public BankerTutorDialogue() { - /** - * empty. - */ - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BankerTutorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = args[0] instanceof NPC ? (NPC) args[0] : null; - npc("Good day, would you like to access your bank account?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("How do I use the bank?", "I'd like to access my bank account please.", "I'd like to check my PIN settings."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - options("Using the bank itself.", "Using Bank deposit boxes.", "What's this PIN thing that people keep talking about?", "Goodbye."); - stage = 9; - break; - case 2: - end(); - player.getBank().open(); - break; - case 3: - end(); - player.getBankPinManager().openSettings(); - break; - } - break; - case 9: - switch (buttonId) { - case 1: - player("Using the bank itself. I'm not sure how....?"); - stage = 10; - break; - case 2: - player("Using Bank deposit boxes.... what are they?"); - stage = 20; - break; - case 3: - player("What's this PIN thing that people keep talking about?"); - stage = 30; - break; - case 4: - player("Goodbye."); - stage = 99; - break; - } - break; - case 10: - 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-"); - stage = 11; - break; - case 11: - npc("click on it once."); - stage = 12; - break; - case 12: - 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."); - stage = 13; - break; - case 13: - 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."); - stage = 14; - break; - case 14: - 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."); - stage = 15; - break; - case 15: - 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 do withdraw the logs as 'certs' or 'notes',"); - stage = 16; - break; - case 16: - npc("then withdraw the items you need."); - stage = 99; - break; - case 20: - 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."); - stage = 21; - break; - case 21: - 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."); - stage = 22; - break; - case 22: - end(); - break; - case 30: - 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"); - stage = 31; - break; - case 31: - npc("no one."); - stage = 32; - break; - case 32: - npc("So if someone did manage to get your password they", "couldn't steal your items if they were in the bank."); - stage = 33; - break; - case 33: - end(); - break; - case 99: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4907 }; - } - -} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/RatPenDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/RatPenDialogue.kt new file mode 100644 index 000000000..3728d6d9c --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/RatPenDialogue.kt @@ -0,0 +1,15 @@ +package content.region.misc.tutisland.dialogue + +import content.region.misc.tutisland.handlers.sendStageDialog +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller + +/** + * Vannaka's angry dialogue when you try to enter his rat pen before you're supposed to + */ +class RatPenDialogue : DialogueLabeller() { + override fun addConversation() { + npc(ChatAnim.ANGRY, "Oi, get away from there!", "Don't enter my rat pen unless I say so!") + exec { player, _ -> sendStageDialog(player) } + } +} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt index 3a16f44c9..f19f67a64 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt @@ -9,10 +9,11 @@ import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable /** * Handles Skippy's skip tutorial dialogue - * @author Ceikry + * @author Ceikry, Player Name */ @Initializable class SkipTutorialDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -21,13 +22,13 @@ class SkipTutorialDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun open(vararg args: Any?): Boolean { - npcl(FacialExpression.FRIENDLY, "Hey, would you like to skip to the end? Choose wisely! This is the only time you get this choice.") + setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Hey, would you like to skip to the end? Choose wisely! This is the only time you get this choice.")) return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> options("Yes, I'd like to skip the tutorial.", "No thanks.").also { stage++ } + 0 -> options("Yes, I'd like to skip the tutorial.", "No, thanks.").also { stage++ } 1 -> when(buttonId) { 1 -> { diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt index 21c914b98..4e0385178 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt @@ -1,119 +1,108 @@ package content.region.misc.tutisland.dialogue -import core.api.addItem +import core.api.addItemOrDrop import core.api.inInventory import core.api.setAttribute -import core.game.component.Component -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import content.region.misc.tutisland.handlers.sendStageDialog +import core.api.getAttribute +import core.api.inEquipment +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.item.Item -/** - * Handles the survival expert's dialogue - * @author Ceikry - */ -@Initializable -class SurvivalExpertDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player?): DialoguePlugin { - return SurvivalExpertDialogue(player) +class SurvivalExpertDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.SURVIVAL_EXPERT_943, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, SurvivalExpertDialogueFile(), node as NPC) + return@on true + } } +} - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - val tutStage = player?.getAttribute("tutorial:stage", 0) ?: 0 - when(tutStage) - { - 4 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - FacialExpression.FRIENDLY, - "Hello there, newcomer. My name is Brynna. My job is", - "to teach you a few survival tips and tricks. First off", - "we're going to start with the most basic survival skill of", - "all: making a fire." - ) - ) +class SurvivalExpertDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SURVIVAL_EXPERT_943) - 11 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - FacialExpression.FRIENDLY, - "Well done! Next we need to get some food in our", - "bellies. We'll need something to cook. There are shrimp", - "in the pond there, so let's catch and cook some." - ) - ) - - 5, 14, 15 -> { - if(!inInventory(player, Items.BRONZE_AXE_1351)) - { - player.dialogueInterpreter.sendItemMessage(Items.BRONZE_AXE_1351, "The Survival Expert gives you a spare bronze axe.") - addItem(player, Items.BRONZE_AXE_1351) + exec { player, _ -> + when (val stage = getAttribute(player, "/save:tutorial:stage", 0)) { + 4 -> loadLabel(player, "hello") + 5, 6, 7, 8, 9, 10, 12, 13, 14 -> { + if (!inInventory(player, Items.BRONZE_AXE_1351) && !inEquipment(player, Items.BRONZE_AXE_1351)) { + loadLabel(player, "spare axe") + } + if (!inInventory(player, Items.TINDERBOX_590)) { + loadLabel(player, "spare tinderbox") + } + if (stage >= 11 && !inInventory(player, Items.SMALL_FISHING_NET_303)) { + loadLabel(player, "spare net") + } + loadLabel(player, "nowhere") } - if(!inInventory(player, Items.TINDERBOX_590)) - { - player.dialogueInterpreter.sendItemMessage(Items.TINDERBOX_590, "The Survival Expert gives you a spare tinderbox.") - addItem(player, Items.TINDERBOX_590) - } - return false + 11 -> loadLabel(player, "fishing") + else -> loadLabel(player, "nowhere") } } - return true - } - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(player?.getAttribute("tutorial:stage", 0)) - { - 4 -> when(stage) - { - 0 -> { - Component.setUnclosable( - player, - interpreter.sendDoubleItemMessage( - Items.TINDERBOX_590, - Items.BRONZE_AXE_1351, - "The Survival Guide gives you a tinderbox and a bronze axe!" - ) - ) - addItem(player, Items.TINDERBOX_590) - addItem(player, Items.BRONZE_AXE_1351) - stage++ - } - 1 -> { - end() - setAttribute(player, "tutorial:stage", 5) - TutorialStage.load(player, 5) - } + label("hello") + npc(ChatAnim.FRIENDLY, "Hello there, newcomer. My name is Brynna. My job is to teach you a few survival tips and tricks. First off we're going to start with the most basic survival skill of all: making a fire.", unclosable = true) + exec { player, _ -> + addItemOrDrop(player, Items.TINDERBOX_590) + addItemOrDrop(player, Items.BRONZE_AXE_1351) + } + item(Item(Items.TINDERBOX_590), Item(Items.BRONZE_AXE_1351), "The Survival Guide gives you a tinderbox and a bronze", "axe!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 5) + TutorialStage.load(player, 5) + } + goto("nowhere") + + label("fishing") + npc(ChatAnim.FRIENDLY, "Well done! Next we need to get some food in our bellies. We'll need something to cook. There are shrimp in the pond there, so let's catch and cook some.", unclosable = true) + exec { player, _ -> addItemOrDrop(player, Items.SMALL_FISHING_NET_303) } + item(Item(Items.SMALL_FISHING_NET_303), "The Survival Guide gives you a", "net!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 12) + TutorialStage.load(player, 12) + } + goto("nowhere") + + label("spare axe") + exec { player, _ -> addItemOrDrop(player, Items.BRONZE_AXE_1351) } + item(Item(Items.BRONZE_AXE_1351), "The Survival Guide gives you a spare bronze axe.", unclosable = true) + exec { player, _ -> + if (!inInventory(player, Items.TINDERBOX_590)) { + loadLabel(player, "spare tinderbox") } - - 11 -> when(stage){ - 0 -> { - Component.setUnclosable( - player, - interpreter.sendItemMessage(303, "The Survival Guide gives you a net!") - ) - addItem(player, Items.SMALL_FISHING_NET_303) - stage++ - } - 1 -> { - end() - setAttribute(player, "tutorial:stage", 12) - TutorialStage.load(player, 12) - } + val stage = getAttribute(player, "/save:tutorial:stage", 0) + if (stage >= 11 && !inInventory(player, Items.SMALL_FISHING_NET_303)) { + loadLabel(player, "spare net") } } - return true - } + goto("nowhere") //closes the dialogue, letting the hook reopen the tutorial stage dialog as appropriate - override fun getIds(): IntArray { - return intArrayOf(NPCs.SURVIVAL_EXPERT_943) - } + label("spare tinderbox") + exec { player, _ -> addItemOrDrop(player, Items.TINDERBOX_590) } + item(Item(Items.TINDERBOX_590), "The Survival Guide gives you a spare tinderbox.", unclosable = true) + exec { player, _ -> + val stage = getAttribute(player, "/save:tutorial:stage", 0) + if (stage >= 11 && !inInventory(player, Items.SMALL_FISHING_NET_303)) { + loadLabel(player, "spare net") + } + } + goto("nowhere") -} \ No newline at end of file + label("spare net") + exec { player, _ -> addItemOrDrop(player, Items.SMALL_FISHING_NET_303) } + item(Item(Items.SMALL_FISHING_NET_303), "The Survival Guide gives you a spare net.", unclosable = true) + goto("nowhere") + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } + } +} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt index 4e73a799d..1ab335e26 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt @@ -1,95 +1,106 @@ package content.region.misc.tutisland.dialogue -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import content.region.misc.tutisland.handlers.sendStageDialog +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.item.Item -/** - * Handles the combat instructor's dialogue - * @author Ceikry - */ -@Initializable -class TutorialCombatInstructorDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player?): DialoguePlugin { - return TutorialCombatInstructorDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - - when(getAttribute(player, "tutorial:stage", 0)) - { - 44 -> playerl(FacialExpression.FRIENDLY, "Hi! My name's ${player.username}.") - 47 -> npcl(FacialExpression.FRIENDLY, "Very good, but that little butter knife isn't going to protect you much. Here, take these.") - 53 -> playerl(FacialExpression.FRIENDLY, "I did it! I killed a giant rat!") - 54 -> { - player.dialogueInterpreter.sendDoubleItemMessage(Items.SHORTBOW_841, Items.BRONZE_ARROW_882, "The Combat Guide gives you some bronze arrows and a shortbow!") - if(!inInventory(player, Items.SHORTBOW_841) && !inEquipment(player, Items.SHORTBOW_841)) - addItem(player, Items.SHORTBOW_841) - if(!inInventory(player, Items.BRONZE_ARROW_882) && !inEquipment(player, Items.BRONZE_ARROW_882)) - addItem(player, Items.BRONZE_ARROW_882, 30) - } +class CombatInstructorDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.COMBAT_INSTRUCTOR_944, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, CombatInstructorDialogueFile(), node as NPC) + return@on true } - return true } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(getAttribute(player, "tutorial:stage", 0)) - { - 44 -> when(stage){ - 0 -> npcl(FacialExpression.ANGRY, "Do I look like I care? To me you're just another newcomer who thinks they're ready to fight.").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "I'm Vannaka, the greatest swordsman alive.").also { stage++ } - 2 -> npcl(FacialExpression.FRIENDLY, "Let's get started by teaching you to wield a weapon.").also { stage++ } - 3 -> { - end() - setAttribute(player, "tutorial:stage", 45) - TutorialStage.load(player, 45) - } - } - - 47 -> when(stage){ - 0 -> { - addItemOrDrop(player, Items.BRONZE_SWORD_1277) - addItemOrDrop(player, Items.WOODEN_SHIELD_1171) - sendDoubleItemDialogue(player, Items.BRONZE_SWORD_1277, Items.WOODEN_SHIELD_1171, "The Combat Guide gives you a bronze sword and a wooden shield!") - stage++ - } - 1 -> { - end() - setAttribute(player, "tutorial:stage", 48) - TutorialStage.load(player, 48) - } - } - - 53 -> when(stage){ - 0 -> npcl(FacialExpression.FRIENDLY, "I saw, ${player.username}. You seem better at this than I thought. Now that you have grasped basic swordplay, let's move on.").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "Let's try some ranged attacking, with this you can kill foes from a distance. Also, foes unable to reach you are as good as dead. You'll be able to attack the rats, without entering the pit.").also { stage++ } - 2 -> { - sendDoubleItemDialogue(player, Items.SHORTBOW_841, Items.BRONZE_ARROW_882, "The Combat Guide gives you some bronze arrows and a shortbow!") - if(!inInventory(player, Items.SHORTBOW_841) && !inEquipment(player, Items.SHORTBOW_841)) - addItem(player, Items.SHORTBOW_841) - if(!inInventory(player, Items.BRONZE_ARROW_882) && !inEquipment(player, Items.BRONZE_ARROW_882)) - addItem(player, Items.BRONZE_ARROW_882, 30) - stage++ - } - 3 -> { - end() - setAttribute(player, "tutorial:stage", 54) - TutorialStage.load(player, 54) - } - } - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.COMBAT_INSTRUCTOR_944) - } - +} + +class CombatInstructorDialogueFile : DialogueLabeller() { + fun lostWeapon(player: Player, id: Int): Boolean { + return !inInventory(player, id) && !inEquipment(player, id) + } + + override fun addConversation() { + assignToIds(NPCs.COMBAT_INSTRUCTOR_944) + + exec { player, _ -> + when (getAttribute(player, "tutorial:stage", 0)) { + 44 -> loadLabel(player, "hello") + 47 -> loadLabel(player, "butter") + 48, 49, 50, 51, 52 -> loadLabel(player, if (lostWeapon(player, Items.BRONZE_SWORD_1277)) "lost sword" else if (lostWeapon(player, Items.WOODEN_SHIELD_1171)) "lost shield" else "nowhere") + 53 -> loadLabel(player, "killed rat") + 54 -> loadLabel(player, if (lostWeapon(player, Items.SHORTBOW_841)) "lost bow" else if (lostWeapon(player, Items.BRONZE_ARROW_882)) "lost arrows" else "nowhere") + else -> loadLabel(player, "nowhere") + } + } + + label("hello") + player(ChatAnim.FRIENDLY, "Hi! My name's ${player?.username}.", unclosable = true) + npc(ChatAnim.ANGRY, "Do I look like I care? To me you're just another newcomer who thinks they're ready to fight.", unclosable = true) + npc(ChatAnim.FRIENDLY, "I'm Vannaka, the greatest swordsman alive.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Let's get started by teaching you to wield a weapon.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 45) + TutorialStage.load(player, 45) + } + + label("butter") + npc(ChatAnim.FRIENDLY, "Very good, but that little butter knife isn't going to protect you much. Here, take these.", unclosable = true) + exec { player, _ -> + addItemOrDrop(player, Items.BRONZE_SWORD_1277) + addItemOrDrop(player, Items.WOODEN_SHIELD_1171) + } + item(Item(Items.BRONZE_SWORD_1277), Item(Items.WOODEN_SHIELD_1171), "The Combat Guide gives you a bronze sword and a", "wooden shield!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 48) + TutorialStage.load(player, 48) + } + goto("nowhere") + + label("lost sword") + exec { player, _ -> addItemOrDrop(player, Items.BRONZE_SWORD_1277) } + item(Item(Items.BRONZE_SWORD_1277), "The Combat Guide gives you a spare sword.", unclosable = true) + exec { player, _ -> if (lostWeapon(player, Items.WOODEN_SHIELD_1171)) loadLabel(player, "lost shield") } + goto("nowhere") + + label("lost shield") + exec { player, _ -> addItemOrDrop(player, Items.WOODEN_SHIELD_1171) } + item(Item(Items.WOODEN_SHIELD_1171), "The Combat Guide gives you a spare shield.", unclosable = true) + goto("nowhere") + + label("killed rat") + player(ChatAnim.FRIENDLY, "I did it! I killed a giant rat!", unclosable = true) + npc(ChatAnim.FRIENDLY, "I saw, ${player?.username}. You seem better at this than I thought. Now that you have grasped basic swordplay, let's move on.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Let's try some ranged attacking, with this you can kill foes from a distance. Also, foes unable to reach you are as good as dead. You'll be able to attack the rats, without entering the pit.", unclosable = true) + exec { player, _ -> + addItemOrDrop(player, Items.SHORTBOW_841) + addItemOrDrop(player, Items.BRONZE_ARROW_882, 30) + } + item(Item(Items.SHORTBOW_841), Item(Items.BRONZE_ARROW_882), "The Combat Guide gives you some bronze arrows and", "a shortbow!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 54) + TutorialStage.load(player, 54) + } + goto("nowhere") + + label("lost bow") + exec { player, _ -> addItemOrDrop(player, Items.SHORTBOW_841) } + item(Item(Items.SHORTBOW_841), "The Combat Guide gives you a spare bow.", unclosable = true) + goto("nowhere") + + label("lost arrows") + exec { player, _ -> addItemOrDrop(player, Items.BRONZE_ARROW_882, 10) } + item(Item(Items.BRONZE_ARROW_882), "You receive some spare arrows.", unclosable = true) + goto("nowhere") + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } + } } diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt index 305911ae5..d94bcd922 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt @@ -7,11 +7,12 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable import core.game.world.GameWorld.settings /** * Handles the finance tutor's dialogue - * @author Ceikry + * @author Ceikry, Player Name */ @Initializable class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { @@ -23,8 +24,8 @@ class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogu npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 58 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello, who are you?") - 59 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Move along, now.").also { return false } + 58 -> setUnclosable(player, playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello, who are you?")) + 59 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Move along, now.")) } return true } @@ -32,21 +33,22 @@ class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogu override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(getAttribute(player, "tutorial:stage", 0)){ 58 -> when(stage++){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm the Financial Advisor. I'm here to tell people how to make money.") - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Okay. How can I make money then?") - 2 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "How you can make money? Quite.") - 3 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well there are three basic ways of making money here: combat, quests, and trading. I will talk you through each of them very quickly.") - 4 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Let's start with combat as it is probably still fresh in your mind. Many enemies, both human and monster will drop items when they die.") - 5 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Now, the next way to earn money quickly is by quests. Many people on " + settings!!.name + " have things they need doing, which they will reward you for.") - 6 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "By getting a high level in skills such as Cooking, Mining, Smithing or Fishing, you can create or catch your own items and sell them for pure profit.") - 7 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Lastly, we have jobs you can get from tutors in Lumbridge. These pay very handsomely early on!").also { stage++ } - 8 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, that about covers it. Move along now.") + 0 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm the Financial Advisor. I'm here to tell people how to make money.")) + 1 -> setUnclosable(player, playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Okay. How can I make money then?")) + 2 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "How you can make money? Quite.")) + 3 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well there are three basic ways of making money here: combat, quests, and trading. I will talk you through each of them very quickly.")) + 4 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Let's start with combat as it is probably still fresh in your mind. Many enemies, both human and monster will drop items when they die.")) + 5 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Now, the next way to earn money quickly is by quests. Many people on " + settings!!.name + " have things they need doing, which they will reward you for.")) + 6 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "By getting a high level in skills such as Cooking, Mining, Smithing or Fishing, you can create or catch your own items and sell them for pure profit.")) + 7 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Lastly, we have jobs you can get from tutors in Lumbridge. These pay very handsomely early on!")).also { stage++ } + 8 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, that about covers it. Move along now.")) 9 -> { end() setAttribute(player, "tutorial:stage", 59) TutorialStage.load(player, 59) } } + 59 -> TutorialStage.load(player, 59) } return true } @@ -54,5 +56,4 @@ class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogu override fun getIds(): IntArray { return intArrayOf(NPCs.FINANCIAL_ADVISOR_947) } - } \ No newline at end of file diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt index 2221a4d79..68707766d 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt @@ -1,205 +1,229 @@ package content.region.misc.tutisland.dialogue +import content.global.handlers.iface.RulesAndInfo import content.region.misc.tutisland.handlers.* +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.InteractionListener +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.IronmanMode import core.game.node.entity.player.link.TeleportManager import core.game.node.item.Item +import core.game.world.GameWorld import core.game.world.map.Location import core.plugin.Initializable +import core.worker.ManagementEvents import org.rs09.consts.Items import org.rs09.consts.NPCs import proto.management.JoinClanRequest -import core.ServerConstants -import content.global.handlers.iface.RulesAndInfo -import core.game.world.GameWorld -import core.tools.END_DIALOGUE -import core.worker.ManagementEvents -/** - * Handles the magic tutor's dialogue - * @author Ceikry - */ @Initializable -class TutorialMagicTutorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - private val STARTER_PACK = arrayOf( - Item(1351, 1), - Item(590, 1), - Item(303, 1), - Item(315, 1), - Item(1925, 1), - Item(1931, 1), - Item(2309, 1), - Item(1265, 1), - Item(1205, 1), - Item(1277, 1), - Item(1171, 1), - Item(841, 1), - Item(882, 25), - Item(556, 25), - Item(558, 15), - Item(555, 6), - Item(557, 4), - Item(559, 2) - ) - private val STARTER_BANK = arrayOf(Item(995, 25)) - - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return TutorialMagicTutorDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - when(getAttribute(player, "tutorial:stage", 0)) - { - 67 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello.") - 69 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Good. This is a list of your spells. Currently you can only cast one offensive spell called Wind Strike. Let's try it out on one of those chickens.") - 70 -> if(!inInventory(player, Items.AIR_RUNE_556) && !inInventory(player, Items.MIND_RUNE_558)) - { - player.dialogueInterpreter.sendDoubleItemMessage(Items.AIR_RUNE_556, Items.MIND_RUNE_558, "You receive some spare runes.") - addItem(player, Items.AIR_RUNE_556, 15) - addItem(player, Items.MIND_RUNE_558, 15) - return false +class TutorialMagicTutorDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.MAGIC_INSTRUCTOR_946, NPC, "talk-to") { player, _ -> + val stage = getAttribute(player, "tutorial:stage", 0) + if (stage == 70 && inInventory(player, Items.AIR_RUNE_556) && inInventory(player, Items.MIND_RUNE_558)) { + // Player should be killing chickens instead, and could be. Instead of opening the dialogue and doing nothing (which will make you lose the tutorial island dialog), do nothing at all + return@on true } - 71 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Alright, last thing. Are you interested in being an ironman or changing your experience rate?") - else -> return false + openDialogue(player, TutorialMagicTutorDialogueFile(), NPC(NPCs.MAGIC_INSTRUCTOR_946)) + return@on true } - return true } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(getAttribute(player, "tutorial:stage", 0)) - { - 67 -> when(stage++){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Good day, newcomer. My name is Terrova. I'm here to tell you about Magic. Let's start by opening your spell list.") - 1 -> { - end() - setAttribute(player, "tutorial:stage", 68) - TutorialStage.load(player, 68) - } - } - 69 -> when(stage++){ - 0 -> { - sendDoubleItemDialogue(player, Items.AIR_RUNE_556, Items.MIND_RUNE_558, "Terrova gives you 15 air runes and 15 mind runes!") - addItemOrDrop(player, Items.AIR_RUNE_556, 5) - addItemOrDrop(player, Items.MIND_RUNE_558, 5) - } - 1 -> { - end() - setAttribute(player, "tutorial:stage", 70) - TutorialStage.load(player, 70) - } - } - 71 -> when(stage){ - 0 -> options("Set Ironman Mode (current: ${player.ironmanManager.mode.name})", "Change XP Rate (current: ${player.skills.experienceMutiplier}x)", "I'm ready now.").also { stage++ } - 1 -> when(buttonId){ - 1 -> options("None","Standard","Hardcore (Permadeath!)","Ultimate","Nevermind.").also { stage = 10 } - 2 -> options("1.0x","2.5x","5.0x","10x").also { stage = 20 } - 3 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, you're all finished here now. I'll give you a reasonable number of starting items when you leave.").also { stage = 30 } - } - - 10 -> { - stage = 0 - if(buttonId < 5) - { - val mode = IronmanMode.values()[buttonId - 1] - player.dialogueInterpreter.sendDialogue("You set your ironman mode to: ${mode.name}.") - player.ironmanManager.mode = mode - if (player.skills.experienceMutiplier == 10.0 && mode != IronmanMode.HARDCORE) player.skills.experienceMutiplier = 5.0 - } - else - { - handle(interfaceId, 0) - } - } - - 20 -> { - val rates = arrayOf(1.0,2.5,5.0,10.0) - val rate = rates[buttonId - 1] - if(rate == 10.0 && player.ironmanManager.mode != IronmanMode.HARDCORE) { - player.dialogueInterpreter.sendDialogue("10.0x is only available to Hardcore Ironmen!") - stage = 0 - return true - } - player.dialogueInterpreter.sendDialogue("You set your XP rate to: ${rate}x.") - player.skills.experienceMutiplier = rate - stage = 0 - } - - 30 -> player.dialogueInterpreter.sendOptions("Leave Tutorial Island?", "Yes, I'm ready.", "No, not yet.").also { stage++ } - 31 -> when(buttonId) - { - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm ready to go now, thank you.").also { stage = 40 } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm not quite ready to go yet, thank you.").also { stage = END_DIALOGUE } - } - - 40 -> { - setAttribute(player, "/save:tutorial:complete", true) - setVarbit(player, 3756, 0) - teleport(player, Location.create(3233, 3230), TeleportManager.TeleportType.NORMAL) - closeOverlay(player) - - player.inventory.clear() - player.bank.clear() - player.equipment.clear() - player.interfaceManager.restoreTabs() - player.interfaceManager.setViewedTab(3) - player.inventory.add(*STARTER_PACK) - player.bank.add(*STARTER_BANK) - - if(player.ironmanManager.mode == IronmanMode.HARDCORE) - { - setAttribute(player, "/save:permadeath", true) - } - else if(player.skills.experienceMutiplier == 10.0) - { - player.skills.experienceMutiplier = 5.0 - } - - //This overwrites the stuck dialogue after teleporting to Lumbridge for some reason - //Dialogue from 2007 or thereabouts - //Original is five lines, but if the same is done here it will break. Need to find another way of showing all this information. - interpreter.sendDialogue( - "Welcome to Lumbridge! To get more help, simply click on the", - "Lumbridge Guide or one of the Tutors - these can be found by looking", - "for the question mark icon on your mini-map. If you find you are lost", - "at any time, look for a signpost or use the Lumbridge Home Port Spell." - ) - stage = 12 - TutorialStage.removeHintIcon(player) - - player.unhook(TutorialKillReceiver) - player.unhook(TutorialFireReceiver) - player.unhook(TutorialResourceReceiver) - player.unhook(TutorialUseWithReceiver) - player.unhook(TutorialInteractionReceiver) - player.unhook(TutorialButtonReceiver) - RulesAndInfo.openFor(player) - - if (GameWorld.settings!!.enable_default_clan) { - player.communication.currentClan = ServerConstants.SERVER_NAME.toLowerCase() - - val clanJoin = JoinClanRequest.newBuilder() - clanJoin.clanName = ServerConstants.SERVER_NAME.toLowerCase() - clanJoin.username = player.name - - ManagementEvents.publish(clanJoin.build()) - } - } - - 12 -> { - player.setAttribute("close_c_", true) - end() - } - } - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.MAGIC_INSTRUCTOR_946) - } - +} + +class TutorialMagicTutorDialogueFile : DialogueLabeller() { + override fun addConversation() { + val STARTER_PACK = arrayOf( + Item(Items.BRONZE_AXE_1351), + Item(Items.TINDERBOX_590), + Item(Items.SMALL_FISHING_NET_303), + Item(Items.SHRIMPS_315), + Item(Items.BUCKET_1925), + Item(Items.EMPTY_POT_1931), + Item(Items.BREAD_2309), + Item(Items.BRONZE_PICKAXE_1265), + Item(Items.BRONZE_DAGGER_1205), + Item(Items.BRONZE_SWORD_1277), + Item(Items.WOODEN_SHIELD_1171), + Item(Items.SHORTBOW_841), + Item(Items.BRONZE_ARROW_882, 25), + Item(Items.AIR_RUNE_556, 25), + Item(Items.MIND_RUNE_558, 15), + Item(Items.WATER_RUNE_555, 6), + Item(Items.EARTH_RUNE_557, 4), + Item(Items.BODY_RUNE_559, 2) + ) + val STARTER_BANK = arrayOf(Item(Items.COINS_995, 25)) + + exec { player, _ -> + when (getAttribute(player, "tutorial:stage", 0)) { + 67 -> loadLabel(player, "hello") + 69 -> loadLabel(player, "spelllist") + 70 -> { + if (!inInventory(player, Items.AIR_RUNE_556, 1) || !inInventory(player, Items.MIND_RUNE_558, 1)) { + loadLabel(player, "givemorerunes") + } else { + goto("nowhere") + } + } + 71 -> loadLabel(player, "finishedtutorial") + else -> goto("nowhere") + } + } + + label("hello") + player(ChatAnim.FRIENDLY, "Hello.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Good day, newcomer. My name is Terrova. I'm here to tell you about Magic. Let's start by opening your spell list.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 68) + TutorialStage.load(player, 68) + } + + label("spelllist") + npc(ChatAnim.FRIENDLY, "Good. This is a list of your spells. Currently you can only cast one offensive spell called Wind Strike. Let's try it out on one of those chickens.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 70) + addItemOrDrop(player, Items.AIR_RUNE_556, 15) + addItemOrDrop(player, Items.MIND_RUNE_558, 15) + } + item(Item(Items.AIR_RUNE_556), Item(Items.MIND_RUNE_558), "Terrova gives you 15 air runes and 15 mind runes!", unclosable = true) + exec { player, _ -> TutorialStage.load(player, 70) } + + label("givemorerunes") + exec { player, _ -> + addItemOrDrop(player, Items.AIR_RUNE_556, 5) + addItemOrDrop(player, Items.MIND_RUNE_558, 5) + } + item(Item(Items.AIR_RUNE_556), Item(Items.MIND_RUNE_558), "You receive some spare runes.", unclosable = true) + exec { player, _ -> TutorialStage.load(player, 70) } + + label("finishedtutorial") + exec { player, _ -> + if (ServerConstants.XP_RATES || ServerConstants.IRONMAN) { + loadLabel(player, "talk about inauthentic") + } else { + loadLabel(player, "leave") + } + } + + label("talk about inauthentic") + npc(ChatAnim.FRIENDLY, "Alright, last thing. Are you interested in our inauthentic ${ServerConstants.SERVER_NAME} features?", unclosable = true) + goto("inauthentic") + + label("inauthentic") + options( + DialogueOption("xprate","Change XP rate (current: ${player?.skills?.experienceMultiplier}x)", skipPlayer = true) { _, _ -> + return@DialogueOption ServerConstants.XP_RATES + }, + DialogueOption("ironman","Set ironman mode (current: ${player?.ironmanManager?.mode?.name?.toLowerCase()})", skipPlayer = true) { _, _ -> + return@DialogueOption ServerConstants.IRONMAN + }, + DialogueOption("leave","I'm ready now."), + unclosable = true) + + label("xprate") + options( + DialogueOption("1.0x","1.0x (default)", skipPlayer = true), + DialogueOption("2.5x","2.5x", skipPlayer = true), + DialogueOption("5.0x","5.0x", skipPlayer = true), + title = "Change XP rate (current: ${player?.skills?.experienceMultiplier}x)", + unclosable = true + ) + for (rate in doubleArrayOf(1.0, 2.5, 5.0)) { + label("${rate}x") + exec { player, _ -> player.skills.experienceMultiplier = rate } + manual(unclosable = true) { player, _ -> player.dialogueInterpreter.sendDialogue("You set your XP rate to: ${rate}x.") } + goto("inauthentic") + } + + label("ironman") + options( + DialogueOption("NONE","None (default)", skipPlayer = true), + DialogueOption("STANDARD","Standard", skipPlayer = true), + DialogueOption("ULTIMATE","Ultimate (no bank)", skipPlayer = true), + title = "Change ironman mode (current: ${player?.ironmanManager?.mode?.name?.toLowerCase()}x)" + ) + for (mode in arrayOf(IronmanMode.NONE, IronmanMode.STANDARD, IronmanMode.ULTIMATE)) { + label(mode.name) + exec { player, _ -> player.ironmanManager.mode = mode } + manual(unclosable = true){ player, _ -> return@manual player.dialogueInterpreter.sendDialogue("You set your ironman mode to: ${mode.name.toLowerCase()}.") } + exec { player, _ -> loadLabel(player, if (player.ironmanManager.mode == IronmanMode.NONE) "inauthentic" else "ironwarning") } + } + + label("ironwarning") + manual(unclosable = true) { player, _ -> player.dialogueInterpreter.sendDialogue(*splitLines("WARNING: You have selected an ironman mode. This is an uncompromising mode that WILL completely restrict your ability to trade. This MAY leave you unable to complete certain content, including quests.")) } + goto("inauthentic") + + label("leave") + npc(ChatAnim.FRIENDLY, "Well, you're all finished here now. I'll give you a reasonable number of starting items when you leave.", unclosable = true) + options( + DialogueOption("leave:yes","Yes, I'm ready.","I'm ready to go now, thank you.", ChatAnim.FRIENDLY), + DialogueOption("nowhere","No, not yet.","I'm not quite ready to go yet, thank you.", ChatAnim.FRIENDLY), + title = "Leave Tutorial Island?", + unclosable = true + ) + + label("leave:yes") + manual { player, _ -> + setAttribute(player, "/save:tutorial:complete", true) + setVarbit(player, 3756, 0) + setVarp(player, 281, 1000, true) + teleport(player, Location.create(3233, 3230), TeleportManager.TeleportType.NORMAL) + closeOverlay(player) + + player.inventory.clear() + player.bank.clear() + player.equipment.clear() + player.interfaceManager.restoreTabs() + player.interfaceManager.setViewedTab(3) + player.inventory.add(*STARTER_PACK) + player.bank.add(*STARTER_BANK) + + TutorialStage.removeHintIcon(player) + player.unhook(TutorialKillReceiver) + player.unhook(TutorialFireReceiver) + player.unhook(TutorialResourceReceiver) + player.unhook(TutorialUseWithReceiver) + player.unhook(TutorialInteractionReceiver) + player.unhook(TutorialButtonReceiver) + player.unhook(TutorialDialogPreserver) + + if (GameWorld.settings!!.enable_default_clan) { + player.communication.currentClan = ServerConstants.SERVER_NAME.toLowerCase() + + val clanJoin = JoinClanRequest.newBuilder() + clanJoin.clanName = ServerConstants.SERVER_NAME.toLowerCase() + clanJoin.username = player.name + + ManagementEvents.publish(clanJoin.build()) + } + + // This shows the actual dialog, which is what this manual stage is for. + // Dialog is from 2007 or thereabouts. + // Original is five lines, but if the same is done here it will break. Need to find another way of showing all this information. + player.dialogueInterpreter.sendDialogue( + "Welcome to Lumbridge! To get more help, simply click on the", + "Lumbridge Guide or one of the Tutors - these can be found by looking", + "for the question mark icon on your mini-map. If you find you are lost", + "at any time, look for a signpost or use the Lumbridge Home Port Spell." + ) + if (ServerConstants.RULES_AND_INFO_ENABLED) { + RulesAndInfo.openFor(player) + // The teleport finishing will release the player, so we need to relock them here + queueScript(player, 4, QueueStrength.SOFT) { _ -> + player.lock() + return@queueScript stopExecuting(player) + } + } + return@manual null + } + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } + } } diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt index 2db699406..89d985f29 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt @@ -1,7 +1,7 @@ package content.region.misc.tutisland.dialogue import core.api.* -import core.game.component.Component +import core.game.component.Component.setUnclosable import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -25,32 +25,17 @@ class TutorialMasterChefDialogue(player: Player? = null) : DialoguePlugin(player npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 18 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - FacialExpression.FRIENDLY, - "Ah! Welcome, newcomer. I am the Master Chef, Lev. It", - "is here I will teach you how to cook food truly fit for a", - "king." - ) - ) - - 19,20 -> { - if(!inInventory(player, Items.BREAD_DOUGH_2307)) - { - if(!inInventory(player, Items.BUCKET_OF_WATER_1929)) - { - sendItemDialogue(player, Items.BUCKET_OF_WATER_1929, "The Master Chef gives you another bucket of water.") - addItem(player, Items.BUCKET_OF_WATER_1929) - TutorialStage.load(player, 19) + 18 -> setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Ah! Welcome, newcomer. I am the Master Chef, Lev. It", "is here I will teach you how to cook food truly fit for a", "king.")) + 19, 20 -> { + if (!inInventory(player, Items.BREAD_DOUGH_2307)) { + if (!inInventory(player, Items.BUCKET_OF_WATER_1929)) { + setUnclosable(player, player.dialogueInterpreter.sendItemMessage(Items.BUCKET_OF_WATER_1929, "The Master Chef gives you another bucket of water.")) + addItemOrDrop(player, Items.BUCKET_OF_WATER_1929) return false } - if(!inInventory(player, Items.POT_OF_FLOUR_1933)) - { - sendItemDialogue(player, Items.POT_OF_FLOUR_1933, "The Master Chef gives you another pot of flour.") - addItem(player, Items.POT_OF_FLOUR_1933) - TutorialStage.load(player, 19) + if (!inInventory(player, Items.POT_OF_FLOUR_1933)) { + setUnclosable(player, player.dialogueInterpreter.sendItemMessage(Items.POT_OF_FLOUR_1933, "The Master Chef gives you another pot of flour.")) + addItemOrDrop(player, Items.POT_OF_FLOUR_1933) return false } } @@ -61,54 +46,21 @@ class TutorialMasterChefDialogue(player: Player? = null) : DialoguePlugin(player } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(getAttribute(player, "tutorial:stage", 0)) - { - 18 -> when(stage) - { - 0 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - player, - FacialExpression.HALF_GUILTY, - "I already know how to cook. Brynna taught me just", - "now." - ) - ).also { stage++ } - 1 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - FacialExpression.LAUGH, - "Hahahahahaha! You call THAT cooking? Some shrimp", - "on an open log fire? Oh, no, no no. I am going to", - "teach you the fine art of cooking bread." - ) - ).also { stage++ } - 2 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - FacialExpression.FRIENDLY, - "And no fine meal is complete without good music, so", - "we'll cover that while you're here too." - ) - ).also { stage++ } + when (getAttribute(player, "tutorial:stage", 0)) { + 18 -> when(stage) { + 0 -> setUnclosable(player, interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I already know how to cook. Brynna taught me just", "now.")).also { stage++ } + 1 -> setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.LAUGH, "Hahahahahaha! You call THAT cooking? Some shrimp", "on an open log fire? Oh, no, no no. I am going to", "teach you the fine art of cooking bread.")).also { stage++ } + 2 -> setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "And no fine meal is complete without good music, so", "we'll cover that while you're here too.")).also { stage++ } 3 -> { - Component.setUnclosable( - player, - interpreter.sendDoubleItemMessage( - Items.BUCKET_OF_WATER_1929, - Items.POT_OF_FLOUR_1933, - "The Cooking Guide gives you a bucket of water and a pot of flour." - ) - ) - addItem(player, Items.BUCKET_OF_WATER_1929) - addItem(player, Items.POT_OF_FLOUR_1933) + setUnclosable(player, interpreter.sendDoubleItemMessage(Items.BUCKET_OF_WATER_1929, Items.POT_OF_FLOUR_1933, "The Cooking Guide gives you a bucket of water and a","pot of flour.")) + addItemOrDrop(player, Items.BUCKET_OF_WATER_1929) + addItemOrDrop(player, Items.POT_OF_FLOUR_1933) stage++ + setAttribute(player, "tutorial:stage", 19) + TutorialStage.load(player, 19) } 4 -> { end() - setAttribute(player, "tutorial:stage", 19) TutorialStage.load(player, 19) } } diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt index ad9f6c77e..fc1f96c81 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt @@ -1,107 +1,95 @@ package content.region.misc.tutisland.dialogue -import core.api.addItem -import core.api.getAttribute -import core.api.inInventory -import core.api.setAttribute -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import content.region.misc.tutisland.handlers.sendStageDialog +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.item.Item -/** - * Handles the mining tutor's dialogue - * @author Ceikry - */ -@Initializable -class TutorialMiningInstructorDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player?): DialoguePlugin { - return TutorialMiningInstructorDialogue(player) +class MiningInstructorDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.MINING_INSTRUCTOR_948, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, MiningInstructorDialogueFile(), node as NPC) + return@on true + } + } +} + +class MiningInstructorDialogueFile : DialogueLabeller() { + fun lostPickaxe(player: Player): Boolean { + return !inInventory(player, Items.BRONZE_PICKAXE_1265) && !inEquipment(player, Items.BRONZE_PICKAXE_1265) } - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - when(getAttribute(player, "tutorial:stage", 0)) { - 30 -> npcl(FacialExpression.FRIENDLY, "Hi there. You must be new around here. So what do I call you? 'Newcomer' seems so impersonal, and if we're going to be working together, I'd rather tell you by name.") - 34 -> playerl(FacialExpression.FRIENDLY, "I prospected both types of rock! One set contains tin and the other has copper ore inside.") - 35 -> { - if(!inInventory(player, Items.BRONZE_PICKAXE_1265)) { - addItem(player, Items.BRONZE_PICKAXE_1265) - player.dialogueInterpreter.sendItemMessage(Items.BRONZE_PICKAXE_1265, "Dezzick gives you a bronze pickaxe!") - stage = 3 - } - else { - TutorialStage.load(player, 35) - } - } - 40 -> playerl(FacialExpression.ASKING, "How do I make a weapon out of this?") - 41 -> { - if(!inInventory(player, Items.HAMMER_2347)) { - addItem(player, Items.HAMMER_2347) - player.dialogueInterpreter.sendItemMessage(Items.HAMMER_2347, "Dezzick gives you a hammer!") - stage = 3 - } - else - { - end() - TutorialStage.load(player, 41) - } + override fun addConversation() { + assignToIds(NPCs.MINING_INSTRUCTOR_948) + + exec { player, _ -> + when (getAttribute(player, "tutorial:stage", 0)) { + 30 -> loadLabel(player, "hello") + 34 -> loadLabel(player, "prospected") + 35, 36, 37, 38, 39 -> loadLabel(player, if (lostPickaxe(player)) "lost pickaxe" else "nowhere") + 40 -> loadLabel(player, "make wep") + 41, 42, 43, 44, 45, 46 -> loadLabel(player, if (lostPickaxe(player)) "lost pickaxe" else if (!inInventory(player, Items.HAMMER_2347)) "lost hammer" else "nowhere") + else -> loadLabel(player, "nowhere") } } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(getAttribute(player, "tutorial:stage", 0)) { - 30 -> when(stage) { - 0 -> playerl(FacialExpression.FRIENDLY, "You can call me ${player.username}.").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "Ok then, ${player.username}. My name is Dezzick and I'm a miner by trade. Let's prospect some of these rocks.").also { stage++ } - 2 -> { - end() - setAttribute(player, "tutorial:stage", 31) - TutorialStage.load(player, 31) - } - } - - 34,35 -> when(stage) { - 0 -> npcl(FacialExpression.FRIENDLY, "Absolutely right, ${player.username}. These two ore types can be smelted together to make bronze.").also { stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "So now you know what ore is in the rocks over there, why don't you have a go at mining some tin and copper? Here, you'll need this to start with.").also { stage++ } - 2 -> { - addItem(player, Items.BRONZE_PICKAXE_1265) - player.dialogueInterpreter.sendItemMessage(Items.BRONZE_PICKAXE_1265, "Dezzick gives you a bronze pickaxe!") - stage++ - } - 3 -> { - end() - setAttribute(player, "tutorial:stage", 35) - TutorialStage.load(player, 35) - } - } - - 40,41 -> when(stage){ - 0 -> npcl(FacialExpression.FRIENDLY, "Okay, I'll show you how to make a dagger out of it. You'll be needing this..").also { stage++ } - 1 -> { - addItem(player, Items.HAMMER_2347) - player.dialogueInterpreter.sendItemMessage(Items.HAMMER_2347, "Drezzick gives you a hammer!") - stage++ - } - 2 -> { - end() - setAttribute(player, "tutorial:stage", 41) - TutorialStage.load(player, 41) - } - } + label("hello") + npc(ChatAnim.FRIENDLY, "Hi there. You must be new around here. So what do I call you? 'Newcomer' seems so impersonal, and if we're going to be working together, I'd rather tell you by name.", unclosable = true) + player(ChatAnim.FRIENDLY, "You can call me ${player?.username}.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Ok then, ${player?.username}. My name is Dezzick and I'm a miner by trade. Let's prospect some of these rocks.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 31) + TutorialStage.load(player, 31) } - return true - } + label("prospected") + player(ChatAnim.FRIENDLY, "I prospected both types of rock! One set contains tin and the other has copper ore inside.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Absolutely right, ${player?.username}. These two ore types can be smelted together to make bronze.", unclosable = true) + npc(ChatAnim.FRIENDLY, "So now you know what ore is in the rocks over there, why don't you have a go at mining some tin and copper? Here, you'll need this to start with.", unclosable = true) + exec { player, _ -> + addItem(player, Items.BRONZE_PICKAXE_1265) + setAttribute(player, "tutorial:stage", 35) + TutorialStage.load(player, 35) + } + item(Item(Items.BRONZE_PICKAXE_1265), "Dezzick gives you a bronze pickaxe!", unclosable = true) + goto("nowhere") - override fun getIds(): IntArray { - return intArrayOf(NPCs.MINING_INSTRUCTOR_948) + label("make wep") + player(ChatAnim.ASKING, "How do I make a weapon out of this?", unclosable = true) + npc(ChatAnim.FRIENDLY, "Okay, I'll show you how to make a dagger out of it. You'll be needing this.", unclosable = true) + exec { player, _ -> + addItem(player, Items.HAMMER_2347) + setAttribute(player, "tutorial:stage", 41) + TutorialStage.load(player, 41) + } + item(Item(Items.HAMMER_2347), "Dezzick gives you a hammer!", unclosable = true) + goto("nowhere") + + label("lost pickaxe") + exec { player, _ -> addItem(player, Items.BRONZE_PICKAXE_1265) } + item(Item(Items.BRONZE_PICKAXE_1265), "Dezzick gives you a spare pickaxe.", unclosable = true) + exec { player, _ -> + val stage = getAttribute(player, "/save:tutorial:stage", 0) + if (stage >= 41 && !inInventory(player, Items.HAMMER_2347)) { + loadLabel(player, "lost hammer") + } + } + goto("nowhere") + + label("lost hammer") + exec { player, _ -> addItem(player, Items.HAMMER_2347) } + item(Item(Items.HAMMER_2347), "Dezzick gives you a spare hammer.", unclosable = true) + goto("nowhere") + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } } } \ No newline at end of file diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt index 5278dc65d..3fb71bd16 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt @@ -9,6 +9,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable /** * Handles the prayer guide's dialogue @@ -24,9 +25,9 @@ class TutorialPrayerDialogue(player: Player? = null) : DialoguePlugin(player) { npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 60 -> npcl(FacialExpression.FRIENDLY, "Greetings! I'd just like to briefly go over two topics with you: Prayer, and Friend's.") - 62 -> npcl(FacialExpression.FRIENDLY, "Prayers have all sorts of wonderful benefits! From boosting defence and damage, to protecting you from outside damage, to saving items on death!") - 65 -> npcl(FacialExpression.FRIENDLY, "For your friend and ignore lists, it's quite simple really! Use your friend list to keep track of players who you like, and ignore those you don't!") + 60 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Greetings! I'd just like to briefly go over two topics with you: Prayer, and Friend's.")) + 62 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Prayers have all sorts of wonderful benefits! From boosting defence and damage, to protecting you from outside damage, to saving items on death!")) + 65 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "For your friend and ignore lists, it's quite simple really! Use your friend list to keep track of players who you like, and ignore those you don't!")) } return true } @@ -35,8 +36,8 @@ class TutorialPrayerDialogue(player: Player? = null) : DialoguePlugin(player) { when(getAttribute(player, "tutorial:stage", 0)) { 60 -> when(stage++){ - 0 -> playerl(FacialExpression.FRIENDLY, "Alright, sounds fun!") - 1 -> npcl(FacialExpression.FRIENDLY, "Right, so first thing: Prayer. Prayer is trained by offering bones to the gods, and can grant you many boons!") + 0 -> setUnclosable(player, playerl(FacialExpression.FRIENDLY, "Alright, sounds fun!")) + 1 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Right, so first thing: Prayer. Prayer is trained by offering bones to the gods, and can grant you many boons!")) 2 -> { end() setAttribute(player, "tutorial:stage", 61) @@ -45,8 +46,8 @@ class TutorialPrayerDialogue(player: Player? = null) : DialoguePlugin(player) { } 62 -> when(stage++){ - 0 -> playerl(FacialExpression.AMAZED, "Very cool!") - 1 -> npcl(FacialExpression.FRIENDLY, "Next up, let's talk about friends.") + 0 -> setUnclosable(player, playerl(FacialExpression.AMAZED, "Very cool!")) + 1 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Next up, let's talk about friends.")) 2 -> { end() setAttribute(player, "tutorial:stage", 63) diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt index 015928854..716bc2cf4 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt @@ -12,6 +12,7 @@ import core.plugin.Initializable import org.rs09.consts.Components import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable /** * Handles the quest guide's dialogue @@ -27,7 +28,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : DialoguePlugin(player npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 27 -> Component.setUnclosable( + 27 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -37,7 +38,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : DialoguePlugin(player ) ) - 28 -> Component.setUnclosable( + 28 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -57,7 +58,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : DialoguePlugin(player when(getAttribute(player, "tutorial:stage", 0)) { 27 -> { - Component.setUnclosable( + setUnclosable( player, interpreter.sendPlaneMessageWithBlueTitle( "Open the Quest Journal.", @@ -72,7 +73,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : DialoguePlugin(player } 28 -> when(stage) { - 0 -> Component.setUnclosable( + 0 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -83,7 +84,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : DialoguePlugin(player "to begin." ) ).also { stage++ } - 1 -> Component.setUnclosable( + 1 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -93,7 +94,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : DialoguePlugin(player "see marking my house." ) ).also { stage++ } - 2 -> Component.setUnclosable( + 2 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -118,5 +119,4 @@ class TutorialQuestGuideDialogue(player: Player? = null) : DialoguePlugin(player override fun getIds(): IntArray { return intArrayOf(NPCs.QUEST_GUIDE_949) } - } diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt index 4d7653695..b3611f89e 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt @@ -1,7 +1,6 @@ package content.region.misc.tutisland.dialogue import core.api.setAttribute -import core.game.component.Component import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -9,10 +8,12 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable /** - * Handles the RuneSccape guide's dialogue + * Handles the 2009scape guide's dialogue * @author Ceikry + * @author Player Name */ @Initializable class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -23,26 +24,12 @@ class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC val tutStage = player?.getAttribute("tutorial:stage", 0) ?: 0 - if(tutStage < 2) { - end() - player.dialogueInterpreter.sendDialogues(npc,FacialExpression.HALF_GUILTY,"Greetings! Please follow the onscreen, instructions!") - return false - } else { - Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - FacialExpression.HALF_GUILTY, - "Greetings! Please follow the onscreen", - "instructions!" - ) - ) - } - - if(tutStage == 2) - { + if (tutStage < 2) { + setUnclosable(player, player.dialogueInterpreter.sendDialogues(npc,FacialExpression.HALF_GUILTY,"Greetings! Please follow the onscreen instructions!")) + stage = 99 + } else if (tutStage == 2) { player.lock() - Component.setUnclosable( + setUnclosable( player, interpreter.sendDialogues( npc, @@ -52,26 +39,17 @@ class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { ) ) stage = 0 - return true - } - else - { - Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - FacialExpression.HALF_GUILTY, - "Please follow the onscreen instructions!" - ) - ) - return false + } else { + setUnclosable(player, player.dialogueInterpreter.sendDialogues(npc,FacialExpression.HALF_GUILTY,"Please follow the onscreen instructions!")) + stage = 99 } + return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage) { - 0 -> Component.setUnclosable( + 0 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -81,7 +59,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { ) ).also { stage++ } - 1 -> Component.setUnclosable( + 1 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -92,7 +70,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { ) ).also { stage++ } - 2 -> Component.setUnclosable( + 2 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -104,7 +82,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { ) ).also { stage++ } - 3 -> Component.setUnclosable( + 3 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -114,7 +92,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { ) ).also { stage++ } - 4 -> Component.setUnclosable( + 4 -> setUnclosable( player, interpreter.sendDialogues( npc, @@ -130,6 +108,11 @@ class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { setAttribute(player, "tutorial:stage", 3) TutorialStage.load(player, 3) } + 99 -> { + end() + val tutStage = player?.getAttribute("tutorial:stage", 0) ?: 0 + TutorialStage.load(player, tutStage) + } } return true } diff --git a/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java b/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java index 6d7b8a5b1..56845ac8b 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java +++ b/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.world.map.Location; +import content.data.Quests; /** * Handles the tutorial rat npc. @@ -65,7 +66,7 @@ public class RatTutorialNPC extends AbstractNPC { } final Player p = ((Player) killer); if (killer instanceof Player) { - if (p.getQuestRepository().getQuest("Witch's Potion").isStarted(p)) { + if (p.getQuestRepository().getQuest(Quests.WITCHS_POTION).isStarted(p)) { GroundItemManager.create(new Item(300), getLocation(), p); } } diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialArea.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialArea.kt new file mode 100644 index 000000000..082da760b --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialArea.kt @@ -0,0 +1,10 @@ +package content.region.misc.tutisland.handlers + +import core.api.* +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction + +class TutorialArea : MapArea { + override fun defineAreaBorders() : Array { return arrayOf(12079, 12080, 12335, 12336, 12436, 12592).map { ZoneBorders.forRegion(it) }.toTypedArray() } + override fun getRestrictions() : Array { return arrayOf(ZoneRestriction.RANDOM_EVENTS) } +} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialDialogs.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialDialogs.kt new file mode 100644 index 000000000..78a493dcd --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialDialogs.kt @@ -0,0 +1,554 @@ +package content.region.misc.tutisland.handlers + +import core.api.getAttribute +import core.api.inInventory +import core.game.component.Component.setUnclosable +import core.game.event.DialogueCloseEvent +import core.game.event.EventHook +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.GameWorld.settings +import org.rs09.consts.Items +import kotlin.also + +fun sendStageDialog(player: Player, stage: Int) { + val message = when (stage) { + 0 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Getting started", + "To start the tutorial use your left mouse button to click on the", + "" + settings!!.name + " Guide in this room. He is indicated by a flashing", + "yellow arrow above his head. If you can't see him, use your", + "keyboard's arrow keys to rotate the view." + ) + 1 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "", + "Game options", + "Please click on the flashing spanner icon found at the bottom", + "right of your screen. This will display your game options." + ) + 2 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Game Options", + "In the interface, you can now see a variety of options such as", + "screen brightness, sound and music volume and whether you", + "want to accept aid from other player's or not. Don't worry", + "about these too much for now; they will become easier as you", + "explore the game. Talk to the " + settings!!.name + " Guide to continue." + ) + 3 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Interacting with scenery", + "You can interact with many items of scenery by simply clicking", + "on them. Right clicking will also give more options. Feel free to", + "try it with the things in this room, then click on the door", + "indicated with the yellow arrow to go though to the next", + "instructor." + ) + 4 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Moving around", + "Follow the path to find the next instructor. Clicking on the", + "ground will walk you to that point. You can also navigate by", + "clicking on the minimap in the top-right corner of your screen.", + "Talk to Survival Expert by the pond to continue the tutorial." + ) + 5 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Viewing the items that you were given.", + "Click on the flashing backpack icon to the right-hand side of", + "the main window to view your inventory. Your inventory is a list", + "of everything you have in your backpack.", + "" + ) + 6 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Cut down a tree", + "You can click on the backpack icon at any time to view the", + "items that you currently have in your inventory. You will see", + "that you now have an axe in your inventory. Use this to get", + "some logs by clicking on one of the trees in the area." + ) + 7 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "Your character is now attempting to cut down the tree. Sit back", + "for a moment while " + (if (player.appearance.isMale) "he" else "she") + " does all the hard work.", + "" + ) + 8 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Making a fire", + "Well done! You managed to cut some logs from the tree! Next,", + "use the tinderbox in your inventory to light the logs.", + "First click on the tinderbox to 'use' it.", + "Then click on the logs in your inventory to light them." + ) + 9 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "Your character is now attempting to light the fire.", + "This should only take a few seconds.", + "" + ) + 10 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "You gained some experience.", + "", + "Click on the flashing bar graph icon near the inventory button", + "to see your skill state.", + "" + ) + 11 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Your skill stats", + "Here you will see how good your skills are. As you move your", + "mouse over any of the icons in this tab, the small yellow popup", + "box will show you the exact amount of experience you have", + "and how much is needed to get to the next level. Speak to the survival guide." + ) + 12 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Catch some shrimp", + "Click on the bubbling fishing spot, indicated by the flashing", + "arrow. Remember, you can check your inventory by clicking the", + "backpack icon.", + "" + ) + 13 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "This should only take a few seconds.", + "As you gain Fishing experience you'll find that there are many", + "types of fish and many ways to catch them." + ) + 14 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Cooking your shrimp", + "Now you have caught some shrimp, let's cook it. First light a", + "fire: chop down a tree and then use the tinderbox on the logs.", + "If you've lost your axe or tinderbox Brynna will give you", + "another." + ).also { + if (!inInventory(player, Items.RAW_SHRIMPS_317, 1)) { + setUnclosable( + player, + player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Cooking your shrimp", + "Now right click on the shrimp and select the use option. Next,", + "left click on the fire you just lit. If while doing this you look in", + "the top left of the screen, you will see the instruction that", + "you're giving your character." + ) + ) + } + } + 15 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Burning your shrimp", + "You have just burnt your first shrimp. This is normal. As you", + "get more experience in Cooking you will burn stuff less often.", + "Let's try cooking without burning it this time. First catch some", + "more shrimp, then use them on a fire." + ) + 16 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Well done, you've just cooked your first " + settings!!.name + " meal.", + "If you'd like a recap on anything you've learnt so far, speak to", + "the Survival Expert. You can now move on to the next", + "instructor. Click on the gate shown and follow the path.", + "Remember, you can move the camera with the arrow keys." + ) + 17 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Find your next instructor", + "Follow the path until you get to the door with the yellow arrow", + "above it. Click on the door to open it. Notice the mini map in the", + "top right; this shows a top down view of the area around you.", + "This can also be used for navigation." + ) + 18 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Find your next instructor", + "Talk to the chef indicated. He will teach you the more advanced", + "aspects of Cooking such as combining ingredients. He will also", + "teach you about your Music Player.", + "" + ) + 19 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Making dough", + "This is the base for many of the meals. To make dough we must", + "mix flour and water. First, right click the bucket of water and", + "select use, then left click on the pot of flour.", + "" + ) + 20 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Cooking dough", + "Now you have made dough, you can cook it. To cook the dough,", + "use it with the range shown by the arrow. If you lose your", + "dough, talk to Lev - he will give you more ingredients.", + "" + ) + 21 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Cooking dough", + "Well done! Your first loaf of bread. As you gain experience in", + "Cooking, you will be able to make other things like pies, cakes", + "and even kebabs. Now you've got the hang of cooking, let's", + "move on. Click on the flashing icon in the bottom right to see", + "the flashing icon in the bottom right to see the Music Player." + ) + 22 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "The Music Player", + "From this interface you can control the music that is played.", + "As you explore the world and complete quests, more of the", + "tunes will become unlocked. Once you've examined this menu,", + "use the next door to continue. If you need a recap on anything", + "you've learnt so far, speak to the Master Chef." + ) + 23 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Emotes", + "", + "Now how about showing some feelings? You will see a flashing", + "icon in the shape of a person. Click on that to access your", + "emotes." + ) + 24 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Emotes", + "For those situations where words don't quite describe how you feel try", + "an emote. Go ahead try one out! You might notice that some of the", + "emotes are grey and cannot be used now. Don't worry! As you", + "progress further into the game you'll gain access to all sorts of things." + ) + 25 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Running", + "", + "It's only a short distance to the next guide.", + "Why not try running there? To do this, click on the run icon", + "next to the minimap." + ) + 26 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Run to the next guide", + "Now that you have the run button turned on, follow the path", + "until you come to the end. You may notice that the number on", + "the button goes down. This is your run energy. If your run", + "energy reaches zero, you'll stop running. Click on the door to", + "pass through it." + ) + 27 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Talk with the Quest Guide.", + "", + "He will tell you all about quests.", + "", + "" + ) + 28 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Your Quest Journal", + "", + "This is your Quest Journal, a list of all the quests in the game.", + "Talk to the Quest Guide again for an explanation.", + "" + ) + 29 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "Moving on", + "It's time to enter some caves. Click on the ladder to go down to", + "the next area.", + "" + ) + 30 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Mining and Smithing", + "", + "Next let's get you a weapon, or more to the point, you can", + "make your first weapon yourself. Don't panic, the Mining", + "Instructor will help you. Talk to him and he'll tell you all about it." + ) + 31 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Prospecting", + "To prospect a mineable rock, just right click it and select the", + "'prospect rock' option. This will tell you the type of ore you can", + "mine from it. Try it now on one of the rocks indicated.", + "" + ) + 32 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "Your character is now attempting to prospect the rock. This", + "should only take a few seconds.", + "" + ) + 33 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "It's tin.", + "", + "So now you know there's tin in the grey rocks, try prospecting the", + "brown ones next.", + "" + ) + 34 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "It's copper.", + "", + "Talk to the Mining Instructor to find out about these types of", + "ore and how you can mine them.", + "He'll even give you the required tools.", + ) + 35 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Mining", + "", + "It's quite simple really. All you need to do is right click on the", + "rock and select 'mine' You can only mine when you have a", + "pickaxe. So give it a try: first mine one tin ore.", + ) + 36 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "Your character is now attempting to mine the rock.", + "This should only take a few seconds.", + "" + ) + 37 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Mining", + "", + "Now you have some tin ore you just need some copper ore,", + "then you'll have all you need to create a bronze bar. As you", + "did before right click on the copper rock and select 'mine'." + ) + 38 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Smelting", + "You should now have both some copper and tin ore. So let's", + "smelt them to make a bronze bar. To do this, right click on", + "either tin or copper ore and select use then left click on the", + "furnace. Try it now." + ) + 39, 40 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "You've made a bronze bar!", + "", + "Speak to the Mining Instructor and he'll show you how to make", + "it into a weapon.", + "" + ) + 41 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Smithing a dagger", + "To smith you'll need a hammer - like the one you were given by", + "Dezzick - access to an anvil like the one with the arrow over it", + "and enough metal bars to make what you are trying to smith.", + "To start the process, use the bar on one of the anvils." + ) + 42 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Smithing a dagger.", + "Now you have the Smithing menu open, you will see a list of all", + "the things you can make. Only the dagger can be made at your", + "skill level; this is shown by the white text under it. You'll need", + "to select the dagger to continue." + ) + 43 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "You've finished in this area.", + "", + "So let's move on. Go through the gates shown by the arrow.", + "Remember, you may need to move the camera to see your", + "surroundings. Speak to the guide for a recap at any time.", + ) + 44 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Combat", + "", + "In this area you will find out about combat with swords and", + "bows. Speak to the guide and he will tell you all about it.", + "" + ) + 45 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Wielding weapons", + "", + "You now have access to a new interface. Click on the flashing", + "icon of a man, the one to the right of your backpack icon.", + "" + ) + 46 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "This is your worn inventory.", + "From here you can see what items you have equipped. Let's", + "get one of those slots filled, go back to your inventory and", + "right click your dagger, select wield from the menu.", + "" + ) + 47 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "You're now holding your dagger.", + "Clothes, armour, weapons and many other items are equipped", + "like this. You can unequip items by clicking on the item in the", + "worn equipment. You can close this window by clicking on the", + "small 'x' in the top-right hand corner. Speak to the Combat", + "Instructor." + ) + 48 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Unequipping items.", + "In your worn inventory panel, right click on the dagger and", + "select the remove option from the drop down list. After you've", + "unequipped the dagger, wield the sword and shield. As you", + "pass the mouse over an item you will see its name appear at", + "the top left of the screen." + ) + 49 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Combat interface.", + "", + "Click on the flashing crossed swords icon to see the combat", + "interface.", + "" + ) + 50 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "This is your combat interface.", + "From this interface you can select the type of attack your", + "character will use. Different monsters have different", + "weaknesses. If you hover your mouse over the buttons, you", + "will see the type of XP you will receive when using each type of", + "attack. Now you have the tools needed for battle why not slay", + "some rats. Click on the gates indicated to continue." + ) + 51 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Attacking", + "To attack the rat, click it and select the attack option. You", + "will then walk over to it and start hitting it.", + "", + "" + ) + 52 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Sit back and watch.", + "While you are fighting you will see a bar over your head. The", + "bar shows how much health you have left. Your opponent will", + "have one too. You will continue to attack the rat until it's dead", + "or you do something else." + ) + 53 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Well done, you've made your first kill!", + "", + "Pass through the gate and talk to the Combat Instructor; he", + "will give you your next task.", + "" + ) + 54 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Rat ranging", + "Now you have a bow and some arrows. Before you can use", + "them you'll need to equip them. Once equipped with the", + "ranging gear try killing another rat. Remember: to attack, right", + "click on the monster and select attack." + ) + 55 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Moving on.", + "You have completed the tasks here. To move on, click on the", + "ladder shown. If you need to go over any of what you learnt", + "here, just talk to the Combat Instructor and he'll tell you what", + "he can." + ) + 56 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Banking.", + "Follow the path and you will come to the front of a building.", + "This is the 'Bank of " + settings!!.name + "' where you can store all your", + "most valued items. To open your bank box just right click on an", + "open booth indicated and select 'use'." + ) + 57 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "This is your bank box.", + "You can store stuff here for safekeeping. If you die, anything", + "in your bank will be saved. To deposit something, right click it", + "and select 'Deposit-1'. Once you've had a good look, close the", + "window and move on through the door indicated." + ) + 58 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Financial advice", + "", + "The guide here will tell you all about making cash. Just click on", + "him to hear what he's got to say.", + "" + ) + 59 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "", + "Continue through the next door.", + "", + "" + ) + 60 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Prayer", + "Follow the path to the chapel and enter it.", + "Once inside talk to the monk. He'll tell you all about the Prayer", + "skill.", + "" + ) + 61 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Your Prayer List", + "", + "Click on the flashing icon to open the Prayer List.", + "", + "" + ) + 62 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "Your Prayer List", + "", + "Talk with Brother Brace and he'll tell you all about prayers.", + "" + ) + 63 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "Friends list", + "You should now see another new icon. Click on the flashing", + "smiling face to open your Friend List.", + "" + ) + 64 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "This is your Friends List.", + "", + "This will be explained by Brother Brace shortly, but first click", + "on the other flashing face in the interface.", + "" + ) + 65 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "This is your Ignore List.", + "The two lists, Friends and Ignore - can be very helpful for", + "keeping track of when your friends are online or for blocking", + "messages from people you simply don't like. Speak with", + "Brother Brace and he will tell you more." + ) + 66 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "Your final instructor!", + "You're almost finished on tutorial island. Pass through the", + "door to find the path leading to your final instructor.", + "" + ) + 67 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Your final instructor!", + "Just follow the path to the Wizard's house, where you will be", + "shown how to cast spells. Just talk with the mage indicated to", + "find out more.", + "" + ) + 68 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Open up your final tab.", + "", + "Open up the Magic Spellbook tab by clicking on the flashing", + "icon next to the Prayer List tab you just learned about.", + "" + ) + 69 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "This is your spell list.", + "", + "Ask the mage about it.", + "" + ) + 70 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Cast Wind Strike at a chicken.", + "Now you have the runes you should see the Wind Strike icon at the", + "top-left of your spellbook, second in from the left. Walk over", + "to the caged chickens, click the Wind Strike icon and then", + "select one of the chickens to cast it on. It may take several", + "tries." + ) + 71 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "You have almost completed the tutorial!", + "", + "All you need to do now is teleport to the mainland. Just speak", + "with Terrova and he'll tell you how to do that.", + "" + ) + else -> null + } + setUnclosable(player, message ?: return) +} + +fun sendStageDialog(entity: Entity) { + val player = entity as Player + val stage = getAttribute(player, "tutorial:stage", 0) + sendStageDialog(player, stage) +} + +// Because we don't have proper interface stacking, the above unclosable dialogs can still be closed, if a new dialog +// opens up over it and that dialog *is* closable. This is the case for npc dialogs and the smelting interface. Fake the +// authentic behavior by making sure we reopen our unclosable dialog when this happens. +object TutorialDialogPreserver : EventHook { + override fun process(entity: Entity, event: DialogueCloseEvent) { + sendStageDialog(entity) + } +} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt index 655c16d1c..c65191bb1 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt @@ -7,13 +7,30 @@ import core.game.node.entity.player.Player import content.global.skill.fishing.FishingSpot import content.global.skill.gather.mining.MiningNode import content.global.skill.gather.woodcutting.WoodcuttingNode +import core.api.animate +import core.api.delayScript +import core.api.forceWalk +import core.api.inInventory +import core.api.lock +import core.api.lockInteractions +import core.api.playAudio +import core.api.queueScript +import core.api.replaceSlot +import core.api.stopExecuting import core.game.event.* +import core.game.interaction.QueueStrength +import core.game.node.entity.impl.Animator +import core.game.node.item.Item +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds /** * Event receivers for tutorial island - * @author Ceikry + * @author Ceikry, Player Name */ object TutorialButtonReceiver : EventHook { @@ -77,7 +94,7 @@ object TutorialButtonReceiver : EventHook } //Open equipment tab SD:548,42 HD:746,45 - 45 -> if((event.iface == 548 && event.buttonId == 42) || (event.iface == 746 && event.buttonId == 45)){ + 45, 46 -> if((event.iface == 548 && event.buttonId == 42) || (event.iface == 746 && event.buttonId == 45)){ setAttribute(entity, "tutorial:stage", 46) TutorialStage.load(entity, 46) } @@ -136,18 +153,6 @@ object TutorialInteractionReceiver : EventHook TutorialStage.load(entity, 13) } - //Prospect rock - Tin - 31 -> if(MiningNode.forId(event.target.id)?.identifier?.equals(2.toByte()) == true && event.option == "prospect"){ - setAttribute(entity, "tutorial:stage", 32) - TutorialStage.load(entity, 32) - } - - //Prospect rock- Copper - 33 -> if(MiningNode.forId(event.target.id)?.identifier?.equals(1.toByte()) == true && event.option == "prospect"){ - setAttribute(entity, "tutorial:stage", 34) - TutorialStage.load(entity, 34) - } - //Mine rock - Tin 35 -> if(MiningNode.forId(event.target.id)?.identifier?.equals(2.toByte()) == true && event.option == "mine"){ setAttribute(entity, "tutorial:stage", 36) @@ -211,30 +216,12 @@ object TutorialResourceReceiver : EventHook TutorialStage.load(entity, 14) } - //Cook a shrimp - 14,15 -> if(event.itemId == Items.BURNT_SHRIMP_7954) - { - setAttribute(entity, "tutorial:stage", 15) - TutorialStage.load(entity, 15) - } - else if(event.itemId == Items.SHRIMPS_315) - { - setAttribute(entity, "tutorial:stage", 16) - TutorialStage.load(entity, 16) - } - //Make some bread dough 19 -> if(event.itemId == Items.BREAD_DOUGH_2307) { setAttribute(entity, "tutorial:stage", 20) TutorialStage.load(entity, 20) } - //Bake some bread - 20 -> if(event.itemId == Items.BREAD_2309 || event.itemId == Items.BURNT_BREAD_2311) { - setAttribute(entity, "tutorial:stage", 21) - TutorialStage.load(entity, 21) - } - //Mine some tin ore 36 -> if(event.itemId == Items.TIN_ORE_438){ setAttribute(entity, "tutorial:stage", 37) diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialFurnaceListener.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialFurnaceListener.kt deleted file mode 100644 index c4139d6f5..000000000 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialFurnaceListener.kt +++ /dev/null @@ -1,50 +0,0 @@ -package content.region.misc.tutisland.handlers - -import core.api.* -import core.game.event.ResourceProducedEvent -import core.game.node.entity.skill.Skills -import content.global.skill.smithing.smelting.Bar -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items -import org.rs09.consts.Scenery -import core.game.interaction.IntType -import core.game.interaction.InteractionListener - -/** - * Listener for tutorial island furnace - * @author Byte - */ -class TutorialFurnaceListener : InteractionListener { - - companion object { - private val ANIMATION = Animation(833) - - private val ORES = intArrayOf( - Items.TIN_ORE_438, - Items.COPPER_ORE_436 - ) - } - - override fun defineListeners() { - onUseWith(IntType.SCENERY, ORES, Scenery.FURNACE_3044) { player, _, _ -> - if (!inInventory(player, Items.TIN_ORE_438) || !inInventory(player, Items.COPPER_ORE_436)) { - return@onUseWith true - } - - animate(player, ANIMATION) - submitIndividualPulse(player, object: Pulse(2) { - override fun pulse(): Boolean { - removeItem(player, Items.TIN_ORE_438) - removeItem(player, Items.COPPER_ORE_436) - addItem(player, Items.BRONZE_BAR_2349) - rewardXP(player, Skills.SMITHING, Bar.BRONZE.experience) - player.dispatch(ResourceProducedEvent(Items.BRONZE_BAR_2349, 1, player, Items.COPPER_ORE_436)) - return true - } - }) - - return@onUseWith true - } - } -} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt index c7dd26f50..5a143acfb 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt @@ -1,13 +1,25 @@ package content.region.misc.tutisland.handlers +import content.global.skill.smithing.smelting.Bar +import content.region.misc.tutisland.dialogue.RatPenDialogue import core.api.* +import core.game.event.ResourceProducedEvent import core.game.node.scenery.Scenery import core.game.system.task.Pulse import core.game.world.map.Location import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.interaction.QueueStrength +import core.game.node.entity.impl.Animator +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item import core.game.world.repository.Repository +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items +import org.rs09.consts.Sounds /** * Handles tutorial-specific node interactions @@ -16,10 +28,13 @@ import core.game.world.repository.Repository class TutorialListeners : InteractionListener { val GUIDE_HOUSE_DOOR = 3014 val COOKS_DOOR = 3017 + val RANGE = 3039 val COOKS_EXIT = 3018 val QUEST_ENTER = 3019 val QUEST_LADDER = 3029 val QUEST_EXIT_LADDER = 3028 + val TIN_ROCK = 3043 + val COPPER_ROCK = 3042 val COMBAT_EXIT = 3030 val BANK_EXIT = 3024 val FINANCE_EXIT = 3025 @@ -27,6 +42,7 @@ class TutorialListeners : InteractionListener { val FIRST_GATE = intArrayOf(3015,3016) val COMBAT_GATES = intArrayOf(3020,3021) val RAT_GATES = intArrayOf(3022, 3023) + val FURNACE = 3044 override fun defineListeners() { on(GUIDE_HOUSE_DOOR, IntType.SCENERY, "open"){ player, door -> @@ -60,6 +76,36 @@ class TutorialListeners : InteractionListener { return@on true } + fun cookBread(player: Player, dough: Item): Boolean { + if (getAttribute(player, "tutorial:stage", 0) < 20) { + return true + } + // Need to reinvent the wheel of cooking. Yes, I do. On tutorial island, we don't want the default stuff like asking the player what dough they want to make. + queueScript(player, 0, QueueStrength.WEAK) { stage -> + if (stage == 0) { + val RANGE_ANIMATION = Animation(883, Animator.Priority.HIGH) + lock(player, RANGE_ANIMATION.duration) + lockInteractions(player, RANGE_ANIMATION.duration) + animate(player, RANGE_ANIMATION) + playAudio(player, Sounds.FRY_2577) + return@queueScript delayScript(player, RANGE_ANIMATION.duration) + } else { + replaceSlot(player, dough.slot, Item(Items.BREAD_2309), dough) + setAttribute(player, "tutorial:stage", 21) + TutorialStage.load(player, 21) + return@queueScript stopExecuting(player) + } + } + return true + } + on(RANGE, IntType.SCENERY, "use") { player, _ -> + val dough = player.inventory.get(Item(Items.BREAD_DOUGH_2307)) ?: return@on true + return@on cookBread(player, dough) + } + onUseWith(IntType.SCENERY, Items.BREAD_DOUGH_2307, RANGE) { player, dough, _ -> + return@onUseWith cookBread(player, dough as Item) + } + on(COOKS_EXIT, IntType.SCENERY, "open"){ player, door -> if(getAttribute(player, "tutorial:stage", 0) != 22) return@on true @@ -105,23 +151,41 @@ class TutorialListeners : InteractionListener { return@on true } - on(COMBAT_GATES, IntType.SCENERY, "open"){ player, gate -> - if(getAttribute(player, "tutorial:stage", 0) != 43) + on(TIN_ROCK, IntType.SCENERY, "prospect") { player, _ -> + if (getAttribute(player, "tutorial:stage", 0) != 31) { return@on true + } + setAttribute(player, "tutorial:stage", 32) + TutorialStage.load(player, 32) + return@on true + } + on(COPPER_ROCK, IntType.SCENERY, "prospect") { player, _ -> + if (getAttribute(player, "tutorial:stage", 0) != 33) { + return@on true + } + setAttribute(player, "tutorial:stage", 34) + TutorialStage.load(player, 34) + return@on true + } - setAttribute(player, "tutorial:stage", 44) - TutorialStage.load(player, 44) + on(COMBAT_GATES, IntType.SCENERY, "open"){ player, gate -> + if (getAttribute(player, "tutorial:stage", 0) < 43) { + return@on true + } + if (getAttribute(player, "tutorial:stage", 0) == 43) { + setAttribute(player, "tutorial:stage", 44) + TutorialStage.load(player, 44) + } core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, gate as Scenery) } on(RAT_GATES, IntType.SCENERY, "open") { player, gate -> val stage = getAttribute(player, "tutorial:stage", 0) - if(stage !in 50..53){ - player.dialogueInterpreter.sendDialogues(NPCs.COMBAT_INSTRUCTOR_944, core.game.dialogue.FacialExpression.ANGRY, "Oi, get away from there!","Don't enter my rat pen unless I say so!") + if (stage !in 50..53) { + openDialogue(player, RatPenDialogue(), NPC(NPCs.COMBAT_INSTRUCTOR_944)) return@on true } - - if(stage == 50) { + if (stage == 50) { setAttribute(player, "tutorial:stage", 51) TutorialStage.load(player, 51) } @@ -130,11 +194,14 @@ class TutorialListeners : InteractionListener { } on(COMBAT_EXIT, IntType.SCENERY, "climb-up") { player, ladder -> - if(getAttribute(player, "tutorial:stage", 0) != 55) + val stage = getAttribute(player, "tutorial:stage", 0) + if (stage < 55) { return@on true - - setAttribute(player, "tutorial:stage", 56) - TutorialStage.load(player, 56) + } + if (stage == 55) { + setAttribute(player, "tutorial:stage", 56) + TutorialStage.load(player, 56) + } core.game.global.action.ClimbActionHandler.climbLadder(player, ladder.asScenery(), "climb-up") } @@ -165,5 +232,28 @@ class TutorialListeners : InteractionListener { core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door as Scenery) } + fun smeltBronzeBar(player: Player): Boolean { + if (getAttribute(player, "tutorial:stage", 0) < 38) { + return true + } + if (!inInventory(player, Items.COPPER_ORE_436) || !inInventory(player, Items.TIN_ORE_438)) { + return true + } + animate(player, 833) + queueScript(player, 2, QueueStrength.WEAK) { + if (removeItem(player, Items.COPPER_ORE_436) && removeItem(player, Items.TIN_ORE_438)) { + addItem(player, Items.BRONZE_BAR_2349) + rewardXP(player, Skills.SMITHING, Bar.BRONZE.experience) + player.dispatch(ResourceProducedEvent(Items.BRONZE_BAR_2349, 1, player, Items.COPPER_ORE_436)) + TutorialStage.load(player, 39) + } + return@queueScript stopExecuting(player) + } + return true + } + on(FURNACE, IntType.SCENERY, "use") { player, _ -> smeltBronzeBar(player) } + for (item in arrayOf(Items.COPPER_ORE_436, Items.TIN_ORE_438)) { + onUseWith(IntType.SCENERY, item, FURNACE) { player, _, _ -> smeltBronzeBar(player) } + } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt index 0fafa1c84..50eeddb00 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt @@ -12,13 +12,13 @@ import core.game.world.map.Location import org.rs09.consts.Components import core.api.Event import core.game.world.GameWorld.Pulser -import core.game.world.GameWorld.settings import core.game.world.repository.Repository -import org.rs09.consts.Items +import org.rs09.consts.NPCs /** * Loads stage-relevant tutorial data * @author Ceikry + * @author Player Name */ object TutorialStage { /** @@ -26,28 +26,30 @@ object TutorialStage { * @param player the player to perform the actions on * @param stage the stage to load */ - fun load(player: Player, stage: Int, login: Boolean = false){ - if(login) - { + fun load(player: Player, stage: Int, login: Boolean = false) { + if (login) { player.hook(Event.ButtonClicked, TutorialButtonReceiver) player.hook(Event.Interacted, TutorialInteractionReceiver) player.hook(Event.ResourceProduced, TutorialResourceReceiver) player.hook(Event.UsedWith, TutorialUseWithReceiver) player.hook(Event.FireLit, TutorialFireReceiver) player.hook(Event.NPCKilled, TutorialKillReceiver) + player.hook(Event.DialogueClosed, TutorialDialogPreserver) openOverlay(player, Components.TUTORIAL_PROGRESS_371) player.packetDispatch.sendInterfaceConfig(371, 4, true) } - updateProgressBar(player) - when(stage) - { + when(stage) { 0 -> { lock(player, 10) teleport(player, Location.create(3094, 3107, 0)) hideTabs(player, login) CharacterDesign.open(player) + // We have two dialogs in this stage. This is awkward, but not a problem. + // The first dialog is impossible to close in any way, so we can send it here manually. + // The second dialog could be lost by e.g. talking to an npc, so this dialog gets implemented in + // TutorialDialogs.kt, which has the hook for restoring it if it does get lost. Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( @@ -59,380 +61,133 @@ object TutorialStage { ) ).also { runTask(player, 10) { - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Getting started", - "To start the tutorial use your left mouse button to click on the", - "" + settings!!.name + " Guide in this room. He is indicated by a flashing", - "yellow arrow above his head. If you can't see him, use your", - "keyboard's arrow keys to rotate the view." - ) - ) + sendStageDialog(player, stage) } } } - - 1 -> { hideTabs(player, login) player.interfaceManager.openTab(Component(Components.OPTIONS_261)) setVarbit(player, 3756, 12) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "", - "Game options", - "Please click on the flashing spanner icon found at the bottom", - "right of your screen. This will display your game options." - ) - ) + sendStageDialog(player, stage) } - 2 -> { setVarbit(player, 3756, 0) hideTabs(player, login) - registerHintIcon(player, Repository.findNPC(945)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Game Options", - "In the interface, you can now see a variety of options such as", - "screen brightness, sound and music volume and whether you", - "want to accept aid from other player's or not. Don't worry", - "about these too much for now; they will become easier as you", - "explore the game. Talk to the " + settings!!.name + " Guide to continue." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.RUNESCAPE_GUIDE_945)!!) + sendStageDialog(player, stage) } - 3 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3098, 3107, 0), 125) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Interacting with scenery", - "You can interact with many items of scenery by simply clicking", - "on them. Right clicking will also give more options. Feel free to", - "try it with the things in this room, then click on the door", - "indicated with the yellow arrow to go though to the next", - "instructor." - ) - ) + sendStageDialog(player, stage) } - 4 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(943)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Moving around", - "Follow the path to find the next instructor. Clicking on the", - "ground will walk you to that point. You can also navigate by", - "clicking on the minimap in the top-right corner of your screen.", - "Talk to Survival Expert by the pond to continue the tutorial." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.SURVIVAL_EXPERT_943)!!) + sendStageDialog(player, stage) } - 5 -> { hideTabs(player, login) player.interfaceManager.openTab(Component(Components.INVENTORY_149)) setVarbit(player, 3756, 4) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Viewing the items that you were given.", - "Click on the flashing backpack icon to the right-hand side of", - "the main window to view your inventory. Your inventory is a list", - "of everything you have in your backpack.", - "" - ) - ) + sendStageDialog(player, stage) } - 6 -> { hideTabs(player, login) setVarbit(player, 3756, 4) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Cut down a tree", - "You can click on the backpack icon at any time to view the", - "items that you currently have in your inventory. You will see", - "that you now have an axe in your inventory. Use this to get", - "some logs by clicking on one of the trees in the area." - ) - ) + sendStageDialog(player, stage) } - 7 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "Your character is now attempting to cut down the tree. Sit back", - "for a moment while " + (if (player.appearance.isMale) "he" else "she") + " does all the hard work.", - "" - ) - ) + sendStageDialog(player, stage) } - 8 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Making a fire", - "Well done! You managed to cut some logs from the tree! Next,", - "use the tinderbox in your inventory to light the logs.", - "First click on the tinderbox to 'use' it.", - "Then click on the logs in your inventory to light them." - ) - ) + sendStageDialog(player, stage) } - 9 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "Your character is now attempting to light the fire.", - "This should only take a few seconds.", - "" - ) - ) + sendStageDialog(player, stage) } - 10 -> { hideTabs(player, login) player.interfaceManager.openTab(Component(Components.STATS_320)) setVarbit(player, 3756, 2) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "You gained some experience.", - "", - "Click on the flashing bar graph icon near the inventory button", - "to see your skill state.", - "" - ) - ) + sendStageDialog(player, stage) } - 11 -> { hideTabs(player, login) setVarbit(player, 3756, 2) - registerHintIcon(player, Repository.findNPC(943)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your skill stats", - "Here you will see how good your skills are. As you move your", - "mouse over any of the icons in this tab, the small yellow popup", - "box will show you the exact amount of experience you have", - "and how much is needed to get to the next level. Speak to the survival guide." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.SURVIVAL_EXPERT_943)!!) + sendStageDialog(player, stage) } - 12 -> { hideTabs(player, login) setVarp(player, 406, 2) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(952)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Catch some shrimp", - "Click on the bubbling fishing spot, indicated by the flashing", - "arrow. Remember, you can check your inventory by clicking the", - "backpack icon.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.FISHING_SPOT_952)!!) + sendStageDialog(player, stage) } - 13 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "This should only take a few seconds.", - "As you gain Fishing experience you'll find that there are many", - "types of fish and many ways to catch them." - ) - ) + sendStageDialog(player, stage) } - 14 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Cooking your shrimp", - "Now you have caught some shrimp, let's cook it. First light a", - "fire: chop down a tree and then use the tinderbox on the logs.", - "If you've lost your axe or tinderbox Brynna will give you", - "another." - ).also { - if (!inInventory(player, Items.RAW_SHRIMPS_317, 1)) { - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Cooking your shrimp", - "Now right click on the shrimp and select the use option. Next,", - "left click on the fire you just lit. If while doing this you look in", - "the top left of the screen, you will see the instruction that", - "you're giving your character." - ) - ) - } - } - ) + sendStageDialog(player, stage) } - 15 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Burning your shrimp", - "You have just burnt your first shrimp. This is normal. As you", - "get more experience in Cooking you will burn stuff less often.", - "Let's try cooking without burning it this time. First catch some", - "more shrimp, then use them on a fire." - ) - ) + sendStageDialog(player, stage) } - 16 -> { hideTabs(player, login) + removeHintIcon(player) registerHintIcon(player, Location.create(3089, 3091, 0), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Well done, you've just cooked your first " + settings!!.name + " meal.", - "If you'd like a recap on anything you've learnt so far, speak to", - "the Survival Expert. You can now move on to the next", - "instructor. Click on the gate shown and follow the path.", - "Remember, you can move the camera with the arrow keys." - ) - ) + sendStageDialog(player, stage) } - 17 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Location.create(3078, 3084, 0), 125) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Find your next instructor", - "Follow the path until you get to the door with the yellow arrow", - "above it. Click on the door to open it. Notice the mini map in the", - "top right; this shows a top down view of the area around you.", - "This can also be used for navigation." - ) - ) + registerHintIcon(player, Location.create(3079, 3084, 0), 125) + sendStageDialog(player, stage) } - 18 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(942)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Find your next instructor", - "Talk to the chef indicated. He will teach you the more advanced", - "aspects of Cooking such as combining ingredients. He will also", - "teach you about your Music Player.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MASTER_CHEF_942)!!) + sendStageDialog(player, stage) } - 19 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Making dough", - "This is the base for many of the meals. To make dough we must", - "mix flour and water. First, right click the bucket of water and", - "select use, then left click on the pot of flour.", - "" - ) - ) + sendStageDialog(player, stage) } - 20 -> { hideTabs(player, login) registerHintIcon(player, Location.create(3076, 3081, 0), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Cooking dough", - "Now you have made dough, you can cook it. To cook the dough,", - "use it with the range shown by the arrow. If you lose your", - "dough, talk to Lev - he will give you more ingredients.", - "" - ) - ) + sendStageDialog(player, stage) } - 21 -> { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.MUSIC_V3_187)) setVarbit(player, 3756, 14) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Cooking dough", - "Well done! Your first loaf of bread. As you gain experience in", - "Cooking, you will be able to make other things like pies, cakes", - "and even kebabs. Now you've got the hang of cooking, let's", - "move on. Click on the flashing icon in the bottom right to see", - "the flashing icon in the bottom right to see the Music Player." - ) - ) + sendStageDialog(player, stage) } - 22 -> { hideTabs(player, login) setVarbit(player, 3756, 0) - registerHintIcon(player, Location.create(3072, 3090, 0), 125) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "The Music Player", - "From this interface you can control the music that is played.", - "As you explore the world and complete quests, more of the", - "tunes will become unlocked. Once you've examined this menu,", - "use the next door to continue. If you need a recap on anything", - "you've learnt so far, speak to the Master Chef." - ) - ) + registerHintIcon(player, Location.create(3073, 3090, 0), 125) + sendStageDialog(player, stage) } - 23 -> { hideTabs(player, login) setVarbit(player, 3756, 13) @@ -440,159 +195,59 @@ object TutorialStage { player.interfaceManager.openTab(Component(Components.EMOTES_464)) stopWalk(player) player.locks.lockMovement(100000) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Emotes", - "", - "Now how about showing some feelings? You will see a flashing", - "icon in the shape of a person. Click on that to access your", - "emotes." - ) - ) + sendStageDialog(player, stage) } - 24 -> { hideTabs(player, login) setVarbit(player, 3756, 0) player.locks.lockMovement(100000) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Emotes", - "For those situations where words don't quite describe how you feel try", - "an emote. Go ahead try one out! You might notice that some of the", - "emotes are grey and cannot be used now. Don't worry! As you", - "progress further into the game you'll gain access to all sorts of things." - ) - ) + sendStageDialog(player, stage) } - 25 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Running", - "", - "It's only a short distance to the next guide.", - "Why not try running there? To do this, click on the run icon", - "next to the minimap." - ) - ) + sendStageDialog(player, stage) } - 26 -> { hideTabs(player, login) - registerHintIcon(player, Repository.findNPC(949)!!) + registerHintIcon(player, Repository.findNPC(NPCs.QUEST_GUIDE_949)!!) player.locks.unlockMovement() - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Run to the next guide", - "Now that you have the run button turned on, follow the path", - "until you come to the end. You may notice that the number on", - "the button goes down. This is your run energy. If your run", - "energy reaches zero, you'll stop running. Click on the door to", - "pass through it." - ) - ) + sendStageDialog(player, stage) } - 27 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(949)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Talk with the Quest Guide.", - "", - "He will tell you all about quests.", - "", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.QUEST_GUIDE_949)!!) + sendStageDialog(player, stage) } - 28 -> { hideTabs(player, login) setVarbit(player, 3756, 0) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your Quest Journal", - "", - "This is your Quest Journal, a list of all the quests in the game.", - "Talk to the Quest Guide again for an explanation.", - "" - ) - ) + sendStageDialog(player, stage) } - 29 -> { hideTabs(player, login) removeHintIcon(player) setVarbit(player, 3756, 0) registerHintIcon(player, Location.create(3088, 3119, 0), 15) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "Moving on", - "It's time to enter some caves. Click on the ladder to go down to", - "the next area.", - "" - ) - ) + sendStageDialog(player, stage) } - 30 -> { hideTabs(player, login) removeHintIcon(player) setVarbit(player, 3756, 0) - registerHintIcon(player, Repository.findNPC(948)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Mining and Smithing", - "", - "Next let's get you a weapon, or more to the point, you can", - "make your first weapon yourself. Don't panic, the Mining", - "Instructor will help you. Talk to him and he'll tell you all about it." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MINING_INSTRUCTOR_948)!!) + sendStageDialog(player, stage) } - 31 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3076, 9504, 0), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Prospecting", - "To prospect a mineable rock, just right click it and select the", - "'prospect rock' option. This will tell you the type of ore you can", - " mine from it. Try it now on one of the rocks indicated.", - "" - ) - ) + sendStageDialog(player, stage) } - 32 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "Your character is now attempting to prospect the rock. This", - "should only take a few seconds.", - "" - ) - ) + sendStageDialog(player, stage) Pulser.submit(object : Pulse(3) { override fun pulse(): Boolean { setAttribute(player, "tutorial:stage", 33) @@ -601,196 +256,79 @@ object TutorialStage { } }) } - 33 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3086, 9501, 0), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "It's tin.", - "", - "So now you know there's tin in the grey rocks, try prospecting the", - "brown ones next.", - "" - ) - ) + sendStageDialog(player, stage) } - 34 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(948)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "It's copper.", - "", - "Talk to the Mining Instructor to find out about these types of", - "ore and how you can mine them.", - "He'll even give you the required tools.", - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MINING_INSTRUCTOR_948)!!) + sendStageDialog(player, stage) } - 35 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3076, 9504), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Mining", - "", - "It's quite simple really. All you need to do is right click on the", - "rock and select 'mine' You can only mine when you have a", - "pickaxe. So give it a try: first mine one tin ore.", - ) - ) + sendStageDialog(player, stage) } - 36 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "Your character is now attempting to mine the rock.", - "This should only take a few seconds.", - "" - ) - ) + sendStageDialog(player, stage) } - 37 -> { hideTabs(player, login) registerHintIcon(player, Location.create(3086, 9501), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Mining", - "", - "Now you have some tin ore you just need some copper ore,", - "then you'll have all you need to create a bronze bar. As you", - "did before right click on the copper rock and select 'mine'." - ) - ) + sendStageDialog(player, stage) } - 38 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3079, 9496), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Smelting", - "You should now have both some copper and tin ore. So let's", - "smelt them to make a bronze bar. To do this, right click on", - "either tin or copper ore and select use then left click on the", - "furnace. Try it now." - ) - ) + sendStageDialog(player, stage) + } + 39 -> { + sendStageDialog(player, stage) } - - //39 -> {} - 40 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(948)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "You've made a bronze bar!", - "", - "Speak to the Mining Instructor and he'll show you how to make", - "it into a weapon.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MINING_INSTRUCTOR_948)!!) + sendStageDialog(player, stage) } - 41 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3083, 9499), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Smithing a dagger", - "To smith you'll need a hammer - like the one you were given by", - "Dezzick - access to an anvil like the one with the arrow over it", - "and enough metal bars to make what you are trying to smith.", - "To start the process, use the bar on one of the anvils." - ) - ) + sendStageDialog(player, stage) } - 42 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Smithing a dagger.", - "Now you have the Smithing menu open, you will see a list of all", - "the things you can make. Only the dagger can be made at your", - "skill level; this is shown by the white text under it. You'll need", - "to select the dagger to continue." - ) - ) + sendStageDialog(player, stage) } - 43 -> { hideTabs(player, login) registerHintIcon(player, Location.create(3095, 9502), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "You've finished in this area.", - "", - "So let's move on. Go through the gates shown by the arrow.", - "Remember, you may need to move the camera to see your", - "surroundings. Speak to the guide for a recap at any time.", - ) - ) + sendStageDialog(player, stage) } - 44 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(944)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Combat", - "", - "In this area you will find out about combat with swords and", - "bows. Speak to the guide and he will tell you all about it.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) } - 45 -> { hideTabs(player, login) removeHintIcon(player) runTask(player, 10) { - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Wielding weapons", - "", - "You now have access to a new interface. Click on the flashing", - "icon of a man, the one to the right of your backpack icon.", - "" - ) - ) + // this part needs sendStageDialog because you could just be logging in here + sendStageDialog(player, stage) }.also { + // for this part, you are locked into the interface so we don't need sendStageDialog here hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.WORNITEMS_387)) @@ -807,429 +345,174 @@ object TutorialStage { ) } } - 46 -> { hideTabs(player, login) setVarbit(player, 3756, 0) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "This is your worn equipment.", - "From here you can see what items you have equipped. You will", - "notice the button 'Show Equipment Stats'. Click on this now to", - "display the details of what you have equipped.", - "" - ) - ) + sendStageDialog(player, stage) } - 47 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "You're now holding your dagger.", - "Clothes, armour, weapons and many other items are equipped", - "like this. You can unequip items by clicking on the item in the", - "worn equipment. You can close this window by clicking on the", - "small 'x' in the top-right hand corner. Speak to the Combat", - "Instructor." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) } - 48 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Unequipping items.", - "In your worn inventory panel, right click on the dagger and", - "select the remove option from the drop down list. After you've", - "unequipped the dagger, wield the sword and shield. As you", - "pass the mouse over an item you will see its name appear at", - "the top left of the screen." - ) - ) + sendStageDialog(player, stage) } - 49 -> { hideTabs(player, login) setVarbit(player, 3756, 1) var wepInter = player.getExtension(WeaponInterface::class.java) - if(wepInter == null) - { + if (wepInter == null) { wepInter = WeaponInterface(player) player.addExtension(WeaponInterface::class.java, wepInter) } player.interfaceManager.openTab(wepInter) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Combat interface.", - "", - "Click on the flashing crossed swords icon to see the combat", - "interface.", - "" - ) - ) + sendStageDialog(player, stage) } - 50 -> { hideTabs(player, login) setVarbit(player, 3756, 0) - registerHintIcon(player, Location.create(3110,9518,0), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "This is your combat interface.", - "From this interface you can select the type of attack your", - "character will use. Different monsters have different", - "weaknesses. If you hover your mouse over the buttons, you", - "will see the type of XP you will receive when using each type of", - "attack. Now you have the tools needed for battle why not slay", - "some rats. Click on the gates indicated to continue." - ) - ) + registerHintIcon(player, Location.create(3111,9518,0), 75) + sendStageDialog(player, stage) } - 51 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Attacking", - "To attack the rat, click it and select the attack option. You", - "will then walk over to it and start hitting it.", - "", - "" - ) - ) + //FIXME: add a hint arrow over the rat closest to you that is not in combat with somebody else. https://www.youtube.com/watch?v=FGQ2BZrJIug. The below should work but doesn't. + registerHintIcon(player, Repository.findNPC(NPCs.GIANT_RAT_86)!!) + sendStageDialog(player, stage) } - 52 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Sit back and watch.", - "While you are fighting you will see a bar over your head. The", - "bar shows how much health you have left. Your opponent will", - "have one too. You will continue to attack the rat until it's dead", - "or you do something else." - ) - ) + //FIXME: add a hint arrow over the rat you're in combat with (also in the ranging part btw). https://www.youtube.com/watch?v=FGQ2BZrJIug + sendStageDialog(player, stage) } - 53 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Well done, you've made your first kill!", - "", - "Pass through the gate and talk to the Combat Instructor; he", - "will give you your next task.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) } - 54 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Rat ranging", - "Now you have a bow and some arrows. Before you can use", - "them you'll need to equip them. Once equipped with the", - "ranging gear try killing another rat. Remember: to attack, right", - "click on the monster and select attack." - ) - ) + sendStageDialog(player, stage) } - 55 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3111,9526), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Moving on.", - "You have completed the tasks here. To move on, click on the", - "ladder shown. If you need to go over any of what you learnt", - "here, just talk to the Combat Instructor and he'll tell you what", - "he can." - ) - ) + sendStageDialog(player, stage) } - 56 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3122,3124), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Banking.", - "Follow the path and you will come to the front of a building.", - "This is the 'Bank of " + settings!!.name + "' where you can store all your", - "most valued items. To open your bank box just right click on an", - "open booth indicated and select 'use'." - ) - ) + sendStageDialog(player, stage) } - 57 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3125, 3124), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "This is your bank box.", - "You can store stuff here for safekeeping. If you die, anything", - "in your bank will be saved. To deposit something, right click it", - "and select 'Deposit-1'. Once you've had a good look, close the", - "window and move on through the door indicated." - ) - ) + sendStageDialog(player, stage) } - 58 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(947)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Financial advice", - "", - "The guide here will tell you all about making cash. Just click on", - "him to hear what he's got to say.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.FINANCIAL_ADVISOR_947)!!) + sendStageDialog(player, stage) } - 59 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3130, 3124, 0), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "", - "Continue through the next door.", - "", - "" - ) - ) + sendStageDialog(player, stage) } - 60 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(954)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Prayer", - "Follow the path to the chapel and enter it.", - "Once inside talk to the monk. He'll tell you all about the Prayer", - "skill.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.BROTHER_BRACE_954)!!) + sendStageDialog(player, stage) } - 61 -> { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.PRAYER_271)) setVarbit(player, 3756, 6) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your Prayer List", - "", - "Click on the flashing icon to open the Prayer List.", - "", - "" - ) - ) + sendStageDialog(player, stage) } - 62 -> { hideTabs(player, login) - registerHintIcon(player, Repository.findNPC(954)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "Your Prayer List", - "", - "Talk with Brother Brace and he'll tell you all about prayers.", - "" - ) - ) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.BROTHER_BRACE_954)!!) + sendStageDialog(player, stage) } - 63 -> { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.FRIENDS2_550)) setVarbit(player, 3756, 9) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "Friends list", - "You should now see another new icon. Click on the flashing", - "smiling face to open your Friend List.", - "" - ) - ) + sendStageDialog(player, stage) } - 64 -> { hideTabs(player, login) setVarbit(player, 3756, 10) player.interfaceManager.openTab(Component(Components.IGNORE2_551)) player.interfaceManager.openTab(Component(Components.CLANJOIN_589)) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "This is your Friends List.", - "", - "This will be explained by Brother Brace shortly, but first click", - "on the other flashing face in the interface.", - "" - ) - ) + sendStageDialog(player, stage) } - 65 -> { hideTabs(player, login) setVarbit(player, 3756, 0) - registerHintIcon(player, Repository.findNPC(945)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "This is your Ignore List.", - "The two lists, Friends and Ignore - can be very helpful for", - "keeping track of when your friends are online or for blocking", - "messages from people you simply don't like. Speak with", - "Brother Brace and he will tell you more." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.BROTHER_BRACE_954)!!) + sendStageDialog(player, stage) } - 66 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3122,3102), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "Your final instructor!", - "You're almost finished on tutorial island. Pass through the", - "door to find the path leading to your final instructor.", - "" - ) - ) + sendStageDialog(player, stage) } - 67 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(946)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your final instructor!", - "Just follow the path to the Wizard's house, where you will be", - "shown how to cast spells. Just talk with the mage indicated to", - "find out more.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) } - 68 -> { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(player.spellBookManager.spellBook)) setVarbit(player, 3756, 7) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Open up your final tab.", - "", - "Open up the Magic Spellbook tab by clicking on the flashing", - "icon next to the Prayer List tab you just learned about.", - "" - ) - ) + sendStageDialog(player, stage) } - 69 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "This is your spell list.", - "", - "Ask the mage about it.", - "" - ) - ) + removeHintIcon(player) + setVarbit(player, 3756, 0) + registerHintIcon(player, Repository.findNPC(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) } - 70 -> { hideTabs(player, login) - registerHintIcon(player, Repository.findNPC(41)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Cast Wind Strike at a chicken.", - "Now you have the runes you should see the Wind Strike icon at the", - "top-left of your spellbook, second in from the left. Walk over", - "to the caged chickens, click the Wind Strike icon and then", - "select one of the chickens to cast it on. It may take several", - "tries." - ) - ) + removeHintIcon(player) + //FIXME: as with the rats, the below should work, but doesn't + registerHintIcon(player, Repository.findNPC(NPCs.CHICKEN_41)!!) + sendStageDialog(player, stage) } - 71 -> { removeHintIcon(player) player.interfaceManager.restoreTabs() - registerHintIcon(player, Repository.findNPC(946)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "You have almost completed the tutorial!", - "", - "All you need to do now is teleport to the mainland. Just speak", - "with Terrova and he'll tell you how to do that.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) } } } @JvmStatic - public fun hideTabs(player: Player, login: Boolean) + fun hideTabs(player: Player, login: Boolean) { val stage = getAttribute(player, "tutorial:stage", 0) if(login && player.interfaceManager.tabs.isNotEmpty()) @@ -1249,10 +532,9 @@ object TutorialStage { player.interfaceManager.openTab(Component(Components.QUESTJOURNAL_V2_274)) if(stage > 45) player.interfaceManager.openTab(Component(Components.WORNITEMS_387)) - if(stage > 49){ + if(stage > 46){ var wepInter = player.getExtension(WeaponInterface::class.java) - if(wepInter == null) - { + if (wepInter == null) { wepInter = WeaponInterface(player) player.addExtension(WeaponInterface::class.java, wepInter) } diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyQueenDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyQueenDialogue.kt index 0f79e6733..7154caa96 100644 --- a/Server/src/main/content/region/misc/zanaris/dialogue/FairyQueenDialogue.kt +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyQueenDialogue.kt @@ -9,6 +9,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests @Initializable class FairyQueenDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -20,7 +21,7 @@ class FairyQueenDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fairytale II - Cure a Queen")) { + if (!isQuestComplete(player, Quests.FAIRYTALE_II_CURE_A_QUEEN)) { options( "How do crops and such survive down here?", "What's so good about this place?" ).also { stage = START_DIALOGUE } diff --git a/Server/src/main/content/region/misc/zanaris/handlers/EvilChickenLairListener.kt b/Server/src/main/content/region/misc/zanaris/handlers/EvilChickenLairListener.kt index a023e5b30..bdc56980b 100644 --- a/Server/src/main/content/region/misc/zanaris/handlers/EvilChickenLairListener.kt +++ b/Server/src/main/content/region/misc/zanaris/handlers/EvilChickenLairListener.kt @@ -9,6 +9,7 @@ import core.game.world.map.Location import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.Scenery +import content.data.Quests class EvilChickenLairListener: InteractionListener { override fun defineListeners() { @@ -17,7 +18,7 @@ class EvilChickenLairListener: InteractionListener { addClimbDest(Location.create(2455, 4380, 0), Location.create(2441, 4381, 0)) onUseWith(IntType.SCENERY, Items.RAW_CHICKEN_2138, Scenery.CHICKEN_SHRINE_12093) { player, _, _ -> - if (!hasRequirement(player, "Legend's Quest")) + if (!hasRequirement(player, Quests.LEGENDS_QUEST)) return@onUseWith false if(removeItem(player,(Item(Items.RAW_CHICKEN_2138)))){ @@ -33,6 +34,10 @@ class EvilChickenLairListener: InteractionListener { } return@onUseWith true } + onUseWith(IntType.SCENERY, Items.EGG_1944, Scenery.CHICKEN_SHRINE_12093) { player, _, _ -> + sendMessage(player, "Nice idea, but nothing interesting happens.") + return@onUseWith true + } onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.TUNNEL_ENTRANCE_12253) { player, _, node -> if(removeItem(player, Item(Items.ROPE_954))) replaceScenery(node as core.game.node.scenery.Scenery, Scenery.TUNNEL_ENTRANCE_12254, 100) diff --git a/Server/src/main/content/region/misc/zanaris/handlers/FairyRingPlugin.kt b/Server/src/main/content/region/misc/zanaris/handlers/FairyRingPlugin.kt index 37072b1e5..72a003a7b 100644 --- a/Server/src/main/content/region/misc/zanaris/handlers/FairyRingPlugin.kt +++ b/Server/src/main/content/region/misc/zanaris/handlers/FairyRingPlugin.kt @@ -1,22 +1,26 @@ package content.region.misc.zanaris.handlers -import core.api.* -import core.game.component.Component +import content.global.handlers.iface.FairyRingInterface +import core.api.anyInEquipment +import core.api.hasRequirement +import core.api.openInterface +import core.game.interaction.IntType +import core.game.interaction.InteractionListener import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager.TeleportType import core.game.world.map.Location import org.rs09.consts.Items -import core.game.interaction.InteractionListener -import core.game.interaction.IntType +import content.data.Quests /** * Handles interactions with fairy rings * @author Ceikry */ -private val RINGS = intArrayOf(12003, 12095, 14058, 14061, 14064, 14067, 14070, 14073, 14076, 14079, 14082, 14085, 14088, 14091, 14094, 14097, 14100, 14103, 14106, 14109, 14112, 14115, 14118, 14121, 14124, 14127, 14130, 14133, 14136, 14139, 14142, 14145, 14148, 14151, 14154, 14157, 14160, 16181, 16184, 23047, 27325, 37727) +private val RINGS = intArrayOf(12095, 14058, 14061, 14064, 14067, 14070, 14073, 14076, 14079, 14082, 14085, 14088, 14091, 14094, 14097, 14100, 14103, 14106, 14109, 14112, 14115, 14118, 14121, 14124, 14127, 14130, 14133, 14136, 14139, 14142, 14145, 14148, 14151, 14154, 14157, 14160, 16181, 16184, 23047, 27325, 37727) private const val MAIN_RING = 12128 private const val ENTRY_RING = 12094 +private const val MARKETPLACE_RING = 12003 class FairyRingPlugin : InteractionListener { @@ -24,7 +28,8 @@ class FairyRingPlugin : InteractionListener { on(RINGS, IntType.SCENERY, "use"){ player, _ -> if (!fairyMagic(player)) return@on true - player.teleporter.send(Location.create(2412, 4434, 0), TeleportType.FAIRY_RING) + val mainRingLocation = Location.create(2412, 4434, 0) + player.teleporter.send(mainRingLocation, TeleportType.FAIRY_RING) return@on true } on(MAIN_RING, IntType.SCENERY, "use"){ player, _ -> @@ -33,15 +38,20 @@ class FairyRingPlugin : InteractionListener { return@on true } on(ENTRY_RING, IntType.SCENERY, "use") { player, _ -> - if (!fairyMagic(player)) return@on true - player.teleporter.send(Location.create(3203, 3168, 0), TeleportType.FAIRY_RING) + val lumbridgeSwampShed = Location.create(3203, 3168, 0) + player.teleporter.send(lumbridgeSwampShed, TeleportType.FAIRY_RING) + return@on true + } + on(MARKETPLACE_RING, IntType.SCENERY, "use"){ player, _ -> + val alKharidBank = Location.create(3260, 3156, 0) + player.teleporter.send(alKharidBank, TeleportType.FAIRY_RING) return@on true } } private fun fairyMagic(player: Player) : Boolean { - if (!hasRequirement(player, "Fairytale I - Growing Pains")) { // should be converted to a FTP2 stage requirement once FTP2 is implemented + if (!hasRequirement(player, Quests.FAIRYTALE_I_GROWING_PAINS)) { // should be converted to a FTP2 stage requirement once FTP2 is implemented player.sendMessage("The fairy ring is inert.") return false } @@ -52,17 +62,7 @@ class FairyRingPlugin : InteractionListener { return true } - private fun reset(player: Player) { - player.removeAttribute("fairy-delay") - player.removeAttribute("fairy_location_combo") - for (i in 0..2) { - setVarp(player, 816 + i, 0) - } - } - private fun openFairyRing(player: Player) { - reset(player) - player.interfaceManager.openSingleTab(Component(735)) - player.interfaceManager.open(Component(734)) + openInterface(player, FairyRingInterface.RINGS_IFACE) } } diff --git a/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.kt b/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.kt index 890d74eb9..f40c15c12 100644 --- a/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.kt +++ b/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.kt @@ -1,5 +1,7 @@ package content.region.misthalin.barbvillage.dialogue +import content.region.kandarin.quest.scorpioncatcher.SCPeksaDialogue +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher import core.api.getQuestStage import core.api.openDialogue import core.game.dialogue.DialoguePlugin @@ -12,6 +14,7 @@ import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.NPCs +import content.data.Quests @Initializable class PeksaDialogue(player: Player? = null) : DialoguePlugin(player){ @@ -34,8 +37,7 @@ class PeksaDialogue(player: Player? = null) : DialoguePlugin(player){ showTopics( Topic("I could be, yes.", GO_SHOPPING), Topic("No, I'll pass on that.", LEAVE), - // todo get the quest state from Scorpion catcher to make this work - // IfTopic("Scorpion Stuff", SCORPION_CATCHER, getQuestStage(player, "Scorpion Catcher") == 50) + IfTopic("I've heard you have a small scorpion in your possession.", DIALOGUE_SCORPION_CATCHER, getQuestStage(player, Quests.SCORPION_CATCHER) == ScorpionCatcher.QUEST_STATE_OTHER_SCORPIONS) ) } @@ -48,9 +50,9 @@ class PeksaDialogue(player: Player? = null) : DialoguePlugin(player){ npcl(FacialExpression.HALF_GUILTY, "Well, come back if you change your mind.").also { stage = END_DIALOGUE } } - // DIALOGUE_SCORPION_CATCHER -> { - // openDialogue(player, PeksaDialogueSC(), npc) - // } + DIALOGUE_SCORPION_CATCHER -> { + openDialogue(player, SCPeksaDialogue(getQuestStage(player, Quests.SCORPION_CATCHER)), npc) + } } return true diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java index 1adee0589..0608d0fea 100644 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java @@ -4,7 +4,6 @@ import core.api.Container; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.emote.Emotes; -import core.game.node.item.Item; import org.rs09.consts.Items; import static core.api.ContentAPIKt.addItem; diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java index 8cef49bf4..1135cefd5 100644 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java @@ -56,7 +56,7 @@ public final class GrainOfPlentyDialogue extends DialoguePlugin { break; } player.getSavedData().getGlobalData().getStrongHoldRewards()[1] = true; - interpreter.sendDialogue("...congratualtions adventurer, you have been deemed worthy of this", "reward. You have also unlocked the Slap Head emote!"); + interpreter.sendDialogue("...congratulations adventurer, you have been deemed worthy of this", "reward. You have also unlocked the Slap Head emote!"); stage = 1; player.getEmoteManager().unlock(Emotes.SLAP_HEAD); break; diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/GuardDialogue.kt b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/GuardDialogue.kt new file mode 100644 index 000000000..0fc1f9dd7 --- /dev/null +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/GuardDialogue.kt @@ -0,0 +1,164 @@ +package content.region.misthalin.barbvillage.stronghold.playersafety + +import core.api.runTask +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.world.GameWorld.settings +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class GuardDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object{ + const val DIALOGUE_COMPLETED = START_DIALOGUE + const val DIALOGUE_NOT_COMPLETED = 50 + + } + + override fun open(vararg args: Any?): Boolean { + val hasRead = player.savedData.globalData.hasReadPlaques() + if (hasRead) { + npcl(FacialExpression.HALF_GUILTY, "Can I help you?").also { stage = DIALOGUE_COMPLETED } + } + else { + npcl(FacialExpression.FURIOUS, "Ahem! Can I help you?").also { stage = DIALOGUE_NOT_COMPLETED } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + DIALOGUE_COMPLETED -> { + playerl( + FacialExpression.HALF_GUILTY, "Can I go upstairs?" + ).also { stage++ } + } + + DIALOGUE_COMPLETED + 1 -> { + npcl( + FacialExpression.FURIOUS, + "Yes, citizen. Before you do I am instructed to give " + + "you one final piece of information" + ).also { stage++ } + } + + DIALOGUE_COMPLETED + 2 -> { + playerl(FacialExpression.HALF_GUILTY, "Oh, okay then.").also { stage++ } + } + + DIALOGUE_COMPLETED + 3 -> { + npcl( + FacialExpression.HALF_GUILTY, + "In your travels around " + settings!!.name + ", should you find a player who acts in a " + + "way that breaks on of our rules, you should report them." + ).also { stage++ } + } + + DIALOGUE_COMPLETED + 4 -> { + npcl( + FacialExpression.HALF_GUILTY, + "Reporting is very simple and easy to do. Simply click the Report Abuse button at " + + "the bottom of the screen and you will be shown the following screen:" + ).also { stage++ } + } + + DIALOGUE_COMPLETED + 5 -> { + player.interfaceManager.openComponent(700) + runTask(player, 5) { + if (player != null) { + player.interfaceManager.close() + } + return@runTask + }.also { stage++ } + } + + DIALOGUE_COMPLETED + 6 -> { + npcl( + FacialExpression.HALF_GUILTY, + "Simply enter the player's name in the box and click the rule that the offender was breaking." + ).also { stage++ } + } + + DIALOGUE_COMPLETED + 7 -> { + playerl(FacialExpression.HALF_GUILTY, "Okay. Then what?").also { stage++ } + } + + DIALOGUE_COMPLETED + 8 -> { + npcl( + FacialExpression.HALF_GUILTY, + "That's it! It really is that simple and it only takes a moment to do. " + + "Now you may enter the training centre. Good luck, citizen." + ).also { stage++ } + } + + DIALOGUE_COMPLETED + 9 -> { + playerl(FacialExpression.HALF_GUILTY, "Thanks!").also { stage = END_DIALOGUE } + } + + DIALOGUE_NOT_COMPLETED -> { + playerl( + FacialExpression.HALF_GUILTY, "I'd like to go up to the training centre please." + ).also { stage++ } + } + DIALOGUE_NOT_COMPLETED + 1 -> { + npcl(FacialExpression.FURIOUS, "Sorry, citizen, you can't go up there.").also { stage++ } + } + DIALOGUE_NOT_COMPLETED + 2 -> { + playerl(FacialExpression.OLD_SNEAKY, "Why not?").also { stage++ } + + } + DIALOGUE_NOT_COMPLETED + 3 -> { + npcl( + FacialExpression.ANNOYED, + "You must learn about player safety before entering the training centre." + ).also { stage++ } + } + DIALOGUE_NOT_COMPLETED + 4 -> { + playerl(FacialExpression.HALF_GUILTY, "Oh. How do I do that?").also { stage++ } + } + DIALOGUE_NOT_COMPLETED + 5 -> { + npcl( + FacialExpression.HALF_GUILTY, + "Each of these gublinches have been caught breaking the rules of " + settings!!.name + ". " + + "You should read the plaques on each of their cells to learn what they did wrong." + ).also { stage++ } + + } + DIALOGUE_NOT_COMPLETED + 6 -> { + playerl( + FacialExpression.HALF_GUILTY, + "Oh, right. I can enter the training centre once I have done that?" + ).also { stage++ } + } + DIALOGUE_NOT_COMPLETED + 7 -> { + npcl( + FacialExpression.HALF_GUILTY, + "Yes. Once you have have examined each of the plaques, come and speak to me " + + "and I will tell you about the Report Abuse function." + ).also { stage++ } + } + DIALOGUE_NOT_COMPLETED + 8 -> { + npcl( + FacialExpression.HALF_GUILTY, + "After that, I can let you into the training centre, upstairs." + ).also { stage++ } + } + DIALOGUE_NOT_COMPLETED + 9 -> { + playerl(FacialExpression.HALF_GUILTY, "Okay, thanks for the help.").also { stage = END_DIALOGUE } + player.sendMessage("You need to read the jail plaques before the guard will allow you upstairs.") + } + } + return true + + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUARD_7142) + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/GuardDialoguePlugin.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/GuardDialoguePlugin.java deleted file mode 100644 index 777ffcfa3..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/GuardDialoguePlugin.java +++ /dev/null @@ -1,144 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -import core.game.component.Component; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.tools.Log; -import core.tools.SystemLogger; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; - -import static core.api.ContentAPIKt.log; - -/** - * @author Tyler Telis - */ -public class GuardDialoguePlugin extends DialoguePlugin { - - /** - * Constructs a new {@code GuardDialogue} instance. - * @param player The {@code Player} instance. - */ - public GuardDialoguePlugin(Player player) { - super(player); - } - - /** - * Constructs a new {@code GuardDialoguePlugin} instance. - */ - public GuardDialoguePlugin() { - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GuardDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - boolean read = player.getSavedData().getGlobalData().hasReadPlaques(); - interpreter.sendDialogues(npc, read ? FacialExpression.HALF_GUILTY : FacialExpression.FURIOUS, read ? "Can I help you?" : "Ahem! Can I help you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - boolean read = player.getSavedData().getGlobalData().hasReadPlaques(); - switch (stage) { - case -1:// END dialogue stage. - end(); - break; - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, read ? "Can I go upstairs?" : "I'd like to go up to the training centre please."); - increment(); - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.FURIOUS, !read ? "Sorry, citizen, you can't go up there." : "", "Yes, citizen. Before you do I am instructed to give", "you one final piece of information"); - increment(); - break; - case 2: - interpreter.sendDialogues(player, read ? FacialExpression.HALF_GUILTY : FacialExpression.OLD_SNEAKY, read ? "Oh, okay then." : "Why not?"); - increment(); - break; - case 3: - if (!read) { - npc("You must learn about player safety before
entering the training centre."); - } else { - npc("In your travels around " + GameWorld.getSettings().getName() + ", should you find a", "player who acts in a way that breaks on of our rules,", "you should report them."); - } - increment(); - break; - case 4: - interpreter.sendDialogues(read ? npc : player, FacialExpression.HALF_GUILTY, !read ? "Oh. How do I do that?" : "Reporting is very simple and easy to do. Simply click", "the Report Abuse button at the bottom of the screen", "and you will be shown the following screen:"); - stage = read ? 10 : stage + 1; - break; - case 5: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Each of these gublinches have been caught breaking the", "Rules of " + GameWorld.getSettings().getName() + ". You should read the plaques on", "each of their cells to learn what they did wrong."); - increment(); - break; - case 6: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, right. I can enter the training centre once I have", "done that?"); - increment(); - break; - case 7: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes. Once you have have examined each of the plaques,", "come and speak to me and I will tell you about the", "Report Abuse function."); - increment(); - break; - case 8: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "After that, I can let you into the training centre,", "upstairs."); - increment(); - break; - case 9: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, thanks for the help."); - player.getPacketDispatch().sendMessage("You need to read the jail plaques before the guard will allow you upstairs."); - stage = -1; - break; - case 10: - if (read) { - player.getInterfaceManager().open(new Component(700)); - GameWorld.getPulser().submit(new Pulse(5) { - - @Override - public boolean pulse() { - if (player != null) { - player.getInterfaceManager().close(); - } - return true; - } - - }); - stage = 12; - } - break; - case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Simply enter the player's name in the box and click the", "rule that the offender was breaking."); - stage = 13; - break; - case 13: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay. Then what?"); - stage = 14; - break; - case 14: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That's it! It reall is that simple and it only takes a ", "moment to do. Now you may enter the training", "centre. Good luck, citizen."); - stage = 15; - break; - case 15: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks!"); - stage = -1; - break; - default: - log(this.getClass(), Log.ERR, "Unhandled dialogue stage=" + stage); - } - return false; - } - - @Override - public int[] getIds() { - return new int[] { 7142 }; - } - -} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PSOptionHandler.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PSOptionHandler.java deleted file mode 100644 index 742b4be80..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PSOptionHandler.java +++ /dev/null @@ -1,176 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_DOWN_STAIRS; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_ENTRANCE_ID_ENTER; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_ENTRANCE_LEAVE; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_ENTRANCE_LOCATION_ENTER; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_ENTRANCE_LOCATION_LEAVE; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_STAIRS_DOWN; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_STAIRS_UP; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_UP_STAIRS; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.TEST_PAPER_ITEM_ID; -import static content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.forId; - -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.dialogue.DialogueAction; -import core.game.global.action.DoorActionHandler; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.emote.Emotes; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.world.map.Location; -import core.plugin.Plugin; - -import content.region.misthalin.barbvillage.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JailPlaques; - -import static core.api.ContentAPIKt.*; - - -/** - * @author Tyler Telis - */ -public class PSOptionHandler extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(JAIL_ENTRANCE_ID_ENTER).getHandlers().put("option:use", this); - SceneryDefinition.forId(JAIL_ENTRANCE_LEAVE).getHandlers().put("option:leave", this); - SceneryDefinition.forId(JAIL_STAIRS_UP).getHandlers().put("option:climb-up", this); - SceneryDefinition.forId(JAIL_STAIRS_DOWN).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(29732).getHandlers().put("option:open", this); - SceneryDefinition.forId(29624).getHandlers().put("option:open", this); - SceneryDefinition.forId(29728).getHandlers().put("option:enter", this); - SceneryDefinition.forId(29735).getHandlers().put("option:pull-back", this); - SceneryDefinition.forId(29623).getHandlers().put("option:use", this); - SceneryDefinition.forId(29730).getHandlers().put("option:pull", this); - SceneryDefinition.forId(29731).getHandlers().put("option:pull", this); - SceneryDefinition.forId(29577).getHandlers().put("option:open", this); - SceneryDefinition.forId(29578).getHandlers().put("option:search", this); - ItemDefinition.forId(TEST_PAPER_ITEM_ID).getHandlers().put("option:take exam", this); - SceneryDefinition.forId(29729).getHandlers().put("option:climb", this); - for (JailPlaques plaque : JailPlaques.values()) { - SceneryDefinition.forId(plaque.getObjectId()).getHandlers().put("option:read-plaque on", this); - } - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - int config = getVarp(player, 1203); - boolean locked = config == (1 << 29) || config == (0 << 26); - switch (node.getId()) { - case 29577:// chest - SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(29578), 60); - return true; - case 29578: - switch (option) { - case "search": - int stage = player.getSavedData().getGlobalData().getTestStage(); - if (stage == 3) { - player.getEmoteManager().unlock(Emotes.SAFETY_FIRST); - player.getInventory().add(new Item(995, 10000), player); - player.getInventory().add(new Item(12629), player); - player.getDialogueInterpreter().sendItemMessage(12629, "You open the chest to find a large pile of gold, along with a pair of safety gloves. Also in the chest is the secret of the 'Safety First' emote."); - player.getSavedData().getGlobalData().setTestStage(4); - } else if (stage == 4) { - if (!player.hasItem(new Item(12629))) { - player.getInventory().add(new Item(12629), player); - player.getDialogueInterpreter().sendItemMessage(12629, "You find a pair of safety gloves."); - } else { - player.getDialogueInterpreter().sendDialogue("You already have a pair of safety gloves."); - } - } - return true; - } - return true; - case 29730:// lever - case 29731:// level down - if (!locked) { - player.sendMessage("You hear cogs and gears moving and the sound of heavy locks falling into place."); - config = (1 << 29); - } else { - player.sendMessage("You hear the cogs and gears moving and a distant unlocking sound."); - config += (1 << 26); - } - setVarp(player, 1203, config, true); - return true; - case 29624: - if (locked) { - player.sendMessage("The door seems to be locked by some kind of mechanism."); - } else { - if (player.getLocation().getZ() == 2) { - player.teleport(new Location(3177, 4266, 0)); - } else { - player.teleport(new Location(3177, 4269, 2)); - } - } - return true; - case 29729: - player.teleport(new Location(3078, 3463, 0)); - return true; - case 29623: - player.teleport(new Location(3077, 4235, 0)); - return true; - case 29735: - player.getDialogueInterpreter().sendDialogue("There appears to be a tunnel behind the poster."); - player.getDialogueInterpreter().addAction(new DialogueAction() { - - @Override - public void handle(Player player, int buttonId) { - player.teleport(new Location(3140, 4230, 2)); - } - - }); - return true; - case 29732: - if (!player.getSavedData().getGlobalData().hasReadPlaques()) { - player.sendMessage("This door is locked."); - } else { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()); - } - return true; - case 29728: - if (player.getSavedData().getGlobalData().getTestStage() < 3) { - player.sendMessage("You need to complete the player safety test first."); - return true; - } - player.teleport(new Location(3158, 4280, 3)); - return true; - } - if (node instanceof Scenery) { - Scenery object = (Scenery) node; - JailPlaques plaque = forId(object.getId()); - if (plaque != null) { - plaque.read(player, object); - return true; - } else if (object.getId() == JAIL_ENTRANCE_ID_ENTER || object.getId() == JAIL_ENTRANCE_LEAVE) { - player.getProperties().setTeleportLocation(object.getId() == JAIL_ENTRANCE_ID_ENTER ? JAIL_ENTRANCE_LOCATION_ENTER : JAIL_ENTRANCE_LOCATION_LEAVE); - return true; - } else if (object.getId() == JAIL_STAIRS_UP || object.getId() == JAIL_STAIRS_DOWN) { - if (!player.getSavedData().getGlobalData().hasReadPlaques() && object.getId() != JAIL_STAIRS_DOWN) { - player.getPacketDispatch().sendMessage("You need to read the jail plaques before the guard will allow you upstairs."); - return true; - } - player.getProperties().setTeleportLocation(object.getId() == JAIL_STAIRS_UP ? JAIL_UP_STAIRS : JAIL_DOWN_STAIRS); - return true; - } - } else if (node instanceof Item) { - Item item = (Item) node; - if (player.getSavedData().getGlobalData().getTestStage() >= 2) { - player.sendMessage("You have already completed the test!"); - return true; - } - if (item.getId() == TEST_PAPER_ITEM_ID) { - player.removeAttribute("s-stage"); - player.getDialogueInterpreter().open("player_safety", node.getId()); - return true; - } - } - return false; - } - -} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PlayerSafetyTest.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PlayerSafetyTest.java deleted file mode 100644 index 41a110c0b..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PlayerSafetyTest.java +++ /dev/null @@ -1,269 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -import core.game.component.Component; -import core.game.component.ComponentPlugin; -import core.game.dialogue.DialogueInterpreter; -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; - -/** - * Handles the player safety test. - * @author Tyler Telis - * @author Vexia - */ -public class PlayerSafetyTest extends DialoguePlugin { - - /** - * The check box button ids. - */ - public static int ANSWER_1 = 3, ANSWER_2 = 4, ANSWER_3 = 12; - - /** - * Constructs a new {@Code PlayerSafetyTest} {@Code Object} - * @param player the player. - */ - public PlayerSafetyTest(Player player) { - super(player); - } - - /** - * Constructs a new {@code PlayerSafetyTest} instance. - */ - public PlayerSafetyTest() { - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PlayerSafetyTest(player); - } - - @Override - public boolean open(Object... args) { - stage = player.getAttribute("s-stage", 0); - if (args.length >= 2) { - handleQuiz(Integer.parseInt(String.valueOf(args[1]))); - } else { - stage = 0; - sendTest(); - } - return true; - } - - /** - * Sends the test. - */ - public void sendTest() { - Component component = getComponentForStage(stage); - component.setPlugin(new ComponentPlugin() { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - if (player == null) { - return false; - } - if (player.getDialogueInterpreter().getDialogue() == null) { - player.getDialogueInterpreter().open(DialogueInterpreter.getDialogueKey("player_safety"), "reopen", button); - } - if (player.getDialogueInterpreter().getDialogue() == null) { - return false; - } - return player.getDialogueInterpreter().getDialogue().handle(component.getId(), button); - } - - }); - player.getInterfaceManager().open(component); - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - return handleQuiz(buttonId); - } - - private boolean handleQuiz(int buttonId) { - if (buttonId == 48 || buttonId == 39 || buttonId == 40) { - if (stage >= TestQuestions.values().length) { - player.getInterfaceManager().close(); - player.getSavedData().getGlobalData().setTestStage(2); - player.sendMessage("Well done! You completed the exams."); - player.getDialogueInterpreter().sendDialogue("Congratulations! The test has been completed. Hand the paper in to", "Professor Henry for marking."); - return false; - } else { - sendTest(); - } - return true; - } - if (stage == TestQuestions.values().length) {// Last question. - player.getDialogueInterpreter().getDialogue().end(); - return true; - } - if (forStage(stage).answer(player, buttonId)) { - return true; - } - return true; - } - - @Override - public boolean close() { - player.setAttribute("s-stage", stage); - return super.close(); - } - - /** - * A test question. - * @author Tyler Tellis - */ - public enum TestQuestions { - QUESTION_1(697, ANSWER_2) { - @Override - public void showAnswer(Player player, boolean correct, int interfaceId) { - player.getPacketDispatch().sendInterfaceConfig(interfaceId, 26, false); - player.getPacketDispatch().sendInterfaceConfig(interfaceId, correct ? 37 : 43, false); - } - }, - QUESTION_2(699, ANSWER_2) { - @Override - public void showAnswer(Player player, boolean correct, int interfaceId) { - player.getPacketDispatch().sendInterfaceConfig(interfaceId, 20, false); - player.getPacketDispatch().sendInterfaceConfig(interfaceId, correct ? 31 : 34, false); - } - }, - QUESTION_3(707, ANSWER_1) { - @Override - public void showAnswer(Player player, boolean correct, int interfaceId) { - player.getPacketDispatch().sendInterfaceConfig(interfaceId, 20, false); - player.getPacketDispatch().sendInterfaceConfig(interfaceId, correct ? 31 : 35, false); - } - }, - QUESTION_4(710, 9) { - @Override - public void showAnswer(Player player, boolean correct, int interfaceId) { - player.getPacketDispatch().sendInterfaceConfig(interfaceId, 20, false); - player.getPacketDispatch().sendInterfaceConfig(interfaceId, correct ? 31 : 34, false); - } - }, - QUESTION_5(704, 10) { - @Override - public void showAnswer(Player player, boolean correct, int interfaceId) { - player.getPacketDispatch().sendInterfaceConfig(interfaceId, 26, false); - player.getPacketDispatch().sendInterfaceConfig(interfaceId, correct ? 37 : 40, false); - } - }, - QUESTION_6(694, 10) { - @Override - public void showAnswer(Player player, boolean correct, int interfaceId) { - player.getPacketDispatch().sendInterfaceConfig(interfaceId, 20, false); - player.getPacketDispatch().sendInterfaceConfig(interfaceId, correct ? 31 : 34, false); - } - }, - QUESTION_8(696, 4) { - @Override - public void showAnswer(Player player, boolean correct, int interfaceId) { - player.getPacketDispatch().sendInterfaceConfig(interfaceId, 20, false); - player.getPacketDispatch().sendInterfaceConfig(interfaceId, correct ? 31 : 34, false); - } - }; - - /** - * The interface id. - */ - private int interfaceId; - - /** - * The correct button id to click. - */ - private int correctButton; - - /** - * Constructs a new {@Code TestQuestions} {@Code Object} - * @param interfaceId the interface. - * @param correctButton the button. - */ - private TestQuestions(int interfaceId, int correctButton) { - this.interfaceId = interfaceId; - this.correctButton = correctButton; - } - - /** - * Answers a question. - * @param player the player. - * @param buttonId the button id. - * @return {@code True} if so. - */ - public boolean answer(Player player, int buttonId) { - boolean correct = buttonId == correctButton; - if (interfaceId == 710 && buttonId == 9) { - correct = true; - } else if (interfaceId == 704 && buttonId == 10) { - correct = true; - } else if (interfaceId == 696 && buttonId == 4) { - correct = true; - } - showAnswer(player, correct, interfaceId); - if (correct) { - player.getDialogueInterpreter().getDialogue().increment(); - } - return player.getInterfaceManager().getComponent(interfaceId) != null; - } - - /** - * Shows the answer. - * @param player the player. - * @param correct the correct. - * @param interfaceId the id. - */ - public abstract void showAnswer(Player player, boolean correct, int interfaceId); - - /** - * Gets the value. - * @return the value. - */ - public int value() { - return ordinal(); - } - - /** - * Gets the inter id. - * @return the id. - */ - public int getInterfaceId() { - return interfaceId; - } - - /** - * Gets the correct button. - * @return the button. - */ - public int getCorrectButton() { - return correctButton; - } - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("player_safety") }; - } - - /** - * Gets a new component for the stage. - * @param stage The stage; - * @return The {@code Component} instance. - */ - public static Component getComponentForStage(int stage) { - return new Component(forStage(stage).getInterfaceId()); - } - - /** - * Gets it for the stage. - * @param stage the stage. - * @return the question. - */ - public static TestQuestions forStage(int stage) { - return TestQuestions.values()[stage]; - } -} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PlayerSafetyTest.kt b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PlayerSafetyTest.kt new file mode 100644 index 000000000..98ad6723b --- /dev/null +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PlayerSafetyTest.kt @@ -0,0 +1,102 @@ +package content.region.misthalin.barbvillage.stronghold.playersafety + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.Player +import core.plugin.Initializable + + +// /** +// * Handles the player safety test. +// * Java version +// * @author Tyler Telis +// * @author Vexia +// * Kotlin +// * @author gregf36665 +// */ +@Initializable +class PlayerSafetyTest : InterfaceListener { + + companion object { + private val testQuestions = listOf( + TestQuestion(697, 26, mapOf(4 to 37, 3 to 40, 5 to 43), 4), + TestQuestion(699, 20, mapOf(4 to 31, 3 to 34), 4), + TestQuestion(707, 20, mapOf(3 to 31, 4 to 35, 5 to 35), 3), + TestQuestion(710, 20, mapOf(9 to 31, 10 to 34), 9), + TestQuestion(704, 26, mapOf(10 to 37, 12 to 43, 11 to 40), 10), + // todo figure out why these 2 are not working + // TestQuestion(694, 20, mapOf(12 to 31, 13 to 34), 13), + // TestQuestion(708, 26, mapOf(12 to 31, 13 to 34), 12), + TestQuestion(696, 20, mapOf(4 to 31, 3 to 34, 5 to 34), 4) + + + ) + private var testQuestionNumber = 0 + } + + /** + * Define a new Test Question + * interfaceId: the base ID for the test question + * baseChild: the ID of the Component child + * answers: A map of button press: child to display + * correctOption: The button number for the correct option + */ + class TestQuestion(val interfaceId: Int, val baseChild : Int, val answers : Map, val correctOption: Int){ + + fun showAnswer(player: Player, button : Int){ + setComponentVisibility(player, interfaceId, baseChild, false) + answers[button]?.let { setComponentVisibility(player, interfaceId, it, false) } + return + } + } + + private fun checkAnswer(player: Player, button: Int){ + val question = testQuestions[testQuestionNumber] + question.showAnswer(player, button) + if (button == question.correctOption){ + testQuestionNumber += 1 + } + } + + private fun completedTest(player: Player) { + closeInterface(player) + player.savedData.globalData.testStage = 2 + sendMessage(player,"Well done! You completed the exams.") + sendDialogue(player, "Congratulations! The test has been completed. " + + "Hand the paper in to Professor Henry for marking.") + } + + fun update(player: Player){ + // Close all open interfaces + closeInterface(player) + // Open the correct one now + testQuestions.forEachIndexed(){ index, testQuestion -> + if (index == testQuestionNumber){ + openInterface(player, testQuestion.interfaceId) + } + } + } + + override fun defineInterfaceListeners() { + onOpen(697) { _, _ -> + testQuestionNumber = 0 // Set the testQuestionNumber back to 0 on first launch + return@onOpen true + } + for (question in testQuestions){ + on(question.interfaceId) { player, _, _, buttonID, _, _ -> + if (buttonID in 0..35) { + checkAnswer(player, buttonID) + } + else{ + update(player) + if (testQuestionNumber == testQuestions.size) { + completedTest(player) + return@on true + } + } + return@on true + } + } + } +} + diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/ProfessorHenryDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/ProfessorHenryDialogue.java deleted file mode 100644 index d4cffed76..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/ProfessorHenryDialogue.java +++ /dev/null @@ -1,221 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -import core.ServerConstants; -import core.api.Container; -import core.game.component.Component; -import core.game.node.entity.player.link.emote.Emotes; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import org.rs09.consts.Items; - -import static core.api.ContentAPIKt.*; - -/** - * @author Tyler Telis - */ -public class ProfessorHenryDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ProfessorHenryDialogue} instance. - * @param player The {@code Player} instance. - */ - public ProfessorHenryDialogue(Player player) { - super(player); - } - - /** - * Constructs a new {@code ProfessorHenryDialogue} instance. - */ - public ProfessorHenryDialogue() { - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ProfessorHenryDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - stage = 0; - if (player.getSavedData().getGlobalData().getTestStage() == 2 && player.getInventory().contains(12626, 1)) { - player("Hello, Professor."); - stage = 900; - return true; - } - if (player.getSavedData().getGlobalData().getTestStage() >= 3) { - npc("Good job " + player.getUsername() + " you completed the test!"); - stage = 800; - return true; - } - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "Hello."); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 800: - end(); - break; - case 900: - npc("Ah, " + player.getUsername() + ". How's the test going?"); - stage++; - break; - case 901: - player("I think I've finished."); - stage++; - break; - case 902: - npc("Excellent! Let me just mark the paper for you then."); - stage++; - break; - case 903: - npc("Hmm. Uh-huh, yes I see. Good! Yes, that's right."); - stage++; - break; - case 904: - npc("Excellent! Allow me to reward you for your work. I", "have these two old lamps that you may find useful."); - stage++; - break; - case 905: - npc("Also, there is an old jail block connected to the cells", "below the training centre, which have been overrun with", "vicious creatures. If you search around the jail cells", "downstairs, you should find it easily enough."); - stage++; - break; - case 906: - npc("Now, your rewards."); - stage++; - break; - case 907: - if (player.getInventory().freeSlots() == 0) { - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "It seems your inventory is full."); - stage = -1; - return false; - } - - if (removeItem(player, Items.TEST_PAPER_12626, Container.INVENTORY)) { - showReward(player); - end(); - } - break; - case -1: - end(); - break; - case 0: - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "Hello what?"); - increment(); - break; - case 1: - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "Uh...hello there?"); - increment(); - break; - case 2: - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "Hello, 'Professor'. Manners cost nothing, you know.", "When you're in my classroom, I ask that you use the", "proper address for my station."); - increment(); - break; - case 3: - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "Your station?"); - increment(); - break; - case 4: - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "Yes. It means 'position', so to speak."); - increment(); - break; - case 5: - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "Oh, okay."); - increment(); - break; - case 6: - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "Now, what can I do for you, exactly?"); - increment(); - break; - case 7: - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "What is this place?"); - increment(); - break; - case 8: - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "This is the Misthalin Training Centre of Ezcelience. It", "is where bold adventurers, such as yourself, can come", "to learn of the dangers of the wide world and gain", "some valuable experience at the same time."); - increment(); - break; - case 9: - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "What can I do here?"); - increment(); - break; - case 10: - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "Here you can take part in the Player Safety test: a set", "of valuable lessons to learn about staying safe in ", ServerConstants.SERVER_NAME + "."); - increment(); - break; - case 11: - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "I can give you a test paper to take and, once", "completed, you can bring it back to me for marking.", "Would you like to take the test?", "It will not cost you anything"); - increment(); - break; - case 12: - interpreter.sendOptions("Select an Option", "Yes, please", "Not right now, thanks."); - increment(); - break; - case 13: - if (buttonId == 1) { - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "Yes, please."); - increment(); - } else if (buttonId == 2) { - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "Not right now, thanks."); - stage = -1; - } - break; - case 14: - if (player.getInventory().freeSlots() == 0) { - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "It seems your inventory is full."); - stage = -1; - return false; - } else if (player.getInventory().containItems(StrongHoldOfPlayerSafetyPlugin.TEST_PAPER_ITEM_ID)) { - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "You already have a test, please fill it out", "and return it to me."); - stage = -1; - return false; - } - player.getSavedData().getGlobalData().setTestStage(1); - player.getInventory().add(new Item(StrongHoldOfPlayerSafetyPlugin.TEST_PAPER_ITEM_ID)); - sendNormalDialogue(npc, FacialExpression.HALF_GUILTY, "Right then. Here is the test paper. When you have", "completed all the questions, bring it back to me for", "marking."); - increment(); - break; - case 15: - sendNormalDialogue(player, FacialExpression.HALF_GUILTY, "Okay, thanks."); - stage = -1; - break; - } - return false; - } - - /** - * Shows the reward for the player. - * @param player the player. - */ - private void showReward(Player player) { - setVarp(player, 1203, 1 << 29, true); - player.getSavedData().getGlobalData().setTestStage(3); - player.getQuestRepository().syncronizeTab(player); - player.getInterfaceManager().open(new Component(277)); - for (int i = 9; i < 18; i++) { - player.getPacketDispatch().sendString("", 277, i); - } - player.getPacketDispatch().sendString("You have completed the Player Safety test!", 277, 4); - player.getPacketDispatch().sendString("2 Experience lamps", 277, 9); - player.getPacketDispatch().sendString("Access to the Stronghold of", 277, 10); - player.getPacketDispatch().sendString("Player Safety Dungeon", 277, 11); - player.getPacketDispatch().sendString("The Safety First' emote", 277, 12); - player.getPacketDispatch().sendString(player.getQuestRepository().getPoints() + "", 277, 7); - player.getPacketDispatch().sendItemZoomOnInterface(12626, 240, 277, 5); - if (!player.hasItem(new Item(4447, 2))) { - player.getInventory().add(new Item(4447, 2), player); - } - player.getEmoteManager().unlock(Emotes.SAFETY_FIRST); - } - - @Override - public int[] getIds() { - return new int[] { 7143 }; - } - -} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/ProfessorHenryDialogue.kt b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/ProfessorHenryDialogue.kt new file mode 100644 index 000000000..3361fedce --- /dev/null +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/ProfessorHenryDialogue.kt @@ -0,0 +1,144 @@ +package content.region.misthalin.barbvillage.stronghold.playersafety + +import core.ServerConstants +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.emote.Emotes +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ProfessorHenryDialogue(player : Player? = null) : DialoguePlugin(player){ + + companion object{ + const val HAND_IN_TEST = 10 + const val MEETING = 100 + const val GET_TEST = 200 + const val iFace = 277 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage){ + START_DIALOGUE -> { + if (player.savedData.globalData.testStage == 2 && inInventory(player, Items.TEST_PAPER_12626)){ + // The player has the test and has completed it but need it marked + player("Hello, Professor.").also { stage = HAND_IN_TEST} + } + else if (player.savedData.globalData.testStage >= 3){ + // The player has already had their test marked and taken + npcl(FacialExpression.HAPPY, "Good job ${player.username}, you completed the test!").also { stage = END_DIALOGUE } + return true + } + else{ + player(FacialExpression.NEUTRAL, "Hello.").also { stage = MEETING } + } + } + MEETING -> npcl(FacialExpression.ANNOYED, "Hello what?").also { stage++ } + MEETING + 1 -> playerl(FacialExpression.HALF_GUILTY, "Uh...hello there?").also { stage++ } + MEETING + 2 -> npcl(FacialExpression.HALF_GUILTY, "Hello, 'Professor'. Manners cost nothing, you know. " + + "When you're in my classroom, I ask that you use the proper address for my station.").also { stage++ } + MEETING + 3 -> playerl(FacialExpression.HALF_GUILTY, "Your station?").also { stage++ } + MEETING + 4 -> npcl(FacialExpression.HALF_GUILTY, "Yes. It means 'position', so to speak.").also { stage++ } + MEETING + 5 -> playerl(FacialExpression.HALF_GUILTY, "Oh, okay.").also { stage++ } + MEETING + 6 -> npcl(FacialExpression.HALF_GUILTY, "Now, what can I do for you, exactly?").also { stage++ } + MEETING + 7 -> playerl(FacialExpression.HALF_GUILTY, "What is this place?").also { stage++ } + MEETING + 8 -> npcl(FacialExpression.HALF_GUILTY, "This is the Misthalin Training Centre of Excellence. " + + "It is where bold adventurers, such as yourself, can come to learn of the dangers of " + + "the wide world and gain some valuable experience at the same time.").also { stage++ } + MEETING + 9 -> playerl(FacialExpression.HALF_GUILTY, "What can I do here?").also { stage++ } + MEETING + 10 -> npcl(FacialExpression.HALF_GUILTY, "Here you can take part in the Player Safety test: " + + "a set of valuable lessons to learn about staying safe " + + "in ${ServerConstants.SERVER_NAME}.").also { stage++ } + MEETING + 11 -> npcl(FacialExpression.HALF_GUILTY, "I can give you a test paper to take and, once completed, " + + "you can bring it back to me for marking. Would you like to take the test? " + + "It will not cost you anything.").also { stage++ } + MEETING + 12 -> + showTopics( + Topic("Yes, please.", GET_TEST), + Topic("Not right now, thanks.", END_DIALOGUE) + ) + + GET_TEST -> { + if (player.inventory.isFull) { + npcl(FacialExpression.HALF_GUILTY, "It seems your inventory is full.").also { stage = END_DIALOGUE } + } else if (amountInInventory(player, Items.TEST_PAPER_12626) > 0) { + npcl( + FacialExpression.HALF_GUILTY, + "You already have a test, please fill it out and return it to me." + ).also { stage = END_DIALOGUE } + } else { + player.savedData.globalData.testStage = 1 + addItem(player, Items.TEST_PAPER_12626) + npcl( FacialExpression.HALF_GUILTY, "Right then. Here is the test paper. " + + "When you have completed all the questions, bring it back to me for marking." + ).also { stage ++ } + } + } + GET_TEST + 1 -> playerl(FacialExpression.HALF_GUILTY, "Okay, thanks.").also { stage = END_DIALOGUE } + + HAND_IN_TEST -> npcl(FacialExpression.HAPPY, + "Ah, ${player.username}. How's the test going?").also { stage ++ } + HAND_IN_TEST + 1 -> playerl(FacialExpression.NEUTRAL, "I think I've finished.").also { stage++ } + HAND_IN_TEST + 2 -> npcl(FacialExpression.HAPPY, "Excellent! Let me just mark the paper for you then.").also { stage++ } + HAND_IN_TEST + 3 -> npcl(FacialExpression.HAPPY, "Hmm. Uh-huh, yes I see. Good! Yes, that's right.").also{ stage++ } + HAND_IN_TEST + 4-> npcl(FacialExpression.HAPPY, "Excellent! Allow me to reward you for your work. " + + "I have these two old lamps that you may find useful.").also { stage++ } + // This needs to be npc for word wrap + HAND_IN_TEST + 5 -> npc("Also, there is an old jail block connected to the cells", + "below the training centre, which have been overrun with", + "vicious creatures. If you search around the jail cells", + "downstairs, you should find it easily enough.").also { stage++ } + HAND_IN_TEST + 6 -> npcl(FacialExpression.HAPPY, "Now, your rewards.").also { stage++ } + HAND_IN_TEST + 7 ->{ + // Check for at least 1 slot + // Player will get 2 lamps but we will take the test from them + if (freeSlots(player) >= 1){ + showReward().also { stage = END_DIALOGUE } + } + else { + npcl(FacialExpression.SAD, "You do not have space in your inventory for the rewards").also { stage = END_DIALOGUE } + } + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.PROFESSOR_HENRY_7143) + } + + private fun showReward(){ + // Show the poster tunnel + setVarp(player, 1203, 1 shl 29, true) + player.savedData.globalData.testStage = 3 + + if (removeItem(player, Items.TEST_PAPER_12626)) { + addItem(player, Items.ANTIQUE_LAMP_4447, 2) + } + player.emoteManager.unlock(Emotes.SAFETY_FIRST) + + openInterface(player, iFace) + + // Clear the other lines + for (i in 9.. 18){ + player.packetDispatch.sendString("", iFace, i) + } + + player.packetDispatch.sendString("You have completed the Player Safety test!", iFace, 4) + player.packetDispatch.sendString(player.getQuestRepository().points.toString() + "", iFace, 7) + + player.packetDispatch.sendString("2 Experience lamps", iFace, 9) + player.packetDispatch.sendString("Access to the Stronghold of", iFace, 10) + player.packetDispatch.sendString("Player Safety Dungeon", iFace, 11) + player.packetDispatch.sendString("The Safety First' emote", iFace, 12) + + sendItemZoomOnInterface(player, iFace, 5, Items.TEST_PAPER_12626) + + } +} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/SafetyMapZone.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/SafetyMapZone.java deleted file mode 100644 index 940125f7c..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/SafetyMapZone.java +++ /dev/null @@ -1,15 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -import core.game.world.map.zone.MapZone; - -public class SafetyMapZone extends MapZone { - - public SafetyMapZone() { - super("strong hold of player safety", true); - } - - @Override - public void configure() { - - } -} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/StrongHoldOfPlayerSafetyListener.kt b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/StrongHoldOfPlayerSafetyListener.kt new file mode 100644 index 000000000..8c6a1ca73 --- /dev/null +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/StrongHoldOfPlayerSafetyListener.kt @@ -0,0 +1,333 @@ +package content.region.misthalin.barbvillage.stronghold.playersafety + + import core.api.* + import core.game.activity.Cutscene + import core.game.component.Component + import core.game.global.action.DoorActionHandler + import core.game.interaction.IntType + import core.game.interaction.InteractionListener + import core.game.interaction.InterfaceListener + import core.game.node.Node + import core.game.node.entity.player.Player + import core.game.node.entity.player.link.emote.Emotes + import core.game.node.scenery.Scenery + import core.game.world.map.Location + import org.rs09.consts.Items + import org.rs09.consts.NPCs + import org.rs09.consts.Scenery as SceneryConst + + +@Suppress("unused") + class StrongHoldOfPlayerSafetyListener : InteractionListener{ + + companion object{ + private val plaquesToIface = mapOf( // Door to interface + SceneryConst.JAIL_DOOR_29595 to 701, + SceneryConst.JAIL_DOOR_29596 to 703, + SceneryConst.JAIL_DOOR_29597 to 711, + SceneryConst.JAIL_DOOR_29598 to 695, + SceneryConst.JAIL_DOOR_29599 to 312, + SceneryConst.JAIL_DOOR_29600 to 706, + SceneryConst.JAIL_DOOR_29601 to 698, + ) + + private val creviceClimbedAttribute = "player_strong:crevice_climbed" + + } + override fun defineListeners() { + + // Test + on(Items.TEST_PAPER_12626, IntType.ITEM, "take exam") { player, _ -> + if (player.savedData.globalData.testStage == 2){ + sendMessage(player, "You have already completed the test. Hand it in to Professor Henry for marking.") + } + else{ + openInterface(player, 697) + } + return@on true + } + + // Students + on(intArrayOf(NPCs.STUDENT_7151, NPCs.STUDENT_7152, NPCs.STUDENT_7153, NPCs.STUDENT_7154, + NPCs.STUDENT_7155, NPCs.STUDENT_7156, NPCs.STUDENT_7157), IntType.NPC, "Talk-to") { player, _ -> + sendMessage(player, "This student is trying to focus on their work.") + return@on true + } + + // Jail teleports + on(SceneryConst.JAIL_ENTRANCE_29603, IntType.SCENERY, "use") { player, _-> + teleport(player, Location.create(3082, 4229, 0)) + return@on true + } + on(SceneryConst.JAIL_ENTRANCE_29602, IntType.SCENERY, "leave") { player, _ -> + teleport(player, Location.create(3074, 3456, 0)) + return@on true + } + on(SceneryConst.STAIRS_29589, IntType.SCENERY, "climb-up") { player, _ -> + if (player.globalData.hasReadPlaques()){ + teleport(player, Location.create(3083, 3452, 0)) + } + else{ + sendMessage(player, "You need to read the jail plaques before the guard will allow you upstairs") + } + + return@on true + } + // Exam room + on(SceneryConst.DOOR_29732, IntType.SCENERY, "open") { player, node -> + if (player.globalData.testStage > 0){ + // The player has talked to the prof + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + else{ + sendMessage(player, "The door is locked") + } + return@on true + } + on(SceneryConst.STAIRS_29592, IntType.SCENERY, "climb-down") { player, _ -> + teleport(player, Location.create(3086, 4247, 0)) + return@on true + } + + // crevice (and rope) + on(SceneryConst.CREVICE_29728, IntType.SCENERY, "enter"){ player, _ -> + if (getAttribute(player, creviceClimbedAttribute, false)){ + teleport(player, Location.create(3159, 4279, 3)) + } + else{ + // todo find what the crevice should say + sendMessage(player, "There's no way down") + } + return@on true + } + on(SceneryConst.ROPE_29729, IntType.SCENERY, "climb"){ player, _ -> + if (!getAttribute(player, creviceClimbedAttribute, false)){ + setAttribute(player, creviceClimbedAttribute, true) + } + teleport(player, Location.create(3077, 3462, 0)) + return@on true + } + + // Plaques + on(intArrayOf(SceneryConst.JAIL_DOOR_29595, SceneryConst.JAIL_DOOR_29596, SceneryConst.JAIL_DOOR_29597, + SceneryConst.JAIL_DOOR_29598, SceneryConst.JAIL_DOOR_29599, SceneryConst.JAIL_DOOR_29600, + SceneryConst.JAIL_DOOR_29601) , IntType.SCENERY, "Read-plaque on") { player, node -> + read(player, node) + return@on true + } + + // The dungeon + on(SceneryConst.POSTER_29586, IntType.SCENERY, "pull-back") { player, _ -> + sendDialogue(player, "There appears to be a tunnel behind this poster.") + teleport(player, Location.create(3140, 4230, 2)) + return@on true + } + on(SceneryConst.TUNNEL_29623, IntType.SCENERY, "use") {player, _ -> + teleport(player, Location.create(3077, 4235, 0)) + return@on true + } + + on(SceneryConst.AN_OLD_LEVER_29730, IntType.SCENERY, "pull") {player, _ -> + sendMessage(player, "You hear the cogs and gears moving and a distant unlocking sound.") + setVarp(player, 1203, (1 shl 29) or (1 shl 26), true) + return@on true + } + + on(SceneryConst.AN_OLD_LEVER_29731, IntType.SCENERY, "pull") {player, _ -> + sendMessage(player, "You hear cogs and gears moving and the sound of heavy locks falling into place.") + setVarp(player, 1203, 1 shl 29, true) + return@on true + } + + // the same jail door is used in 4 different places + on(SceneryConst.JAIL_DOOR_29624, IntType.SCENERY, "open") { player, _ -> + if (getVarp(player, 1203) and (1 shl 26) == 0) { + // The door is locked + sendMessage(player, "The door seems to be locked by some kind of mechanism.") + return@on true + } + if (player.location.z == 2) { + // Floor 2 to hidden tunnel + teleport(player, Location.create(3177, 4266, 0)) + } + else if (player.location.z == 1){ + // Floor 1 to hidden tunnel + teleport(player, Location.create(3143, 4270, 0)) + } + else { + // Leaving the hidden tunnel + if (player.location.x < 3150){ + // Leaving by the west exit (to floor 1) + teleport(player, Location.create(3142, 4272, 1)) + } + else{ + // Must be exiting by the east exit (to floor 2) + teleport(player, Location.create(3177, 4269, 2)) + } + } + return@on true + } + + // the 4 stairs in the middle of the 1st/2nd floor + // NE + on(SceneryConst.STAIRS_29667, IntType.SCENERY, "climb-down") { player, _ -> + teleport(player, Location.create(3160, 4249, 1)) + return@on true + } + on(SceneryConst.STAIRS_29668, IntType.SCENERY, "climb-up") { player, _ -> + teleport(player, Location.create(3158, 4250, 2)) + return@on true + } + + // SE + on(SceneryConst.STAIRS_29663, IntType.SCENERY, "climb-down") { player, _ -> + teleport(player, Location.create(3160, 4246, 1)) + return@on true + } + on(SceneryConst.STAIRS_29664, IntType.SCENERY, "climb-up") { player, _ -> + teleport(player, Location.create(3158, 4245, 2)) + return@on true + } + + // SW + on(SceneryConst.STAIRS_29655, IntType.SCENERY, "climb-down") { player, _ -> + teleport(player, Location.create(3146, 4246, 1)) + return@on true + } + on(SceneryConst.STAIRS_29656, IntType.SCENERY, "climb-up") { player, _ -> + teleport(player, Location.create(3149, 4244, 2)) + return@on true + } + + // NW + on(SceneryConst.STAIRS_29659, IntType.SCENERY, "climb-down") { player, _ -> + teleport(player, Location.create(3146, 4249, 1)) + return@on true + } + on(SceneryConst.STAIRS_29660, IntType.SCENERY, "climb-up") { player, _ -> + teleport(player, Location.create(3148, 4250, 2)) + return@on true + } + + + // rewards chest + on(SceneryConst.TREASURE_CHEST_29577, IntType.SCENERY, "open"){ player, _ -> + setVarbit(player, 4499, 1, true) + return@on true + } + on(SceneryConst.TREASURE_CHEST_29578, IntType.SCENERY, "search"){ player, _ -> + // Give the player rewards + if (player.globalData.testStage == 3){ + // Check the player has enough slots + if ((freeSlots(player) == 0) or + ((freeSlots(player) == 1) and !inInventory(player, Items.COINS_995))){ + sendDialogue(player, "You do not have enough inventory space!") + } + else{ + player.emoteManager.unlock(Emotes.SAFETY_FIRST) + addItem(player, Items.COINS_995, 10000) + addItem(player, Items.SAFETY_GLOVES_12629) + sendItemDialogue(player, Items.SAFETY_GLOVES_12629, + "You open the chest to find a large pile of gold, along with a pair of safety gloves. ") + + player.globalData.testStage = 4 + } + } + else { + // The player may have lost their gloves + if (hasAnItem(player, Items.SAFETY_GLOVES_12629).exists()){ + sendDialogue(player, "The chest is empty") + } + else{ + if (freeSlots(player) == 0){ + sendDialogue(player, "You do not have enough inventory space!") + } + else { + addItem(player, Items.SAFETY_GLOVES_12629) + sendItemDialogue( + player, Items.SAFETY_GLOVES_12629, + "You open the chest to find a pair of safety gloves." + ) + } + } + + } + return@on true + } + } + + fun read(player: Player, plaque: Node){ + if (plaque !is Scenery) return + player.interfaceManager.openChatbox(plaquesToIface[plaque.id]!!) + } + + + class PlaqueListener : InterfaceListener { + + var scene : PlaqueCutscene? = null + + override fun defineInterfaceListeners() { + for ((index, iface) in plaquesToIface.values.withIndex()){ + onClose(iface){ player, _ -> + scene?.end(fade = false) + player.globalData.readPlaques[index] = true + return@onClose true + } + + onOpen(iface) { player, component -> + scene = PlaqueCutscene(player, component) + scene?.start(hideMiniMap = false) + return@onOpen true + } + + on(iface) { player, _, _, buttonID, _, _ -> + // If thumbs up is clicked + if (buttonID == 2){ + scene?.incrementStage() + player.interfaceManager.closeChatbox() + } + return@on true + } + } + } + + class PlaqueCutscene(player: Player, val component: Component): Cutscene(player) { + + // Since the component does not know the door's location + // there needs to be some translation from player position + // to the door location. This is component -> rotation (dx, dy) + private val rotationMapping = mapOf( + 701 to listOf(-1, 0), + 703 to listOf(-1, 0), + 711 to listOf(-1, 0), + 695 to listOf(0, 1), + 312 to listOf(1, 0), + 706 to listOf(1, 0), + 698 to listOf(1, 0), + ) + + override fun setup() { + setExit(player.location) + } + + override fun runStage(stage: Int) { + when (stage){ + 0 -> { + // Go to head height + moveCamera(player.location.localX, player.location.localY, 245, speed = 50) + // Spin in the right direction + rotateCamera(player.location.localX + rotationMapping[component.id]!![0], + player.location.localY + rotationMapping[component.id]!![1], + 245, speed = 50) + } + 1 -> { + resetCamera() + } + } + } + + } + + } + + } diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/StrongHoldOfPlayerSafetyPlugin.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/StrongHoldOfPlayerSafetyPlugin.java deleted file mode 100644 index f286c6058..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/StrongHoldOfPlayerSafetyPlugin.java +++ /dev/null @@ -1,211 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.net.packet.PacketRepository; -import core.net.packet.context.CameraContext; -import core.net.packet.context.CameraContext.CameraType; -import core.net.packet.out.CameraViewPacket; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -/** - * @author Tyler Telis - */ -@Initializable -public class StrongHoldOfPlayerSafetyPlugin implements Plugin { - - /** - * when u pull back the poster and the dialogue pops up - * you get teleported to this map tile: [3140, 4230, 2] - */ - - /** - * The jail object ids. - */ - public static final int JAIL_ENTRANCE_ID_ENTER = 29603, JAIL_ENTRANCE_LEAVE = 29602, JAIL_STAIRS_UP = 29589, JAIL_STAIRS_DOWN = 29592; - - /** - * The guard NPC id. - */ - public static final int GUARD_NPC_ID = 7142; - - /** - * The test item id. - */ - public static final int TEST_PAPER_ITEM_ID = 12626; - - /** - * The jail {@code Location}'s - */ - public static final Location JAIL_ENTRANCE_LOCATION_ENTER = Location.create(3082, 4229, 0), JAIL_ENTRANCE_LOCATION_LEAVE = Location.create(3074, 3456, 0), JAIL_UP_STAIRS = Location.create(3083, 3452, 0), JAIL_DOWN_STAIRS = Location.create(3086, 4247, 0); - - @Override - public Plugin newInstance(Object object) throws Throwable { - ClassScanner.definePlugin(new PSOptionHandler()); - ClassScanner.definePlugin(new GuardDialoguePlugin()); - ClassScanner.definePlugin(new PlayerSafetyTest()); - ClassScanner.definePlugin(new ProfessorHenryDialogue()); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... object) { - return null; - } - - public enum JailPlaques { - - /** - * This sneaky gublinch wanted to set up a webcam conversation with a - * player! The player refused and sensibly used the Report Abuse button - * to enable us to catch him. - */ - PLAQUE_1(29599, 312), - - /** - * This naughty gublinchette gave a player her email address. The wise - * player hit the Report Abuse button straight away, allowing us to - * catch her! - */ - PLAQUE_2(29598, 695), - - /** - * This evil gublinch asked a younger player where she went to school. - * The player was smart enough to not tell the gublinch and report him - * instead. - */ - PLAQUE_3(29597, 711), - - /** - * This vile gublinchette was asking a player for his home address. The - * clever player didn't give her their address, but used the Report - * Abuse button to report her instead. - */ - PLAQUE_4(29596, 703), - - /** - * This sly gublinch wanted to meet one of his player friends in the - * real world. The player obviously refused and reported him straight - * away. - */ - PLAQUE_5(29600, 706), - - /** - * This cheeky gublinch claimed to be a Jagex Moderator! He was reported - * straight away and dealt with. - */ - PLAQUE_6(29601, 698), - - /** - * This wretched gublinch was asking a player for his telephone number. - * The player helped us catch him by refusing to give their information - * away and reporting the gublinch straight away! - */ - PLAQUE_7(29595, 701); - - /** - * The object id & interface id. - */ - private int objectId, interfaceId; - - /** - * Constructs a new {@code JailPlaques} instance. - * @param objectId The object id of the jail plague. - * @param interfaceId The interface id to send on the chat box. - */ - private JailPlaques(int objectId, int interfaceId) { - this.objectId = objectId; - this.interfaceId = interfaceId; - } - - /** - * Gets the object id. - * @return objectId. - */ - public int getObjectId() { - return objectId; - } - - /** - * Gets the interface id. - * @return interfaceId. - */ - public int getInterfaceId() { - return interfaceId; - } - - /** - * Gets the value of the jail-plaque. - * @return The ordinal. - */ - public int value() { - return ordinal(); - } - - /** - * Sends the jail-plague interface. - * @param player The {@code Player} instance. - */ - public void read(Player player, Scenery object) { - int x = object.getLocation().getX(); - int y = object.getLocation().getY(); - int rotationX = x; - int rotationY = y; - Component component = new Component(interfaceId); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.SET, x, y, 245, 1, 50)); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.ROTATION, rotationX, rotationY, 245, 1, 50)); - player.getInterfaceManager().openChatbox(component); - component.setCloseEvent(new CloseEvent() { - - @Override - public boolean close(Player player, Component c) { - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.RESET, 0, 0, 0, 1, 100)); - if (!player.getSavedData().getGlobalData().getReadPlaques()[value()]) { - player.getSavedData().getGlobalData().getReadPlaques()[value()] = true; - } - return true; - } - - }); - component.setPlugin(new ComponentPlugin() { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (button) { - case 2: - player.getInterfaceManager().closeChatbox(); - player.getSavedData().getGlobalData().getReadPlaques()[value()] = true; - return true; - } - return false; - } - - }); - } - } - - /** - * Find the {@link JailPlaques} instance for the object id. - * @param objectId The object id. - * @return The {@link JailPlaques} instance. - */ - public static JailPlaques forId(int objectId) { - for (JailPlaques plaque : JailPlaques.values()) { - if (plaque.getObjectId() == objectId) { - return plaque; - } - } - return null; - } -} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/Student.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/Student.java deleted file mode 100644 index a9977779a..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/Student.java +++ /dev/null @@ -1,30 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -import core.cache.def.impl.NPCDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles interactions with exam center students, to address #106 - * @author ceik - */ -@Initializable -public class Student extends OptionHandler { - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(7153).getHandlers().put("option:talk-to",this); - NPCDefinition.forId(7154).getHandlers().put("option:talk-to",this); - NPCDefinition.forId(7155).getHandlers().put("option:talk-to",this); - NPCDefinition.forId(7156).getHandlers().put("option:talk-to",this); - NPCDefinition.forId(7157).getHandlers().put("option:talk-to",this); - return this; - } - public final boolean handle(Player player, Node node, String options){ - if(options.equals("talk-to")){ - player.getPacketDispatch().sendMessage("This student is trying to focus on their work."); - } - return true; - } -} diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/ExaminerDialogue.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/ExaminerDialogue.kt index 659f0a7cf..d9f94dfba 100644 --- a/Server/src/main/content/region/misthalin/digsite/dialogue/ExaminerDialogue.kt +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ExaminerDialogue.kt @@ -1,5 +1,6 @@ package content.region.misthalin.digsite.dialogue +import content.data.Quests import content.region.misthalin.digsite.quest.thedigsite.TheDigSite import core.api.* import core.game.dialogue.* @@ -29,7 +30,7 @@ class ExaminerDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages(TheDigSite.questName, 100) + b.onQuestStages(Quests.THE_DIG_SITE, 100) .npcl(FacialExpression.HAPPY, "Hello there! My colleague tells me you helped to uncover a hidden altar to the god Zaros.") .npcl(FacialExpression.HAPPY, "A great scholar and archaeologist indeed! Good health and prosperity to you.") .options().let { optionBuilder -> @@ -43,10 +44,21 @@ class ExaminerDialogueFile : DialogueBuilderFile() { } } - b.onQuestStages(TheDigSite.questName, 6,7,8,9,10,11,12) - .npcl(FacialExpression.FRIENDLY, "Well, what are you doing here? Get digging!") + b.onQuestStages(Quests.THE_DIG_SITE, 6, 7, 8, 9, 10, 11, 12) + .branch { player -> if(inInventory(player, Items.TROWEL_676)) { 0 } else { 1 } } + .let{ branch -> + branch.onValue(0) + .npcl(FacialExpression.FRIENDLY, "Well, what are you doing here? Get digging!") + .end() + branch.onValue(1) + .playerl("I have lost my trowel.") + .npcl("Deary me. That was a good one as well. It's a good job I have another. Here you go...") + .endWith { _, player -> + addItemOrDrop(player, Items.TROWEL_676) + } + } - b.onQuestStages(TheDigSite.questName, 5) + b.onQuestStages(Quests.THE_DIG_SITE, 5) .playerl(FacialExpression.FRIENDLY, "Hello.") .npcl(FacialExpression.FRIENDLY, "Ah, hello again.") .options().let { optionBuilder -> @@ -169,16 +181,16 @@ class ExaminerDialogueFile : DialogueBuilderFile() { .playerl(FacialExpression.FRIENDLY, "I can dig wherever I want now!") .npcl(FacialExpression.FRIENDLY, "Perhaps you should use your newfound skills to find an artefact on the digsite that will impress the archaeological expert.") .endWith { _, player -> - if(getQuestStage(player, TheDigSite.questName) == 5) { - setQuestStage(player, TheDigSite.questName, 6) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 5) { + setQuestStage(player, Quests.THE_DIG_SITE, 6) } openInterface(player, 444) - setInterfaceText(player, player.name, 444, 5) + setInterfaceText(player, player.username, 444, 5) } } - b.onQuestStages(TheDigSite.questName, 4) + b.onQuestStages(Quests.THE_DIG_SITE, 4) .playerl(FacialExpression.FRIENDLY, "Hello.") .npcl(FacialExpression.FRIENDLY, "Hello again.") .options().let { optionBuilder -> @@ -301,22 +313,22 @@ class ExaminerDialogueFile : DialogueBuilderFile() { } .npcl(FacialExpression.FRIENDLY, "You have now passed the Earth Sciences level 2 intermediate exam. Here is your certificate. Of course, you'll want to get studying for your next exam now!") .endWith { _, player -> - if(getQuestStage(player, TheDigSite.questName) == 4) { - setQuestStage(player, TheDigSite.questName, 5) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 4) { + setQuestStage(player, Quests.THE_DIG_SITE, 5) } openInterface(player, 441) - setInterfaceText(player, player.name, 441, 5) + setInterfaceText(player, player.username, 441, 5) } } - b.onQuestStages(TheDigSite.questName, 1,2,3) + b.onQuestStages(Quests.THE_DIG_SITE, 1, 2, 3) // This is kinda messy due to the dialogue being reused in different stages. .branch { player -> - if (getQuestStage(player, TheDigSite.questName) == 2 && !inInventory(player, Items.SEALED_LETTER_683)){ + if (getQuestStage(player, Quests.THE_DIG_SITE) == 2 && !inInventory(player, Items.SEALED_LETTER_683)){ return@branch 1 // Reuse quest stage 1 if sealed letter is not in inventory. } - return@branch getQuestStage(player, TheDigSite.questName) + return@branch getQuestStage(player, Quests.THE_DIG_SITE) } .let{ branch -> val continuePath = b.placeholder() @@ -360,8 +372,8 @@ class ExaminerDialogueFile : DialogueBuilderFile() { if (inInventory(player, Items.SEALED_LETTER_683)) { removeItem(player, Items.SEALED_LETTER_683) } - if(getQuestStage(player, TheDigSite.questName) == 2) { - setQuestStage(player, TheDigSite.questName, 3) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 2) { + setQuestStage(player, Quests.THE_DIG_SITE, 3) } } .npcl(FacialExpression.NEUTRAL, "Good, good. We will begin the exam...") @@ -481,11 +493,11 @@ class ExaminerDialogueFile : DialogueBuilderFile() { .endWith { _, player -> // Because of onQuestStages and onPredicate, changing quest stage before the dialogue finishes breaks the flow. // As every stage, the onQuestStages and onPredicate functions are ran, so changing the values will switch out the stages. - if(getQuestStage(player, TheDigSite.questName) == 3) { - setQuestStage(player, TheDigSite.questName, 4) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 3) { + setQuestStage(player, Quests.THE_DIG_SITE, 4) } openInterface(player, 440) - setInterfaceText(player, player.name, 440, 5) + setInterfaceText(player, player.username, 440, 5) } } @@ -511,8 +523,8 @@ class ExaminerDialogueFile : DialogueBuilderFile() { .npcl(FacialExpression.FRIENDLY, "He's also a very busy man, so I write the letters and he justs stamps them if he approves.") .playerl(FacialExpression.FRIENDLY, "Oh, I see. I'll ask him if he'll approve me, and bring my stamped letter back here. Thanks.") .endWith { _, player -> - if(getQuestStage(player, TheDigSite.questName) == 0) { - setQuestStage(player, TheDigSite.questName, 1) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 0) { + setQuestStage(player, Quests.THE_DIG_SITE, 1) } } optionBuilder.option_playerl("Interesting...") diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/ResearcherDialogue.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/ResearcherDialogue.kt index 43001efaf..64af6e116 100644 --- a/Server/src/main/content/region/misthalin/digsite/dialogue/ResearcherDialogue.kt +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ResearcherDialogue.kt @@ -1,6 +1,6 @@ package content.region.misthalin.digsite.dialogue -import content.region.misthalin.digsite.quest.thedigsite.TheDigSite +import content.data.Quests import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -13,7 +13,7 @@ import org.rs09.consts.NPCs @Initializable class ResearcherDialogue (player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - if (isQuestComplete(player, TheDigSite.questName)){ + if (isQuestComplete(player, Quests.THE_DIG_SITE)){ when (stage) { START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Hello there. What are you doing here?").also { stage++ } 1 -> playerl(FacialExpression.FRIENDLY, "Just looking around at the moment.").also { stage++ } diff --git a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertDialogue.kt b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertDialogue.kt index 6672f61aa..3f4ea3d0e 100644 --- a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertDialogue.kt +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertDialogue.kt @@ -1,5 +1,6 @@ package content.region.misthalin.digsite.quest.thedigsite +import content.region.desert.quest.deserttreasure.DesertTreasure import core.api.* import core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile @@ -8,6 +9,7 @@ import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable +import org.rs09.consts.Items @Initializable @@ -26,6 +28,64 @@ class ArchaeologicalExpertDialogue (player: Player? = null) : DialoguePlugin(pla class ArchaeologicalExpertDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { + b.onQuestStages(DesertTreasure.questName, 4,5,6,7,8,9,10,11,12,13,14,15,100) + .npc("Hello again.", "Was that translation any use to Asgarnia?") + .playerl("I think it was, thanks!") + .end() + + b.onQuestStages(DesertTreasure.questName, 3) + .branch { player -> + return@branch if (inInventory(player, Items.TRANSLATION_4655)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npc("Hello again.", "Was that translation any use to Asgarnia?") + .playerl(FacialExpression.SUSPICIOUS, "I, uh, kind of didn't take it to him yet...") + .npc(FacialExpression.THINKING, "Whyever not?", "You're the strangest delivery @g[boy,girl] I've ever met!") + .end() + branch.onValue(0) + .playerl("I lost that translation you gave me...") + .npcl("Oh, no matter, I mostly remember what it said anyway! Here you go!") + .endWith { _, player -> + addItemOrDrop(player, Items.TRANSLATION_4655) + } + } + b.onQuestStages(DesertTreasure.questName, 2) + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.TRANSLATION_4655) + } + .npcl("There you go, that book contains the sum of my translating ability. If you would be so kind as to take that back to Asgarnia, I think it will reassure him that he is on the") + .npcl("right track for a find of great archaeological importance!") + .playerl("Wow! You write really quickly don't you?") + .npcl("What can I say? It's a skill I picked up through my many years of taking field notes!") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 2) { + setQuestStage(player, DesertTreasure.questName, 3) + } + } + + b.onQuestStages(DesertTreasure.questName, 1) + .playerl("Hello, are you Terry Balando?") + .npcl("That's right, who wants to know...?") + .npcl("Ah yes, I recognise you! You're the fellow who found that strange artefact about Zaros for the museum, aren't you? What can I do for you now?") + .playerl("That's right. I was in the desert down by the Bedabin Camp, and I found an archaeologist who asked me to deliver this to you.") + .npcl("You spoke to the legendary Asgarnia Smith??? Quickly, let me see what he had to give you! He is always at the forefront of archaeological breakthroughs!") + .betweenStage { df, player, _, _ -> + removeItem(player, Items.ETCHINGS_4654) // remove if exists + } + .playerl("So what does the inscription say? Anything interesting?") + .npcl("This... this is fascinating! These cuneiforms seem to predate even the settlement we are excavating here... Yes, yes, this is most interesting indeed!") + .playerl("Can you translate it for me?") + .npc("Well, I am not familiar with this particular language, but", "the similarities inherent in the pictographs seem to show", "a prevalent trend towards a syllabary consistent with", "the phonemes we have discovered in this excavation!") + .playerl("Um... So, can you translate it for me or not?") + .npcl("Well, unfortunately this is the only example of this particular language I have ever seen, but I might be able to make a rough translation, of sorts...") + .npcl("It might be slightly obscure on the finer details, but it should be good enough to understand the rough meaning of what was originally written. Please, just wait a moment, I will write up what I can") + .npcl("translate into a journal for you. Then you can take it back to Asgarnia, I think he will be extremely interested in the translation!") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 1) { + setQuestStage(player, DesertTreasure.questName, 2) + } + } + // Fallback dialogue. b.onPredicate { player -> true } .playerl(FacialExpression.FRIENDLY, "Hello. Who are you?") diff --git a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertListener.kt b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertListener.kt index cec1d1051..296e9fce0 100644 --- a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertListener.kt +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertListener.kt @@ -1,10 +1,9 @@ package content.region.misthalin.digsite.quest.thedigsite -import content.region.misthalin.digsite.dialogue.ArchaeologistcalExpertUsedOnDialogueFile +import content.data.Quests import core.api.* import core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile -import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression import core.game.interaction.IntType import core.game.interaction.InteractionListener @@ -109,8 +108,8 @@ class ArchaeologicalExpertListenerDialogueFile(val it: Int) : DialogueBuilderFil if (removeItem(player, Items.ANCIENT_TALISMAN_681)) { addItemOrDrop(player, Items.INVITATION_LETTER_696) } - if(getQuestStage(player, TheDigSite.questName) == 6) { - setQuestStage(player, TheDigSite.questName, 7) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 6) { + setQuestStage(player, Quests.THE_DIG_SITE, 7) } } @@ -124,8 +123,8 @@ class ArchaeologicalExpertListenerDialogueFile(val it: Int) : DialogueBuilderFil .item(Items.GOLD_BAR_2357, "The expert gives you two gold bars as payment.") .endWith { _, player -> if (removeItem(player, Items.STONE_TABLET_699)) { - if(getQuestStage(player, TheDigSite.questName) == 11) { - finishQuest(player, TheDigSite.questName) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 11) { + finishQuest(player, Quests.THE_DIG_SITE) } } } diff --git a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/DigsiteWorkmanDialogue.kt b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/DigsiteWorkmanDialogue.kt index 054541082..38830c490 100644 --- a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/DigsiteWorkmanDialogue.kt +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/DigsiteWorkmanDialogue.kt @@ -1,5 +1,6 @@ package content.region.misthalin.digsite.quest.thedigsite +import content.data.Quests import core.api.* import core.game.dialogue.* import core.game.interaction.IntType @@ -64,8 +65,8 @@ class DigsiteWorkmanDialogueFile : DialogueBuilderFile() { .npc(FacialExpression.FRIENDLY, "I give permission... blah de blah... err. Okay, that's all in", "order, you may use the mineshaft now. I'll hang onto", "this scroll, shall I?") .endWith { _, player -> removeItem(player, Items.INVITATION_LETTER_696) - if(getQuestStage(player, TheDigSite.questName) == 7) { - setQuestStage(player, TheDigSite.questName, 8) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 7) { + setQuestStage(player, Quests.THE_DIG_SITE, 8) } } } diff --git a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/StudentsDialogue.kt b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/StudentsDialogue.kt index 7e0764190..5ac84d0f5 100644 --- a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/StudentsDialogue.kt +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/StudentsDialogue.kt @@ -1,5 +1,6 @@ package content.region.misthalin.digsite.quest.thedigsite +import content.data.Quests import core.api.* import core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile @@ -28,10 +29,10 @@ class StudentGreenDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages(TheDigSite.questName, 6,7,8,9,10,11,12,13,100) + b.onQuestStages(Quests.THE_DIG_SITE, 6, 7, 8, 9, 10, 11, 12, 13, 100) .npcl(FacialExpression.FRIENDLY, " Oh, hi again. News of your find has spread fast; you are quite famous around here now.") - b.onQuestStages(TheDigSite.questName, 5) + b.onQuestStages(Quests.THE_DIG_SITE, 5) .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "I need more help with the exam.") @@ -42,7 +43,7 @@ class StudentGreenDialogueFile : DialogueBuilderFile() { setAttribute(player, TheDigSite.attributeStudentGreenExam3ObtainAnswer, true) } - b.onQuestStages(TheDigSite.questName, 4) + b.onQuestStages(Quests.THE_DIG_SITE, 4) .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "I need more help with the exam.") @@ -52,7 +53,7 @@ class StudentGreenDialogueFile : DialogueBuilderFile() { .endWith { _, player -> setAttribute(player, TheDigSite.attributeStudentGreenExam2ObtainAnswer, true) } - b.onPredicate { player -> getQuestStage(player, TheDigSite.questName) == 3 && getAttribute(player, TheDigSite.attributeStudentGreenExam1ObtainAnswer, false)} + b.onPredicate { player -> getQuestStage(player, Quests.THE_DIG_SITE) == 3 && getAttribute(player, TheDigSite.attributeStudentGreenExam1ObtainAnswer, false)} .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "I need more help with the exam.") @@ -62,7 +63,7 @@ class StudentGreenDialogueFile : DialogueBuilderFile() { .endWith { _, player -> setAttribute(player, TheDigSite.attributeStudentGreenExam1ObtainAnswer, true) } - b.onPredicate { player -> getQuestStage(player, TheDigSite.questName) == 3 && getAttribute(player, TheDigSite.attributeStudentGreenExam1Talked, false)} + b.onPredicate { player -> getQuestStage(player, Quests.THE_DIG_SITE) == 3 && getAttribute(player, TheDigSite.attributeStudentGreenExam1Talked, false)} .branch { player -> return@branch if (inInventory(player, Items.ANIMAL_SKULL_671)) { 1 } else { 0 } }.let{ branch -> @@ -86,7 +87,7 @@ class StudentGreenDialogueFile : DialogueBuilderFile() { } return@let branch } - b.onQuestStages(TheDigSite.questName, 3) + b.onQuestStages(Quests.THE_DIG_SITE, 3) .playerl(FacialExpression.FRIENDLY, "Hello there. Can you help me with the Earth Sciences exams at all?") .npcl(FacialExpression.FRIENDLY, "Well... Maybe I will if you help me with something.") .playerl(FacialExpression.FRIENDLY, "What's that?") @@ -127,7 +128,7 @@ class StudentPurpleDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onPredicate { player -> getQuestStage(player, TheDigSite.questName) == 5 && getAttribute(player, TheDigSite.attributeStudentPurpleExam3ObtainAnswer, false)} + b.onPredicate { player -> getQuestStage(player, Quests.THE_DIG_SITE) == 5 && getAttribute(player, TheDigSite.attributeStudentPurpleExam3ObtainAnswer, false)} .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "I am stuck on some more exam questions.") @@ -138,7 +139,7 @@ class StudentPurpleDialogueFile : DialogueBuilderFile() { setAttribute(player, TheDigSite.attributeStudentPurpleExam3ObtainAnswer, true) } - b.onPredicate { player -> getQuestStage(player, TheDigSite.questName) == 5 && getAttribute(player, TheDigSite.attributeStudentPurpleExam3Talked, false)} + b.onPredicate { player -> getQuestStage(player, Quests.THE_DIG_SITE) == 5 && getAttribute(player, TheDigSite.attributeStudentPurpleExam3Talked, false)} .branch { player -> return@branch if (inInventory(player, Items.OPAL_1609) || inInventory(player, Items.UNCUT_OPAL_1625)) { 1 } else { 0 } }.let{ branch -> @@ -170,7 +171,7 @@ class StudentPurpleDialogueFile : DialogueBuilderFile() { return@let branch } - b.onQuestStages(TheDigSite.questName, 5) + b.onQuestStages(Quests.THE_DIG_SITE, 5) .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "What, you want more help?") .playerl(FacialExpression.FRIENDLY, "Err... Yes please!") @@ -184,7 +185,7 @@ class StudentPurpleDialogueFile : DialogueBuilderFile() { setAttribute(player, TheDigSite.attributeStudentPurpleExam3Talked, true) } - b.onQuestStages(TheDigSite.questName, 4) + b.onQuestStages(Quests.THE_DIG_SITE, 4) .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "I am stuck on some more exam questions.") @@ -194,7 +195,7 @@ class StudentPurpleDialogueFile : DialogueBuilderFile() { .endWith { _, player -> setAttribute(player, TheDigSite.attributeStudentPurpleExam2ObtainAnswer, true) } - b.onPredicate { player -> getQuestStage(player, TheDigSite.questName) == 3 && getAttribute(player, TheDigSite.attributeStudentPurpleExam1ObtainAnswer, false)} + b.onPredicate { player -> getQuestStage(player, Quests.THE_DIG_SITE) == 3 && getAttribute(player, TheDigSite.attributeStudentPurpleExam1ObtainAnswer, false)} .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "I am stuck on some more exam questions.") @@ -204,7 +205,7 @@ class StudentPurpleDialogueFile : DialogueBuilderFile() { .endWith { _, player -> setAttribute(player, TheDigSite.attributeStudentPurpleExam1ObtainAnswer, true) } - b.onPredicate { player -> getQuestStage(player, TheDigSite.questName) == 3 && getAttribute(player, TheDigSite.attributeStudentPurpleExam1Talked, false)} + b.onPredicate { player -> getQuestStage(player, Quests.THE_DIG_SITE) == 3 && getAttribute(player, TheDigSite.attributeStudentPurpleExam1Talked, false)} .branch { player -> return@branch if (inInventory(player, Items.TEDDY_673)) { 1 } else { 0 } }.let{ branch -> @@ -229,7 +230,7 @@ class StudentPurpleDialogueFile : DialogueBuilderFile() { } return@let branch } - b.onQuestStages(TheDigSite.questName, 3) + b.onQuestStages(Quests.THE_DIG_SITE, 3) .playerl(FacialExpression.FRIENDLY, "Hello there. Can you help me with the Earth Sciences exams at all?") .npcl(FacialExpression.FRIENDLY, "I can if you help me...") .playerl(FacialExpression.FRIENDLY, "How can I do that?") @@ -266,7 +267,7 @@ class StudentBrownDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages(TheDigSite.questName, 5) + b.onQuestStages(Quests.THE_DIG_SITE, 5) .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "There are more exam questions I'm stuck on.") @@ -277,7 +278,7 @@ class StudentBrownDialogueFile : DialogueBuilderFile() { setAttribute(player, TheDigSite.attributeStudentBrownExam3ObtainAnswer, true) } - b.onQuestStages(TheDigSite.questName, 4) + b.onQuestStages(Quests.THE_DIG_SITE, 4) .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "There are more exam questions I'm stuck on.") @@ -287,7 +288,7 @@ class StudentBrownDialogueFile : DialogueBuilderFile() { .endWith { _, player -> setAttribute(player, TheDigSite.attributeStudentBrownExam2ObtainAnswer, true) } - b.onPredicate { player -> getQuestStage(player, TheDigSite.questName) == 3 && getAttribute(player, TheDigSite.attributeStudentBrownExam1ObtainAnswer, false)} + b.onPredicate { player -> getQuestStage(player, Quests.THE_DIG_SITE) == 3 && getAttribute(player, TheDigSite.attributeStudentBrownExam1ObtainAnswer, false)} .playerl(FacialExpression.FRIENDLY, "Hello there.") .npcl(FacialExpression.FRIENDLY, "How's it going?") .playerl(FacialExpression.FRIENDLY, "There are more exam questions I'm stuck on.") @@ -297,7 +298,7 @@ class StudentBrownDialogueFile : DialogueBuilderFile() { .endWith { _, player -> setAttribute(player, TheDigSite.attributeStudentBrownExam1ObtainAnswer, true) } - b.onPredicate { player -> getQuestStage(player, TheDigSite.questName) == 3 && getAttribute(player, TheDigSite.attributeStudentBrownExam1Talked, false)} + b.onPredicate { player -> getQuestStage(player, Quests.THE_DIG_SITE) == 3 && getAttribute(player, TheDigSite.attributeStudentBrownExam1Talked, false)} .playerl(FacialExpression.FRIENDLY, "Hello there. How's the study going?") .npcl(FacialExpression.FRIENDLY, "I'm getting there. Have you found my special cup yet?") .branch { player -> @@ -319,7 +320,7 @@ class StudentBrownDialogueFile : DialogueBuilderFile() { } return@let branch } - b.onQuestStages(TheDigSite.questName, 3) + b.onQuestStages(Quests.THE_DIG_SITE, 3) .playerl(FacialExpression.FRIENDLY, "Hello there. Can you help me with the Earth Sciences exams at all?") .npcl(FacialExpression.FRIENDLY, "I can't do anything unless I find my special cup.") .playerl(FacialExpression.FRIENDLY, "Your what?") diff --git a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSite.kt b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSite.kt index a368c031d..a32e9f6f8 100644 --- a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSite.kt +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSite.kt @@ -1,12 +1,12 @@ package content.region.misthalin.digsite.quest.thedigsite -import content.region.morytania.quest.creatureoffenkenstrain.CreatureOfFenkenstrain import core.api.* import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * The Dig Site Quest @@ -29,9 +29,8 @@ import org.rs09.consts.Items * 100 - Talked to expert after showing him the STONE_TABLET_699 */ @Initializable -class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { +class TheDigSite : Quest(Quests.THE_DIG_SITE, 47, 46, 2, 131, 0, 1, 9) { companion object { - const val questName = "The Dig Site" const val attributeStudentGreenExam1Talked = "/save:quest:thedigsite-studentgreenexam1talked" const val attributeStudentGreenExam1ObtainAnswer = "/save:quest:thedigsite-studentgreenexam1obtainanswer" const val attributeStudentPurpleExam1Talked = "/save:quest:thedigsite-studentpurpleexam1talked" @@ -83,7 +82,7 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { var line = 11 var stage = getStage(player) - var started = getQuestStage(player, questName) > 0 + var started = getQuestStage(player, Quests.THE_DIG_SITE) > 0 if(!started){ line++ @@ -93,6 +92,7 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { line(player, "Level 10 Agility", line++, hasLevelStat(player, Skills.AGILITY, 10)) line(player, "Level 10 Herblore", line++, hasLevelStat(player, Skills.HERBLORE, 10)) line(player, "Level 25 Thieving", line++, hasLevelStat(player, Skills.THIEVING, 25)) + limitScrolling(player, line, true) } else { line(player, "I should speak to an examiner about taking Earth Science", line++, true) line(player, "Exams.", line++, true) @@ -136,23 +136,23 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { line(player, "the exams.", line++) } if (stage >= 4 || getAttribute(player, attributeStudentBrownExam1Talked, false)) { - line(player, "I need to speak to the student in the brown top about the", line++, true) + line(player, "I need to speak to the student in the orange top about the", line++, true) line(player, "exams.", line++, true) } else if (stage >= 3) { - line(player, "I need to speak to the student in the brown top about the", line++) + line(player, "I need to speak to the student in the orange top about the", line++) line(player, "exams.", line++) } if (stage >= 4 || getAttribute(player, attributeStudentGreenExam1ObtainAnswer, false)) { line(player, "I have agreed to help the student in the green top.", line++, true) - line(player, "He has lost his animal skull and thinks he may have", line++, true) - line(player, "dropped it around the site. I need to find it and return", line++, true) - line(player, "it to him. Maybe one of the workmen has picked it up?", line++, true) + line(player, "He has lost his Animal Skull and thinks he may have", line++, true) + line(player, "dropped it around the digsite. I need to find it and return it", line++, true) + line(player, "to him. Maybe one of the workmen has picked it up?", line++, true) } else if (stage >= 3 && getAttribute(player, attributeStudentGreenExam1Talked, false)) { line(player, "I have agreed to help the student in the green top.", line++) - line(player, "He has lost his animal skull and thinks he may have", line++) - line(player, "dropped it around the site. I need to find it and return", line++) - line(player, "it to him. Maybe one of the workmen has picked it up?", line++) + line(player, "He has lost his !!Animal Skull?? and thinks he may have", line++) + line(player, "dropped it around the digsite. I need to find it and return it", line++) + line(player, "to him. Maybe one of the workmen has picked it up?", line++) } if (stage >= 4) { line(player, "I should talk to him to see if he can help with my exams.", line++, true) @@ -166,38 +166,36 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { if (stage >= 4 || getAttribute(player, attributeStudentPurpleExam1ObtainAnswer, false)) { line(player, "I have agreed to help the student in the purple skirt.", line++, true) - line(player, "She has lost her lucky teddy bear mascot and thinks she", line++, true) - line(player, "may have dropped it by the strange relic at the centre of", line++, true) - line(player, "the campus, maybe in a bush. I need to find it and return", line++, true) - line(player, "it to her.", line++, true) + line(player, "She has lost her Lucky Mascot and thinks she may have", line++, true) + line(player, "dropped it around the large urns on the digsite. I need to", line++, true) + line(player, "find it and return it to her.", line++, true) } else if (stage >= 3 && getAttribute(player, attributeStudentPurpleExam1Talked, false)) { line(player, "I have agreed to help the student in the purple skirt.", line++) - line(player, "She has lost her lucky teddy bear mascot and thinks she", line++) - line(player, "may have dropped it by the strange relic at the centre of", line++) - line(player, "the campus, maybe in a bush. I need to find it and return", line++) - line(player, "it to her.", line++) + line(player, "She has lost her !!Lucky Mascot?? and thinks she may have", line++) + line(player, "dropped it around the large urns on the digsite. I need to", line++) + line(player, "find it and return it to her.", line++) } if (stage >= 4) { line(player, "I should talk to her to see if she can help with my exams.", line++, true) - line(player, "She gave me an answer to one of the questions on the first", line++, true) - line(player, "exam.", line++, true) + line(player, "She gave me an answer to one of the questions on the", line++, true) + line(player, "first exam.", line++, true) } else if (stage >= 3 && getAttribute(player, attributeStudentPurpleExam1ObtainAnswer, false)) { line(player, "I should talk to her to see if she can help with my exams.", line++) - line(player, "She gave me an answer to one of the questions on the first", line++) - line(player, "exam.", line++) + line(player, "She gave me an answer to one of the questions on the", line++) + line(player, "first exam.", line++) } if (stage >= 4 || getAttribute(player, attributeStudentBrownExam1ObtainAnswer, false)) { - line(player, "I have agreed to help the student in the brown top.", line++, true) - line(player, "He has lost his special cup and thinks he may have dropped", line++, true) - line(player, "it while he was near the panning site, possibly in the", line++, true) - line(player, "water. I need to find it and return it.", line++, true) + line(player, "I have agreed to help the student in the orange top.", line++, true) + line(player, "He has lost his Special Cup and thinks he may have", line++, true) + line(player, "dropped it around the tents near the panning site. I need", line++, true) + line(player, "to find it and return it.", line++, true) } else if (stage >= 3 && getAttribute(player, attributeStudentBrownExam1Talked, false)) { - line(player, "I have agreed to help the student in the brown top.", line++) - line(player, "He has lost his special cup and thinks he may have dropped", line++) - line(player, "it while he was near the panning site, possibly in the", line++) - line(player, "water. I need to find it and return it.", line++) + line(player, "I have agreed to help the student in the orange top.", line++) + line(player, "He has lost his !!Special Cup?? and thinks he may have", line++) + line(player, "dropped it around the tents near the panning site. I need", line++) + line(player, "to find it and return it.", line++) } if (stage >= 4) { line(player, "I should talk to him to see if he can help with my exams.", line++, true) @@ -226,11 +224,11 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { } if (stage >= 5) { - line(player, "I need to study for my second exam. Perhaps the students", line++, true) - line(player, "on the site can help?", line++, true) + line(player, "I need to study for my second exam. Perhaps the three", line++, true) + line(player, "students on the digsite can help me again?", line++, true) } else if (stage >= 4) { - line(player, "I need to study for my second exam. Perhaps the students", line++) - line(player, "on the site can help?", line++) + line(player, "I need to study for my second exam. Perhaps the three", line++) + line(player, "students on the digsite can help me again?", line++) } if (stage >= 5 || getAttribute(player, attributeStudentGreenExam2ObtainAnswer, false)) { line(player, "I need to speak to the student in the green top about the", line++, true) @@ -241,16 +239,16 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { } if (stage >= 5 || getAttribute(player, attributeStudentPurpleExam2ObtainAnswer, false)) { line(player, "I need to speak to the student in the purple skirt about", line++, true) - line(player, "the exams. 2 ", line++, true) + line(player, "the exams.", line++, true) } else if (stage >= 4) { line(player, "I need to speak to the student in the purple skirt about", line++) line(player, "the exams.", line++) } if (stage >= 5 || getAttribute(player, attributeStudentBrownExam2ObtainAnswer, false)) { - line(player, "I need to speak to the student in the brown top about the", line++, true) + line(player, "I need to speak to the student in the orange top about the", line++, true) line(player, "exams.", line++, true) } else if (stage >= 4) { - line(player, "I need to speak to the student in the brown top about the", line++) + line(player, "I need to speak to the student in the orange top about the", line++) line(player, "exams.", line++) } if (stage >= 5) { @@ -269,11 +267,11 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { } if (stage >= 6) { - line(player, "I need to study for my third exam. Perhaps the students", line++, true) - line(player, "on the site can help?", line++, true) + line(player, "I should research for my third exam. Perhaps the students", line++, true) + line(player, "can help me again?", line++, true) } else if (stage >= 5) { - line(player, "I need to study for my third exam. Perhaps the students", line++) - line(player, "on the site can help?", line++) + line(player, "I should research for my third exam. Perhaps the students", line++) + line(player, "can help me again?", line++) } if (stage >= 6 || getAttribute(player, attributeStudentGreenExam3ObtainAnswer, false)) { line(player, "I need to speak to the student in the green top about the", line++, true) @@ -284,7 +282,7 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { } if (stage >= 6 || getAttribute(player, attributeStudentPurpleExam3Talked, false)) { line(player, "I need to speak to the student in the purple skirt about", line++, true) - line(player, "the exams. 3", line++, true) + line(player, "the exams.", line++, true) } else if (stage >= 5) { line(player, "I need to speak to the student in the purple skirt about", line++) line(player, "the exams.", line++) @@ -295,10 +293,10 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { line(player, "I need to bring her an Opal.", line++) } if (stage >= 6 || getAttribute(player, attributeStudentBrownExam3ObtainAnswer, false)) { - line(player, "I need to speak to the student in the brown top about the", line++, true) + line(player, "I need to speak to the student in the orange top about the", line++, true) line(player, "exams.", line++, true) } else if (stage >= 5) { - line(player, "I need to speak to the student in the brown top about the", line++) + line(player, "I need to speak to the student in the orange top about the", line++) line(player, "exams.", line++) } if (stage >= 6) { @@ -364,6 +362,7 @@ class TheDigSite : Quest("The Dig Site", 47, 46, 2, 131, 0, 1, 9) { line++ line(player,"QUEST COMPLETE!", line) } + limitScrolling(player, line, false) } } diff --git a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSiteListeners.kt b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSiteListeners.kt index c6745cf98..a79a331bd 100644 --- a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSiteListeners.kt +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSiteListeners.kt @@ -1,5 +1,6 @@ package content.region.misthalin.digsite.quest.thedigsite +import content.data.Quests import content.global.skill.thieving.ThievingListeners import core.api.* import core.api.utils.PlayerCamera @@ -162,7 +163,7 @@ class TheDigSiteListeners : InteractionListener { return@on true } sendMessage(player, "You attempt to pick the workman's pocket...") - if (getQuestStage(player, TheDigSite.questName) == 3) { + if (getQuestStage(player, Quests.THE_DIG_SITE) == 3) { player.animator.animate(ThievingListeners.PICKPOCKET_ANIM) val rollOutcome = ThievingListeners.pickpocketRoll(player, 84.0, 240.0, workmanPickpocketingTable) if (rollOutcome != null) { @@ -363,7 +364,7 @@ class TheDigSiteListeners : InteractionListener { val level3DigRight = ZoneBorders(3370, 3437, 3377, 3442) val level3DigLeft = ZoneBorders(3350, 3404, 3357, 3412) if (level3DigRight.insideBorder(player.location) || level3DigLeft.insideBorder(player.location)) { - if (getQuestStage(player, TheDigSite.questName) >= 6) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 6) { queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> when (stage) { 0 -> { @@ -392,7 +393,7 @@ class TheDigSiteListeners : InteractionListener { val level2Dig = ZoneBorders(3350, 3424, 3363, 3430) if (level2Dig.insideBorder(player.location)) { - if (getQuestStage(player, TheDigSite.questName) >= 5) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 5) { queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> when (stage) { 0 -> { @@ -422,7 +423,7 @@ class TheDigSiteListeners : InteractionListener { val level1DigCentre = ZoneBorders(3360, 3402, 3363, 3414) val level1DigRight = ZoneBorders(3367, 3403, 3372, 3414) if (level1DigCentre.insideBorder(player.location) || level1DigRight.insideBorder(player.location)) { - if (getQuestStage(player, TheDigSite.questName) >= 4) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 4) { queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> when (stage) { 0 -> { @@ -452,7 +453,7 @@ class TheDigSiteListeners : InteractionListener { val trainingDigLeft = ZoneBorders(3352, 3396, 3357, 3400) val trainingDigRight = ZoneBorders(3367, 3397, 3372, 3400) if (trainingDigLeft.insideBorder(player.location) || trainingDigRight.insideBorder(player.location)) { - if (getQuestStage(player, TheDigSite.questName) >= 3) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 3) { queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> when (stage) { 0 -> { @@ -483,12 +484,12 @@ class TheDigSiteListeners : InteractionListener { // 8: North East Winch goes to Doug Deeping on(Scenery.WINCH_2350, SCENERY, "operate") { player, _ -> - if (getQuestStage(player, TheDigSite.questName) >= 11) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 11) { sendMessage(player, "You try to climb down the rope...") sendMessage(player, "You lower yourself into the shaft...") teleport(player, Location(3369, 9763)) sendMessage(player, "You find yourself in a cavern...") - } else if (getQuestStage(player, TheDigSite.questName) >= 8) { + } else if (getQuestStage(player, Quests.THE_DIG_SITE) >= 8) { if (getAttribute(player, TheDigSite.attributeRopeNorthEastWinch, false)) { sendMessage(player, "You try to climb down the rope...") sendMessage(player, "You lower yourself into the shaft...") @@ -520,7 +521,7 @@ class TheDigSiteListeners : InteractionListener { // 8: Tie rope to winch onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.WINCH_2350) { player, used, with -> if (removeItem(player, used)) { - if (getQuestStage(player, TheDigSite.questName) >= 8) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 8) { setAttribute(player, TheDigSite.attributeRopeNorthEastWinch, true) sendMessage(player, "You tie the rope to the bucket.") } else { @@ -545,12 +546,12 @@ class TheDigSiteListeners : InteractionListener { // 8: West Winch goes to Skeletons, Explosion and Stone Tablet on(Scenery.WINCH_2351, SCENERY, "operate") { player, _ -> - if (getQuestStage(player, TheDigSite.questName) >= 11) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 11) { sendMessage(player, "You try to climb down the rope...") sendMessage(player, "You lower yourself into the shaft...") teleport(player, Location(3352, 9753)) sendMessage(player, "You find yourself in a cavern...") - } else if (getQuestStage(player, TheDigSite.questName) >= 8) { + } else if (getQuestStage(player, Quests.THE_DIG_SITE) >= 8) { if (getAttribute(player, TheDigSite.attributeRopeWestWinch, false)) { sendMessage(player, "You try to climb down the rope...") sendMessage(player, "You lower yourself into the shaft...") @@ -581,7 +582,7 @@ class TheDigSiteListeners : InteractionListener { // 8: Tie rope to winch onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.WINCH_2351) { player, used, with -> if (removeItem(player, used)) { - if (getQuestStage(player, TheDigSite.questName) >= 8) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 8) { setAttribute(player, TheDigSite.attributeRopeWestWinch, true) sendMessage(player, "You tie the rope to the bucket.") } else { @@ -614,14 +615,14 @@ class TheDigSiteListeners : InteractionListener { // 8: Investigating brick. Transitions to stage 9. on(Scenery.BRICK_2362, SCENERY, "search") { player, _ -> - if(getQuestStage(player, TheDigSite.questName) == 8) { + if(getQuestStage(player, Quests.THE_DIG_SITE) == 8) { sendPlayerDialogue(player, "Hmmm, there's a room past these bricks. If I could move them out of the way then I could find out what's inside. Maybe there's someone around here who can help...", FacialExpression.THINKING) - setQuestStage(player, TheDigSite.questName, 9) + setQuestStage(player, Quests.THE_DIG_SITE, 9) } - if(getQuestStage(player, TheDigSite.questName) == 9) { + if(getQuestStage(player, Quests.THE_DIG_SITE) == 9) { sendPlayerDialogue(player, "Hmmm, there's a room past these bricks. If I could move them out of the way then I could find out what's inside. Maybe there's someone around here who can help...", FacialExpression.THINKING) } - if(getQuestStage(player, TheDigSite.questName) == 10) { + if(getQuestStage(player, Quests.THE_DIG_SITE) == 10) { sendPlayerDialogue(player, "The brick is covered with the chemicals I made.", FacialExpression.THINKING) } return@on true @@ -749,12 +750,12 @@ class TheDigSiteListeners : InteractionListener { // 8/9: Pouring CHEMICAL_COMPOUND_707 on brick. Transitions to stage 10. onUseWith(SCENERY, Items.CHEMICAL_COMPOUND_707, Scenery.BRICK_2362) { player, used, with -> - if (getQuestStage(player, TheDigSite.questName) == 9) { + if (getQuestStage(player, Quests.THE_DIG_SITE) == 9) { if(removeItem(player, used)) { addItemOrDrop(player, Items.VIAL_229) sendMessage(player, "You pour the compound over the bricks...") sendPlayerDialogue(player, "Ok, the mixture is all over the bricks. I need some way to ignite this compound.", FacialExpression.THINKING) - setQuestStage(player, TheDigSite.questName, 10) + setQuestStage(player, Quests.THE_DIG_SITE, 10) } } return@onUseWith true @@ -762,8 +763,8 @@ class TheDigSiteListeners : InteractionListener { // 10: Lighting brick. Transitions to stage 11. onUseWith(SCENERY, Items.TINDERBOX_590, Scenery.BRICK_2362) { player, used, with -> - if(getQuestStage(player, TheDigSite.questName) == 10) { - setQuestStage(player, TheDigSite.questName, 11) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 10) { + setQuestStage(player, Quests.THE_DIG_SITE, 11) lock(player, 15) queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> when (stage) { @@ -825,43 +826,53 @@ class TheDigSiteListeners : InteractionListener { } on(Items.UNIDENTIFIED_LIQUID_702, ITEM, "drop") { player, node -> - removeItem(player, node) - impact(player, 25) - sendChat(player, "Ow! The liquid exploded!") - sendMessage(player, "You were injured by the burning liquid.") - return@on true + if (removeItem(player, node)) { + impact(player, 25) + sendChat(player, "Ow! The liquid exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false } on(Items.NITROGLYCERIN_703, ITEM, "drop") { player, node -> - removeItem(player, node) - impact(player, 35) - sendChat(player, "Ow! The nitroglycerin exploded!") - sendMessage(player, "You were injured by the burning liquid.") - return@on true + if (removeItem(player, node)) { + impact(player, 35) + sendChat(player, "Ow! The nitroglycerin exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false } on(Items.MIXED_CHEMICALS_705, ITEM, "drop") { player, node -> - removeItem(player, node) - impact(player, 45) - sendChat(player, "Ow! The liquid exploded!") - sendMessage(player, "You were injured by the burning liquid.") - return@on true + if (removeItem(player, node)) { + impact(player, 45) + sendChat(player, "Ow! The liquid exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false } on(Items.MIXED_CHEMICALS_706, ITEM, "drop") { player, node -> - removeItem(player, node) - impact(player, 55) - sendChat(player, "Ow! The liquid exploded!") - sendMessage(player, "You were injured by the burning liquid.") - return@on true + if (removeItem(player, node)) { + impact(player, 55) + sendChat(player, "Ow! The liquid exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false } on(Items.CHEMICAL_COMPOUND_707, ITEM, "drop") { player, node -> - removeItem(player, node) - impact(player, 65) - sendChat(player, "Ow! The liquid exploded!") - sendMessage(player, "You were injured by the burning liquid.") - return@on true + if (removeItem(player, node)) { + impact(player, 65) + sendChat(player, "Ow! The liquid exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false } // Scenery not tied to quest diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/MistagDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/MistagDialogue.kt index efdb1a9be..a05bfeb93 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/MistagDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/MistagDialogue.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.dialogue +import content.data.Quests import core.game.component.Component import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -23,7 +24,7 @@ class MistagDialogue (player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - val ltStage = player.questRepository.getStage("Lost Tribe") + val ltStage = player.questRepository.getStage(Quests.THE_LOST_TRIBE) if(args.size > 1 && args[1] == "greeting"){ npc("A human knows ancient greeting?") diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DukeHoracioTLTDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DukeHoracioTLTDialogue.kt index 93bb1f871..c71192c37 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DukeHoracioTLTDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DukeHoracioTLTDialogue.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.api.* import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -48,7 +49,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { "this mystery. If there is a blocked tunnel then perhaps", "you should try to un-block it." ) - player!!.questRepository.getQuest("Lost Tribe").setStage(player, 30) + player!!.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player, 30) stage = END_DIALOGUE } } @@ -108,7 +109,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { "brooch. The librarian in Varrock might be able to help", "identify the symbol." ) - player!!.questRepository.getQuest("Lost Tribe").setStage(player, 40) + player!!.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player, 40) stage = END_DIALOGUE } } @@ -140,7 +141,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { player!!.name.capitalize() + ", I would still like you to find out more", "about this tribe. It cannot hurt to know one's enemy." ) - player!!.questRepository.getQuest("Lost Tribe").setStage(player, 45) + player!!.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player, 45) stage = END_DIALOGUE } } @@ -169,7 +170,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 4 -> { npc("Unless it is returned, I am afraid I will have no option", "but war.") - player!!.questRepository.getQuest("Lost Tribe").setStage(player, 47) + player!!.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player, 47) stage = END_DIALOGUE } } @@ -216,7 +217,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { "their leader to sign it." ) addItemOrDrop(player!!, Items.PEACE_TREATY_5012) - player!!.questRepository.getQuest("Lost Tribe").setStage(player, 50) + player!!.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player, 50) setVarbit(player!!, 532, 9, true) stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/HistoryOfTheGoblinRace.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/HistoryOfTheGoblinRace.kt index 57c14a8f0..d6d5ed9ab 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/HistoryOfTheGoblinRace.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/HistoryOfTheGoblinRace.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.game.component.Component import core.game.component.ComponentDefinition import core.game.component.ComponentPlugin @@ -23,11 +24,11 @@ class HistoryOfTheGoblinRace : ComponentPlugin() { player ?: return super.open(player, component) player.packetDispatch.sendInterfaceConfig(183,17,true) - val qstage = player.questRepository.getQuest("Lost Tribe").getStage(player) + val qstage = player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) component?.setCloseEvent { player, c -> if(qstage == 42 || qstage == 41 ) { player.dialogueInterpreter.sendDialogues(player, FacialExpression.THINKING, "Hey... The symbol of the 'Dorgeshuun' tribe looks just", "like the symbol on the brooch I found.") - player.questRepository.getQuest("Lost Tribe").setStage(player, 43) + player.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player, 43) } player.removeAttribute("hgr-index") true diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribe.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribe.kt index fec111418..2bbcda87a 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribe.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribe.kt @@ -8,13 +8,14 @@ import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items import core.api.* +import content.data.Quests @Initializable /** * Represents the lost tribe quest and quest journal * @author Ceikry */ -class LostTribe : Quest("Lost Tribe",84,83,1) { +class LostTribe : Quest(Quests.THE_LOST_TRIBE,84,83,1) { override fun newInstance(`object`: Any?): Quest { return this } @@ -27,8 +28,8 @@ class LostTribe : Quest("Lost Tribe",84,83,1) { line(player,"I can start this quest by speaking to !!Sigmund?? in !!Lumbridge??",line++) line(player,"!!Castle.??",line++) line(player,"I must have completed:",line++) - line(player,"Rune Mysteries",line++,player?.questRepository?.isComplete("Rune Mysteries") == true) - line(player,"Goblin Diplomacy",line++,player?.questRepository?.isComplete("Goblin Diplomacy") == true) + line(player,"Rune Mysteries" ,line++,player?.questRepository?.isComplete(Quests.RUNE_MYSTERIES) == true) + line(player,"Goblin Diplomacy" ,line++,player?.questRepository?.isComplete(Quests.GOBLIN_DIPLOMACY) == true) line(player,"and have:",line++) line(player,"Level 17 mining",line++,player.skills.getLevel(Skills.MINING) >= 17) line(player,"Level 13 agility",line++,player.skills.getLevel(Skills.AGILITY) >= 13) diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeCutscene.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeCutscene.kt index a9413b011..f155df3f5 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeCutscene.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeCutscene.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.api.animate import core.api.face import core.game.activity.Cutscene @@ -105,7 +106,7 @@ class LostTribeCutscene(player: Player) : Cutscene(player) { } 19 -> { end { - player.questRepository.getQuest("Lost Tribe").finish(player) + player.questRepository.getQuest(Quests.THE_LOST_TRIBE).finish(player) } } } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeOptionHandler.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeOptionHandler.kt index 53c5cdf1e..a361dcfbc 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeOptionHandler.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeOptionHandler.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.api.addItemOrDrop import core.cache.def.impl.ItemDefinition import core.cache.def.impl.NPCDefinition @@ -39,7 +40,8 @@ class LostTribeOptionHandler : OptionHandler(){ 5008 -> player.interfaceManager.open(Component(50)) 5009 -> player.interfaceManager.open(Component(183)) 6916 -> { - if(!player.inventory.containsItem(BOOK) && !player.bank.containsItem(BOOK) && player.questRepository.getQuest("Lost Tribe").getStage(player) >= 41){ + if(!player.inventory.containsItem(BOOK) && !player.bank.containsItem(BOOK) && player.questRepository.getQuest( + Quests.THE_LOST_TRIBE).getStage(player) >= 41){ player.dialogueInterpreter.sendDialogue("'A History of the Goblin Race.' This must be it.") player.inventory.add(BOOK) } else { @@ -47,10 +49,10 @@ class LostTribeOptionHandler : OptionHandler(){ } } 6911 -> { - if(!player.inventory.containsItem(Item(Items.SILVERWARE_5011)) && player.questRepository.getQuest("Lost Tribe").getStage(player) == 48){ + if(!player.inventory.containsItem(Item(Items.SILVERWARE_5011)) && player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 48){ player.dialogueInterpreter.sendItemMessage(Items.SILVERWARE_5011,"You find the missing silverware!") addItemOrDrop(player, Items.SILVERWARE_5011) - player.questRepository.getQuest("Lost Tribe").setStage(player,49) + player.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player,49) } else { player.sendMessage("You find nothing.") } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/MistagLTDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/MistagLTDialogue.kt index 8e01d2f77..8c637a839 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/MistagLTDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/MistagLTDialogue.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.game.component.Component import core.game.dialogue.FacialExpression import core.game.dialogue.DialogueFile @@ -20,7 +21,8 @@ class MistagLTDialogue(val isGreeting: Boolean, val questStage: Int) : DialogueF 4 -> player("Did you break in to the castle cellar?").also { stage++ } 5 -> npc("It was an accident. We were following a seam of iron","and suddenly we found ourselves in a room!").also { stage++ } 6 -> npc("We blocked up our tunnel behind us and ran back","here. Then we did what cave goblins always do when","there is a problem: we hid and hoped it would go away.").also { stage++ } - 7 -> npc("We meant no harm! Please tell the ruler of the above","people that we want to make peace.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest("Lost Tribe").setStage(player,46) } + 7 -> npc("We meant no harm! Please tell the ruler of the above","people that we want to make peace.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest( + Quests.THE_LOST_TRIBE).setStage(player,46) } } } @@ -32,7 +34,7 @@ class MistagLTDialogue(val isGreeting: Boolean, val questStage: Int) : DialogueF 2 -> player("Did you break in to the castle cellar?").also { stage++ } 3 -> npc("It was an accident. We were following a seam of iron","and suddenly we found ourselves in a room!").also { stage++ } 4 -> npc("We blocked up our tunnel behind us and ran back","here. Then we did what cave goblins always do when","there is a problem: we hid and hoped it would go away.").also { stage++ } - 5 -> npc("We meant no harm! Please tell the ruler of the above","people that we want to make peace.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest("Lost Tribe").setStage(player,46) } + 5 -> npc("We meant no harm! Please tell the ruler of the above","people that we want to make peace.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player,46) } } } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickaxeOnRubble.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickaxeOnRubble.kt index 62c3b392d..a1a871135 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickaxeOnRubble.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickaxeOnRubble.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler import core.game.node.entity.skill.Skills @@ -25,7 +26,7 @@ class PickaxeOnRubble : UseWithHandler(1265,1267,1269,1271,1273,1275){ override fun handle(event: NodeUsageEvent?): Boolean { val player = event?.player ?: return false - val stage = player.questRepository.getQuest("Lost Tribe").getStage(player) + val stage = player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) if(stage < 30){ player.dialogueInterpreter.sendItemMessage(event.usedItem.id,"I should probably figure out what happened","before vandalizing the castle more.") return true diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickpocketSigmund.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickpocketSigmund.kt index fa6194e68..a4e519efa 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickpocketSigmund.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickpocketSigmund.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.game.node.item.Item import core.game.system.task.Pulse import core.game.world.update.flag.context.Animation @@ -25,7 +26,7 @@ class PickpocketSigmund : InteractionListener { when(counter++){ 0 -> player.animator.animate(Animation(881)) 3 -> { - if(player.questRepository.getQuest("Lost Tribe").getStage(player) == 47 && !player.inventory.containsItem(Item(Items.KEY_423))){ + if(player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 47 && !player.inventory.containsItem(Item(Items.KEY_423))){ player.inventory.add(Item(Items.KEY_423)) player.dialogueInterpreter.sendItemMessage(Items.KEY_423,"You find a small key on Sigmund.") } else { diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/SigmundChestHandler.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/SigmundChestHandler.kt index 246b139f8..ef26dc557 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/SigmundChestHandler.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/SigmundChestHandler.kt @@ -1,5 +1,6 @@ package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.cache.def.impl.SceneryDefinition import core.game.interaction.OptionHandler import core.game.node.Node @@ -23,14 +24,14 @@ class SigmundChestHandler : OptionHandler() { override fun handle(player: Player?, node: Node?, option: String?): Boolean { player ?: return false - if(player.questRepository.getQuest("Lost Tribe").getStage(player) == 47 && player.inventory.contains(Items.KEY_423,1)){ + if(player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 47 && player.inventory.contains(Items.KEY_423,1)){ player.inventory.remove(Item(Items.KEY_423)) for(item in arrayOf(Items.HAM_ROBE_4300,Items.HAM_SHIRT_4298,Items.HAM_HOOD_4302).map { Item(it) }){ if(!player.inventory.add(item)){ GroundItemManager.create(item,player) } } - player.questRepository.getQuest("Lost Tribe").setStage(player,48) + player.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player,48) } else { player.sendMessage("This chest requires a key.") } diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/AggieDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/AggieDialogue.java index 7e3dbe614..fe7fb4830 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/AggieDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/AggieDialogue.java @@ -9,6 +9,7 @@ import core.game.node.item.Item; import core.game.world.map.Location; import core.plugin.Initializable; import core.game.world.update.flag.context.Animation; +import content.data.Quests; /** * Represents the dialogue plugin used for the aggie npc. @@ -115,7 +116,7 @@ public final class AggieDialogue extends DialoguePlugin { stage = 42; return true; } - quest = player.getQuestRepository().getQuest("Prince Ali Rescue"); + quest = player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE); npc("What can I help you with?"); stage = 0; return true; diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/JoeGuardDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/JoeGuardDialogue.java index 8351ddd49..b7b91c6d8 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/JoeGuardDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/JoeGuardDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue of the Joe guard NPC. @@ -49,7 +50,7 @@ public final class JoeGuardDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Prince Ali Rescue"); + quest = player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE); switch (quest.getStage(player)) { case 40: if (player.getAttribute("guard-drunk", false)) { diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/LeelaDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/LeelaDialogue.java index 026f2ff53..133cebfb1 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/LeelaDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/LeelaDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represnets the dialogue used to handle the Leela npc. @@ -76,7 +77,7 @@ public final class LeelaDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Prince Ali Rescue"); + quest = player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE); switch (quest.getStage(player)) { case 60: case 100: diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/MissSchismDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/MissSchismDialogue.java index 3cc707120..afa313cd1 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/MissSchismDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/MissSchismDialogue.java @@ -5,6 +5,7 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; +import content.data.Quests; /** * Represents the miss schism dialogue plugin. @@ -90,7 +91,7 @@ public final class MissSchismDialogue extends DialoguePlugin { break; case 110: - if (player.getQuestRepository().isComplete("Vampire Slayer")) { + if (player.getQuestRepository().isComplete(Quests.VAMPIRE_SLAYER)) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, there's nothing to tell NOW. You killed it."); stage = 111; } else { @@ -156,7 +157,7 @@ public final class MissSchismDialogue extends DialoguePlugin { stage = 23; break; case 23: - if(player.getQuestRepository().isComplete("Vampire Slayer")) { + if(player.getQuestRepository().isComplete(Quests.VAMPIRE_SLAYER)) { interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, now that I've cleared the vampire out of the manor,", "I guess you won't have too much trouble turning it into a", "museum."); stage = 24; } else { diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/MorganDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/MorganDialogue.java index eb6270e1e..e7d150cf7 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/MorganDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/MorganDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the dialogue plugin used for the morgan npc. @@ -39,9 +40,9 @@ public final class MorganDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest("Vampire Slayer"); + quest = player.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER); npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Vampire Slayer"); + quest = player.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER); if (quest.getStage(player) == 0) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Please please help us, bold adventurer!"); stage = 0; diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/PrinceAliDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/PrinceAliDialogue.java index eb500dfe2..cb13ff920 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/PrinceAliDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/PrinceAliDialogue.java @@ -8,6 +8,7 @@ import core.game.node.item.Item; import core.game.system.task.Pulse; import core.plugin.Initializable; import core.game.world.GameWorld; +import content.data.Quests; /** * Represents the dialogue used to handle the Pricne Ali NPC. @@ -52,7 +53,7 @@ public class PrinceAliDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Prince Ali Rescue"); + quest = player.getQuestRepository().getQuest(Quests.PRINCE_ALI_RESCUE); switch (quest.getStage(player)) { case 50: interpreter.sendDialogues(player, null, "Prince, I come to rescue you."); diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/ProfessorOddensteinPlugin.java b/Server/src/main/content/region/misthalin/draynor/dialogue/ProfessorOddensteinPlugin.java index 294c14ec7..b90bdc349 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/ProfessorOddensteinPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/ProfessorOddensteinPlugin.java @@ -14,6 +14,7 @@ import core.game.world.repository.Repository; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.game.world.update.flag.context.Graphics; +import content.data.Quests; /** * Represents the plugin dialogue to handle the interaction with professor @@ -81,7 +82,7 @@ public class ProfessorOddensteinPlugin extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("Ernest the Chicken"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ERNEST_THE_CHICKEN); switch (quest.getStage(player)) { case 0: case 10: @@ -102,7 +103,7 @@ public class ProfessorOddensteinPlugin extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Ernest the Chicken"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ERNEST_THE_CHICKEN); switch (quest.getStage(player)) { case 0: case 100: diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/VeronicaDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/VeronicaDialogue.java index 25796ff04..3480ca1b4 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/VeronicaDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/VeronicaDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the dialogue used to handle the interaction between veronica. @@ -39,7 +40,7 @@ public class VeronicaDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Ernest the Chicken"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ERNEST_THE_CHICKEN); switch (quest.getStage(player)) { case 0: switch (stage) { @@ -169,7 +170,7 @@ public class VeronicaDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("Ernest the Chicken"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ERNEST_THE_CHICKEN); switch (quest.getStage(player)) { case 0: interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Can you please help me? I'm in a terrible spot of", "trouble."); diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt new file mode 100644 index 000000000..423e57dec --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt @@ -0,0 +1,141 @@ +package content.region.misthalin.draynor.handlers + +import core.api.hasLineOfSight +import core.game.interaction.MovementPulse +import core.game.node.entity.Entity +import core.game.node.entity.impl.PulseType +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.map.path.Pathfinder +import core.plugin.Initializable + +/** + * Represents the Draynor Manor Chair NPC, these are the chairs that follow the player + * around the manor. + * + * @author Broseki + */ + +private const val DRAYNOR_MANOR_CHAIR_NPC_ID = 3293 +private const val FOLLOWING_DISTANCE = 5 +private const val STOP_FOLLOWING_DISTANCE = 30 + +@Initializable +class DraynorManorChairNPC(id: Int = DRAYNOR_MANOR_CHAIR_NPC_ID, location: Location? = null) : + AbstractNPC(id, location) { + + // The player this NPC is currently following, null if nobody is being followed + private var following: Player? = null + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return DraynorManorChairNPC(id, location) + } + + override fun handleTickActions() { + super.handleTickActions() + val closestPlayer = findClosestPlayer() + // If there is a player nearby, follow them + if (closestPlayer != null && following != closestPlayer) { + stopFollowing() + following = closestPlayer + follow(closestPlayer) + following?.let { face(it) } + } else { + // If we didn't find a player within `FOLLOWING_DISTANCE` + // but we are following a player, check that they are still + // within `STOP_FOLLOWING_DISTANCE`, if they are gone, stop + // trying to follow them. + following?.let { player -> + if (findDistanceToPlayer(player) > STOP_FOLLOWING_DISTANCE + || !player.isActive + || player.isInvisible) { + stopFollowing() + } else { + if (!pulseManager.hasPulseRunning()) { + follow(player) + } + face(player) + } + } + } + } + + /** + * Stops following `following` + */ + fun stopFollowing() { + following = null + resetWalk() + pulseManager.clear(PulseType.STANDARD) + } + + /** + * Finds the closest player to the current entity within `FOLLOWING_DISTANCE` + * that is currently in the chair's line of sight. + * + * @return The Player object representing the closest player, or null if there are no players nearby. + */ + fun findClosestPlayer(): Player? { + val players = RegionManager.getLocalPlayers(this, FOLLOWING_DISTANCE) + if (players.isEmpty()) { + return null + } + + var closestPlayer: Player? = null + var closestDistance = Double.MAX_VALUE + + for (player in players) { + // Make sure the chair does not try to start + // following a player in another room + if (!hasLineOfSight(this, player)) { + continue + } + + // If the player is invisible, don't follow them + if (player.isInvisible) { + continue + } + + val distance = findDistanceToPlayer(player) + if (distance < closestDistance) { + closestDistance = distance + closestPlayer = player + } + } + + return closestPlayer + } + + /** + * Calculates the distance between the current entity and the specified player. + * + * @param player The player whose distance from the current entity is to be calculated. + * @return The distance between the current entity and the specified player as a Double value. + */ + fun findDistanceToPlayer(player: Player): Double { + return this.location.getDistance(player.location) + } + + /** + * Triggers the current entity to follow the specified player using a movement pulse. + * + * @param player The player that the entity should follow. + */ + fun follow(player: Player) { + pulseManager.run((object : MovementPulse(this, player, Pathfinder.DUMB) { + override fun pulse(): Boolean { + return false + } + }), PulseType.STANDARD) + } + + override fun getIds(): IntArray { + return intArrayOf(DRAYNOR_MANOR_CHAIR_NPC_ID) + } + + override fun shouldPreventStacking(mover: Entity?): Boolean { + return mover is DraynorManorChairNPC + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt new file mode 100644 index 000000000..9478c6656 --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt @@ -0,0 +1,57 @@ +package content.region.misthalin.draynor.handlers + +import core.game.node.entity.Entity +import core.game.world.map.Location +import core.game.world.map.RegionManager.getLocalNpcs +import core.game.world.map.path.Pathfinder +import core.game.world.map.zone.MapZone +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneBuilder +import core.plugin.Initializable +import core.plugin.Plugin + +/** + * Represents the interior of Draynor Manor. + * + * @author Broseki + */ +@Initializable +class DraynorManorHouseZone : MapZone("Draynor Manor House", true), Plugin { + + override fun newInstance(arg: Any?): Plugin { + ZoneBuilder.configure(this) + return this + } + + override fun move(e: Entity, loc: Location?, destination: Location): Boolean { + for (n in getLocalNpcs(e, 5)) { + if (n.isInvisible() || n === e) { + continue + } + if (n.shouldPreventStacking(e)) { + val s1 = e.size() + val s2 = n.size() + val x = destination.getX() + val y = destination.getY() + val l = n.getLocation() + if (Pathfinder.isStandingIn(x, y, s1, s1, l.getX(), l.getY(), s2, s2)) { + return false + } + } + } + return true + } + + override fun enter(entity: Entity?): Boolean { + return super.enter(entity) + } + + override fun fireEvent(identifier: String?, vararg args: Any?): Any? { + return null + } + + override fun configure() { + register(ZoneBorders(3097, 3373, 3119, 3364)) + register(ZoneBorders(3090, 3363, 3126, 3354)) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceDialogue.java index 0dcfe9778..0d81ec43b 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceDialogue.java @@ -1,5 +1,6 @@ package content.region.misthalin.draynor.quest.anma; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -41,7 +42,7 @@ public final class AliceDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(AnimalMagnetism.NAME); + quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); switch (quest.getStage(player)) { default: options("What are you selling?", "I'm okay, thank you."); diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceHusbandDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceHusbandDialogue.java index b9dbb405c..b584565ff 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceHusbandDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceHusbandDialogue.java @@ -1,10 +1,13 @@ package content.region.misthalin.draynor.quest.anma; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import content.region.misthalin.draynor.quest.anma.AnmaCutscene; +import org.rs09.consts.Items; /** * Handles the husband of alice's npc dialogue. @@ -46,7 +49,11 @@ public final class AliceHusbandDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(AnimalMagnetism.NAME); + if (!player.getEquipment().containsAtLeastOneItem(Items.GHOSTSPEAK_AMULET_552)) { + npc("Wooo wooo wooooo!"); + return true; + } + quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); switch (quest.getStage(player)) { case 0: npc("Hi, I don't feel like talking."); @@ -85,6 +92,10 @@ public final class AliceHusbandDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { + if (!player.getEquipment().containsAtLeastOneItem(Items.GHOSTSPEAK_AMULET_552)) { + end(); + return true; + } switch (quest.getStage(player)) { default: switch (stage) { diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetism.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetism.java index 4e2f9dc9c..a04a6c9de 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetism.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetism.java @@ -11,6 +11,7 @@ import content.region.misthalin.draynor.quest.anma.AnimalMagnetismPlugin.HammerM import content.region.misthalin.draynor.quest.anma.AnimalMagnetismPlugin.ResearchNoteHandler; import core.plugin.Initializable; import content.region.misthalin.draynor.quest.anma.AnimalMagnetismPlugin.UndeadTreePlugin; +import content.data.Quests; /** * Handles the animal magnetism quest. @@ -18,12 +19,6 @@ import content.region.misthalin.draynor.quest.anma.AnimalMagnetismPlugin.UndeadT */ @Initializable public final class AnimalMagnetism extends Quest { - - /** - * The name of this quest. - */ - public static String NAME = "Animal Magnetism"; - /** * The crone made amulet item. */ @@ -103,7 +98,7 @@ public final class AnimalMagnetism extends Quest { * Constructs a new {@code AnimalMagnetism} {@code Object}. */ public AnimalMagnetism() { - super("Animal Magnetism", 33, 32, 1); + super(Quests.ANIMAL_MAGNETISM, 33, 32, 1); } @Override @@ -219,9 +214,9 @@ public final class AnimalMagnetism extends Quest { @Override public boolean hasRequirements(Player player) { - requirements[0] = player.getQuestRepository().isComplete("The Restless Ghost"); - requirements[1] = player.getQuestRepository().isComplete("Ernest the Chicken"); - requirements[2] = player.getQuestRepository().isComplete("Priest in Peril"); + requirements[0] = player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST); + requirements[1] = player.getQuestRepository().isComplete(Quests.ERNEST_THE_CHICKEN); + requirements[2] = player.getQuestRepository().isComplete(Quests.PRIEST_IN_PERIL); requirements[3] = player.getSkills().getStaticLevel(Skills.RANGE) >= 30; requirements[4] = player.getSkills().getStaticLevel(Skills.SLAYER) >= 18; requirements[5] = player.getSkills().getStaticLevel(Skills.CRAFTING) >= 19; diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetismPlugin.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetismPlugin.java index 141e8a52f..6156fe9bd 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetismPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetismPlugin.java @@ -19,7 +19,6 @@ import core.game.node.Node; import core.game.node.entity.impl.Animator.Priority; 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.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.game.system.task.Pulse; @@ -33,9 +32,9 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.plugin.ClassScanner; import core.tools.RandomFunction; -import org.rs09.consts.Sounds; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Handles the animal magnetism plugin. @@ -62,14 +61,14 @@ public final class AnimalMagnetismPlugin extends OptionHandler { public boolean handle(Player player, Node node, String option) { switch (node.getId()) { case 5167: - if (!hasRequirement(player, "Creature of Fenkenstrain")) { + if (!hasRequirement(player, Quests.CREATURE_OF_FENKENSTRAIN)) { break; } player.teleport(new Location(3577, 9927)); break; case 5198: case 5199: - if (player.getQuestRepository().getQuest(AnimalMagnetism.NAME).getStage(player) == 0) { + if (player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM).getStage(player) == 0) { player.getDialogueInterpreter().sendDialogues((NPC) node, null, "Hello there, I'm busy with my research. Come back in a", "bit, could you?"); break; } @@ -198,7 +197,7 @@ public final class AnimalMagnetismPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest(AnimalMagnetism.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); if (quest.getStage(player) <= 28) { SkillingTool tool = SkillingTool.getHatchet(player); if (tool == null || tool.ordinal() < 4) { @@ -243,7 +242,7 @@ public final class AnimalMagnetismPlugin extends OptionHandler { public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); final Animation animation = getAnimation(event.getUsedItem().getId()); - final Quest quest = player.getQuestRepository().getQuest(AnimalMagnetism.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); player.animate(animation, 2); if (quest.getStage(player) == 28) { quest.setStage(player, 29); @@ -293,7 +292,7 @@ public final class AnimalMagnetismPlugin extends OptionHandler { final Object[] data = getIndex(button); final boolean toggled = (boolean) data[1]; final int[] configs = getConfigs((int) data[0]); - final Quest quest = player.getQuestRepository().getQuest(AnimalMagnetism.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); player.getPacketDispatch().sendInterfaceConfig(480, configs[0], !toggled); player.getPacketDispatch().sendInterfaceConfig(480, (int) data[2], toggled); if (quest.getStage(player) == 33) { diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/AnmaCutscene.kt b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnmaCutscene.kt index 18f6a0438..da0a40472 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/AnmaCutscene.kt +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnmaCutscene.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.game.world.map.Direction import org.rs09.consts.Animations import org.rs09.consts.NPCs +import content.data.Quests class AnmaCutscene(player: Player) : Cutscene(player) { override fun setup() { @@ -182,7 +183,7 @@ class AnmaCutscene(player: Player) : Cutscene(player) { } 32 -> { end { - setQuestStage(player, "Animal Magnetism", 20) + setQuestStage(player, Quests.ANIMAL_MAGNETISM, 20) } } } diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/AvaDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AvaDialogue.java index ce546f229..bae10eb6d 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/AvaDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AvaDialogue.java @@ -3,6 +3,7 @@ package content.region.misthalin.draynor.quest.anma; import java.util.ArrayList; import java.util.List; +import content.data.Quests; import core.game.container.Container; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; @@ -55,7 +56,7 @@ public final class AvaDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(AnimalMagnetism.NAME); + quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); if (!quest.hasRequirements(player)) { player.getPacketDispatch().sendMessage("She doesn't seem interested in talking to you."); return false; diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/AvasDevice.kt b/Server/src/main/content/region/misthalin/draynor/quest/anma/AvasDevice.kt index 511e4137f..9607a29ca 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/AvasDevice.kt +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AvasDevice.kt @@ -11,6 +11,7 @@ import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.tools.secondsToTicks import core.tools.colorize +import content.data.Quests /** * Handles Ava's device @@ -19,7 +20,7 @@ import core.tools.colorize class AvasDevice : InteractionListener, EventHook { override fun defineListeners() { onEquip(devices) { player, _ -> - if (!isQuestComplete(player, "Animal Magnetism")) { + if (!isQuestComplete(player, Quests.ANIMAL_MAGNETISM)) { sendMessage(player, "You need to complete Animal Magnetism to equip this.") return@onEquip false } diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java index c5f97165f..eb76716f4 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import content.region.misthalin.lumbridge.quest.therestlessghost.RestlessGhost; +import content.data.Quests; /** * Handles the dialogue used for the old crone. @@ -49,7 +50,7 @@ public final class OldCronDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Animal Magnetism"); + quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); switch (quest.getStage(player)) { case 16: case 17: diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/WitchDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/WitchDialogue.java index 2e53bb979..7c23e06c0 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/WitchDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/WitchDialogue.java @@ -1,5 +1,6 @@ package content.region.misthalin.draynor.quest.anma; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; @@ -45,7 +46,7 @@ public final class WitchDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(AnimalMagnetism.NAME); + quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); switch (quest.getStage(player)) { case 25: npc("Hello, hello, my poppet. What brings you to my little", "room?"); diff --git a/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestDialogue.java index bc29f3b16..8fbf7d11f 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestDialogue.java @@ -5,6 +5,7 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; +import content.data.Quests; /** * Represents the dialogue which handles the interaction with ernest. @@ -73,12 +74,12 @@ public final class ErnestDialogue extends DialoguePlugin { * Method used to finish the quest. */ public void finish() { - if (player.getQuestRepository().isComplete("Ernest the Chicken")) { + if (player.getQuestRepository().isComplete(Quests.ERNEST_THE_CHICKEN)) { npc.clear(); return; } npc.clear(); - player.getQuestRepository().getQuest("Ernest the Chicken").finish(player); + player.getQuestRepository().getQuest(Quests.ERNEST_THE_CHICKEN).finish(player); } @Override diff --git a/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChicken.java b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChicken.java index d18e635cd..58a9fecef 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChicken.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChicken.java @@ -8,6 +8,7 @@ import core.game.node.item.Item; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the ernest the chicken quest. @@ -40,7 +41,7 @@ public final class ErnestTheChicken extends Quest { * Constructs a new {@code ErnestTheChicken} {@code Object}. */ public ErnestTheChicken() { - super("Ernest the Chicken", 19, 18, 4, 32, 0, 1, 3); + super(Quests.ERNEST_THE_CHICKEN, 19, 18, 4, 32, 0, 1, 3); } @Override @@ -128,7 +129,7 @@ public final class ErnestTheChicken extends Quest { @Override public boolean isHidden(final Player player) { - return player.getQuestRepository().getQuest("Ernest the Chicken").getStage(player) == 100 || player.getAttribute("ernest-hide", false); + return player.getQuestRepository().getQuest(Quests.ERNEST_THE_CHICKEN).getStage(player) == 100 || player.getAttribute("ernest-hide", false); } @Override @@ -174,7 +175,7 @@ public final class ErnestTheChicken extends Quest { @Override public boolean isHidden(final Player player) { Player target = getAttribute("target", null); - if (target != null && target.getQuestRepository().getQuest("Ernest the Chicken").getStage(player) == 100) { + if (target != null && target.getQuestRepository().getQuest(Quests.ERNEST_THE_CHICKEN).getStage(player) == 100) { clear(); return super.isHidden(player); } diff --git a/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayer.java b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayer.java index 6231e659e..94e7f13cb 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayer.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayer.java @@ -4,6 +4,7 @@ import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the vampire quest. @@ -16,7 +17,7 @@ public class VampireSlayer extends Quest { * Constructs a new {@code VampireSlayer} {@code Object}. */ public VampireSlayer() { - super("Vampire Slayer", 30, 29, 3, 178, 0, 1, 3); + super(Quests.VAMPIRE_SLAYER, 30, 29, 3, 178, 0, 1, 3); } @Override diff --git a/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerNPC.java b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerNPC.java index 35d9e8df3..17c72b2f2 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerNPC.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerNPC.java @@ -11,6 +11,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.game.world.map.Location; import core.tools.RandomFunction; +import content.data.Quests; /** * Handles the Vampie Slayer npc. @@ -127,7 +128,7 @@ public class VampireSlayerNPC extends AbstractNPC { return; } final Player p = ((Player) killer); - final Quest quest = p.getQuestRepository().getQuest("Vampire Slayer"); + final Quest quest = p.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER); if (p.getInventory().containsItem(HAMMER) && p.getInventory().remove(STAKE)) { if (quest.getStage(p) == 30) { quest.finish(p); diff --git a/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerPlugin.java b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerPlugin.java index 243b76966..f8f582536 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerPlugin.java @@ -11,6 +11,7 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the plugin to handle vampire slayer node handling. @@ -51,7 +52,7 @@ public final class VampireSlayerPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Vampire Slayer"); + final Quest quest = player.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER); switch (option) { case "open": int id = ((Scenery) node).getId(); diff --git a/Server/src/main/content/region/misthalin/edgeville/handlers/PosterListener.kt b/Server/src/main/content/region/misthalin/edgeville/handlers/PosterListener.kt deleted file mode 100644 index 63de7555e..000000000 --- a/Server/src/main/content/region/misthalin/edgeville/handlers/PosterListener.kt +++ /dev/null @@ -1,18 +0,0 @@ -package content.region.misthalin.edgeville.handlers - -import core.api.sendDialogue -import core.api.teleport -import core.game.world.map.Location -import org.rs09.consts.Scenery -import core.game.interaction.IntType -import core.game.interaction.InteractionListener - -class PosterListener : InteractionListener { - override fun defineListeners() { - on(Scenery.POSTER_29586, IntType.SCENERY, "pull-back") { player, _ -> - sendDialogue(player, "There appears to be a tunnel behind this poster.") - teleport(player, Location.create(3140, 4230, 2)) - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.java deleted file mode 100644 index e98e13693..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.java +++ /dev/null @@ -1,569 +0,0 @@ -package content.region.misthalin.lumbridge.dialogue; - -import core.cache.def.impl.ItemDefinition; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import content.data.RepairItem; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.AchievementDiary; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import content.global.handlers.item.equipment.BarrowsEquipmentRegister; - -/** - * Represents the dialogue plugin used for the bob npc who repairs items. - * @author 'Vexia - * @version 1.0 - */ -public final class BobDialogue extends DialoguePlugin { - - /** - * Represents the item id being repaired. - */ - private int itemId = 0; - - /** - * Represents the item being repaired. - */ - private Item item; - - /** - * Represents the item repairing. - */ - private static RepairItem repairitem = null; - - /** - * The achievement diary. - */ - private final int level = 1; - - /** - * Constructs a new {@code BobDialogue} {@code Object}. - */ - public BobDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BobDialogue} {@code Object}. - * @param player the player. - */ - public BobDialogue(Player player) { - super(player); - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 754: - options("Yes, please.", "No, thank you."); - stage = 755; - break; - case 755: - switch (buttonId) { - case 1: - player("Yes, please."); - stage = 757; - break; - case 2: - player("No, thank you."); - stage = 756; - break; - } - break; - case 756: - end(); - break; - case 757: - end(); - if (repairitem != null) { - if (!player.getInventory().contains(995, repairitem.getCost())) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough to pay him."); - break; - } - if (!player.getInventory().contains(itemId, 1)) { - end(); - return true; - } - player.getInventory().remove(new Item(995, repairitem.getCost())); - if (player.getInventory().remove(new Item(itemId, 1))) { - player.getInventory().add(repairitem.getProduct()); - } - String cost = "free"; - if (repairitem.getCost() != 0) { - cost = repairitem.getCost() + "gp"; - } - player.getPacketDispatch().sendMessage("Bob fixes your " + ItemDefinition.forId(itemId).getName().toLowerCase().replace("broken", "").trim() + " for " + cost + "."); - } - if (repairitem == null) { - String cost = "free"; - String type = BarrowsEquipment.formatedName(itemId); - String single = BarrowsEquipment.getSingleName(type); - String equipment = BarrowsEquipment.getEquipmentType(type); - String newString = type.toLowerCase().replace(single, "").trim().replace("'s", ""); - StringBuilder newewString = new StringBuilder(); - newewString.append(newString).append(" " + equipment); - final BarrowsEquipment.BarrowsFullEquipment fullequip = BarrowsEquipment.BarrowsFullEquipment.forName(newewString.toString()); - if (BarrowsEquipment.getFormatedCost(equipment, item) != 0) { - cost = String.valueOf(BarrowsEquipment.getFormatedCost(equipment, item) + "gp"); - } - if (!player.getInventory().contains(995, BarrowsEquipment.getFormatedCost(equipment, item))) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough to pay him."); - break; - } - if (fullequip == null || fullequip.getFull() == null) { - player.getPacketDispatch().sendMessage("Report this to an administrator!"); - return true; - } - if (!player.getInventory().contains(itemId, 1)) { - end(); - return true; - } - player.getInventory().remove(new Item(995, BarrowsEquipment.getFormatedCost(equipment, item))); - if (player.getInventory().remove(new Item(itemId, 1))) { - player.getInventory().add(fullequip.getFull()); - } - player.getPacketDispatch().sendMessage("Bob fixes your " + equipment + " for " + cost + "."); - } - break; - case 678: - end(); - break; - case 0: - switch (buttonId) { - case 1: - player("Give me a quest!"); - stage = -5; - break; - case 2: - player("Have you anything to sell?"); - stage = 10; - break; - case 3: - player("Can you repair my items for me?"); - stage = 20; - break; - case 4: - player("I'd like to talk about Achievement Diaries."); - stage = 30; - break; - } - break; - case -5: - interpreter.sendDialogues(npc, FacialExpression.FURIOUS, "Get yer own!"); - stage = -4; - break; - case -4: - end(); - break; - case 10: - npc("Yes! I buy and sell axes! Take your pick (or axe)!"); - stage = 11; - break; - case 11: - end(); - npc.openShop(player); - break; - case 20: - npc("Of course I'll repair it, though the materials may cost", "you. Just hand me the item and I'll have a look."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - if (AchievementDiary.canClaimLevelRewards(player, DiaryType.LUMBRIDGE, level)) { - player("I've done all the medium tasks in my Lumbridge", "Achievement Diary."); - stage = 150; - break; - } - if (AchievementDiary.canReplaceReward(player, DiaryType.LUMBRIDGE, level)) { - player("I've seemed to have lost my explorer's ring..."); - stage = 160; - break; - } - options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); - stage = 31; - break; - case 31: - switch (buttonId) { - case 1: - player("What is the Achievement Diary?"); - stage = 110; - break; - case 2: - player("What are the rewards?"); - stage = 120; - break; - case 3: - player("How do I claim the rewards?"); - stage = 130; - break; - case 4: - player("See you later!"); - stage = 140; - break; - } - break; - case 110: - npc("Ah, well, it's a diary that helps you keep track of", "particular achievements you've made in the world of", "Gielinor. In Lumbridge and Draynor I can help you", "discover some very useful things indeed."); - stage++; - break; - case 111: - npc("Eventually with enough exploration you will be", "rewarded for your explorative efforts."); - stage++; - break; - case 112: - npc("You can access your Achievement Diary by going to", "the Quest Journal. When you've opened the Quest", "Journal click on the green star icon on the top right", "hand corner. This will open the diary."); - stage = 30; - break; - case 120: - npc("Ah, well there are different rewards for each", "Achievement Diary. For completing the Lumbridge and", "Draynor diary you are presented with an explorer's", "ring."); - stage++; - break; - case 121: - npc("This ring will become increasingly useful with each", "section of the diary that you complete."); - stage = 30; - break; - case 130: - npc("You need to complete the tasks so that they're all ticked", "off, then you can claim your reward. Most of them are", "straightforward although you might find some required", "quests to be started, if not finished."); - stage++; - break; - case 131: - npc("To claim the explorer's ring speak to Explorer Jack", " in Lumbridge, Ned in Draynor Village or myself."); - stage = 30; - break; - case 140: - end(); - break; - case 150: - npc("Yes I see that, you'll be wanting your", "reward then I assume?"); - stage++; - break; - case 151: - player("Yes please."); - stage++; - break; - case 152: - AchievementDiary.flagRewarded(player, DiaryType.LUMBRIDGE, level); - npc("This ring is a representation of the adventures you", "went on to complete your tasks."); - stage ++; - break; - case 153: - player("Wow, thanks!"); - stage = 30; - break; - case 160: - AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level); - npc("You better be more careful this time."); - stage = -1; - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BobDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - boolean repair = false; - boolean wrong = false; - if (npc.getId() == 3797 && args.length == 1) { - player("Can you repair my items for me?"); - stage = 20; - return true; - } - if (args.length == 1) { - options("Give me a quest!", "Have you anything to sell?", "Can you repair my items for me?", "Talk about Achievement Diaries"); - stage = 0; - return true; - } - if (args[1] != null) { - repair = (boolean) args[1]; - } - if (args[2] != null) { - wrong = (boolean) args[2]; - } - if (args[3] != null) { - repairitem = RepairItem.forId((int) args[3]); - itemId = (int) args[3]; - } - if (args[4] != null) { - item = (Item) args[4]; - } - if (repair && !wrong) { - String cost = "free"; - if (repairitem != null) { - if (repairitem.getCost() != 0) { - cost = String.valueOf(repairitem.getCost() + "gp"); - } - } else { - String type = BarrowsEquipment.formatedName(itemId); - String single = BarrowsEquipment.getSingleName(type); - String equipment = BarrowsEquipment.getEquipmentType(type); - String newString = type.toLowerCase().replace(single, "").trim().replace("'s", ""); - StringBuilder newewString = new StringBuilder(); - newewString.append(newString).append(" " + equipment); - if (BarrowsEquipment.getFormatedCost(equipment, item) != 0) { - cost = String.valueOf(BarrowsEquipment.getFormatedCost(equipment, item) + "gp"); - } - } - npc("Quite badly damaged, but easy to repair. Would you", "like me to repair it for " + cost + "?"); - stage = 754; - return true; - } - if (repair == true && wrong == true) { - npc("Sorry friend, but I can't do anything with that."); - stage = 678; - return true; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 519, 3797 }; - } - - /** - * Barrows equipment information. - * @author 'Vexia - */ - public static class BarrowsEquipment { - - /** - * Represents the base names. - */ - private String[] base = new String[] { "dharok", "verac", "ahrim", "torag", "guthan" }; - - /** - * The weapon names. - */ - private static final String[] weapon_names = new String[] { "flail", "greataxe", "spear", "x-bow", "hammer", "hammers", "staff" }; - - /** - * The weapon body names. - */ - private static final String[] body_names = new String[] { "top", "platebody", "body" }; - - /** - * The helm names. - */ - private static final String[] helm_names = new String[] { "hood", "helm", "coif" }; - - /** - * The leg names. - */ - private static final String[] leg_names = new String[] { "skirt", "legs", "plateskirt", "platelegs" }; - - /** - * The prices. - */ - private static final Object[][] prices = new Object[][] { { "weapon", 100 }, { "body", 90 }, { "legs", 80 }, { "helm", 60 } }; - - /** - * Represents the items. - */ - private static final Object[][] ITEMS = { { 4856, "Ahrim's hood" }, { 4857, "Ahrim's hood" }, { 4858, "Ahrim's hood" }, { 4859, "Ahrim's hood" }, { 4860, "Ahrim's hood" }, { 4862, "Ahrim's staff" }, { 4863, "Ahrim's staff" }, { 4864, "Ahrim's staff" }, { 4865, "Ahrim's staff" }, { 4866, "Ahrim's staff" }, { 4868, "Ahrim's top" }, { 4869, "Ahrim's top" }, { 4870, "Ahrim's top" }, { 4871, "Ahrim's top" }, { 4872, "Ahrim's top" }, { 4874, "Ahrim's skirt" }, { 4875, "Ahrim's skirt" }, { 4876, "Ahrim's skirt" }, { 4877, "Ahrim's skirt" }, { 4878, "Ahrim's skirt" }, { 4880, "Dharok's helm" }, { 4881, "Dharok's helm" }, { 4882, "Dharok's helm" }, { 4883, "Dharok's helm" }, { 4884, "Dharok's helm" }, { 4886, "Dharok's greataxe" }, { 4887, "Dharok's greataxe" }, { 4888, "Dharok's greataxe" }, { 4889, "Dharok's greataxe" }, { 4890, "Dharok's greataxe" }, { 4892, "Dharok's platebody" }, { 4893, "Dharok's platebody" }, { 4894, "Dharok's platebody" }, { 4895, "Dharok's platebody" }, { 4896, "Dharok's platebody" }, { 4898, "Dharok's platelegs" }, { 4899, "Dharok's platelegs" }, { 4900, "Dharok's platelegs" }, { 4901, "Dharok's platelegs" }, { 4902, "Dharok's platelegs" }, { 4904, "Guthan's helm" }, { 4905, "Guthan's helm" }, { 4906, "Guthan's helm" }, { 4907, "Guthan's helm" }, { 4908, "Guthan's helm" }, { 4910, "Guthan's spear" }, { 4911, "Guthan's spear" }, { 4912, "Guthan's spear" }, { 4913, "Guthan's spear" }, { 4914, "Guthan's spear" }, { 4916, "Guthan's body" }, { 4917, "Guthan's body" }, { 4918, "Guthan's body" }, { 4919, "Guthan's body" }, { 4920, "Guthan's body" }, { 4922, "Guthan's skirt" }, { 4923, "Guthan's skirt" }, { 4924, "Guthan's skirt" }, { 4925, "Guthan's skirt" }, { 4926, "Guthan's skirt" }, { 4928, "Karil's coif" }, { 4929, "Karil's coif" }, { 4930, "Karil's coif" }, { 4931, "Karil's coif" }, { 4932, "Karil's coif" }, { 4934, "Karil's x-bow" }, { 4935, "Karil's x-bow" }, { 4936, "Karil's x-bow" }, { 4937, "Karil's x-bow" }, { 4938, "Karil's x-bow" }, { 4940, "Karil's top" }, { 4941, "Karil's top" }, { 4942, "Karil's top" }, { 4943, "Karil's top" }, { 4944, "Karil's top" }, { 4946, "Karil's skirt" }, { 4947, "Karil's skirt" }, { 4948, "Karil's skirt" }, { 4949, "Karil's skirt" }, { 4950, "Karil's skirt" }, { 4952, "Torag's helm" }, { 4953, "Torag's helm" }, { 4954, "Torag's helm" }, { 4955, "Torag's helm" }, { 4956, "Torag's helm" }, { 4958, "Torag's hammers" }, { 4959, "Torag's hammers" }, { 4960, "Torag's hammers" }, { 4961, "Torag's hammers" }, { 4962, "Torag's hammers" }, { 4964, "Torag's body" }, { 4965, "Torag's body" }, { 4966, "Torag's body" }, { 4967, "Torag's body" }, { 4968, "Torag's body" }, { 4970, "Torag's legs" }, { 4971, "Torag's legs" }, { 4972, "Torag's legs" }, { 4973, "Torag's legs" }, { 4974, "Torag's legs" }, { 4976, "Verac's helm" }, { 4977, "Verac's helm" }, { 4978, "Verac's helm" }, { 4979, "Verac's helm" }, { 4980, "Verac's helm" }, { 4982, "Verac's flail" }, { 4983, "Verac's flail" }, { 4984, "Verac's flail" }, { 4985, "Verac's flail" }, { 4986, "Verac's flail" }, { 4988, "Verac's top" }, { 4989, "Verac's top" }, { 4990, "Verac's top" }, { 4991, "Verac's top" }, { 4992, "Verac's top" }, { 4994, "Verac's skirt" }, { 4995, "Verac's skirt" }, { 4996, "Verac's skirt" }, { 4997, "Verac's skirt" }, { 4998, "Verac's skirt" } }; - - /** - * Gets the cost. - * @param name the name. - * @return the price. - */ - public static int getFormatedCost(String name, Item item) { - int ticks = BarrowsEquipmentRegister.TICKS; - int[] degrades = new int[] { 100, 75, 50, 25, 0 }; - for (int i = 0; i < prices.length; i++) { - String check = (String) prices[i][0]; - if (check.equals(name)) { - int degrade = 0; - for (int d : degrades) { - if (item.getName().contains(String.valueOf(d))) { - degrade = d; - break; - } - } - degrade -= 25 - (25 * ((double)item.getCharge() / (double)ticks)); - int max = (int) prices[i][1] * 1000; - return (int) (max - (max * (degrade * 0.01))); - } - } - return 0; - } - - /** - * Gets the cost of the item type. - * @param name the name. - * @return the return type. - */ - public static int getCost(String name) { - for (int i = 0; i < prices.length; i++) { - String check = (String) prices[i][0]; - if (check.equals(name)) { - return (int) prices[i][1]; - } - } - return 0; - } - - /** - * Represents if an item is a barrows item. - * @param id the id. - * @return {@code True} if so. - */ - public static boolean isBarrowsItem(int id) { - for (int i = 0; i < ITEMS.length; i++) { - if ((int) ITEMS[i][0] == id) { - return true; - } - } - return false; - } - - /** - * Gets the formatted name. - * @param id the id. - * @return the name. - */ - public static String formatedName(int id) { - for (int i = 0; i < ITEMS.length; i++) { - if ((int) ITEMS[i][0] == id) { - return (String) ITEMS[i][1]; - } - } - return null; - } - - /** - * Gets the equipment type. - * @param name the name. - * @return the type. - */ - public static String getEquipmentType(String name) { - name = name.toLowerCase().replace("verac's", "").replace("karil's", "").replace("dharok's", "").replace("torag's", "").replace("guthan's", "").replace("ahrim's", "").trim(); - for (int i = 0; i < weapon_names.length; i++) { - if (weapon_names[i].contains(name)) { - return "weapon"; - } - } - for (int k = 0; k < body_names.length; k++) { - if (body_names[k].contains(name)) { - return "body"; - } - } - for (int z = 0; z < leg_names.length; z++) { - if (leg_names[z].contains(name)) { - return "legs"; - } - } - for (int q = 0; q < helm_names.length; q++) { - if (helm_names[q].contains(name)) { - return "helm"; - } - } - return null; - } - - /** - * Method used t get its single name. - * @param name the name. - * @return the name. - */ - public static String getSingleName(String name) { - name = name.toLowerCase().replace("verac's", "").replace("karil's", "").replace("dharok's", "").replace("torag's", "").replace("guthan's", "").replace("ahrim's", "").trim(); - for (int i = 0; i < weapon_names.length; i++) { - if (weapon_names[i].contains(name)) { - return weapon_names[i]; - } - } - for (int k = 0; k < body_names.length; k++) { - if (body_names[k].contains(name)) { - return body_names[k]; - } - } - for (int z = 0; z < leg_names.length; z++) { - if (leg_names[z].contains(name)) { - return leg_names[z]; - } - } - for (int q = 0; q < helm_names.length; q++) { - if (helm_names[q].contains(name)) { - return helm_names[q]; - } - } - return null; - } - - /** - * Gets the bases. - * @return the base. - */ - public String[] getBase() { - return base; - } - - /** - * Represents the multiple full barrows equipment items. - * @author 'Vexia - * @version 1.0 - */ - public enum BarrowsFullEquipment { - VERAC_LEGS(new Item(4759, 1)), VERAC_TOP(new Item(4757, 1)), VERAC_WEAPON(new Item(4755, 1)), VERAC_HELM(new Item(4753, 1)), TORAG_LEGS(new Item(4751, 1)), TORAG_BODY(new Item(4749, 1)), TORAG_HELM(new Item(4745, 1)), TORAG_WEAPON(new Item(4747, 1)), KARIL_HELM(new Item(4732, 1)), KARIL_WEAPON(new Item(4734, 1)), KARIL_BODY(new Item(4736, 1)), KARIL_LEGS(new Item(4738, 1)), GUTHAN_HELM(new Item(4724, 1)), GUTHAN_BODY(new Item(4728, 1)), GUTHAN_LEGS(new Item(4730, 1)), GUTHAN_WEAPON(new Item(4726, 1)), DHAROK_HELM(new Item(4716, 1)), DHAROK_BODY(new Item(4720, 1)), DHAROK_LEGS(new Item(4722, 1)), DHAROK_WEAPON(new Item(4718, 1)), AHRIM_HELM(new Item(4708, 1)), AHRIM_BODY(new Item(4712, 1)), AHRIM_LEGS(new Item(4714, 1)), AHRIM_WEAPON(new Item(4710, 1)); - - /** - * Constructs a new {@code BarrowsEquipment} {@code Object}. - * @param full the full item. - */ - BarrowsFullEquipment(Item full) { - this.full = full; - } - - /** - * Represents the full item. - */ - private final Item full; - - /** - * for name - * @param name thename. - * @return the equipment. - */ - public static BarrowsFullEquipment forName(String name) { - if (name.equals("guthan body body")) { - name = "guthan body"; - } else if (name.equals("torag body body")) { - name = "torag body"; - } else if (name.equals("verac body")) { - name = "verac top"; - } - for (BarrowsFullEquipment barrow : BarrowsFullEquipment.values()) { - if (barrow.name().toLowerCase().replace("_", " ").trim().equalsIgnoreCase(name)) { - return barrow; - } - } - return null; - } - - /** - * Gets the full. - * @return The full. - */ - public Item getFull() { - return full; - } - } - - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt new file mode 100644 index 000000000..c759ef0b2 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt @@ -0,0 +1,266 @@ +package content.region.misthalin.lumbridge.dialogue + +import content.data.RepairItem +import content.global.handlers.item.equipment.BarrowsEquipment +import core.cache.def.impl.ItemDefinition +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.item.Item +import core.plugin.Initializable + +/** + * Represents the dialogue plugin used for the bob npc who repairs items. + * @author 'Vexia + * @author Damighty - Kotlin conversion + * @version 1.0 + */ +@Initializable +class BobDialogue(player: Player? = null) : DialoguePlugin(player) { + + /** + * Represents the item id being repaired. + */ + private var itemId = 0 + + /** + * Represents the item being repaired. + */ + private lateinit var item: Item + + /** + * Represents the item repairing. + */ + private var repairItem: RepairItem? = null + + /** + * The achievement diary. + */ + private val level = 1 + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 754 -> { + options("Yes, please.", "No, thank you.") + stage = 755 + } + 755 -> when (buttonId) { + 1 -> { + player("Yes, please.") + stage = 757 + } + 2 -> { + player("No, thank you.") + stage = 756 + } + } + 756 -> end() + 757 -> { + end() + val repairItemDef = RepairItem.forId(itemId) + + if (repairItemDef != null) { + // Standard repairable items + if (!player.inventory.contains(995, repairItemDef.cost)) { + player.packetDispatch.sendMessage("You don't have enough to pay him.") + return true + } + if (player.inventory.remove(Item(itemId, 1))) { + player.inventory.remove(Item(995, repairItemDef.cost)) + player.inventory.add(repairItemDef.product) + val costText = if (repairItemDef.cost > 0) "${repairItemDef.cost}gp" else "free" + player.packetDispatch.sendMessage("Bob fixes your ${item.name.lowercase().replace("broken", "").trim()} for $costText.") + } + } else { + // Barrows items + val barrowsDef = BarrowsEquipment.getDefinition(itemId) ?: return true + val repairCost = BarrowsEquipment.getRepairCost(item) + + if (!player.inventory.contains(995, repairCost)) { + player.packetDispatch.sendMessage("You don't have enough to pay him.") + return true + } + if (player.inventory.remove(Item(itemId, 1))) { + player.inventory.remove(Item(995, repairCost)) + player.inventory.add(Item(barrowsDef.repairedId)) + val costText = if (repairCost > 0) "${repairCost}gp" else "free" + player.packetDispatch.sendMessage("Bob fixes your ${barrowsDef.itemName.lowercase()} for $costText.") + } + } + return true + } + 678 -> end() + 0 -> when (buttonId) { + 1 -> { + player("Give me a quest!") + stage = -5 + } + 2 -> { + player("Have you anything to sell?") + stage = 10 + } + 3 -> { + player("Can you repair my items for me?") + stage = 20 + } + 4 -> { + player("I'd like to talk about Achievement Diaries.") + stage = 30 + } + } + -5 -> { + interpreter.sendDialogues(npc, FacialExpression.FURIOUS, "Get yer own!") + stage = -4 + } + -4 -> end() + 10 -> { + npc("Yes! I buy and sell axes! Take your pick (or axe)!") + stage = 11 + } + 11 -> { + end() + npc.openShop(player) + } + 20 -> { + npc("Of course I'll repair it, though the materials may cost", "you. Just hand me the item and I'll have a look.") + stage = 21 + } + 21 -> end() + 30 -> { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.LUMBRIDGE, level)) { + player("I've done all the medium tasks in my Lumbridge", "Achievement Diary.") + stage = 150 + } else if (AchievementDiary.canReplaceReward(player, DiaryType.LUMBRIDGE, level)) { + player("I've seemed to have lost my explorer's ring...") + stage = 160 + } else { + options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later.") + stage = 31 + } + } + 31 -> when (buttonId) { + 1 -> { + player("What is the Achievement Diary?") + stage = 110 + } + 2 -> { + player("What are the rewards?") + stage = 120 + } + 3 -> { + player("How do I claim the rewards?") + stage = 130 + } + 4 -> { + player("See you later!") + stage = 140 + } + } + 110 -> { + npc("Ah, well, it's a diary that helps you keep track of", "particular achievements you've made in the world of", "Gielinor. In Lumbridge and Draynor I can help you", "discover some very useful things indeed.") + stage++ + } + 111 -> { + npc("Eventually with enough exploration you will be", "rewarded for your explorative efforts.") + stage++ + } + 112 -> { + npc("You can access your Achievement Diary by going to", "the Quest Journal. When you've opened the Quest", "Journal click on the green star icon on the top right", "hand corner. This will open the diary.") + stage = 30 + } + 120 -> { + npc("Ah, well there are different rewards for each", "Achievement Diary. For completing the Lumbridge and", "Draynor diary you are presented with an explorer's", "ring.") + stage++ + } + 121 -> { + npc("This ring will become increasingly useful with each", "section of the diary that you complete.") + stage = 30 + } + 130 -> { + npc("You need to complete the tasks so that they're all ticked", "off, then you can claim your reward. Most of them are", "straightforward although you might find some required", "quests to be started, if not finished.") + stage++ + } + 131 -> { + npc("To claim the explorer's ring speak to Explorer Jack", " in Lumbridge, Ned in Draynor Village or myself.") + stage = 30 + } + 140 -> end() + 150 -> { + npc("Yes I see that, you'll be wanting your", "reward then I assume?") + stage++ + } + 151 -> { + player("Yes please.") + stage++ + } + 152 -> { + AchievementDiary.flagRewarded(player, DiaryType.LUMBRIDGE, level) + npc("This ring is a representation of the adventures you", "went on to complete your tasks.") + stage++ + } + 153 -> { + player("Wow, thanks!") + stage = 30 + } + 160 -> { + AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level) + npc("You better be more careful this time.") + stage = -1 + } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return BobDialogue(player) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + var repair = false + var wrong = false + + if (npc.id == 3797 && args.size == 1) { + player("Can you repair my items for me?") + stage = 20 + return true + } + + if (args.size == 1) { + options("Give me a quest!", "Have you anything to sell?", "Can you repair my items for me?", "Talk about Achievement Diaries") + stage = 0 + return true + } + + if (args.size > 1) repair = args[1] as Boolean + if (args.size > 2) wrong = args[2] as Boolean + if (args.size > 3) { + repairItem = RepairItem.forId(args[3] as Int) + itemId = args[3] as Int + } + if (args.size > 4) item = args[4] as Item + + if (repair && !wrong) { + val cost = RepairItem.forId(itemId)?.cost ?: BarrowsEquipment.getRepairCost(item) + val costText = if (cost > 0) "${cost}gp" else "free" + npc("Quite badly damaged, but easy to repair. Would you", "like me to repair it for $costText?") + stage = 754 + return true + } + + if (repair && wrong) { + npc("Sorry friend, but I can't do anything with that.") + stage = 678 + return true + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(519, 3797) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/DonieDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DonieDialogue.java deleted file mode 100644 index dbe66ddbf..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/DonieDialogue.java +++ /dev/null @@ -1,129 +0,0 @@ -package content.region.misthalin.lumbridge.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.world.GameWorld; - -/** - * Represents the dialogue plugin used for the donie npc. - * @author 'Vexia - * @version 1.00 - */ -@Initializable -public final class DonieDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code DonieDialogue} {@code Object}. - */ - public DonieDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DonieDialogue} {@code Object}. - * @param player the player. - */ - public DonieDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DonieDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello there, can I help you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Where am I?", "How are you today?", "Your shoe lace is untied."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where am I?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How are you today?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Your shoe lace is untied."); - stage = 30; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "This is the town of Lumbridge my friend."); - stage = 11; - break; - case 11: - interpreter.sendOptions("Select an Option", "Where am I?", "How are you today?", "Your shoe lace is untied."); - stage = 1; - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Aye, not too bad thank you. Lovely weather in", "" + GameWorld.getSettings().getName() + " this fine day."); - stage = 21; - break; - case 21: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Weather?"); - stage = 22; - break; - case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes weather, you know."); - stage = 23; - break; - case 23: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The state or condition of the atmosphere at a time and", "place, with respect to variables such as temperature,", "moisture, wind velocity, and barometric pressure."); - stage = 24; - break; - case 24: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "..."); - stage = 25; - break; - case 25: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Not just a pretty face eh? Ha ha ha."); - stage = 26; - break; - case 26: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No it's not!"); - stage = 31; - break; - case 31: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No you're right. I have nothing to back that up."); - stage = 32; - break; - case 32: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Fool! Leave me alone!"); - stage = 33; - break; - case 33: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2238 }; - } -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/DonieDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DonieDialogue.kt new file mode 100644 index 000000000..d699af8c3 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DonieDialogue.kt @@ -0,0 +1,59 @@ +package content.region.misthalin.lumbridge.dialogue + +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import core.game.world.GameWorld.settings +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class DonieDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return DonieDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DonieDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DONIE_2238) + } +} + +class DonieDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.DONIE_2238) + + npc(ChatAnim.FRIENDLY, "Hello there, can I help you?") + options( + DialogueOption("whereami", "Where am I?", expression = ChatAnim.THINKING), + DialogueOption("howareyou", "How are you today?"), + DialogueOption("shoelace", "Your shoe lace is untied.", skipPlayer=true), + ) + + label("whereami") + npc("This is the town of Lumbridge my friend.") + + label("howareyou") + npc("Aye, not too bad thank you. Lovely weather in", ""+ (GameWorld.settings?.name ?: "2009Scape") +" this fine day.") + player("Weather?") + npc("Yes weather, you know.") + npc("The state or condition of the atmosphere at a time and", "place, with respect to variables such as temperature,", "moisture, wind velocity, and barometric pressure.") + player("...") + npc("Not just a pretty face eh? Ha ha ha.") + + label("shoelace") + open(player!!, DonieDialogueShoelaceFile(), npc!!) + + } +} +class DonieDialogueShoelaceFile : DialogueLabeller() { + override fun addConversation() { + player("Your shoe lace is untied.") + npc(ChatAnim.ANGRY, "No it's not!") + player("No you're right. I have nothing to back that up.") + npc(ChatAnim.ANGRY, "Fool! Leave me alone!") + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/DukeHoracioDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DukeHoracioDialogue.kt index 59e3b4775..da9ead1cc 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/DukeHoracioDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DukeHoracioDialogue.kt @@ -10,6 +10,7 @@ import content.region.misthalin.lumbridge.quest.runemysteries.DukeHoracioRMDialo import content.region.misthalin.dorgeshuun.quest.thelosttribe.DukeHoracioTLTDialogue import core.tools.DIALOGUE_INITIAL_OPTIONS_HANDLE import core.tools.END_DIALOGUE +import content.data.Quests /** * Core dialogue plugin for Duke Horacio, redirects to more specific DialogueFiles. @@ -23,11 +24,11 @@ class DukeHoracioDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any): Boolean { npc = args[0] as NPC - if ((player.questRepository.getQuest("Dragon Slayer").getStage(player) == 100 && !player.inventory.containsItem(DragonSlayer.SHIELD) && !player.bank.containsItem(DragonSlayer.SHIELD) )|| (player.questRepository.getQuest("Dragon Slayer").isStarted(player) && !player.questRepository.getQuest("Dragon Slayer").isCompleted(player))) { - addOption("Dragon Slayer", DukeHoracioDSDialogue(player.questRepository.getStage("Dragon Slayer"))) + if ((player.questRepository.getQuest(Quests.DRAGON_SLAYER).getStage(player) == 100 && !player.inventory.containsItem(DragonSlayer.SHIELD) && !player.bank.containsItem(DragonSlayer.SHIELD) )|| (player.questRepository.getQuest(Quests.DRAGON_SLAYER).isStarted(player) && !player.questRepository.getQuest(Quests.DRAGON_SLAYER).isCompleted(player))) { + addOption("Dragon Slayer", DukeHoracioDSDialogue(player.questRepository.getStage(Quests.DRAGON_SLAYER))) } - if (!player.questRepository.isComplete("Lost Tribe") && player.questRepository.getQuest("Lost Tribe").isStarted(player)) { - addOption("Lost Tribe", DukeHoracioTLTDialogue(player.questRepository.getStage("Lost Tribe"))) + if (!player.questRepository.isComplete(Quests.THE_LOST_TRIBE) && player.questRepository.getQuest(Quests.THE_LOST_TRIBE).isStarted(player)) { + addOption("Lost Tribe", DukeHoracioTLTDialogue(player.questRepository.getStage(Quests.THE_LOST_TRIBE))) } if (!sendChoices()) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings. Welcome to my castle.") @@ -66,8 +67,8 @@ class DukeHoracioDialogue(player: Player? = null) : DialoguePlugin(player) { } 20 -> { npc("Let me see...") - if (!player.questRepository.isComplete("Rune Mysteries")) { - loadFile(DukeHoracioRMDialogue(player.questRepository.getStage("Rune Mysteries"))) + if (!player.questRepository.isComplete(Quests.RUNE_MYSTERIES)) { + loadFile(DukeHoracioRMDialogue(player.questRepository.getStage(Quests.RUNE_MYSTERIES))) } else { stage++ } diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmerDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmerDialogue.kt index 151f8e034..357c76e2d 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmerDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmerDialogue.kt @@ -11,6 +11,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests @Initializable class FredTheFarmerDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -20,8 +21,8 @@ class FredTheFarmerDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any): Boolean { npc = args[0] as NPC - if (getQuestStage(player, "Sheep Shearer") in 1..99) { - openDialogue(player, SSFredTheFarmerDialogue(getQuestStage(player, "Sheep Shearer")), npc) + if (getQuestStage(player, Quests.SHEEP_SHEARER) in 1..99) { + openDialogue(player, SSFredTheFarmerDialogue(getQuestStage(player, Quests.SHEEP_SHEARER)), npc) } else { npc(FacialExpression.ANGRY, "What are you doing on my land? You're not the one", "who keeps leaving all my gates open and letting out all", "my sheep are you?").also { stage = START_DIALOGUE } } @@ -31,7 +32,7 @@ class FredTheFarmerDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> showTopics( - IfTopic(FacialExpression.NEUTRAL, "I'm looking for a quest.", 1000, getQuestStage(player!!, "Sheep Shearer") == 0), + IfTopic(FacialExpression.NEUTRAL, "I'm looking for a quest.", 1000, getQuestStage(player!!, Quests.SHEEP_SHEARER) == 0), Topic(FacialExpression.HALF_GUILTY, "I'm looking for something to kill.", 100), Topic(FacialExpression.HALF_GUILTY, "I'm lost.", 200) ) @@ -40,7 +41,7 @@ class FredTheFarmerDialogue(player: Player? = null) : DialoguePlugin(player) { 200 -> npc(FacialExpression.HALF_GUILTY, "How can you be lost? Just follow the road east and", "south. You'll end up in Lumbridge fairly quickly.").also { stage = END_DIALOGUE } - 1000 -> openDialogue(player, SSFredTheFarmerDialogue(getQuestStage(player, "Sheep Shearer")), npc) + 1000 -> openDialogue(player, SSFredTheFarmerDialogue(getQuestStage(player, Quests.SHEEP_SHEARER)), npc) } return true } diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/HansDialoguePlugin.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/HansDialoguePlugin.java index 59b1f70f6..97610d36f 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/HansDialoguePlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/HansDialoguePlugin.java @@ -98,7 +98,7 @@ public final class HansDialoguePlugin extends DialoguePlugin { stage = 41; break; case 2: - npc("Your current XP rate is: " + player.getSkills().experienceMutiplier); + npc("Your current XP rate is: " + player.getSkills().experienceMultiplier); stage = 11; break; case 3: @@ -123,7 +123,7 @@ public final class HansDialoguePlugin extends DialoguePlugin { break; case 11: - if (player.getSkills().experienceMutiplier == 5.0) { + if (player.getSkills().experienceMultiplier == 5.0) { player.newPlayer = player.getSkills().getTotalLevel() < 50; options("Change xp rate", "Nevermind."); stage++; @@ -152,7 +152,7 @@ public final class HansDialoguePlugin extends DialoguePlugin { switch(buttonId){ case 1: if(player.newPlayer) { - player.getSkills().experienceMutiplier = 1.0; + player.getSkills().experienceMultiplier = 1.0; stage = 14; } else { stage = 15; @@ -161,7 +161,7 @@ public final class HansDialoguePlugin extends DialoguePlugin { break; case 2: if(player.newPlayer){ - player.getSkills().experienceMutiplier = 2.5; + player.getSkills().experienceMultiplier = 2.5; stage = 14; } else { stage = 15; @@ -173,7 +173,7 @@ public final class HansDialoguePlugin extends DialoguePlugin { return true; case 4: if (player.newPlayer) { - player.getSkills().experienceMutiplier = 10.0; + player.getSkills().experienceMultiplier = 10.0; stage = 14; } else { stage = 15; @@ -183,7 +183,7 @@ public final class HansDialoguePlugin extends DialoguePlugin { npc("One moment, please..."); break; case 14: - npc("Tada, your xp rate is now " + player.getSkills().experienceMutiplier); + npc("Tada, your xp rate is now " + player.getSkills().experienceMultiplier); stage = 131; break; case 15: @@ -376,7 +376,7 @@ public final class HansDialoguePlugin extends DialoguePlugin { switch (buttonId) { case 1: interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Tada! Your experience rate is now 10x.", "Happy Scaping!"); - player.getSkills().experienceMutiplier = 10.0; + player.getSkills().experienceMultiplier = 10.0; stage = 50; break; case 2: diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeGuideDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeGuideDialogue.kt index f2f157707..d01bdf614 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeGuideDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeGuideDialogue.kt @@ -13,6 +13,7 @@ import org.rs09.consts.NPCs import core.game.dialogue.IfTopic import core.game.dialogue.Topic import core.tools.END_DIALOGUE +import content.data.Quests @Initializable class LumbridgeGuideDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -23,8 +24,8 @@ class LumbridgeGuideDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { val staff = player.isStaff val ironman = player.ironmanManager.isIronman - val sheepShearerComplete = isQuestComplete(player, "Sheep Shearer") - val cooksAssistantComplete = isQuestComplete(player, "Cook's Assistant") + val sheepShearerComplete = isQuestComplete(player, Quests.SHEEP_SHEARER) + val cooksAssistantComplete = isQuestComplete(player, Quests.COOKS_ASSISTANT) when (stage) { 0 -> npcl(FacialExpression.FRIENDLY, "Greetings, adventurer. I am Phileas, the Lumbridge Guide. I am here to give information and directions to new players. Do you require any help?").also { stage++ } diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/SigmundDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/SigmundDialogue.java index f384019e9..e4e521668 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/SigmundDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/SigmundDialogue.java @@ -7,6 +7,7 @@ import core.plugin.Initializable; import core.game.node.entity.player.Player; import static core.tools.DialogueConstKt.END_DIALOGUE; +import content.data.Quests; /** @@ -35,7 +36,7 @@ public class SigmundDialogue extends DialoguePlugin { public boolean open(Object... args) { npc = (NPC) args[0]; interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Can I help you?"); - if(player.getQuestRepository().getQuest("Lost Tribe").getStage(player) > 0 && player.getQuestRepository().getQuest("Lost Tribe").getStage(player) < 100){ + if(player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).getStage(player) > 0 && player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).getStage(player) < 100){ npc("Have you found out what it was?"); stage = 34; return true; @@ -80,12 +81,12 @@ public class SigmundDialogue extends DialoguePlugin { end(); break; case 10: - if(player.getQuestRepository().hasStarted("Lost Tribe") && !player.getQuestRepository().isComplete("Lost Tribe")){ + if(player.getQuestRepository().hasStarted(Quests.THE_LOST_TRIBE) && !player.getQuestRepository().isComplete(Quests.THE_LOST_TRIBE)){ npc("No, not right now."); stage = 12; break; } - if(player.getQuestRepository().isComplete("Goblin Diplomacy") && player.getQuestRepository().isComplete("Rune Mysteries") && !player.getQuestRepository().hasStarted("Lost Tribe")){ + if(player.getQuestRepository().isComplete(Quests.GOBLIN_DIPLOMACY) && player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES) && !player.getQuestRepository().hasStarted(Quests.THE_LOST_TRIBE)){ npc("There was recently some damage to the castle cellar.","Part of the wall has collapsed."); stage = 30; break; @@ -107,7 +108,7 @@ public class SigmundDialogue extends DialoguePlugin { case 31: npc("You should ask other people around the town if they","saw anything."); stage = END_DIALOGUE; - player.getQuestRepository().getQuest("Lost Tribe").start(player); + player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).start(player); player.setAttribute("/save:tlt-witness", TLTNPCS[0]); break; case 34: diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/SirVantDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/SirVantDialogue.kt new file mode 100644 index 000000000..8b61bea29 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/SirVantDialogue.kt @@ -0,0 +1,53 @@ +package content.region.misthalin.lumbridge.dialogue + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Sir Vant Dialogue + * + * @comments + * I'll be honest, this is a waste of time. + * Sir Vant is part of the new tutorial between 14 July 2008 and 17 Sept 2009, a.k.a. Tutorial 2(Learing the Ropes). + * Learning the Ropes was no longer in use by the time 530 came around. They were already working on Tutorial 3(Unstable Foundations) with a different dialogue that was fully voiced. Learning the Ropes was removed eventually. + * I'm doing this to preserve the dialogue during this period, as there are sceneries left around for this. + * So much effort was put into Tutorial 1(Tutorial Island), that this shouldn't replace it. In fact, modern RS3 has tried to multiple times between 2016 and 2022, but they always end up going back to Tutorial Island. For example, Tutorial Island was removed on 28 November 2012, returned as a special underwater version on 14 December 2015, and was fully reinstated on 14 May 2018. + */ +@Initializable +class SirVantDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, SirVantDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return SirVantDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.SIR_VANT_7942) + } +} + +class SirVantDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { true } + .npcl(FacialExpression.HAPPY,"Hello there. I'm Sir Vant.") + .playerl(FacialExpression.THINKING, "Why are you down in this cave?") + .npcl("I'm guarding the entrance to the dragon's lair.") + .playerl(FacialExpression.WORRIED, "Dragon?") + .npcl("Yes, but not just any dragon - this one has three heads.") + .playerl("So why don't you kill it?") + .npc(FacialExpression.SAD,"I'm too exhausted, but I can hold it off. I've sent for", "more help from Falador, but it might be a while before", "the message gets there.") + .playerl("I could kill the dragon for you.") // Technically you have a list of options, but they are all a repeat of tutorial with an action cutscene. + .npc("Thank you very much for the offer; however, I would", "be remiss in my duties as a White Knight of Falador if", "I were to let you do that.") + //.npc("Thank you very much for the offer; however, I would", " be remiss in my duties as a White Knight of Falador if", "I were to let you do that. Here, for your kind thoughts", "you may have these old lamps.") + // He gives you 2 exp lamps of 250XP each, but I didn't want to implement it. + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt b/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt index af65c278c..5a8ca76db 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt @@ -19,6 +19,7 @@ import core.game.diary.AreaDiaryTask import core.game.diary.DiaryEventHookBase import core.game.diary.DiaryLevel import core.game.event.* +import content.data.Quests class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { @@ -346,7 +347,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { override fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) { when (event.dialogue) { is DukeHoracioDSDialogue -> { - val dragonSlayerStage = getQuestStage(player, "Dragon Slayer") + val dragonSlayerStage = getQuestStage(player, Quests.DRAGON_SLAYER) if ((dragonSlayerStage == 100 && event.currentStage == 4) || event.currentStage == 12) { diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.java deleted file mode 100644 index 12025ef46..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.java +++ /dev/null @@ -1,48 +0,0 @@ -package content.region.misthalin.lumbridge.handlers; - -import content.region.misthalin.lumbridge.dialogue.BobDialogue; -import content.data.RepairItem; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; -import core.plugin.Initializable; -import core.plugin.ClassScanner; - -/** - * Represents the plugin used to handle an item being used on bob. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BobRepairItem extends UseWithHandler { - - /** - * Constructs a new {@code BobRepairItem} {@code Object}. - */ - public BobRepairItem() { - super(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); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(519, NPC_TYPE, this); - addHandler(3797, NPC_TYPE, this); - ClassScanner.definePlugin(new BobDialogue()); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final RepairItem repair = RepairItem.forId(event.getUsedItem().getId()); - if (repair == null && !BobDialogue.BarrowsEquipment.isBarrowsItem(event.getUsedItem().getId())) { - player.getDialogueInterpreter().open(519, ((NPC) event.getUsedWith()), true, true, null); - return true; - } - player.getDialogueInterpreter().open(519, ((NPC) event.getUsedWith()), true, false, event.getUsedItem().getId(), event.getUsedItem()); - return true; - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.kt new file mode 100644 index 000000000..d939865f1 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.kt @@ -0,0 +1,47 @@ +package content.region.misthalin.lumbridge.handlers + +import content.global.handlers.item.equipment.BarrowsEquipment +import content.data.RepairItem +import core.game.interaction.NodeUsageEvent +import core.game.interaction.UseWithHandler +import core.game.node.entity.npc.NPC +import core.plugin.Plugin +import core.plugin.Initializable + +private val ALL_REPAIRABLE_ITEM_IDS = (RepairItem.repairableItemIds + BarrowsEquipment.getAllRepairableBarrowsIds()).toIntArray() + +/** + * Handles items being used on Bob for repair services. + * @author 'Vexia + * @author Damighty - Kotlin conversion + */ +@Initializable +class BobRepairItem : UseWithHandler(*ALL_REPAIRABLE_ITEM_IDS) { + + override fun newInstance(arg: Any?): Plugin { + addHandler(519, NPC_TYPE, this) + addHandler(3797, NPC_TYPE, this) + return this + } + + override fun handle(event: NodeUsageEvent): Boolean { + val player = event.player + val item = event.usedItem + val npc = event.usedWith as NPC + + val isBarrowsItem = BarrowsEquipment.isBarrowsItem(item.id) + + if (isBarrowsItem) { + if (BarrowsEquipment.isFullyRepaired(item.id)) { + player.dialogueInterpreter.open(519, npc, true, true, item.id) + return true + } + } else if (RepairItem.forId(item.id) == null) { + player.dialogueInterpreter.open(519, npc, true, true) + return true + } + + player.dialogueInterpreter.open(519, npc, true, false, item.id, item) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java index ecdbd5bed..7a810e667 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java @@ -22,6 +22,7 @@ import core.plugin.Initializable; import core.tools.RandomFunction; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles the lumbridge basement. @@ -55,7 +56,6 @@ public class LumbridgeBasementPlugin extends OptionHandler { SceneryDefinition.forId(40849).getHandlers().put("option:jump-down", this); SceneryDefinition.forId(40260).getHandlers().put("option:climb-through", this); SceneryDefinition.forId(41077).getHandlers().put("option:crawl-through", this); - ClassScanner.definePlugins(new LightCreatureNPC(), new LightCreatureHandler()); SceneryBuilder.add(new Scenery(40260, Location.create(2526, 5828, 2), 2)); return this; } @@ -170,92 +170,4 @@ public class LumbridgeBasementPlugin extends OptionHandler { return null; } - /** - * Handles the sapphire lantern on a light creature. - * @author Vexia - * - */ - public class LightCreatureHandler extends UseWithHandler { - - /** - * Constructs the {@code LightCreatureHandler} - */ - public LightCreatureHandler() { - super( 4700, 4701, 4702); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2021, NPC_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (!hasRequirement(player, "While Guthix Sleeps")) - return true; - player.lock(2); - player.teleport(Location.create(2538, 5881, 0)); - return true; - } - - @Override - public Location getDestination(Player player, Node with) { - if (player.getLocation().withinDistance(with.getLocation())) { - return player.getLocation(); - } - return null; - } - - } - - - /** - * Handles the light creature npc. - * @author Vexia - * - */ - public class LightCreatureNPC extends AbstractNPC { - - /** - * Constructs the {@code LightCreatureNPC} - */ - public LightCreatureNPC() { - super(0, null); - this.setWalks(true); - this.setWalkRadius(10); - } - - /** - * Constructs the {@code LightCreatureNPC} - */ - public LightCreatureNPC(int id, Location location) { - super(id, location); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new LightCreatureNPC(id, location); - } - - @Override - public void handleTickActions() { - if (!getLocks().isMovementLocked()) { - if (isWalks() && !getPulseManager().hasPulseRunning() && nextWalk < GameWorld.getTicks()) { - setNextWalk(); - Location l = getLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0); - if (canMove(l)) { - Pathfinder.find(this, l, true, Pathfinder.PROJECTILE).walk(this); - } - } - } - } - - @Override - public int[] getIds() { - return new int[] {2021}; - } - - } } diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeNodePlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeNodePlugin.java index e170abbe7..0a0d61107 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeNodePlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeNodePlugin.java @@ -1,5 +1,6 @@ package content.region.misthalin.lumbridge.handlers; +import core.api.Container; import core.cache.def.impl.SceneryDefinition; import core.game.node.entity.player.link.diary.DiaryType; import core.game.component.Component; @@ -21,6 +22,9 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; /** * Represents the node option handler for lumbridge. @@ -45,6 +49,7 @@ public final class LumbridgeNodePlugin extends OptionHandler { SceneryDefinition.forId(22114).getHandlers().put("option:open", this); SceneryDefinition.forId(29355).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(37655).getHandlers().put("option:view", this); + SceneryDefinition.forId(org.rs09.consts.Scenery.LOGS_36974).getHandlers().put("option:take-axe", this); return this; } @@ -114,8 +119,13 @@ public final class LumbridgeNodePlugin extends OptionHandler { case 37655: player.getInterfaceManager().open(new Component(270)); break; - - + case org.rs09.consts.Scenery.LOGS_36974: + if (!addItem(player, Items.BRONZE_AXE_1351, 1, Container.INVENTORY)) { + sendMessage(player, "You don't have enough inventory space to hold that item."); + } else { + replaceScenery(node.asScenery(), org.rs09.consts.Scenery.LOGS_36975, 300, null); + } + return true; } return true; } diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeSwampHoleListener.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeSwampHoleListener.kt new file mode 100644 index 000000000..e5602d5b7 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeSwampHoleListener.kt @@ -0,0 +1,63 @@ +package content.region.misthalin.lumbridge.handlers + +import core.api.* +import org.rs09.consts.Animations +import org.rs09.consts.Items +import org.rs09.consts.Scenery as Sceneries +import core.game.component.Component +import core.game.global.action.ClimbActionHandler +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.node.scenery.Scenery +import core.game.node.scenery.SceneryBuilder +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation + +class LumbridgeSwampHoleListener : InteractionListener { + + val CAVE_ENTRANCE = Sceneries.DARK_HOLE_5947 + val CAVE_EXIT_ROPE = Sceneries.CLIMBING_ROPE_5946 + val WARNING_SIGN = Sceneries.WARNING_SIGN_15566 + val TOOL_STORE_FULL = Sceneries.TOOLS_10375 + val TOOL_STORE_EMPTY = 10373 + + override fun defineListeners() { + on(CAVE_ENTRANCE, IntType.SCENERY, "climb-down") { player, _ -> + if (!player.getSavedData().getGlobalData().hasTiedLumbridgeRope()) { + sendDialogue(player, "There is a sheer drop below the hole. You will need a rope.") + } else { + val insideCave = Location.create(3168, 9572, 0) + ClimbActionHandler.climb(player, Animation(Animations.HUMAN_BURYING_BONES_827), insideCave) + } + + return@on true + } + on(CAVE_EXIT_ROPE, IntType.SCENERY, "climb") { player, _ -> + val outsideCave = Location.create(3169, 3173, 0) + player.getProperties().setTeleportLocation(outsideCave); + return@on true + } + on(WARNING_SIGN, IntType.SCENERY, "read") { player, _ -> + player.getPacketDispatch().sendString("~-~-~ WARNING ~-~-~", 220, 5); + player.getPacketDispatch().sendString("Noxious gases vent into this cave.", 220, 7); + player.getPacketDispatch().sendString("Naked flames may cause an explosion!", 220, 8); + player.getPacketDispatch().sendString("Beware of vicious head-grabbing beasts!", 220, 10); + player.getPacketDispatch().sendString("Contact a Slayer master for protective headgear.", 220, 11); + player.getInterfaceManager().open(Component(220)); + return@on true + } + on(TOOL_STORE_FULL, IntType.SCENERY, "take") { player: Player, node: Node -> + if(freeSlots(player) < 2) { + sendDialogue(player, "You do not have enough inventory space.") + } + else if (player.getInventory().add(Item(Items.RAKE_5341), Item(Items.SPADE_952))) { + SceneryBuilder.replace((node as Scenery), (node as Scenery).transform(TOOL_STORE_EMPTY), 300); + } + + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeSwampHolePlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeSwampHolePlugin.java deleted file mode 100644 index e22f68882..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeSwampHolePlugin.java +++ /dev/null @@ -1,73 +0,0 @@ -package content.region.misthalin.lumbridge.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.game.global.action.ClimbActionHandler; -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.game.node.scenery.SceneryBuilder; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle the swamp hole. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LumbridgeSwampHolePlugin extends OptionHandler { - - /** - * Represents the top location. - */ - private static final Location TOP = Location.create(3169, 3173, 0); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(10375).getHandlers().put("option:take", this); - SceneryDefinition.forId(5947).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(5946).getHandlers().put("option:climb", this); - SceneryDefinition.forId(15566).getHandlers().put("option:read", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "climb-down": - if (!player.getSavedData().getGlobalData().hasTiedLumbridgeRope()) { - player.getDialogueInterpreter().open(70099, "There is a sheer drop below the hole. You will need a rope."); - return true; - } else { - ClimbActionHandler.climb(player, new Animation(827), Location.create(3168, 9572, 0)); - } - case "climb": - player.getProperties().setTeleportLocation(TOP); - break; - case "take": - if (player.getInventory().freeSlots() < 2) { - player.getPacketDispatch().sendMessage("You do not have enough inventory space."); - return true; - } - if (player.getInventory().add(new Item(5341), new Item(952))) { - SceneryBuilder.replace(((Scenery) node), ((Scenery) node).transform(10373), 300); - } - break; - case "read": - player.getPacketDispatch().sendString("~-~-~ WARNING ~-~-~", 220, 5); - player.getPacketDispatch().sendString("Noxious gases vent into this cave.", 220, 7); - player.getPacketDispatch().sendString("Naked flames may cause an explosion!", 220, 8); - player.getPacketDispatch().sendString("Beware of vicious head-grabbing beasts!", 220, 10); - player.getPacketDispatch().sendString("Contact a Slayer master for protective headgear.", 220, 11); - player.getInterfaceManager().open(new Component(220)); - break; - } - return true; - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonArea.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonArea.kt index 22aef3339..2b5a9003d 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonArea.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonArea.kt @@ -6,5 +6,5 @@ import core.game.world.map.zone.ZoneRestriction class TormentedDemonArea : MapArea { override fun defineAreaBorders() : Array { return arrayOf (ZoneBorders.forRegion(10329)) } - override fun getRestrictions() : Array { return arrayOf (ZoneRestriction.CANNON) } + override fun getRestrictions() : Array { return arrayOf (ZoneRestriction.CANNON, ZoneRestriction.RANDOM_EVENTS) } } diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/WallBeastBehavior.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/WallBeastBehavior.kt new file mode 100644 index 000000000..309fc9e0e --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/WallBeastBehavior.kt @@ -0,0 +1,145 @@ +package content.region.misthalin.lumbridge.handlers + +import core.api.* +import core.game.interaction.MovementPulse +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.ImpactHandler.HitsplatType +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.repository.Repository +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * NPCs.WALL_BEAST_7823 starts out as NPCs.HOLE_IN_THE_WALL_2058 first. + * On death, it will respawn a new hole in the wall. + * + * h2QAZzGds9o + * + * npc anim 1807 + * {3161,9547,0,0,3}-{3164,9556,0,0,4}-{3162,9574,0,0,3}-{3198,9554,0,0,7}-{3198,9572,0,0,1}-{3215,9560,0,0,1}-{3216,9588,0,0,1}- + */ +class WallBeastBehavior : NPCBehavior(NPCs.HOLE_IN_THE_WALL_2058, NPCs.WALL_BEAST_7823) { + override fun onCreation(self: NPC) { + // This NPC should never move. + self.walkRadius = 0 + self.isNeverWalks = true + } + + override fun onDeathFinished(self: NPC, entity: Entity) { + self.reTransform() + // I don't think this is needed since the respawn will be immediate. + } +} + +class WallBeastNTrigger : MapArea { + + companion object { + /** Finds the nearest NPC of npcId to location. */ + fun findNearestNPC(location: Location, npcId: Int): NPC? { + // maybe replace contentAPI's version of this + return Repository.npcs + .filter { it.id == npcId } + .toMutableList() + .sortedWith { a, b -> + return@sortedWith (Location.getDistance(a.location, location) - Location.getDistance(b.location, location)).toInt() + } + .firstOrNull() + } + + fun isHelmetInEquipment(entity: Player): Boolean { + return inEquipment(entity, Items.SPINY_HELMET_4551) || + inEquipment(entity, Items.SLAYER_HELMET_13263) || + inEquipment(entity, Items.SPIKED_HELMET_13105) || + inEquipment(entity, Items.SLAYER_HELMET_E_14636) || + inEquipment(entity, Items.SLAYER_HELMET_CHARGED_14637) + } + } + override fun defineAreaBorders(): Array { + // The goals here are in order + return arrayOf( + ZoneBorders(3161,9546,3161,9546), + ZoneBorders(3164,9555,3164,9555), + ZoneBorders(3162,9573,3162,9573), + ZoneBorders(3198,9553,3198,9553), + ZoneBorders(3198,9571,3198,9571), + ZoneBorders(3215,9559,3215,9559), + ZoneBorders(3216,9587,3216,9587), + ) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + val nearbyWallbeast = findNearestNPC(entity.location, NPCs.HOLE_IN_THE_WALL_2058) // wallbeasts are always 1 square north. + // println(nearbyWallbeast) + if (nearbyWallbeast != null) { + lock(entity, 2) + stopWalk(entity) + // Stop walk doesn't stop the player from walking on. + // You have to clear the movement pulse that is continually feeding the walk location. + val movementPulse = entity.getPulseManager().current; + if (movementPulse is MovementPulse) { + movementPulse.stop(); + } + face(entity, nearbyWallbeast.location) + queueScript(nearbyWallbeast, 2, QueueStrength.STRONG) { stage: Int -> + when (stage) { + 0 -> { + if (isHelmetInEquipment(entity)) { + animate(nearbyWallbeast, 1805) + } else { + animate(nearbyWallbeast, 1806) + } + return@queueScript delayScript(nearbyWallbeast, 1) + } + 1 -> { + if (isHelmetInEquipment(entity)) { + // You can only attack the wallbeast if you have a helmet + transformNpc(nearbyWallbeast, NPCs.WALL_BEAST_7823, 300) + return@queueScript stopExecuting(nearbyWallbeast) + } else { + animate(nearbyWallbeast, 1807) + return@queueScript delayScript(nearbyWallbeast, 4) + } + } + 2 -> { + animate(nearbyWallbeast, 1808) + return@queueScript stopExecuting(nearbyWallbeast) + } + else -> return@queueScript stopExecuting(nearbyWallbeast) + } + } + queueScript(entity, 2, QueueStrength.STRONG) { stage: Int -> + when (stage) { + 0 -> { + if (isHelmetInEquipment(entity)) { + return@queueScript stopExecuting(entity) + } + lock(entity, 5) + return@queueScript delayScript(entity, 1) + } + + 1 -> { + animate(entity, 1810) // 734 + return@queueScript delayScript(entity, 4) + } + + 2 -> { + // I don't know how much they are supposed to hit... osrs says up to 18... + impact(entity, (13 .. 18).random() , HitsplatType.NORMAL) + animate(entity, 1811) // 734 + return@queueScript stopExecuting(entity) + } + + else -> return@queueScript stopExecuting(entity) + } + } + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt index 790b6aec2..54d38f426 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt @@ -4,6 +4,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable +import content.data.Quests /** * The Quest Journal and Configuration for the Cook's Assistant Quest. @@ -11,7 +12,7 @@ import core.plugin.Initializable */ @Initializable -class CooksAssistant : Quest("Cook's Assistant",15, 14, 1, 29, 0, 1, 2){ +class CooksAssistant : Quest(Quests.COOKS_ASSISTANT,15, 14, 1, 29, 0, 1, 2){ val MILK = 1927 val FLOUR = 1933 val EGG = 1944 @@ -21,7 +22,7 @@ class CooksAssistant : Quest("Cook's Assistant",15, 14, 1, 29, 0, 1, 2){ super.drawJournal(player, stage) var line = 12 - var stage = player?.questRepository?.getStage("Cook's Assistant")!! + var stage = player?.questRepository?.getStage(Quests.COOKS_ASSISTANT)!! if(stage < 10){ //If the quest has not been started diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/LumbridgeCookDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/LumbridgeCookDialogue.kt index 67e96da73..ae7b6439f 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/LumbridgeCookDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/LumbridgeCookDialogue.kt @@ -6,6 +6,7 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable +import content.data.Quests /** * Dialogue for the Lumbridge Cook. @@ -29,16 +30,16 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if(player?.questRepository?.getQuest("Lost Tribe")?.getStage(player) == 10){ + if (player?.questRepository?.getQuest(Quests.THE_LOST_TRIBE)?.getStage(player) == 10) { player("Did you see what happened in the cellar?") stage = 0 return true } - if (player?.questRepository?.getQuest("Cook's Assistant")!!.getStage(player) <= 0) { //If the player has ot started cook's assistant + if (player?.questRepository?.getQuest(Quests.COOKS_ASSISTANT)!!.getStage(player) <= 0) { //If the player has ot started cook's assistant npc(FacialExpression.SAD, "What am I to do?") stage = 0 return true - } else if (player?.questRepository?.getQuest("Cook's Assistant")!!.getStage(player) in 10..99) { //During the Cook's Assistant Quest + } else if (player?.questRepository?.getQuest(Quests.COOKS_ASSISTANT)!!.getStage(player) in 10..99) { //During the Cook's Assistant Quest if (player.getAttribute("cooks_assistant:all_submitted", false) || (player.getAttribute("cooks_assistant:milk_submitted", false) && player.getAttribute("cooks_assistant:flour_submitted", false) && player.getAttribute("cooks_assistant:egg_submitted", false))){ //If the player has handed all the ingredients to the chef but did not continue the dialogue npc(FacialExpression.HAPPY, "You've brought me everything I need! I am saved!", "Thank you!") stage = 200 @@ -57,14 +58,14 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - if(player.questRepository.getQuest("Lost Tribe").getStage(player) == 10){ + if (player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 10) { when(stage){ //Lost Tribe 0 -> npc("Last night I was in the kitchen and I heard a noise","from the cellar. I opened the trapdoor and saw a","creature dart into a hole in the wall.").also { stage++ } 1 -> npc("It looked a bit like a goblin, but it had big bulging eyes.","It wasn't wearing armour, but it had this odd helmet","with a light on it.").also { stage++ } 2 -> npc("The tunnel was too dark for me to follow it, so I went","to tell the Duke. But when we went down to the cellar","the hole had been blocked up, and no one believes me.").also { stage++ } 3 -> player("I believe you.").also { stage++ } - 4 -> npc("Thank you, ${player.username}! If you can convince the Duke","I'm telling the truth then we can get to the bottom of","this mystery.").also { stage = 1000; player.questRepository.getQuest("Lost Tribe").setStage(player,20) } + 4 -> npc("Thank you, ${player.username}! If you can convince the Duke","I'm telling the truth then we can get to the bottom of","this mystery.").also { stage = 1000; player.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player,20) } 5 -> end() } return true @@ -117,7 +118,7 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ 44 -> npc(FacialExpression.ANGRY, "I AM a real cook! I haven't got time to be chatting", "about culinary fashion. I'm in desperate need of help!").also { stage = 21 } //Yes, I'll help you - 50 -> npc(FacialExpression.HAPPY, "Oh thank you, thank you. I need milk, an egg and", "flour. I'd be very grateful if you can get them for me.").also{ player?.questRepository?.getQuest("Cook's Assistant")?.start(player!!); stage++ } + 50 -> npc(FacialExpression.HAPPY, "Oh thank you, thank you. I need milk, an egg and", "flour. I'd be very grateful if you can get them for me.").also{ player?.questRepository?.getQuest(Quests.COOKS_ASSISTANT)?.start(player!!); stage++ } 51 -> player(FacialExpression.NEUTRAL, "So where do I find these ingredients then?").also { stage = 60 } //Where do I find these ingredients? @@ -241,10 +242,10 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ 203 -> npc(FacialExpression.NEUTRAL, "Maybe, but I won't be holding my breath.").also { stage++ } //Activate the Cook's Assistant Quest Complete Certificate - 204 -> end().also { player?.questRepository?.getQuest("Cook's Assistant")?.finish(player!!) } + 204 -> end().also { player?.questRepository?.getQuest(Quests.COOKS_ASSISTANT)?.finish(player!!) } //Dialogue after Cook's Assistant Completion - 300 -> if(player.questRepository.getQuest("Lost Tribe").getStage(player) == 10) { + 300 -> if(player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 10) { player("Do you know what happened in the castle cellar?").also { stage = 600 } } else { options("I am getting strong and mighty.", "I keep on dying.", "Can I use your range?").also { stage++ } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/DramenTreeListener.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/DramenTreeListener.kt deleted file mode 100644 index 735e384d7..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/DramenTreeListener.kt +++ /dev/null @@ -1,46 +0,0 @@ -package content.region.misthalin.lumbridge.quest.lostcity - -import core.game.node.scenery.Scenery -import content.data.skill.SkillingTool -import content.global.skill.gather.woodcutting.WoodcuttingSkillPulse -import core.game.world.map.Location -import org.rs09.consts.NPCs -import org.rs09.consts.Scenery as Sceneries -import core.game.interaction.InteractionListener -import core.api.getQuestStage -import core.api.sendMessage -import core.game.interaction.IntType - -class DramenTreeListener : InteractionListener { - - override fun defineListeners() { - - on(Sceneries.DRAMEN_TREE_1292, IntType.SCENERY, "chop down"){ player, node -> - val questStage = getQuestStage(player,"Lost City") - if (SkillingTool.getHatchet(player) == null) { - sendMessage(player,"You do not have an axe which you have the level to use.") - return@on true - } - if (questStage < 20) { - return@on true - } - if (questStage == 20) { - if (player.getAttribute("treeSpawned", false)) { - return@on true - } - val spirit = TreeSpiritNPC(NPCs.TREE_SPIRIT_655, Location(2862, 9734, 0)) - spirit.target = player - spirit.init() - spirit.attack(player) - player.setAttribute("treeSpawned", true) - spirit.sendChat("You must defeat me before touching the tree!") - return@on true - } - - player.pulseManager.run(WoodcuttingSkillPulse(player, node as Scenery)) - return@on true - } - - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt index f83e0248a..89f48fe42 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt @@ -1,21 +1,29 @@ package content.region.misthalin.lumbridge.quest.lostcity +import core.api.Event +import core.api.LoginListener +import core.api.clearScripts +import core.api.getQuestStage +import core.game.event.EventHook +import core.game.event.InteractionEvent +import core.game.node.entity.Entity import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.game.node.item.Item +import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery /** * LostCity class for the Lost City quest - * @author lila - * @author Vexia - * @author Aero + * @author lila, Vexia, Aero, Player Name */ @Initializable -class LostCity : Quest("Lost City", 83, 82, 3, 147, 0, 1, 6) { - +class LostCity : Quest(Quests.LOST_CITY, 83, 82, 3, 147, 0, 1, 6), LoginListener { class SkillRequirement(val skill: Int?, val level: Int?) val requirements = arrayListOf() @@ -66,9 +74,45 @@ class LostCity : Quest("Lost City", 83, 82, 3, 147, 0, 1, 6) { line(player, BLUE + "and be able to defeat a " + RED + "Level 101 Spirit without weapons", line++) } + private val DramenTreeHook = object : EventHook { + override fun process(entity: Entity, event: InteractionEvent) { + if (event.target.id == Scenery.DRAMEN_TREE_1292 && event.option == "chop down") { + val player = entity as Player + val questStage = getQuestStage(player, Quests.LOST_CITY) + if (questStage == 20) { + if (player.getAttribute("treeSpawned", false)) { + return + } + val spirit = TreeSpiritNPC(NPCs.TREE_SPIRIT_655, Location(2862, 9734, 0)) + spirit.target = player + spirit.init() + spirit.attack(player) + player.setAttribute("treeSpawned", true) + spirit.sendChat("You must defeat me before touching the tree!") + } + } + } + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + if (stage <= 20) { + player.hook(Event.Interacted, DramenTreeHook) + } + if (stage == 21) { + player.unhook(DramenTreeHook) + } + } + + override fun login(player: Player) { + if (getQuestStage(player, Quests.LOST_CITY) <= 20) { + player.hook(Event.Interacted, DramenTreeHook) + } + } + override fun newInstance(`object`: Any?): Quest { requirements.add(SkillRequirement(Skills.WOODCUTTING, 36)) requirements.add(SkillRequirement(Skills.CRAFTING, 31)) return this } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCityListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCityListeners.kt index c922a107e..32bef2049 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCityListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCityListeners.kt @@ -13,6 +13,7 @@ import core.game.interaction.IntType import org.rs09.consts.Scenery as Sceneries import core.game.interaction.InteractionListener import core.game.world.GameWorld +import content.data.Quests /** * This class covers some listeners for the Lost City quest @@ -27,12 +28,11 @@ class LostCityListeners : InteractionListener { // the shed teleport, to allow players to access zanaris if they enter the shed while wielding the dramen staff on(Sceneries.DOOR_2406, IntType.SCENERY,"open"){ player, node -> core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,node as Scenery) - val quest = "Lost City" val isOutsideShed = player.location.x < node.location.x - val canDramenTeleport = inEquipment(player,Items.DRAMEN_STAFF_772) && ( getQuestStage(player,quest) > 20 ) && isOutsideShed - if(canDramenTeleport) { + val canDramenTeleport = inEquipment(player,Items.DRAMEN_STAFF_772) && getQuestStage(player, Quests.LOST_CITY) > 20 && isOutsideShed + if (canDramenTeleport) { var count = 0 - // pulser to handle the teleport. after 2 ticks it checks if the player hasnt completed lost city; if so, then it finishes the quest after the teleport + // pulser to handle the teleport. after 2 ticks it checks if the player hasn't completed Lost City; if so, then it finishes the quest after the teleport GameWorld.Pulser.submit(object : Pulse(2) { override fun pulse(): Boolean { when (count++) { @@ -44,9 +44,9 @@ class LostCityListeners : InteractionListener { teleport(player, Location(2452, 4473, 0), TeleportType.FAIRY_RING) } } - 1 -> return isQuestComplete(player,quest) + 1 -> return isQuestComplete(player, Quests.LOST_CITY) 2 -> { - finishQuest(player,quest) + finishQuest(player, Quests.LOST_CITY) return true } } @@ -66,8 +66,8 @@ class LostCityListeners : InteractionListener { if (removeItem(player, Item(Items.DRAMEN_BRANCH_771, 1), Container.INVENTORY)) { sendDialogue(player,"You carve the branch into a staff.") addItem(player, Items.DRAMEN_STAFF_772, 1, Container.INVENTORY) - } } + } return@onUseWith true } } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/ShamusDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/ShamusDialogue.kt index 8a3e0baf2..15800e2f7 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/ShamusDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/ShamusDialogue.kt @@ -5,6 +5,7 @@ import core.plugin.Initializable import org.rs09.consts.NPCs import core.api.getQuestStage import core.api.setQuestStage +import content.data.Quests /** * ShamusDialogue, to handle the dialogue of Shamus the Leprechaun from the Lost City quest @@ -13,9 +14,6 @@ import core.api.setQuestStage */ @Initializable class ShamusDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - - val quest = "Lost City" - override fun open(vararg args: Any?): Boolean { npcl(core.game.dialogue.FacialExpression.ANNOYED,"Ay yer big elephant! Yer've caught me, to be sure! What would an elephant like yer be wanting wid ol' Shamus then?") stage = 0 @@ -23,7 +21,7 @@ class ShamusDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(getQuestStage(player,quest)) { + when(getQuestStage(player, Quests.LOST_CITY)) { 0 -> when(stage++) { 0 -> playerl(core.game.dialogue.FacialExpression.THINKING, "I'm not sure.") 1 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Well you'll have to be catchin' me again when yer are, elephant!") @@ -45,7 +43,7 @@ class ShamusDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin 12 -> end().also { ShamusTreeListener.disappearShamus() sendDialogue("The leprechaun magically disappears.") - setQuestStage(player,quest,20) + setQuestStage(player, Quests.LOST_CITY, 20) } } else -> when(stage++) { diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/TreeSpiritNPC.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/TreeSpiritNPC.kt index 0449ca69e..754a82d69 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/TreeSpiritNPC.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/TreeSpiritNPC.kt @@ -1,7 +1,6 @@ package content.region.misthalin.lumbridge.quest.lostcity import core.game.node.entity.Entity -import core.game.node.entity.combat.CombatStyle import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.player.Player import core.game.world.map.Location @@ -10,6 +9,7 @@ import org.rs09.consts.NPCs import core.api.getQuestStage import core.api.sendDialogue import core.api.setQuestStage +import content.data.Quests /** * TreeSpiritNPC class to handle the tree spirit that spawns out of the dramen tree @@ -47,9 +47,8 @@ class TreeSpiritNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, l override fun finalizeDeath(killer: Entity) { super.finalizeDeath(killer) if (killer is Player) { - val quest = "Lost City" - if (getQuestStage(killer,quest) == 20) { - setQuestStage(killer,quest,21) + if (getQuestStage(killer, Quests.LOST_CITY) == 20) { + setQuestStage(killer, Quests.LOST_CITY,21) sendDialogue(killer, "With the Tree Spirit defeated you can now chop the tree.") } } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/WarriorDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/WarriorDialogue.kt index a82989f47..9281c9153 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/WarriorDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/WarriorDialogue.kt @@ -7,6 +7,7 @@ import core.game.dialogue.Topic import core.tools.END_DIALOGUE import core.api.getQuestStage import core.api.startQuest +import content.data.Quests /** * WarriorDialogue, to handle the dialogue for the Warrior in the Lost City quest @@ -17,7 +18,7 @@ import core.api.startQuest class WarriorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { - when(getQuestStage(player,"Lost City")) { + when(getQuestStage(player,Quests.LOST_CITY)) { 10 -> playerl(core.game.dialogue.FacialExpression.THINKING,"So let me get this straight: I need to search the trees around here for a leprechaun; and then when I find him, he will tell me where this 'Zanaris' is?").also { stage = 1000 } 20, 21 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Have you found anything yet?").also { stage = 2000 } 100 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Hey, thanks for all the information. It REALLY helped me out in finding the lost city of Zanaris and all.").also { stage = 3000 } @@ -49,7 +50,7 @@ class WarriorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi 6 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"So a leprechaun knows where Zanaris is eh?").also { stage = 600 } 7 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Thanks for the help!").also { stage = 700 } 8 -> end().also { - startQuest(player,"Lost City") + startQuest(player,Quests.LOST_CITY) } 100 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"We're looking for Zanaris...GAH! I mean we're not here for any particular reason at all.").also { stage = 3 } 101 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Well we're on an adventure right now. Mind you, this is OUR adventure and we don't want to share it - find your own!").also { stage = 2 } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/DukeHoracioRMDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/DukeHoracioRMDialogue.kt index 7eb8b9547..42ad4fa36 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/DukeHoracioRMDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/DukeHoracioRMDialogue.kt @@ -6,6 +6,7 @@ import core.game.node.item.Item import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests class DukeHoracioRMDialogue(val questStage: Int) : DialogueFile() { @@ -29,7 +30,7 @@ class DukeHoracioRMDialogue(val questStage: Int) : DialogueFile() { 10 -> npc("Thank you very much, stranger. I am sure the head", "wizard will reward you for such an interesting find.").also { stage++ } 11 -> { interpreter!!.sendDialogue("The Duke hands you an " + Quest.BLUE + "air talisman.").also { stage++ } - player!!.questRepository.getQuest("Rune Mysteries").start(player) + player!!.questRepository.getQuest(Quests.RUNE_MYSTERIES).start(player) if (!player!!.inventory.add(TALISMAN)) { GroundItemManager.create(TALISMAN, player!!.location, player) } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/RuneMysteries.java b/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/RuneMysteries.java index ebe46af39..85e329907 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/RuneMysteries.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/RuneMysteries.java @@ -3,6 +3,7 @@ package content.region.misthalin.lumbridge.quest.runemysteries; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the rune mysteries fortress quest. @@ -15,7 +16,7 @@ public class RuneMysteries extends Quest { * Constructs a new {@code RuneMysteries} {@code Object}. */ public RuneMysteries() { - super("Rune Mysteries", 27, 26, 1, 63, 0, 1, 6); + super(Quests.RUNE_MYSTERIES, 27, 26, 1, 63, 0, 1, 6); } @Override diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SSFredTheFarmerDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SSFredTheFarmerDialogue.kt index 9392a5926..12a1af387 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SSFredTheFarmerDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SSFredTheFarmerDialogue.kt @@ -6,6 +6,7 @@ import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.Items +import content.data.Quests class SSFredTheFarmerDialogue(val questStage: Int) : DialogueFile() { companion object { @@ -58,7 +59,7 @@ class SSFredTheFarmerDialogue(val questStage: Int) : DialogueFile() { 2000 -> { // NOTE: In a July 2009 video, this only happens when the dialogue ends - startQuest(player!!, "Sheep Shearer") + startQuest(player!!, Quests.SHEEP_SHEARER) npc(FacialExpression.NEUTRAL, "Good! Now one more thing, do you actually know how", "to shear a sheep?").also { stage++ } } 2001 -> options("Of course!", "Err. No, I don't know actually.").also { stage++ } @@ -150,7 +151,7 @@ class SSFredTheFarmerDialogue(val questStage: Int) : DialogueFile() { 30101 -> { val ballsOfWoolDelivered = SheepShearer.deliverBallsOfWool(player!!) if (SheepShearer.getBallsOfWoolRequired(player!!) == 0) { - setQuestStage(player!!, "Sheep Shearer", 90) + setQuestStage(player!!, Quests.SHEEP_SHEARER, 90) player(FacialExpression.HAPPY, "That's the last of them.").also { stage = 30300 } } else { sendDialogue(player!!, "You give Fred $ballsOfWoolDelivered balls of wool").also { stage = 30200 } @@ -162,7 +163,7 @@ class SSFredTheFarmerDialogue(val questStage: Int) : DialogueFile() { 30202 -> player(FacialExpression.NEUTRAL, "Ok I'll work on it.").also { stage = END_DIALOGUE } 30300 -> npc(FacialExpression.SAD, "I guess I'd better pay you then.").also { stage++ } - STAGE_FINISH_QUEST -> finishQuest(player!!, "Sheep Shearer").also { stage = END_DIALOGUE } + STAGE_FINISH_QUEST -> finishQuest(player!!, Quests.SHEEP_SHEARER).also { stage = END_DIALOGUE } 31000 -> npc(FacialExpression.NEUTRAL, "You need to collect ${SheepShearer.getBallsOfWoolRequired(player!!)} more balls of wool.").also { stage++ } 31001 -> { diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearer.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearer.kt index a30669774..634041363 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearer.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearer.kt @@ -8,9 +8,10 @@ import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items import kotlin.math.min +import content.data.Quests @Initializable -class SheepShearer : Quest("Sheep Shearer", 28, 27, 1, 179, 0, 20, 21) { +class SheepShearer : Quest(Quests.SHEEP_SHEARER, 28, 27, 1, 179, 0, 20, 21) { companion object { val ATTR_NUM_BALLS_OF_WOOL_DELIVERED = "/save:sheep-shearer:num-balls-of-wool-delivered" val ATTR_IS_PENGUIN_SHEEP_SHEARED = "/save:sheep-shearer:is-penguin-sheep-sheared" diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/JunaDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/JunaDialogue.kt new file mode 100644 index 000000000..b5a2a1685 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/JunaDialogue.kt @@ -0,0 +1,204 @@ +package content.region.misthalin.lumbridge.quest.tearsofguthix + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +/** + * Anim 2056 - Juna puts head into itself + * Anim 2055 - Juna lifts tail + */ +class JunaDialogue : InteractionListener { + override fun defineListeners() { + // Juna is a scenery. + on(Scenery.JUNA_31302, SCENERY, "talk-to") { player, _ -> + openDialogue(player, JunaDialogueFile(), NPC(NPCs.JUNA_2023)) + return@on true + } + // This is quest varbit 451 controlled. When it is set to 2, JUNA changes in ID + on(Scenery.JUNA_31303, SCENERY, "talk-to") { player, node -> + openDialogue(player, JunaDialogueFile(), NPC(NPCs.JUNA_2023)) + return@on true + } + } +} + +class JunaDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(Quests.TEARS_OF_GUTHIX, 0) + .branch { player -> if(TearsOfGuthix.hasRequirements(player)) { 1 } else { 0 } } + .let { branch -> + branch.onValue(0) + // Inauthentic, but absolutely no source on this... + .npcl(FacialExpression.OLD_NORMAL, "You are not strong enough of an adventurer to partake this quest. Come back when you are stronger.") + .linel("You do not meet the quest requirements for Tears of Guthix.") + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Tell me... a story...") + .playerl(FacialExpression.THINKING, "A story?") + .npcl(FacialExpression.OLD_NORMAL, "I have been waiting here three thousand years, guarding the Tears of Guthix. I serve my master faithfully, but I am bored.") + .npcl(FacialExpression.OLD_NORMAL, "An adventurer such as yourself must have many tales to tell. If you can entertain me, I will let you into the cave for a time.") + .npcl(FacialExpression.OLD_NORMAL, "The more I enjoy your story, the more time I will give you in the cave.") + .npcl(FacialExpression.OLD_NORMAL, "Then you can drink of the power of balance, which will make you stronger in whatever area you are weakest.") + + .let { builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + + optionBuilder.option_playerl("Okay...") + .linel("You tell Juna some stories of your adventures.") + // Yes I know. + .playerl("Blah blah blah something about recent quest I did that I'm forced to talk about, but looks like you want me to shut up.") + .npcl(FacialExpression.OLD_NORMAL,"Blah blah blah that's so cool, I'm totally interested in what you are saying and am totally not falling asleep at this point.") + // ^ I'm not going to type out 120 fucking quest dialogue for shit you wouldn't care reading. + .npcl(FacialExpression.OLD_NORMAL,"Your stories have entertained me. I will let you into the cave for a short time.") + .npcl(FacialExpression.OLD_NORMAL,"But first you will need to make a bowl in which to collect the tears.") + // The camera pans south to the part of the cave containing guthix-infused rocks. + .npcl(FacialExpression.OLD_NORMAL,"There is a cave on the south side of the chasm that is similarly infused with the power of Guthix. The stone in that cave is the only substance that can catch the Tears of Guthix.") + .npcl(FacialExpression.OLD_NORMAL,"Mine some stone from that cave, make it into a bowl, and bring it to me, and then I will let you catch the Tears.") + .endWith { _, player -> + if(getQuestStage(player, Quests.TEARS_OF_GUTHIX) == 0) { + setQuestStage(player, Quests.TEARS_OF_GUTHIX, 1) + } + } + + optionBuilder.option_playerl("Not now.") + .end() + + optionBuilder.option_playerl("What are the Tears of Guthix?") + .npcl(FacialExpression.OLD_NORMAL, "The Third Age of the world was a time of great conflict, of destruction never seen before or since, when all the gods save Guthix warred for control.") + .npcl(FacialExpression.OLD_NORMAL, "The colossal Wyrms, of whom today's dragons are a pale reflection, turned all the sky to fire, while on the ground armies of foot soldiers, goblins and trolls and humans, filled the valleys and plains with blood.") + .npcl(FacialExpression.OLD_NORMAL, "In time the noise of the conflict woke Guthix from His deep slumber, and He rose and stood in the centre of the battlefield so that the splendour of His wrath filled the world, and He called for the conflict to cease!") + .npcl(FacialExpression.OLD_NORMAL, "Silence fell, for the gods knew that none could challenge the power of the mighty Guthix -- for His power is that of nature itself, to which all other things are subject, in the end.") + .npcl(FacialExpression.OLD_NORMAL, "Guthix reclaimed that which had been stolen from Him, and went back underground to return to His sleep and continue to draw the world's power into Himself.") + .npcl(FacialExpression.OLD_NORMAL, "But on His way into the depths of the earth He sat and rested in this cave; and, thinking of the battle-scarred desert that now stretched from one side of His world to the other, He wept.") + .npcl(FacialExpression.OLD_NORMAL, "And so great was His sorrow, and so great was His life- giving power, that the rocks themselves began to weep with Him.") + .npcl(FacialExpression.OLD_NORMAL, "Later, Guthix noticed that the rocks continued to weep, and that their tears were infused with a small part of His power.") + .npcl(FacialExpression.OLD_NORMAL, "So He set me, His servant, to guard the cave, and He entrusted to me the task of judging who was and was not worthy to access the tears.") + .npcl(FacialExpression.OLD_NORMAL, "Tell me... a story...") + .goto(returnJoin) + } + return@let builder.goto(returnJoin) + } + + + b.onQuestStages(Quests.TEARS_OF_GUTHIX, 1) + .npc(FacialExpression.OLD_NORMAL, "Before you can collect the Tears of Guthix you must", "make a bowl out of the stone in the cave on the south", "of the chasm.") + .branch { player -> if(inInventory(player, Items.STONE_BOWL_4704)) { 1 } else { 0 } } + .let{ branch -> + branch.onValue(0) + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("But I don't know how to reach the cave!") + .npcl(FacialExpression.OLD_NORMAL, "I will tell you the story of the light-creatures.") + .npcl(FacialExpression.OLD_NORMAL, "Myriad and beautiful were the creatures and civilizations of the early ages of the world. Gielinor was a work of art, shaped lovingly over the millennia by the creative mind of Guthix.") + .npcl(FacialExpression.OLD_NORMAL, "Only the sturdiest races survived the Godwars, and even then only by abandoning their high culture and gearing their societies towards war. Of the more delicate races there is now no trace, and almost no memory.") + .npcl(FacialExpression.OLD_NORMAL, "One such race had bodies as fragile as snowflakes, yet they built crystal cities that stood for a thousand years.") + .npcl(FacialExpression.OLD_NORMAL, "The wind would whisper through the spires and fill them with sweet harmonies, and the rising sun would shine through the precious gems that studded the towers and create inter plays of light as if rainbows were dancing.") + .npcl(FacialExpression.OLD_NORMAL, "Indeed, so marvellous was this light-show at its height that the patterns of light themselves became alive, and great flocks of luminous creatures rode along the gem- cast beams, each drawn to its own colour.") + .npcl(FacialExpression.OLD_NORMAL, "The creatures you see floating in this chasm are the last sorry remnants of that age. I do not know how they made their way here and survived to this time, but I am grateful for their company.") + .end() + + optionBuilder.option_playerl("What are the Tears of Guthix?") + .npcl(FacialExpression.OLD_NORMAL, "The Third Age of the world was a time of great conflict, of destruction never seen before or since, when all the gods save Guthix warred for control.") + .npcl(FacialExpression.OLD_NORMAL, "The colossal Wyrms, of whom today's dragons are a pale reflection, turned all the sky to fire, while on the ground armies of foot soldiers, goblins and trolls and humans, filled the valleys and plains with blood.") + .npcl(FacialExpression.OLD_NORMAL, "In time the noise of the conflict woke Guthix from His deep slumber, and He rose and stood in the centre of the battlefield so that the splendour of His wrath filled the world, and He called for the conflict to cease!") + .npcl(FacialExpression.OLD_NORMAL, "Silence fell, for the gods knew that none could challenge the power of the mighty Guthix -- for His power is that of nature itself, to which all other things are subject, in the end.") + .npcl(FacialExpression.OLD_NORMAL, "Guthix reclaimed that which had been stolen from Him, and went back underground to return to His sleep and continue to draw the world's power into Himself.") + .npcl(FacialExpression.OLD_NORMAL, "But on His way into the depths of the earth He sat and rested in this cave; and, thinking of the battle-scarred desert that now stretched from one side of His world to the other, He wept.") + .npcl(FacialExpression.OLD_NORMAL, "And so great was His sorrow, and so great was His life- giving power, that the rocks themselves began to weep with Him.") + .npcl(FacialExpression.OLD_NORMAL, "Later, Guthix noticed that the rocks continued to weep, and that their tears were infused with a small part of His power.") + .npcl(FacialExpression.OLD_NORMAL, "So He set me, His servant, to guard the cave, and He entrusted to me the task of judging who was and was not worthy to access the tears.") + .end() + + optionBuilder.option_playerl("Not now.") + .end() + } + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) + .playerl("I have a bowl.") + .npcl(FacialExpression.OLD_NORMAL, "I will keep your bowl for you, so that you may collect the tears many times in the future.") + .npcl(FacialExpression.OLD_NORMAL, "Now... tell me another story, and I will let you collect the tears for the first time.") + .endWith { _, player -> + if (removeItem(player, Items.STONE_BOWL_4704)) { + finishQuest(player, Quests.TEARS_OF_GUTHIX) + } + } + + b.onQuestStages(Quests.TEARS_OF_GUTHIX, 100) + .npcl(FacialExpression.OLD_NORMAL, "Tell me... a story...") + .let { builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Okay...") + .linel("You tell Juna some stories of your adventures.") + // Yes I know. + .playerl("Blah blah blah something about recent quest I did that I'm forced to talk about, but looks like you want me to shut up.") + .npcl(FacialExpression.OLD_NORMAL,"Blah blah blah that's so cool, I'm totally interested in what you are saying and am totally not falling asleep at this point.") + // ^ I'm not going to type out 120 fucking quest dialogue for shit you wouldn't care reading. + .branch { player -> + if(TearsOfGuthix.daysLeft(player) > 0 && TearsOfGuthix.xpLeft(player) > 0 && TearsOfGuthix.questPointsLeft(player) > 0) { + 3 + } else if(TearsOfGuthix.xpLeft(player) > 0 && TearsOfGuthix.questPointsLeft(player) > 0) { + 2 + } else if(TearsOfGuthix.daysLeft(player) > 0) { + 1 + } else { + 0 // Success branch + } + } + .let{ branch -> + // https://www.youtube.com/watch?v=X3M9CiS_BeU if not enough time has passed. + branch.onValue(3) + .manualStage { df, player, _, _ -> npcl(FacialExpression.OLD_NORMAL,"Your stories have entertained me. But I will not permit any adventurer to access the tears more than once a week. Come back in " + TearsOfGuthix.daysLeft(player).toString() + " days.") } + .npcl(FacialExpression.OLD_NORMAL,"You should use that time to have more adventures! You may not re-enter the cave until you have more stories to tell.") + .manualStage { df, player, _, _ -> sendDialogue(player, "You cannot enter the cave again until you have gained either one quest point or " + TearsOfGuthix.xpLeft(player) + " total XP.") } + .end() + + branch.onValue(2) + .npc(FacialExpression.OLD_NORMAL,"Your story has entertained me. But it is a poor sort", "of adventurer who only tells stories of the past and", "does not find new stories to tell. I will not let you", "into the cave again until you have had more adventures.") + .manualStage { df, player, _, _ -> sendDialogue(player, "You cannot enter the cave again until you have gained either one quest point or " + TearsOfGuthix.xpLeft(player) + " total XP.") } + .end() + + branch.onValue(1) + .manualStage { df, player, _, _ -> npcl(FacialExpression.OLD_NORMAL,"Your stories have entertained me. But I will not permit any adventurer to access the tears more than once a week. Come back in " + TearsOfGuthix.daysLeft(player).toString() + " days.") } + .end() + + return@let branch + }.onValue(0) + .npcl(FacialExpression.OLD_NORMAL,"Your stories have entertained me. I will let you into the cave for a short time.") + .branch { player -> if(TearsOfGuthix.isHandsFree(player)) { 1 } else { 0 } } + .let{ branch -> + // https://www.youtube.com/watch?v=J76OGo-hHlA your hands must be free. + branch.onValue(0) + .npc(FacialExpression.OLD_NORMAL,"But you must have both hands free to carry the bowl.", "Speak to me again when your hands are free.") + .end() + return@let branch + }.onValue(1) + // https://www.youtube.com/watch?v=Mj3pW-Brv9c + .npc(FacialExpression.OLD_NORMAL,"Collect as much as you can from the blue streams. If", "you let in water from the green streams, it will take", "away from the blue. For Guthix is god of balance, and", "balance lies in the juxtaposition of opposites.") + .endWith { _, player -> + TearsOfGuthixMinigame.startGame(player) + } + + optionBuilder.option_playerl("Not now.") + .end() + } + return@let builder.goto(returnJoin) + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/LightCreatureBehavior.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/LightCreatureBehavior.kt new file mode 100644 index 000000000..daa8c6885 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/LightCreatureBehavior.kt @@ -0,0 +1,129 @@ +package content.region.misthalin.lumbridge.quest.tearsofguthix + +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.world.GameWorld.ticks +import core.game.world.map.Location +import core.game.world.map.path.Pathfinder +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +class LightCreatureBehavior : NPCBehavior(NPCs.LIGHT_CREATURE_2021) { + + companion object { + fun moveLightCreature(self: NPC, location: Location) { + self.setNextWalk() + Pathfinder.find(self, location, true, Pathfinder.PROJECTILE).walk(self) + } + + } + + override fun tick(self: NPC): Boolean { + if (!self.locks.isMovementLocked) { + self.isWalks = true + self.walkRadius = 20 + if (self.isWalks && !self.pulseManager.hasPulseRunning() && self.nextWalk < ticks) { + self.setNextWalk() + val l: Location = self.location.transform(-5 + RandomFunction.random(self.walkRadius), -5 + RandomFunction.random(self.walkRadius), 0) + if (self.canMove(l)) { + Pathfinder.find(self, l, true, Pathfinder.PROJECTILE).walk(self) + } + } + } + return true + } + +} + +/* + + /** + * Handles the sapphire lantern on a light creature. + * @author Vexia + * + */ + public class LightCreatureHandler extends UseWithHandler { + + /** + * Constructs the {@code LightCreatureHandler} + */ + public LightCreatureHandler() { + super( 4700, 4701, 4702); + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + addHandler(2021, NPC_TYPE, this); + return this; + } + + @Override + public boolean handle(NodeUsageEvent event) { + final Player player = event.getPlayer(); + if (!hasRequirement(player, "While Guthix Sleeps")) + return true; + player.lock(2); + player.teleport(Location.create(2538, 5881, 0)); + return true; + } + + @Override + public Location getDestination(Player player, Node with) { + if (player.getLocation().withinDistance(with.getLocation())) { + return player.getLocation(); + } + return null; + } + + } + + + /** + * Handles the light creature npc. + * @author Vexia + * + */ + public class LightCreatureNPC extends AbstractNPC { + + /** + * Constructs the {@code LightCreatureNPC} + */ + public LightCreatureNPC() { + super(0, null); + this.setWalks(true); + this.setWalkRadius(10); + } + + /** + * Constructs the {@code LightCreatureNPC} + */ + public LightCreatureNPC(int id, Location location) { + super(id, location); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new LightCreatureNPC(id, location); + } + + @Override + public void handleTickActions() { + if (!getLocks().isMovementLocked()) { + if (isWalks() && !getPulseManager().hasPulseRunning() && nextWalk < GameWorld.getTicks()) { + setNextWalk(); + Location l = getLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0); + if (canMove(l)) { + Pathfinder.find(this, l, true, Pathfinder.PROJECTILE).walk(this); + } + } + } + } + + @Override + public int[] getIds() { + return new int[] {2021}; + } + + } +} + */ \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthix.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthix.kt new file mode 100644 index 000000000..072c108a8 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthix.kt @@ -0,0 +1,133 @@ +package content.region.misthalin.lumbridge.quest.tearsofguthix + +import content.data.Quests +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +/** + * Tears of Guthix Quest + * + * Most of the attributes are for the minigame. + * In order to reset, setQuestStage + * + * if (VARPBIT[451] > 1) return 2; if (VARPBIT[451] == 0) return 0; return 1; }; if (arg0 == 88) + */ +@Initializable +class TearsOfGuthix : Quest(Quests.TEARS_OF_GUTHIX, 120, 119, 1, 449, 451, 0, 1, 2) { + + companion object { + const val attributePreviousDate = "/save:quest:tearsofguthix-previousdateofaccess" // The date in milliseconds in which TOG was played. + const val attributePreviousXPAmount = "/save:quest:tearsofguthix-previousxpamount" // The last snapshot of XP user had. + const val attributePreviousQuestPoints = "/save:quest:tearsofguthix-previousquestpoints" // The last snapshot of quest points user had. + + fun isHandsFree(player: Player): Boolean { + return getItemFromEquipment(player, EquipmentSlot.WEAPON) == null + && getItemFromEquipment(player, EquipmentSlot.SHIELD) == null + } + + fun daysLeft(player: Player): Int { + val currentTime = System.currentTimeMillis() + val previousTime = getAttribute(player, attributePreviousDate, 0) + + val numberOfDaysLeft = (currentTime - previousTime) / 86400000L + return 6 - numberOfDaysLeft.toInt() + } + + fun xpLeft(player: Player): Int { + val currentXP = player.skills.totalXp + val previousXP = getAttribute(player, attributePreviousXPAmount, 0) + return 100000 - (currentXP - previousXP) + } + + fun questPointsLeft(player: Player): Int { + val currentQuestPoints = getQuestPoints(player) + val previousQuestPoints = getAttribute(player, attributePreviousQuestPoints, 0) + return 1 - (currentQuestPoints - previousQuestPoints) + } + + fun hasRequirements(player: Player): Boolean { + return arrayOf( + getQuestPoints(player) >= 43, + hasLevelStat(player, Skills.FIREMAKING, 49), + hasLevelStat(player, Skills.CRAFTING, 20), + hasLevelStat(player, Skills.MINING, 20), + ).all { it } + } + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, Quests.TEARS_OF_GUTHIX) > 0 + + if (!started) { + line(player, "I can start this quest by speaking to !!Juna the serpent?? who", line++, false) + line(player, "lives deep in the !!Lumbridge Swamp Caves??.", line++, false) + line(player, "I will need to have:", line++, false) + line(player, "Level 49 firemaking", line++, hasLevelStat(player, Skills.FIREMAKING, 49)) + line(player, "!!Level 20 crafting??", line++, hasLevelStat(player, Skills.CRAFTING, 20)) + line(player, "!!Level 20 mining??", line++, hasLevelStat(player, Skills.MINING, 20)) + line(player, "!!43 quest points??", line++, getQuestPoints(player) >= 43) + line(player, "!!Level 49 crafting would be an advantage??", line++, hasLevelStat(player, Skills.CRAFTING, 49)) + line(player, "!!Level 49 smithing would be an advantage??", line++, hasLevelStat(player, Skills.SMITHING, 49)) + } else if (stage < 100) { + line(player, "I met Juna the serpent in a deep chasm beneath the", line++, true) + line(player, "Lumbridge Swamp Caves.", line++, true) + line(player, "I told her a story and she said she would let me into the", line++, false) + line(player, "Tears of Guthix cave if I brought her a !!bowl?? made from the", line++, false) + line(player, "stone in !!the cave on the South side of the chasm??.", line++, false) + } else { + line(player, "I met Juna the serpent in a deep chasm beneath the", line++, true) + line(player, "Lumbridge Swamp Caves. I made a bowl out of magical", line++, true) + line(player, "stone in order to catch the Tears of Guthix.", line++, true) + line++ + line(player,"QUEST COMPLETE!", line++) + line++ + line(player, "Now Juna will let me into the cave to collect the Tears if I", line++, false) + line(player, "!!tell her stories?? of my adventures.", line++, false) + // TOG Minigame + if (daysLeft(player) > 0 && xpLeft(player) > 0 && questPointsLeft(player) > 0) { + line(player, "I will be able to collect the Tears of Guthix !!in " + daysLeft(player) + " days??, as", line++, false) + line(player, "long as I gain either !!1 Quest Point?? or !!" + xpLeft(player) + " total XP??", line++, false) + } else if (xpLeft(player) > 0 && questPointsLeft(player) > 0) { + line(player, "I will be able to collect the Tears of Guthix, as long as I", line++, false) + line(player, "gain either !!1 Quest Point?? or !!" + xpLeft(player) + " total XP??", line++, false) + } else if (daysLeft(player) > 0) { + line(player, "I will be able to collect the Tears of Guthix !!in " + daysLeft(player) + " days??.", line++, false) + } else { + line(player, "I have had enough adventures to tell Juna more stories,", line++, false) + line(player, "and a week has passed since I last collected the Tears. I", line++, false) + line(player, "can visit Juna again now.", line++, false) + } + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Tears of Guthix quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.STONE_BOWL_4704,230,277,5) + + drawReward(player, "1 quest point", ln++) + drawReward(player, "1,000 Crafting XP", ln++) + drawReward(player, "Access to the Tears of Guthix", ln++) + drawReward(player, "cave", ln++) + + rewardXP(player, Skills.CRAFTING, 1000.0) + } + + override fun reset(player: Player) { + removeAttribute(player, attributePreviousDate) + removeAttribute(player, attributePreviousXPAmount) + removeAttribute(player, attributePreviousQuestPoints) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixListeners.kt new file mode 100644 index 000000000..bd7c48800 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixListeners.kt @@ -0,0 +1,143 @@ +package content.region.misthalin.lumbridge.quest.tearsofguthix + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.impl.ForceMovement +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class TearsOfGuthixListeners : InteractionListener { + + companion object { + fun crossTheChasm(player: Player, with: NPC) { + // Unless you have time to animate this, this fucking thing is waaay too complicated. + // THIS IS JUST AESTHETICS + // You have to do the following: + // 1 - Get the light creature to your location. + // 2 - Animate both you and the light creature to float up. + // 3 - Walk both YOU AND THE LIGHT CREATURE to the other side. + // 4 - Float both you and the light creature to the ground. + // + // 2046 - Magically float into the air + // 2047 - Magically float back to the ground + // 2048 - Keep floating in the air + + // Instead you will just get fucking thrown to that other side. + val lightCreature = with as NPC + sendMessage(player, "The light-creature is attracted to your beam and comes towards you...") + LightCreatureBehavior.moveLightCreature(lightCreature, player.location) + // Could also do player.appearance.setAnimations(Animation(913)) which is the group animation for floating. + if (player.location.y > 9516) { + forceMove(player, player.location, Location.create(3229, 9504, 2), 0, 400, null, 2048) + } else { + forceMove(player, player.location, Location.create(3228, 9527, 2), 0, 400, null, 2048) + } + } + } + + override fun defineListeners() { + // Similar to RockClimbShortcut.kt + on(Scenery.ROCKS_6673, SCENERY, "climb") { player, _ -> + if (player.location.x > 3240) { + ForceMovement.run(player, player.location, Location.create(player.location).transform(-2, 0, 0), Animation(1148), Animation(1148), Direction.WEST, 13).endAnimation = Animation.RESET + } else { + ForceMovement.run(player, player.location, Location.create(player.location).transform(2, 0, 0), Animation(1148), Animation(1148), Direction.WEST, 13).endAnimation = Animation.RESET + } + return@on true + } + // Similar to RockClimbShortcut.kt + on(Scenery.ROCKS_6672, SCENERY, "climb") { player, _ -> + if (player.location.x > 3239) { + sendMessage(player, "You could climb down here, but it is too uneven to climb up.") + } else { + ForceMovement.run(player, player.location, Location.create(player.location).transform(2, 0, 0), Animation(1148), Animation(1148), Direction.WEST, 13).endAnimation = Animation.RESET + } + return@on true + } + + // Please note: part of this is already done in craftBullseyeLantern() except for the swapping out which is here. + onUseWith(ITEM, Items.BULLSEYE_LANTERN_4548, Items.SAPPHIRE_1607) { player, used, with -> + sendMessage(player, "You swap the lantern's lens for a sapphire.") + if(removeItem(player, with) && removeItem(player, used)) { + addItemOrDrop(player, Items.SAPPHIRE_LANTERN_4701) + addItemOrDrop(player, Items.LANTERN_LENS_4542) + } + return@onUseWith true + } + onUseWith(ITEM, Items.SAPPHIRE_LANTERN_4701, Items.LANTERN_LENS_4542) { player, used, with -> + sendMessage(player, "You swap the lantern's sapphire for a lens.") + if(removeItem(player, with) && removeItem(player, used)) { + addItemOrDrop(player, Items.BULLSEYE_LANTERN_4548) + addItemOrDrop(player, Items.SAPPHIRE_1607) + } + return@onUseWith true + } + onUseWith(ITEM, Items.BULLSEYE_LANTERN_4549, Items.SAPPHIRE_1607) { player, used, with -> + sendMessage(player, "The lantern is too hot to do that while it is lit.") + return@onUseWith true + } + onUseWith(ITEM, Items.SAPPHIRE_LANTERN_4702, Items.LANTERN_LENS_4542) { player, used, with -> + sendMessage(player, "The lantern is too hot to do that while it is lit.") + return@onUseWith true + } + + // MAGIC_STONE are set in MiningNode, but I can't change the messages without screwing up + // When examining ore: sendMessage(player, "This rock contains a magical kind of stone.") + // When mining: sendMessage(player, "You manage to mine some stone.") + // If you have stone in your inventory: sendMessage(player, "You have already mined some stone. You don't need any more.") + + // Note: The construction MAGIC_STONE is MAGIC_STONE_8788 NOT MAGIC_STONE_4703 WHICH IS FOR TEARS OF GUTHIX + onUseWith(ITEM, Items.MAGIC_STONE_4703, Items.CHISEL_1755) { player, used, with -> + sendMessage(player, "You make a stone bowl.") + if(removeItem(player, used)) { + addItemOrDrop(player, Items.STONE_BOWL_4704) + } + return@onUseWith true + } + + onUseWith(NPC, Items.SAPPHIRE_LANTERN_4702, NPCs.LIGHT_CREATURE_2021) { player, used, with -> + if (hasRequirement(player, Quests.WHILE_GUTHIX_SLEEPS)) { + // Options when you have WGS - B6KHH7AQc2Q + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> interpreter!!.sendOptions("Select an Option", "Across the Chasm.", "Into the Chasm.").also { stage++ } + 1 -> when(buttonID){ + 1 -> { + crossTheChasm(player, with as NPC) + end() + } + 2 -> { + // This was old. + player.lock(2) + player.teleport(Location.create(2538, 5881, 0)) + end() + } + } + } + } + }) + } else { + crossTheChasm(player, with as NPC) + } + return@onUseWith true + } + + } + + override fun defineDestinationOverrides() { + setDest(IntType.NPC, intArrayOf(NPCs.LIGHT_CREATURE_2021),"use"){ player, node -> + return@setDest player.location + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt new file mode 100644 index 000000000..ab27cbd71 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt @@ -0,0 +1,402 @@ +package content.region.misthalin.lumbridge.quest.tearsofguthix + +import content.data.Quests +import core.api.* +import core.game.component.Component +import core.game.event.EventHook +import core.game.event.TickEvent +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +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.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +/** + * // ::setvarbit 454 (varp 449 7-11) 0-10 where its just time bar length + * // ::setvarbit 455 (varp 449 12-20) X where x is number of points he gets + * + * anim 2040 - Hold bowl + * anim 2041 - Walk with ToG Bowl + * anim 2042 - Run with ToG Bowl + * anim 2043 - Fill ToG bowl + * anim 2044 - Finish filling from ToG + * anim 2045 - Drink from bowl + * + */ +class TearsOfGuthixMinigame : InteractionListener, EventHook, MapArea { + companion object { + + const val varbitTimeBar = 454 + const val varbitPoints = 455 + const val attributeTicksRemaining = "minigame:tearsofguthix-ticksremaining" + const val attributeTearsCollected = "minigame:tearsofguthix-tearscollected" + const val attributeIsCollecting = "minigame:tearsofguthix-iscollecting" + + // In order specified by RS + private val rewardArray = arrayOf( + Skills.COOKING, + Skills.CRAFTING, + Skills.FIREMAKING, + Skills.FISHING, + Skills.MAGIC, + Skills.MINING, + Skills.PRAYER, + Skills.RANGE, + Skills.RUNECRAFTING, + Skills.SMITHING, + Skills.WOODCUTTING, + Skills.AGILITY, + Skills.HERBLORE, + Skills.FLETCHING, + Skills.THIEVING, + Skills.SLAYER, + Skills.ATTACK, + Skills.DEFENCE, + Skills.STRENGTH, + Skills.HITPOINTS, + Skills.FARMING, + Skills.CONSTRUCTION, + Skills.HUNTER, + Skills.SUMMONING, // This isn't but lmao. + ) + // In order specified by RS + val rewardText = arrayOf( + "You have a brief urge to cook some food.", + "Your fingers feel nimble and suited to delicate work.", + "You have a brief urge to set light to something.", + "You gain a deep understanding of the creatures of the sea.", + "You feel the power of the runes surging through you. ", + "You gain a deep understanding of the stones of the earth.", + "You suddenly feel very close to the gods.", + "Your aim improves.", + "You gain a deep understanding of runes.", + "You gain a deep understanding of all types of metal.", + "You gain a deep understanding of the trees in the wood.", + "You feel very nimble.", + "You gain a deep understanding of all kinds of strange plants.", + "You gain a deep understanding of wooden sticks.", + "You feel your respect for others' property slipping away.", + "You gain a deep understanding of many strange creatures.", + "You feel a brief surge of aggression.", + "You feel more able to defend yourself.", + "Your muscles bulge.", + "You feel very healthy.", + "You gain a deep understanding of the cycles of nature.", + "You feel homesick.", + "You briefly experience the joy of the hunt.", + "You feel at one with nature.", + ) + + /** Calculates the XP to reward. */ + fun rewardTears(player: Player) { + val lowestSkill = rewardArray.reduce{ acc, curr -> + // If you don't have construction, you cannot earn xp on it. + if (curr == Skills.CONSTRUCTION && !hasHouse(player)) { + acc + } + // If you don't have Druidic Ritual completed, you cannot earn xp on it. + else if (curr == Skills.HERBLORE && !isQuestComplete(player, Quests.DRUIDIC_RITUAL)) { + acc + } + // If you don't have Rune Mysteries, you cannot earn xp on it. + else if (curr == Skills.RUNECRAFTING && !isQuestComplete(player, Quests.RUNE_MYSTERIES)) { + acc + } + // If you don't have Wolf Whistle, you cannot earn xp on it. + else if (curr == Skills.SUMMONING && !isQuestComplete(player, Quests.WOLF_WHISTLE)) { + acc + } + else if (player.skills.getExperience(acc) <= player.skills.getExperience(curr)) { + acc + } else { + curr + } + } + + var perTearXP = 60.0 // Caps at level 30, giving 60 per XP. + if (getStatLevel(player, lowestSkill) < 30) { + perTearXP = (getStatLevel(player, lowestSkill) - 1) * 1.724137 // From 50/29 + perTearXP += 10 + } + + sendMessage(player, rewardText[rewardArray.indexOf(lowestSkill)]) + + val tearsCollected = getAttribute(player, attributeTearsCollected, 0) + rewardXP(player, lowestSkill, perTearXP * tearsCollected) + } + + /** Opens interface, walks the player to the center and start game. */ + fun startGame(player: Player) { + lock(player, 15) + // Opens the Tears of Guthix Interface in the tab. + player.interfaceManager.openSingleTab(Component(Components.TOG_WATER_BOWL_4)) + // Sets up the interface varbits. + setAttribute(player, attributeTicksRemaining, getQuestPoints(player) + 15) // 15 to offset the stupid walking. + setAttribute(player, attributeTearsCollected, 0) + setVarbit(player, varbitTimeBar, 10) + setVarbit(player,varbitPoints, 0) + + // Forces the player to hold a bowl and animate accordingly. + replaceSlot(player, EquipmentSlot.WEAPON.ordinal, Item(Items.STONE_BOWL_4704), null, Container.EQUIPMENT) + // Change the player's SET ANIMATIONS to the bowl holding set. Found using ::ranim + player.appearance.setAnimations(Animation(357)) // THIS WAS TRIAL AND ERROR AND WAS FUCKING HARD TO FIND + player.appearance.sync() + + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + val distance = player.location.getDistance(Location(3251, 9516, 2)).toInt() + 1// Per tick? + forceMove(player, player.location, Location(3251, 9516, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 1 -> { + face(player, Location(3252, 9516, 2)) + val junaScenery = getScenery(Location(3252, 9516, 2)) + if (junaScenery != null) { + animateScenery(junaScenery, 2055) + } + return@queueScript delayScript(player, 2) + } + 2 -> { + val distance = player.location.getDistance(Location(3253, 9516, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3253, 9516, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 3 -> { + face(player, Location(3253, 9517, 2)) + return@queueScript delayScript(player, 2) + } + 4 -> { + val distance = player.location.getDistance(Location(3253, 9517, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3253, 9517, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 5 -> { + face(player, Location(3257, 9517, 2)) + return@queueScript delayScript(player, 2) + } + 6 -> { + val distance = player.location.getDistance(Location(3257, 9517, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3257, 9517, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 7 -> { + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + fun endGame(player: Player) { + lock(player, 22) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "Your time in the cave is up.") + val distance = player.location.getDistance(Location(3253, 9517, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3253, 9517, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 1 -> { + face(player, Location(3253, 9516, 2)) + return@queueScript delayScript(player, 2) + } + 2 -> { + val distance = player.location.getDistance(Location(3253, 9516, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3253, 9516, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 3 -> { + face(player, Location(3251, 9516, 2)) + val junaScenery = getScenery(Location(3252, 9516, 2)) + if (junaScenery != null) { + animateScenery(junaScenery, 2055) + } + return@queueScript delayScript(player, 2) + } + 4 -> { + val distance = player.location.getDistance(Location(3251, 9516, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3251, 9516, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 5 -> { + sendMessage(player, "You drink the liquid...") + animate(player, 2045) + return@queueScript delayScript(player, 3) + } + 6 -> { + rewardTears(player) + setAttribute(player, TearsOfGuthix.attributePreviousDate, System.currentTimeMillis()) + setAttribute(player, TearsOfGuthix.attributePreviousXPAmount, player.skills.totalXp) + setAttribute(player, TearsOfGuthix.attributePreviousQuestPoints, getQuestPoints(player)) + removeAttribute(player, attributeTearsCollected) + if (player.interfaceManager.singleTab?.id == 4) { + player.interfaceManager.closeSingleTab() + } + player.interfaceManager.restoreTabs() + removeItem(player, Items.STONE_BOWL_4704, Container.EQUIPMENT) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + } + + override fun defineListeners() { + + on(Scenery.WEEPING_WALL_6660, SCENERY, "collect-from") { player, node -> + animate(player, 2043) + val index = TearsOfGuthixGlobalTick.allWalls.indexOf(node.location) + setAttribute(player, attributeIsCollecting, index) + return@on true + } + + } + + // Timer step per tick while you are in the minigame. + override fun process(entity: Entity, event: TickEvent) { + if (entity is Player) { + if (getAttribute(entity, attributeTicksRemaining, -1) > 0) { + setAttribute(entity, attributeTicksRemaining, getAttribute(entity, attributeTicksRemaining, 0) - 1) + setVarbit(entity, varbitTimeBar, (getAttribute(entity, attributeTicksRemaining, 0) * 10 / getQuestPoints(entity)), false) + if (getAttribute(entity, attributeIsCollecting, 0) != 0) { + val currentArrayIndex = getAttribute(entity, attributeIsCollecting, 0) + val currentTearState = TearsOfGuthixGlobalTick.globalWallState[currentArrayIndex] + if (currentTearState == 1) { + setAttribute(entity, attributeTearsCollected, getAttribute(entity, attributeTearsCollected, 0) + 1) + } else if (currentTearState == 2 && getAttribute(entity, attributeTearsCollected, 0) > 0){ + setAttribute(entity, attributeTearsCollected, getAttribute(entity, attributeTearsCollected, 0) - 1) + } + setVarbit(entity, varbitPoints, getAttribute(entity, attributeTearsCollected, 0)) + } + } else if (getAttribute(entity, attributeTicksRemaining, -1) == 0) { + removeAttribute(entity, attributeTicksRemaining) + endGame(entity) + } + } + } + + + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(3253, 9513, 3262, 9522, 2)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + if (getAttribute(entity, attributeTicksRemaining, 0) <= 0) { + removeItem(entity, Items.STONE_BOWL_4704, Container.EQUIPMENT) + teleport(entity, Location(3251, 9516, 2)) + } else { + entity.hook(Event.Tick, this) + } + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.unhook(this) + if (logout) { + removeItem(entity, Items.STONE_BOWL_4704, Container.EQUIPMENT) + removeAttribute(entity, attributeTearsCollected) + removeAttribute(entity, attributeTicksRemaining) + teleport(entity, Location(3251, 9516, 2)) + } + } + } + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + entity.hook(Event.Tick, this) + setAttribute(entity, attributeIsCollecting, 0) // If you move, you ain't collecting + } + } +} + +/** + * Global Tick class to randomize the walls consistently for everyone. + */ +class TearsOfGuthixGlobalTick : TickListener { + + companion object { + var ticks = 0 + var globalWallState = intArrayOf(0, 0, 2, 1, 2, 1, 0, 0, 2, 1) + val allWalls = arrayOf( + // Blank + Location(0, 0, 0), + // Left Walls + Location(3258, 9520, 2), + Location(3261, 9516, 2), + Location(3261, 9518, 2), + Location(3257, 9514, 2), + Location(3259, 9514, 2), + // Right Walls + Location(3257, 9520, 2), + Location(3259, 9520, 2), + Location(3261, 9517, 2), + Location(3258, 9514, 2), + ) + } + + override fun tick() { + // Do this every 10 ticks. + if (ticks++ > 10) { ticks = 0 } else { return } + // Shuffle the walls + val wallStates = intArrayOf(0, 0, 0, 1, 1, 1, 2, 2, 2) // 0 is absent, 1 is blue, 2 is green + wallStates.shuffle() + globalWallState = intArrayOf(0) + wallStates + + /* + * Explanation: The walls are layered sceneries, which makes it rabidly fucked to change them. + * What I did was to add the tears scenery first (essentially overriding the tears scenery), + * then add the WEEPING_WALL_6660 right after it so that the interactions are still there. + * this is how a layer is like: + * 1 - WEEPING_WALL_6660 - No model, but holds the option "collect-from" + * 2 - BLUE/GREEN/ABSENT - Model of the blue/green/absent waterfall. + * 3 - WEEPING_WALL_6664 - The actual model, but not interactive. + * 6661 - 6664 is left side, 6665 to 6668 is right side + */ + wallStates.forEachIndexed { index, state -> + val scenery = getScenery(allWalls[index + 1])!! + val newSceneryId = if (state == 2) { + if (index + 1 <= 5) { + Scenery.GREEN_TEARS_6662 + } else { + Scenery.GREEN_TEARS_6666 + } + } else if (state == 1) { + if (index + 1 <= 5) { + Scenery.BLUE_TEARS_6661 + } else { + Scenery.BLUE_TEARS_6665 + } + } else { + if (index + 1 <= 5) { + Scenery.ABSENCE_OF_TEARS_6663 + } else { + Scenery.ABSENCE_OF_TEARS_6667 + } + } + addScenery(core.game.node.scenery.Scenery( + newSceneryId, + scenery.location, + 4, + scenery.rotation + )) + addScenery(core.game.node.scenery.Scenery(Scenery.WEEPING_WALL_6660, scenery.location, 0, scenery.rotation)) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java index c301fc637..acf0ca8c2 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.link.diary.DiaryType; import core.plugin.Initializable; import core.game.node.entity.player.Player; import core.game.dialogue.DialoguePlugin; +import content.data.Quests; /** @@ -42,7 +43,7 @@ public final class FatherAereckDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - int questStage = player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player); + int questStage = player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player); if (questStage == 10) { npc("Have you got rid of the ghost yet?"); stage = 520; @@ -72,7 +73,7 @@ public final class FatherAereckDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getQuestRepository().isComplete("The Restless Ghost")) { + if (player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST)) { interpreter.sendOptions("What would you like to say?", "Can you change my gravestone now?", "Who's Saradomin?", "Nice place you've got here."); stage = 1; } else { @@ -128,7 +129,7 @@ public final class FatherAereckDialogue extends DialoguePlugin { end(); break; case 510: - player.getQuestRepository().getQuest(RestlessGhost.NAME).start(player); + player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).start(player); player.getQuestRepository().syncronizeTab(player); npc("Thank you. The problem is, there is a ghost in the", "church graveyard. I would like you to get rid of it."); stage = 511; diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java index abedfb9a0..1097d1d5e 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java @@ -1,5 +1,6 @@ package content.region.misthalin.lumbridge.quest.therestlessghost; +import content.data.Quests; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; @@ -49,13 +50,13 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player) == 0) { + if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 0) { options("Well, that's friendly.", "I've come to respossess your house."); stage = 1; - } else if (player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player) == 10) { + } else if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 10) { options("Well, that's friendly.", "I've come to respossess your house.", "Father Aereck sent me to talk to you."); stage = 500; - } else if (player.getGameAttributes().getAttributes().containsKey("restless-ghost:urhney") || player.getQuestRepository().isComplete(RestlessGhost.NAME)) { + } else if (player.getGameAttributes().getAttributes().containsKey("restless-ghost:urhney") || player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST)) { options("Well, that's friendly.", "I've come to respossess your house.", "I've lost the Amulet of Ghostspeak."); stage = 514; } @@ -112,7 +113,7 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { } interpreter.sendItemMessage(552, "Father Urhney hands you an amulet."); player.getInventory().add(new Item(552, 1)); - player.getQuestRepository().getQuest(RestlessGhost.NAME).setStage(player, 20); + player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 20); player.getGameAttributes().setAttribute("/save:restless-ghost:urhney", true); stage = 509; break; diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java index ccf60a649..11fd553b1 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java @@ -9,6 +9,7 @@ import core.plugin.Initializable; import core.plugin.ClassScanner; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** @@ -18,22 +19,16 @@ import static core.api.ContentAPIKt.*; */ @Initializable public class RestlessGhost extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "The Restless Ghost"; - /** * The ghost speak amulet. */ public static final Item AMULET = new Item(552); - + /** * Constructs a new {@Code RestlessGhost} {@Code Object} */ public RestlessGhost() { - super(NAME, 25, 24, 1, 107, 0, 4, 5); + super(Quests.THE_RESTLESS_GHOST, 25, 24, 1, 107, 0, 4, 5); } @Override diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java index b282450cd..41b9f046b 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java @@ -1,5 +1,6 @@ package content.region.misthalin.lumbridge.quest.therestlessghost; +import content.data.Quests; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; @@ -43,17 +44,17 @@ public class RestlessGhostDialogue extends DialoguePlugin { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wooo wooo wooooo!"); stage = 1; } else { - if (player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player) == 20) { + if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 20) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Not very good actually."); stage = 500; break; } - if (player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player) == 30) { + if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 30) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How are you doing finding my skull?"); stage = 520; break; } - if (player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player) == 40) { + if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 40) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How are you doing finding my skull?"); stage = 550; break; @@ -111,7 +112,7 @@ public class RestlessGhostDialogue extends DialoguePlugin { break; case 511: interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok. I will try and get the skull back for you, then you", "can rest in peace."); - player.getQuestRepository().getQuest(RestlessGhost.NAME).setStage(player, 30); + player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 30); stage = 512; break; case 512: diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java index 15e31d74a..d19f260a1 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java @@ -1,5 +1,6 @@ package content.region.misthalin.lumbridge.quest.therestlessghost; +import content.data.Quests; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -106,11 +107,11 @@ public final class RestlessGhostPlugin extends OptionHandler { searchAltar(player, object); break; case 15051: - if (!player.getQuestRepository().isComplete(RestlessGhost.NAME) && !player.getBank().containsItem(SKULL) && !player.getInventory().containsItem(SKULL)) { + if (!player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST) && !player.getBank().containsItem(SKULL) && !player.getInventory().containsItem(SKULL)) { player.getInventory().add(SKULL); player.getPacketDispatch().sendMessage("You find another skull."); } - player.getQuestRepository().getQuest(RestlessGhost.NAME).setStage(player, 40); + player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 40); break; case 2145: toggleCoffin(player, object); @@ -132,7 +133,7 @@ public final class RestlessGhostPlugin extends OptionHandler { player.animate(open ? OPEN_ANIM : CLOSE_ANIM); SceneryBuilder.replace(object, object.transform(open ? 15061 : 2145)); player.getPacketDispatch().sendMessage("You " + (open ? "open" : "close") + " the coffin."); - if (open && !player.getQuestRepository().isComplete(RestlessGhost.NAME)) { + if (open && !player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST)) { sendGhost(); } } @@ -161,7 +162,7 @@ public final class RestlessGhostPlugin extends OptionHandler { */ private void searchAltar(final Player player, final Scenery object) { final boolean hasSkull = object.getId() == 15051; - if (player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player) != 30) { + if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) != 30) { player.getPacketDispatch().sendMessage("You search the altar and find nothing."); return; } @@ -170,7 +171,7 @@ public final class RestlessGhostPlugin extends OptionHandler { GroundItemManager.create(SKULL, player); } setVarp(player, 728, 5, true); - player.getQuestRepository().getQuest(RestlessGhost.NAME).setStage(player, 40); + player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 40); player.getPacketDispatch().sendMessage("The skeleton in the corner suddenly comes to life!"); sendSkeleton(player); } @@ -259,7 +260,7 @@ public final class RestlessGhostPlugin extends OptionHandler { if (this.getRespawnTick() > GameWorld.getTicks()) { return true; } - return player.getQuestRepository().isComplete(RestlessGhost.NAME) || (pl != null && player != pl); + return player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST) || (pl != null && player != pl); } @Override diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java index 58bb7d0b1..4a899c64a 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java @@ -1,5 +1,6 @@ package content.region.misthalin.lumbridge.quest.therestlessghost; +import content.data.Quests; import core.api.Container; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -42,7 +43,7 @@ public final class RestlessGhostSkull extends UseWithHandler { } if (removeItem(event.getPlayer(), Items.SKULL_964, Container.INVENTORY)) { event.getPlayer().getPacketDispatch().sendMessage("You put the skull in the coffin."); - event.getPlayer().getQuestRepository().getQuest(RestlessGhost.NAME).finish(event.getPlayer()); + event.getPlayer().getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).finish(event.getPlayer()); } return true; } diff --git a/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBane.java b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBane.java index 3cc8a184c..91f1f33ef 100644 --- a/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBane.java +++ b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBane.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.content.quest.members.asoulsbane.SoulsBaneLaunaDialogue; import core.plugin.PluginManager; +import content.data.Quests; */ /** @@ -14,10 +15,8 @@ import core.plugin.PluginManager; @Initializable public class ASoulsBane extends Quest { - public static final String NAME = "A Soul's Bane"; - public ASoulsBane() { - super(NAME, 115, 114, 1, 709, 0, 1, 1261); + super(Quests.A_SOULS_BANE, 115, 114, 1, 709, 0, 1, 1261); } // config 710 does a lot of shit diff --git a/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBaneListeners.kt b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBaneListeners.kt index 1813665e6..256c7e17d 100644 --- a/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBaneListeners.kt +++ b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBaneListeners.kt @@ -4,6 +4,7 @@ import core.api.* import core.game.interaction.InteractionListener import core.game.world.map.Location import org.rs09.consts.Scenery +import content.data.Quests // Temporary access since the monsters in there drop nothing. class ASoulsBaneListener : InteractionListener { @@ -13,7 +14,7 @@ class ASoulsBaneListener : InteractionListener { } override fun defineListeners() { on(RIFT_IDS, SCENERY, "enter") { player, _ -> - if (hasRequirement(player, "A Soul's Bane")) { + if (hasRequirement(player, Quests.A_SOULS_BANE)) { teleport(player, Location(3297, 9824, 0)) } return@on true diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/DoorPerilDialogue.java b/Server/src/main/content/region/misthalin/quest/priestinperil/DoorPerilDialogue.java index 9731861ef..c21b288a8 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/DoorPerilDialogue.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/DoorPerilDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.scenery.Scenery; +import content.data.Quests; /** * Represents the door peril dialogue. @@ -46,7 +47,7 @@ public final class DoorPerilDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { door = (Scenery) args[0]; - Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); + Quest quest = player.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); if (quest.getStage(player) == 10) { interpreter.sendDialogue("You knock at the door...You hear a voice from inside.", "Who are you, and what do you want?"); stage = 0; @@ -106,7 +107,7 @@ public final class DoorPerilDialogue extends DialoguePlugin { stage = 10; break; case 10: - Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); + Quest quest = player.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); quest.setStage(player, 11); end(); break; diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelDialogue.java b/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelDialogue.java index 5f1dc8b97..4d06a70d8 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelDialogue.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelDialogue.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for the drezel npc. @@ -49,7 +50,7 @@ public final class DrezelDialogue extends DialoguePlugin { npc = (NPC) args[0]; npc.setName("Drezel"); NPCDefinition.forId(getIds()[0]).setName("Drezel"); - Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); + Quest quest = player.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); if (quest.getStage(player) == 13) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello."); stage = 0; @@ -68,7 +69,7 @@ public final class DrezelDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); + final Quest quest = player.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); switch (stage) { case 0: interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh! You do not appear to be on of those Zamorakians", "who imprisoned me here! Who are you and why are", "you here?"); @@ -369,7 +370,7 @@ public final class DrezelDialogue extends DialoguePlugin { stage = 802; break; case 802: - Quest quests = player.getQuestRepository().getQuest("Priest in Peril"); + Quest quests = player.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); quests.setStage(player, 17); end(); break; diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelMonumentDialogue.java b/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelMonumentDialogue.java index 20072af6c..06c77b5cc 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelMonumentDialogue.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelMonumentDialogue.java @@ -13,6 +13,7 @@ import org.rs09.consts.NPCs; import content.region.morytania.quest.naturespirit.NSDrezelDialogue; import static core.tools.DialogueConstKt.END_DIALOGUE; +import content.data.Quests; /** * Represents the dialogue plugin used for the drezel monument. @@ -52,7 +53,7 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); + Quest quest = player.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); if (quest.getStage(player) == 17) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah, " + player.getUsername() + ". I see you finally made it down here.", "Things are worse than I feared. I'm not sure if I will", "be able to repair the damage."); stage = 900; @@ -79,7 +80,7 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { stage = 420; }*/ - quest = player.getQuestRepository().getQuest("Nature Spirit"); + quest = player.getQuestRepository().getQuest(Quests.NATURE_SPIRIT); if(quest.getStage(player) <= 5){ interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings again adventurer, How go your travels in", "Morytania? Is it as evil as I have heard?"); @@ -95,7 +96,7 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); + final Quest quest = player.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); switch (stage) { case 400: interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah, " + player.getUsername() + ". For all the assistance you have given", "both myself and Misthalin in your actions, I cannot let", "you pass without warning you."); diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/KingRoaldPIPDialogue.kt b/Server/src/main/content/region/misthalin/quest/priestinperil/KingRoaldPIPDialogue.kt index aba02ad76..d8f8f35d8 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/KingRoaldPIPDialogue.kt +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/KingRoaldPIPDialogue.kt @@ -3,6 +3,7 @@ package content.region.misthalin.quest.priestinperil import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests class KingRoaldPIPDialogue(val questStage: Int) : DialogueFile() { @@ -25,7 +26,7 @@ class KingRoaldPIPDialogue(val questStage: Int) : DialogueFile() { 8 -> when(buttonID){ 1 -> { player("Sure. I don't have anything better to do right now.") - player!!.questRepository.getQuest("Priest in Peril").start(player) + player!!.questRepository.getQuest(Quests.PRIEST_IN_PERIL).start(player) stage++ } 2 -> { @@ -58,7 +59,7 @@ class KingRoaldPIPDialogue(val questStage: Int) : DialogueFile() { 9 -> npc("You get back there and do whatever is necessary to", "safeguard my kingdom from attack, or I will see you", "beheaded for high treason!").also { stage++ } 10 -> { player("Y-yes your Highness.") - player!!.questRepository.getQuest("Priest in Peril").setStage(player,13) + player!!.questRepository.getQuest(Quests.PRIEST_IN_PERIL).setStage(player,13) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/MonkOfZamorakNPC.java b/Server/src/main/content/region/misthalin/quest/priestinperil/MonkOfZamorakNPC.java index c7811d7cd..9ca6c99be 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/MonkOfZamorakNPC.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/MonkOfZamorakNPC.java @@ -8,6 +8,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.Initializable; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the monk of zamorak npc. @@ -57,7 +58,7 @@ public final class MonkOfZamorakNPC extends AbstractNPC { public void finalizeDeath(final Entity killer) { super.finalizeDeath(killer); final Player p = ((Player) killer); - final Quest quest = p.getQuestRepository().getQuest("Priest in Peril"); + final Quest quest = p.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); if (quest.isStarted(p)) { GroundItemManager.create(GOLDEN_KEY, getLocation(), p); } diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPeril.java b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPeril.java index b123278b8..c14d08d0d 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPeril.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPeril.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the Quest priest in peril. @@ -18,7 +19,7 @@ public class PriestInPeril extends Quest { * Constructs a new {@code PriestInPeril} {@code Object}. */ public PriestInPeril() { - super("Priest in Peril", 99, 98, 1, 302, 0, 1, 100); + super(Quests.PRIEST_IN_PERIL, 99, 98, 1, 302, 0, 1, 100); } @Override diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilOptionPlugin.java b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilOptionPlugin.java index 4e0c6ef3d..538afabc8 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilOptionPlugin.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilOptionPlugin.java @@ -15,6 +15,7 @@ import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; import org.rs09.consts.NPCs; +import content.data.Quests; /** * Represents the quest node plugin handler. @@ -22,6 +23,7 @@ import org.rs09.consts.NPCs; */ @Initializable public class PriestInPerilOptionPlugin extends OptionHandler { + /** * (non-Javadoc) * @see Plugin#newInstance(Object) @@ -92,7 +94,7 @@ public class PriestInPerilOptionPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); + final Quest quest = player.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); int id = node instanceof Scenery ? ((Scenery) node).getId() : ((NPC) node).getId(); switch (option) { case "study": @@ -105,6 +107,8 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } else { item = 2347; } + player.getPacketDispatch().sendItemZoomOnInterface(item, 512, 272, 4); + player.getPacketDispatch().sendAngleOnInterface(272, 4, 512, 128, 0); message = "Saradomin is the hammer that crushes evil everywhere."; } if (id == 3498) { @@ -113,6 +117,8 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } else { item = 1733; } + player.getPacketDispatch().sendItemZoomOnInterface(item, 512, 272, 4); + player.getPacketDispatch().sendAngleOnInterface(272, 4, 512, 128, 0); message = "Saradomin is the needle that binds our lives together."; } if (id == 3495) { @@ -121,6 +127,8 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } else { item = 1931; } + player.getPacketDispatch().sendItemZoomOnInterface(item, 512, 272, 4); + player.getPacketDispatch().sendAngleOnInterface(272, 4, 512, 128, 0); message = "Saradomin is the vessel that keeps our lives from harm."; } if (id == 3497) { @@ -129,6 +137,8 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } else { item = 314; } + player.getPacketDispatch().sendItemZoomOnInterface(item, 512, 272, 4); + player.getPacketDispatch().sendAngleOnInterface(272, 4, 512, 128, 0); message = "Saradomin is the delicate touch that brushes us with love."; } if (id == 3494) { @@ -137,6 +147,8 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } else { item = 36; } + player.getPacketDispatch().sendItemZoomOnInterface(item, 512, 272, 4); + player.getPacketDispatch().sendAngleOnInterface(272, 4, 512, 256, 0); message = "Saradomin is the light that shines throughout our lives."; } if (id == 3499) { @@ -145,6 +157,8 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } else { item = 2944; } + player.getPacketDispatch().sendItemZoomOnInterface(item, 512, 272, 4); + player.getPacketDispatch().sendAngleOnInterface(272, 4, 512, 256, 0); message = "Saradomin is the key that unlocks the mysteries of life."; } if (id == 3493) { @@ -153,10 +167,13 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } else { item = 590; } + player.getPacketDispatch().sendItemZoomOnInterface(item, 320, 272, 4); + player.getPacketDispatch().sendAngleOnInterface(272, 4, 320, 256, 0); message = "Saradomin is the spark that lights the fire in our hearts."; } player.getPacketDispatch().sendString(message, 272, 17); - player.getPacketDispatch().sendItemZoomOnInterface(item, 175, 272, 4); + // In SD, this is fine. in HD, this gets clipped when you zoom out too much or zoom in too much. + //player.getPacketDispatch().sendItemZoomOnInterface(item, 175, 272, 4); break; case "take-from": player.getImpactHandler().handleImpact(player, 2, CombatStyle.MELEE); @@ -178,7 +195,7 @@ public class PriestInPerilOptionPlugin extends OptionHandler { break; case 3443: /** the barrier. */ - if (!player.getQuestRepository().isComplete("Priest in Peril")) { + if (!player.getQuestRepository().isComplete(Quests.PRIEST_IN_PERIL)) { player.getPacketDispatch().sendMessage("A magic force prevents you from passing through."); } else { player.getProperties().setTeleportLocation(Location.create(3425, 3485, 0)); diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilUseListener.kt b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilUseListener.kt index dfc4e05fc..61a36a6c8 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilUseListener.kt +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilUseListener.kt @@ -7,6 +7,7 @@ import org.rs09.consts.NPCs import org.rs09.consts.Scenery import core.game.interaction.IntType import core.game.interaction.InteractionListener +import content.data.Quests /** * Listener for Priest in Peril usage interactions @@ -76,7 +77,12 @@ class PriestInPerilUseListener : InteractionListener { } onUseWith(IntType.SCENERY, Items.GOLDEN_KEY_2944, Scenery.MONUMENT_3499) { player, used, _ -> - if (!getAttribute(player, "priest_in_peril:key", false) && removeItem(player, used)) { + // See GL #2112 and sources therein for why we do it this way + val hasNeverGrabbedKey = !getAttribute(player, "priest_in_peril:key", false) + val needsKeyForQuestStage = getQuestStage(player, Quests.PRIEST_IN_PERIL) <= 15 + val hasTotallyLostKey = !hasAnItem(player, Items.IRON_KEY_2945, true).exists() + val giveNewKey = hasNeverGrabbedKey || (needsKeyForQuestStage && hasTotallyLostKey) + if (giveNewKey && removeItem(player, used)) { addItem(player, Items.IRON_KEY_2945) sendMessage(player, "You swap the golden key for the iron key.") setAttribute(player, "/save:priest_in_peril:key", true) @@ -101,7 +107,7 @@ class PriestInPerilUseListener : InteractionListener { return@onUseWith false } - setQuestStage(player, "Priest in Peril", 15) + setQuestStage(player, Quests.PRIEST_IN_PERIL, 15) sendMessage(player, "You have unlocked the cell door.") val npc = core.game.node.entity.npc.NPC.create(NPCs.DREZEL_7690, player.location) @@ -117,7 +123,7 @@ class PriestInPerilUseListener : InteractionListener { } addItem(player, Items.BUCKET_1925) - setQuestStage(player, "Priest in Peril", 16) + setQuestStage(player, Quests.PRIEST_IN_PERIL, 16) sendMessage(player, "You pour the blessed water over the coffin...") return@onUseWith true diff --git a/Server/src/main/content/region/misthalin/quest/priestinperil/TempleGuardianNPC.java b/Server/src/main/content/region/misthalin/quest/priestinperil/TempleGuardianNPC.java index c7bc48ebf..23dcd52c7 100644 --- a/Server/src/main/content/region/misthalin/quest/priestinperil/TempleGuardianNPC.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/TempleGuardianNPC.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.world.map.Location; +import content.data.Quests; /** * Handles the temple guardian npc. @@ -49,7 +50,7 @@ public class TempleGuardianNPC extends AbstractNPC { public void finalizeDeath(final Entity killer) { super.finalizeDeath(killer); final Player p = ((Player) killer); - final Quest quest = p.getQuestRepository().getQuest("Priest in Peril"); + final Quest quest = p.getQuestRepository().getQuest(Quests.PRIEST_IN_PERIL); if (quest.getStage(p) == 11) { quest.setStage(p, 12); } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BatBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BatBehavior.kt index cd3590531..932997b5f 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BatBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BatBehavior.kt @@ -1,5 +1,6 @@ package content.region.misthalin.silvarea.quest.ragandboneman +import content.data.Quests import core.api.isQuestInProgress import core.game.node.entity.Entity import core.game.node.entity.npc.NPC @@ -20,7 +21,7 @@ class BatBehavior : NPCBehavior(*batIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Bat Wing during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.BAT_WING_7833)); } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BearBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BearBehavior.kt index 644372584..c489832bc 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BearBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BearBehavior.kt @@ -1,10 +1,10 @@ package content.region.misthalin.silvarea.quest.ragandboneman +import content.data.Quests import core.api.* import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.npc.drop.DropFrequency import core.game.node.entity.player.Player import core.game.node.item.Item import core.tools.RandomFunction @@ -27,7 +27,7 @@ class BearBehavior : NPCBehavior(*bearIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Bear Ribs during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.BEAR_RIBS_7815)); } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BigFrogBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BigFrogBehavior.kt index f3aede96b..cd4cd6c4f 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BigFrogBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BigFrogBehavior.kt @@ -1,10 +1,10 @@ package content.region.misthalin.silvarea.quest.ragandboneman +import content.data.Quests import core.api.isQuestInProgress import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.npc.drop.DropFrequency import core.game.node.entity.player.Player import core.game.node.item.Item import core.tools.RandomFunction @@ -21,7 +21,7 @@ class BigFrogBehavior : NPCBehavior(*bigFrogIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Big Frog Leg during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.BIG_FROG_LEG_7908)); } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantBatBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantBatBehavior.kt index bad83bbf6..0ffe43400 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantBatBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantBatBehavior.kt @@ -1,5 +1,6 @@ package content.region.misthalin.silvarea.quest.ragandboneman +import content.data.Quests import core.api.isQuestInProgress import core.game.node.entity.Entity import core.game.node.entity.npc.NPC @@ -29,7 +30,7 @@ class GiantBatBehavior : NPCBehavior(*giantBatIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Giant Bat Wing during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.GIANT_BAT_WING_7827)); } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantRatBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantRatBehavior.kt index ba1f06f82..b670f24de 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantRatBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantRatBehavior.kt @@ -1,10 +1,10 @@ package content.region.misthalin.silvarea.quest.ragandboneman +import content.data.Quests import core.api.isQuestInProgress import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.npc.drop.DropFrequency import core.game.node.entity.player.Player import core.game.node.item.Item import core.tools.RandomFunction @@ -35,7 +35,7 @@ class GiantRatBehavior : NPCBehavior(*giantRatIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Giant Rat Bone during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.GIANT_RAT_BONE_7824)); } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GoblinBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GoblinBehavior.kt index 168f3dcde..923b9a5ef 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GoblinBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GoblinBehavior.kt @@ -1,11 +1,10 @@ package content.region.misthalin.silvarea.quest.ragandboneman -import content.global.handlers.npc.ChromaticDragonBehavior +import content.data.Quests import core.api.isQuestInProgress import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.npc.drop.DropFrequency import core.game.node.entity.player.Player import core.game.node.item.Item import core.tools.RandomFunction @@ -179,7 +178,7 @@ class GoblinBehavior : NPCBehavior(*goblinIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Goblin Skull during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.GOBLIN_SKULL_7812)) } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/MonkeyBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/MonkeyBehavior.kt index 98c505819..2646a8676 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/MonkeyBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/MonkeyBehavior.kt @@ -1,10 +1,10 @@ package content.region.misthalin.silvarea.quest.ragandboneman +import content.data.Quests import core.api.isQuestInProgress import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.npc.drop.DropFrequency import core.game.node.entity.player.Player import core.game.node.item.Item import core.tools.RandomFunction @@ -45,7 +45,7 @@ class MonkeyBehavior : NPCBehavior(*monkeyIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Monkey Paw during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.MONKEY_PAW_7854)); } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/OddOldManDialogueFile.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/OddOldManDialogueFile.kt index 440a279b4..9351b6c1b 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/OddOldManDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/OddOldManDialogueFile.kt @@ -1,6 +1,6 @@ package content.region.misthalin.silvarea.quest.ragandboneman -import content.region.asgarnia.burthorpe.quest.deathplateau.DeathPlateau +import content.data.Quests import core.api.* import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression @@ -14,7 +14,7 @@ class OddOldManDialogueFile : DialogueFile() { // BONES_3674 is the Sack on ODD_OLD_MAN_3670 // There are probably FacialExpressions for the bone sack, but that's too much work. override fun handle(componentID: Int, buttonID: Int) { - when (getQuestStage(player!!, RagAndBoneMan.questName)) { + when (getQuestStage(player!!, Quests.RAG_AND_BONE_MAN)) { 0 -> { when (stage) { START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Can I help you with something?").also { stage++ } @@ -82,7 +82,7 @@ class OddOldManDialogueFile : DialogueFile() { 57 -> npcl(FacialExpression.FRIENDLY, "It takes a while for the vinegar to evaporate, but the bone will be nice and clean in the end.").also { stage++ } 58 -> playerl(FacialExpression.FRIENDLY, "All right, I'll be back later.").also { stage++ } 59 -> npcl(FacialExpression.FRIENDLY, "Bye!").also { - setQuestStage(player!!, RagAndBoneMan.questName, 1) + setQuestStage(player!!, Quests.RAG_AND_BONE_MAN, 1) stage = END_DIALOGUE } @@ -137,7 +137,7 @@ class OddOldManDialogueFile : DialogueFile() { 8 -> npcl(FacialExpression.FRIENDLY, "I'm always on the lookout for fresh bones, so if you see some bring them right over.").also { stage++ } 9 -> playerl(FacialExpression.FRIENDLY, "No problem, I'll be sure to bring anything you might like over if I find something.").also { stage++ } 10 -> playerl(FacialExpression.FRIENDLY, "I can't wait to see the displays once they are finished.").also { stage++ } - 11 -> finishQuest(player!!, RagAndBoneMan.questName).also { + 11 -> finishQuest(player!!, Quests.RAG_AND_BONE_MAN).also { end() } 20 -> playerl(FacialExpression.FRIENDLY, "Not at the moment. Can you just give me a run down on which bones I have left to get?").also { stage++ } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RagAndBoneMan.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RagAndBoneMan.kt index 0da5e32ec..97e5e9a35 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RagAndBoneMan.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RagAndBoneMan.kt @@ -6,6 +6,7 @@ import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * Rag and Bone Man Quest @@ -24,9 +25,8 @@ import org.rs09.consts.Items * Quest Journal 2012 https://www.youtube.com/watch?v=0I8fNTeAwA8&t=764 */ @Initializable -class RagAndBoneMan : Quest("Rag and Bone Man",100, 99, 2, 714, 0, 1, 4) { +class RagAndBoneMan : Quest(Quests.RAG_AND_BONE_MAN,100, 99, 2, 714, 0, 1, 4) { companion object { - const val questName = "Rag and Bone Man" const val attributeGoblinBone = "/save:quest:ragandboneman-goblinbonesubmit" const val attributeBearBone = "/save:quest:ragandboneman-bearbonesubmit" const val attributeBigFrogBone = "/save:quest:ragandboneman-bigfrogbonesubmit" diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RamBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RamBehavior.kt index fa54dabbe..b98630ed6 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RamBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RamBehavior.kt @@ -1,10 +1,10 @@ package content.region.misthalin.silvarea.quest.ragandboneman +import content.data.Quests import core.api.* import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.npc.drop.DropFrequency import core.game.node.entity.player.Player import core.game.node.item.Item import core.tools.RandomFunction @@ -25,7 +25,7 @@ class RamBehavior : NPCBehavior(*ramIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Ram Skull during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.RAM_SKULL_7818)); } diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/UnicornBehavior.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/UnicornBehavior.kt index 54f5ba15e..894f0261d 100644 --- a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/UnicornBehavior.kt +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/UnicornBehavior.kt @@ -1,10 +1,10 @@ package content.region.misthalin.silvarea.quest.ragandboneman +import content.data.Quests import core.api.isQuestInProgress import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.npc.drop.DropFrequency import core.game.node.entity.player.Player import core.game.node.item.Item import core.tools.RandomFunction @@ -23,7 +23,7 @@ class UnicornBehavior : NPCBehavior(*unicornIds) { override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { super.onDropTableRolled(self, killer, drops) // Drops the Unicorn Bone during Rag and Bone Man quest - if (killer is Player && isQuestInProgress(killer, RagAndBoneMan.questName, 1, 99)) { + if (killer is Player && isQuestInProgress(killer, Quests.RAG_AND_BONE_MAN, 1, 99)) { if(RandomFunction.roll(4)) { drops.add(Item(Items.UNICORN_BONE_7821)); } diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/ApothecaryDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/ApothecaryDialogue.java index 73f8b00bc..d9ca490aa 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/ApothecaryDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ApothecaryDialogue.java @@ -8,6 +8,7 @@ import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for the apothecary npc. @@ -67,12 +68,12 @@ public final class ApothecaryDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - if (player.getQuestRepository().getQuest("Romeo & Juliet").getStage(player) == 40) { + if (player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).getStage(player) == 40) { interpreter.sendDialogues(player, null, "Apothecary. Father Lawrence sent me."); stage = 500; return true; } - if (player.getQuestRepository().getQuest("Romeo & Juliet").getStage(player) == 50) { + if (player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).getStage(player) == 50) { if (!player.getInventory().contains(753, 1)) { npc("Keep searching for those Cadava berries. They're needed", "for the potion."); stage = 507; @@ -83,7 +84,7 @@ public final class ApothecaryDialogue extends DialoguePlugin { return true; } } - if (player.getQuestRepository().getQuest("Romeo & Juliet").getStage(player) == 60) { + if (player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).getStage(player) == 60) { if (!player.getInventory().contains(756, 1) && !player.getBank().contains(756, 1)) { if (player.getInventory().contains(753, 1)) { npc("Well done. You have the berries."); @@ -237,7 +238,7 @@ public final class ApothecaryDialogue extends DialoguePlugin { stage = 506; break; case 506: - player.getQuestRepository().getQuest("Romeo & Juliet").setStage(player, 50); + player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).setStage(player, 50); interpreter.sendDialogues(player, null, "Ok, thanks."); stage = 507; break; @@ -262,7 +263,7 @@ public final class ApothecaryDialogue extends DialoguePlugin { stage = 640; break; case 640: - player.getQuestRepository().getQuest("Romeo & Juliet").setStage(player, 60); + player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).setStage(player, 60); end(); break; } diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/BaraekDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/BaraekDialogue.java index 953bd1b72..ab062f4b4 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/BaraekDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/BaraekDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for the baraek npc. @@ -61,7 +62,7 @@ public final class BaraekDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Shield of Arrav"); + quest = player.getQuestRepository().getQuest(Quests.SHIELD_OF_ARRAV); switch (quest.getStage(player)) { case 30: if (!player.getInventory().containsItem(FUR)) { diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/DrHarlowDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/DrHarlowDialogue.java index a906641b0..0e14e004f 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/DrHarlowDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/DrHarlowDialogue.java @@ -8,6 +8,7 @@ import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue used for dr harlow. @@ -56,10 +57,10 @@ public final class DrHarlowDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getQuestRepository().getQuest("Vampire Slayer").getStage(player) == 10) { + if (player.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER).getStage(player) == 10) { interpreter.sendOptions("Select an Option", "No, you've had enough.", "Morgan needs your help!"); stage = 1; - } else if (player.getQuestRepository().getQuest("Vampire Slayer").getStage(player) == 20) { + } else if (player.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER).getStage(player) == 20) { if (player.getInventory().contains(1917, 1)) { interpreter.sendDialogues(player, null, "Here you go."); stage = 20; @@ -67,7 +68,7 @@ public final class DrHarlowDialogue extends DialoguePlugin { interpreter.sendDialogues(player, null, "I'll just go and buy one."); stage = 2; } - } else if (player.getQuestRepository().getQuest("Vampire Slayer").getStage(player) == 30) { + } else if (player.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER).getStage(player) == 30) { if (!player.getBank().contains(1549, 1) && !player.getInventory().contains(1549, 1)) { if (!player.getInventory().add(ITEMS[0])) { GroundItem item = new GroundItem(ITEMS[0], npc.getLocation(), player); @@ -122,7 +123,7 @@ public final class DrHarlowDialogue extends DialoguePlugin { stage = 10; break; case 10: - player.getQuestRepository().getQuest("Vampire Slayer").setStage(player, 20); + player.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER).setStage(player, 20); end(); break; case 2: @@ -131,7 +132,7 @@ public final class DrHarlowDialogue extends DialoguePlugin { case 20: if (player.getInventory().remove(ITEMS[1])) { interpreter.sendItemMessage(1917, "You give a beer to Dr Harlow."); - player.getQuestRepository().getQuest("Vampire Slayer").setStage(player, 30); + player.getQuestRepository().getQuest(Quests.VAMPIRE_SLAYER).setStage(player, 30); stage = 21; } break; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/FatherLawrenceDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/FatherLawrenceDialogue.java index 6e45fa50c..db0f198ad 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/FatherLawrenceDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/FatherLawrenceDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the father lawrence dialogue plugin. @@ -35,7 +36,7 @@ public final class FatherLawrenceDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("Romeo & Juliet"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROMEO_JULIET); if (quest.getStage(player) < 30) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh to be a father in the times of whiskey."); stage = 0; @@ -68,7 +69,7 @@ public final class FatherLawrenceDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Romeo & Juliet"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROMEO_JULIET); switch (stage) { case 0: interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I sing and I drink and I wake up in gutters."); diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudeDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudeDialogue.java index dae9474cd..9fcbd6bb2 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudeDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudeDialogue.java @@ -1,7 +1,6 @@ package content.region.misthalin.varrock.dialogue; import content.global.skill.summoning.pet.Pet; -import content.global.skill.summoning.pet.PetDetails; import core.game.container.Container; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -11,8 +10,11 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Items; -import java.util.Map; +import content.data.Quests; + +import static core.api.ContentAPIKt.*; /** * Represents the gertrude dialogue plugin. @@ -52,17 +54,16 @@ public final class GertrudeDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); - switch (quest.getStage(player)) { + switch (getQuestStage(player, Quests.GERTRUDES_CAT)) { case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, are you ok?"); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, are you OK?"); break; case 10: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Gertrude."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, Gertrude."); stage = 210; break; case 20: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Gertrude."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, Gertrude."); stage = 230; break; case 30: @@ -91,10 +92,10 @@ public final class GertrudeDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); switch (stage) { case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do I look ok? Those kids drive me crazy."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do I look OK? Those kids drive me crazy."); stage = 1; break; case 1: @@ -260,7 +261,7 @@ public final class GertrudeDialogue extends DialoguePlugin { stage = 323; break; case 323: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's ok, I like to do my bit."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's OK, I like to do my bit."); stage = 324; break; case 324: @@ -331,7 +332,7 @@ public final class GertrudeDialogue extends DialoguePlugin { case 503: boolean has = false; int[] kittens = new int[] { 1555, 1556, 1557, 1558, 1559, 1560, 7583 }; - if (player.getFamiliarManager().hasFamiliar()) { + if (player.getFamiliarManager().hasFamiliar() && player.getFamiliarManager().hasPet()) { Pet pet = (Pet) player.getFamiliarManager().getFamiliar(); for (int i : kittens) { if (pet.getItemId() == i) { @@ -362,12 +363,15 @@ public final class GertrudeDialogue extends DialoguePlugin { case 901: switch (buttonId) { case 1:// yes - if (!player.getInventory().contains(995, 100)) { - player.getPacketDispatch().sendMessage("You need a 100 coins to buy a kitten."); + boolean hasMoney = inInventory(player, Items.COINS_995, 100); + if (!hasMoney) { + player.getPacketDispatch().sendMessage("You need 100 coins to buy a kitten."); end(); break; } else { - if (player.getInventory().freeSlots() == 0) { + boolean hasExtraMoney = inInventory(player, Items.COINS_995, 101); + boolean hasSpace = freeSlots(player) > 0 || !hasExtraMoney; + if (player.getFamiliarManager().hasFamiliar() && !hasSpace) { player.getPacketDispatch().sendMessage("You don't have enough inventory space."); end(); break; @@ -382,7 +386,7 @@ public final class GertrudeDialogue extends DialoguePlugin { } break; case 903: - interpreter.sendDialogues(npc, null, "Ok then, here you go."); + interpreter.sendDialogues(npc, null, "OK then, here you go."); stage = 904; break; case 904: @@ -390,16 +394,15 @@ public final class GertrudeDialogue extends DialoguePlugin { stage = 905; break; case 905: - if (!player.getInventory().containsItem(COINS)) { - end(); - return true; - } if (player.getInventory().remove(COINS)) { interpreter.sendDialogue("Gertrude gives you another kitten."); stage = 906; final Item kitten = getKitten(); - player.getInventory().add(kitten); - player.getFamiliarManager().summon(kitten, true); + if (player.getFamiliarManager().hasFamiliar()) { + player.getInventory().add(kitten); + } else { + player.getFamiliarManager().summon(kitten, true, false); + } } break; case 906: diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudesCatDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudesCatDialogue.java index edca24e12..93d5a003e 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudesCatDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudesCatDialogue.java @@ -8,6 +8,7 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.plugin.Initializable; import core.game.world.update.flag.context.Animation; +import content.data.Quests; /** * Represents the gertrude cat dialogue plugin. @@ -53,7 +54,7 @@ public final class GertrudesCatDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); switch (stage) { case 545: end(); diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/KanelDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/KanelDialogue.java index ce33e799b..4cf81e311 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/KanelDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/KanelDialogue.java @@ -7,13 +7,11 @@ import core.plugin.Initializable; import core.game.node.entity.player.Player; /** - * Represents the dialogue plugin used for kanel. - * @author 'Vexia - * @version 1.0 + * Kanel - Child in Gertrude's House */ @Initializable public final class KanelDialogue extends DialoguePlugin { - + // https://www.youtube.com/watch?v=ANI7DaRVEbg /** * Constructs a new {@code KanelDialogue} {@code Object}. */ @@ -39,7 +37,7 @@ public final class KanelDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hello there."); stage = 0; return true; } @@ -49,15 +47,15 @@ public final class KanelDialogue extends DialoguePlugin { switch (stage) { case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hel-lo?"); + interpreter.sendDialogues(npc, FacialExpression.CHILD_THINKING, "Hel-lo?"); stage = 1; break; case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Right. Goodbye."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Right. Goodbye."); stage = 2; break; case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Bye?"); + interpreter.sendDialogues(npc, FacialExpression.CHILD_THINKING, "Bye?"); stage = 3; break; case 3: diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/KingRoaldDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/KingRoaldDialogue.kt index b4e0f7ecf..1f775acc6 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/KingRoaldDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/KingRoaldDialogue.kt @@ -11,6 +11,7 @@ import content.region.misthalin.quest.priestinperil.KingRoaldPIPDialogue import core.tools.DIALOGUE_INITIAL_OPTIONS_HANDLE import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests /** * Central dialogue plugin for King Roald. Reroutes to the more specific DialogueFiles @@ -32,15 +33,15 @@ class KingRoaldDialogue(player: Player? = null) : DialoguePlugin(player) { return true } - if(player.questRepository.isComplete("Priest in Peril")) { - if (!player.questRepository.hasStarted("All Fired Up") || player.questRepository.getQuest("All Fired Up").getStage(player) == 90) { - addOption("All Fired Up", KingRoaldAFUDialogue(player.questRepository.getStage("All Fired Up"))) + if(player.questRepository.isComplete(Quests.PRIEST_IN_PERIL)) { + if (!player.questRepository.hasStarted(Quests.ALL_FIRED_UP) || player.questRepository.getQuest(Quests.ALL_FIRED_UP).getStage(player) == 90) { + addOption("All Fired Up", KingRoaldAFUDialogue(player.questRepository.getStage(Quests.ALL_FIRED_UP))) } } else { - addOption("Priest in Peril", KingRoaldPIPDialogue(player.questRepository.getStage("Priest in Peril"))) + addOption("Priest in Peril", KingRoaldPIPDialogue(player.questRepository.getStage(Quests.PRIEST_IN_PERIL))) } - if (player.questRepository.getQuest("Shield of Arrav").isStarted(player) && !player.questRepository.getQuest("Shield of Arrav").isCompleted(player)) { + if (player.questRepository.getQuest(Quests.SHIELD_OF_ARRAV).isStarted(player) && !player.questRepository.getQuest(Quests.SHIELD_OF_ARRAV).isCompleted(player)) { addOption("Shield of Arrav", KingRoaldArravDialogue()) } diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/KnockAtBankDoor.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/KnockAtBankDoor.kt new file mode 100644 index 000000000..d62ded784 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/KnockAtBankDoor.kt @@ -0,0 +1,59 @@ +package content.region.misthalin.varrock.dialogue + +import core.api.lock +import core.api.queueScript +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +class KnockAtBankDoor : DialogueFile() { + private val femaleBankerNPC = NPC(NPCs.BANKER_45) + private val maleBankerNPC = NPC(NPCs.BANKER_44) + private val femaleBankerDoorLoc = Location(3182, 3434, 0) + + override fun handle(componentID: Int, buttonID: Int) { + npc = if (player!!.location == femaleBankerDoorLoc) femaleBankerNPC else maleBankerNPC + + when (stage) { + START_DIALOGUE -> { + player!!.dialogueInterpreter.sendPlainMessage( + true, "Knock knock..." + ).also { + lock(player!!, 3) + queueScript(player!!, 3) { + npcl(FacialExpression.NEUTRAL, "Who's there?") + stage++ + return@queueScript true + } + } + } + + 1 -> showTopics( + Topic("I'm ${player!!.username}. Please let me in.", 10), + Topic("Boo.", 20), + Topic("Kanga.", 30), + Topic("Thank.", 40), + Topic("Doctor.", 50) + ) + 10 -> npcl("No. Staff only beyond this point. You can't come in here.").also { stage = END_DIALOGUE } + 20 -> npcl("Boo who?").also { stage++ } + 21 -> playerl("There's no need to cry!").also { stage++ } + 22 -> npcl(FacialExpression.FURIOUS, "What? I'm not... oh, just go away!").also { stage = END_DIALOGUE } + 30 -> npcl("Kanga who?").also { stage++ } + 31 -> playerl("No, 'kangaroo'.").also { stage++ } + 32 -> npcl(FacialExpression.FURIOUS, "Stop messing about and go away!").also { stage = END_DIALOGUE } + 40 -> npcl("Thank who?").also { stage++ } + 41 -> playerl("You're welcome!").also { stage++ } + 42 -> npcl(FacialExpression.FURIOUS, "Stop it!").also { stage = END_DIALOGUE } + 50 -> npcl( + FacialExpression.FURIOUS, + "Doctor. wh.. hang on, I'm not falling for that one again! Go away." + ).also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardDialogue.java deleted file mode 100644 index b988a0eb7..000000000 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardDialogue.java +++ /dev/null @@ -1,88 +0,0 @@ -package content.region.misthalin.varrock.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.global.action.DoorActionHandler; -import core.game.node.entity.player.Player; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.game.world.map.RegionManager; - -/** - * Represents the museum guard dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MuseumGuardDialogue extends DialoguePlugin { - - /** - * Represents the gate location. - */ - private static final Location LOCATION = new Location(3261, 3446, 0); - - /** - * Constructs a new {@code MuseumGuardDialogue} {@code Object}. - */ - public MuseumGuardDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MuseumGuardDialogue} {@code Object}. - * @param player the player. - */ - public MuseumGuardDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MuseumGuardDialogue(player); - } - - @Override - public boolean open(Object... args) { - interpreter.sendDialogues(5941, FacialExpression.HALF_GUILTY, "Welcome! Would you like to go into the Dig Site", "archaeology cleaning area?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, I'll go in!", "No thanks, I'll take a look around out there."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, I'll go in!"); - stage = 20; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks, I'll take a look around out there."); - stage = 3; - break; - } - break; - case 3: - end(); - break; - case 20: - end(); - DoorActionHandler.handleAutowalkDoor(player, RegionManager.getObject(LOCATION)); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 5941 }; - } - -} diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardVarrockDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardVarrockDialogue.java deleted file mode 100644 index 9bfee30fa..000000000 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardVarrockDialogue.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.region.misthalin.varrock.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the MuseumGuardVarrockDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class MuseumGuardVarrockDialogue extends DialoguePlugin { - - public MuseumGuardVarrockDialogue() { - - } - - public MuseumGuardVarrockDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 5943 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, how do I get in?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, the main entrance is 'round the front. Just head", "west then north slightly, you can't miss it!"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What about these doors?"); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "They're primarily for the workmen bringing finds from the", "Dig Site, but you can go through if you want."); - stage = 5; - break; - case 5: - end(); - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MuseumGuardVarrockDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello there. Come to see the new museum?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardsDialoguePlugin.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardsDialoguePlugin.kt new file mode 100644 index 000000000..6880892b7 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumGuardsDialoguePlugin.kt @@ -0,0 +1,90 @@ +package content.region.misthalin.varrock.dialogue + +import content.data.Quests +import content.region.misthalin.varrock.handlers.MuseumInteractionListener.Companion.handleMuseumDoor +import core.api.forceWalk +import core.api.getScenery +import core.api.isQuestComplete +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class DoorGuardDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + npcl(FacialExpression.NEUTRAL, "Hello there. Come to see the new museum?").also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.NEUTRAL, "Yes, how do I get in?").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "Well, the main entrance is 'round the front. Just head west then north slightly, you can't miss it!").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "What about these doors?").also { stage++ } + 3 -> { + if (isQuestComplete(player, Quests.THE_DIG_SITE)) { + npcl(FacialExpression.NEUTRAL, "They're primarily for the workmen bringing finds from the Dig Site, but you can go through if you want.").also { stage++ } + } else { + npcl(FacialExpression.NEUTRAL, "They're for the workmen bringing finds from the Dig Site; sorry, but you can't go through.").also { stage = END_DIALOGUE } + } + } + 4 -> playerl(FacialExpression.NEUTRAL, "Okay, thanks.").also { stage++ } + 5 -> { + end() + handleMuseumDoor(player, getScenery(3264, 3441, 0)) + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DoorGuardDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MUSEUM_GUARD_5943) + } +} + +@Initializable +class GateGuardDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + // Shows the player walking to this spot first https://www.youtube.com/watch?v=t-oeY3a-ZSA&t=53s + if (player.location != Location(3261, 3447)) forceWalk(player, Location(3261, 3447), "smart") + + if (isQuestComplete(player, Quests.THE_DIG_SITE)) { + npcl(FacialExpression.NEUTRAL, "Welcome! Would you like to go into the Dig Site archaeology cleaning area?").also { stage = START_DIALOGUE } + } else { + npcl(FacialExpression.NEUTRAL, "You're not permitted in this area.").also { stage = END_DIALOGUE } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> showTopics( + Topic("Yes, I'll go in!", 1, true), + Topic("No thanks, I'll take a look around out here.", END_DIALOGUE, true) + ) + 1 -> { + end() + handleMuseumDoor(player, getScenery(3261, 3446, 0)) + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return GateGuardDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MUSEUM_GUARD_5941) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/PhilopDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/PhilopDialogue.java index 6d7afec28..9cf02f497 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/PhilopDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/PhilopDialogue.java @@ -7,12 +7,11 @@ import core.plugin.Initializable; import core.game.node.entity.player.Player; /** - * Handles the PhilopDialogue dialogue. - * @author 'Vexia + * Philop - Child in Gertrude's House */ @Initializable public class PhilopDialogue extends DialoguePlugin { - + // https://www.youtube.com/watch?v=ANI7DaRVEbg public PhilopDialogue() { } @@ -31,23 +30,23 @@ public class PhilopDialogue extends DialoguePlugin { switch (stage) { case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Gwwrr!"); + interpreter.sendDialogues(npc, FacialExpression.CHILD_ANGRY, "Gwwrr!"); stage = 1; break; case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err, hello there. What's that you have there?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Err, hello there. What's that you have there?"); stage = 2; break; case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Gwwwrrr! Dwa-gon Gwwwwrrrr!"); + interpreter.sendDialogues(npc, FacialExpression.CHILD_ANGRY, "Gwwwrrr! Dwa-gon Gwwwwrrrr!"); stage = 3; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Enjoy playing with your dragon, then."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Enjoy playing with your dragon, then."); stage = 4; break; case 4: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Gwwwrrr!"); + interpreter.sendDialogues(npc, FacialExpression.CHILD_ANGRY, "Gwwwrrr!"); stage = 5; break; case 5: diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/ShilopDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/ShilopDialogue.java index 7c875fd45..d17b6b329 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/ShilopDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ShilopDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for the shilop npc. @@ -55,25 +56,25 @@ public final class ShilopDialogue extends DialoguePlugin { } else if (args[0] instanceof Integer) { id = (Integer) args[0]; } - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); switch (quest.getStage(player)) { case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); stage = 0; break; case 10: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there, I've been looking for you."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello there, I've been looking for you."); stage = 100; break; case 20: case 30: case 40: case 50: - interpreter.sendDialogues(player, null, "Where did you say you saw Fluffs?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Where did you say you saw Fluffs?"); stage = 130; break; default: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); stage = 0; break; } @@ -82,26 +83,26 @@ public final class ShilopDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); switch (stage) { case 0: - interpreter.sendDialogues(id, FacialExpression.OLD_NORMAL, "You think you're tough do you?"); + interpreter.sendDialogues(id, FacialExpression.CHILD_ANGRY, "You think you're tough do you?"); stage = 1; break; case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Pardon?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Pardon?"); stage = 2; break; case 2: - interpreter.sendDialogues(id, FacialExpression.OLD_NORMAL, "I can beat anyone up!"); + interpreter.sendDialogues(id, FacialExpression.CHILD_ANGRY, "I can beat anyone up!"); stage = 3; break; case 3: - interpreter.sendDialogues(783, FacialExpression.OLD_NORMAL, "He can you know!"); + interpreter.sendDialogues(783, FacialExpression.CHILD_ANGRY, "He can you know!"); stage = 4; break; case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Really?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Really?"); stage = 5; break; case 5: @@ -112,59 +113,59 @@ public final class ShilopDialogue extends DialoguePlugin { end(); break; case 100:// stage 10 - interpreter.sendDialogues(id, FacialExpression.OLD_NORMAL, "I didn't mean to take it! I just forgot to pay."); + interpreter.sendDialogues(id, FacialExpression.CHILD_SURPRISED, "I didn't mean to take it! I just forgot to pay."); stage = 101; break; case 101: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What? I'm trying to help your mum find Fluffs."); + interpreter.sendDialogues(player, FacialExpression.THINKING, "What? I'm trying to help your mum find Fluffs."); stage = 102; break; case 102: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "I might be able to help. Fluffs followed me to our secret", "play area and I haven't seen her since."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SIDE_EYE, "I might be able to help. Fluffs followed me to our secret", "play area and I haven't seen her since."); stage = 103; break; case 103: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where is this play area?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Where is this play area?"); stage = 104; break; case 104: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "If I told you that, it wouldn't be a secret."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SIDE_EYE, "If I told you that, it wouldn't be a secret."); stage = 105; break; case 105: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What will make you tell me?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "What will make you tell me?"); stage = 106; break; case 106: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "Well...now you ask, I am a bit short on cash."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SUSPICIOUS, "Well...now you ask, I am a bit short on cash."); stage = 107; break; case 107: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How much?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "How much?"); stage = 108; break; case 108: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "10 coins."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SIDE_EYE, "10 coins."); stage = 109; break; case 109: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "10 coins?!"); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SURPRISED, "10 coins?!"); stage = 110; break; case 110: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "I'll handle this."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_NORMAL, "I'll handle this."); stage = 111; break; case 111: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "100 coins should cover it."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_FRIENDLY, "100 coins should cover it."); stage = 112; break; case 112: - interpreter.sendDialogues(player, null, "100 coins! Why should I pay you?"); + interpreter.sendDialogues(player, FacialExpression.ANGRY, "100 coins! Why should I pay you?"); stage = 113; break; case 113: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "You shouldn't, but we won't help otherwise. We never", "liked that cat anyway, so what do you say?"); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_NORMAL, "You shouldn't, but we won't help otherwise. We never", "liked that cat anyway, so what do you say?"); stage = 114; break; case 114: @@ -174,17 +175,17 @@ public final class ShilopDialogue extends DialoguePlugin { case 115: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "I'm not paying you a penny."); + interpreter.sendDialogues(player, FacialExpression.ANGRY, "I'm not paying you a penny."); stage = 116; break; case 2: - interpreter.sendDialogues(player, null, "Okay then, I'll pay."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay then, I'll pay."); stage = 118; break; } break; case 116: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "Okay then, I'll find another way to make money."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SIDE_EYE, "Okay then, I'll find another way to make money."); stage = 117; break; case 117: @@ -206,26 +207,26 @@ public final class ShilopDialogue extends DialoguePlugin { quest.setStage(player, 20); break; case 119: - interpreter.sendDialogues(player, null, "There you go, now where did you see Fluffs?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "There you go, now where did you see Fluffs?"); stage = 120; break; case 120: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "We play at an abandoned lumber mill to the north east.", "Just beyond the Jolly Boar Inn. I saw Fluffs running", "around in there."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_NORMAL, "We play at an abandoned lumber mill to the north east.", "Just beyond the Jolly Boar Inn. I saw Fluffs running", "around in there."); stage = 121; break; case 121: - interpreter.sendDialogues(player, null, "Anything else?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Anything else?"); stage = 122; break; case 122: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "Well, you'll have to find the broken fence to get in. I'm", "sure you can manage that."); + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SIDE_EYE, "Well, you'll have to find the broken fence to get in. I'm", "sure you can manage that."); stage = 123; break; case 123: end(); break; case 130: - interpreter.sendDialogues(id, null, "Weren't you listening? I saw the flea bag in the old", "lumber mill just north east of here. Just walk past the", "Jolly Boar Inn and you should find it."); + interpreter.sendDialogues(id, FacialExpression.CHILD_SIDE_EYE, "Weren't you listening? I saw the flea bag in the old", "lumber mill just north east of here. Just walk past the", "Jolly Boar Inn and you should find it."); stage = 131; break; case 131: diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/SinkethsDiary.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/SinkethsDiary.kt index 57b98c3e2..79a1f59cc 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/SinkethsDiary.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SinkethsDiary.kt @@ -10,8 +10,8 @@ import core.game.interaction.InteractionListener import core.game.node.entity.player.Player import org.rs09.consts.Items -class SinkethsDiary - : InteractionListener { +// This is not formatted well. See _-88E9n9jWA +class SinkethsDiary : InteractionListener { // Obtainable during the What Lies Below quest. companion object { private val TITLE = "Sin'keth's diary" diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/WiloughDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/WiloughDialogue.java index f25b6048d..02deb3337 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/WiloughDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/WiloughDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue used for wilough. @@ -55,25 +56,25 @@ public final class WiloughDialogue extends DialoguePlugin { } else if (args[0] instanceof Integer) { id = (int) args[0]; } - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); switch (quest.getStage(player)) { case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); stage = 0; break; case 10: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there, I've been looking for you."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello there, I've been looking for you."); stage = 100; break; case 20: case 30: case 40: case 50: - interpreter.sendDialogues(player, null, "Where did you say you saw Fluffs?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Where did you say you saw Fluffs?"); stage = 130; break; default: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); stage = 0; break; } @@ -82,26 +83,26 @@ public final class WiloughDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); switch (stage) { case 0: - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "You think you're tough do you?"); + interpreter.sendDialogues(id, FacialExpression.CHILD_ANGRY, "You think you're tough do you?"); stage = 1; break; case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Pardon?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Pardon?"); stage = 2; break; case 2: - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "I can beat anyone up!"); + interpreter.sendDialogues(id, FacialExpression.CHILD_ANGRY, "I can beat anyone up!"); stage = 3; break; case 3: - interpreter.sendDialogues(781, FacialExpression.HALF_GUILTY, "He can you know!"); + interpreter.sendDialogues(781, FacialExpression.CHILD_ANGRY, "He can you know!"); stage = 4; break; case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Really?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Really?"); stage = 5; break; case 5: @@ -112,59 +113,59 @@ public final class WiloughDialogue extends DialoguePlugin { end(); break; case 100:// stage 10 - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "I didn't mean to take it! I just forgot to pay."); + interpreter.sendDialogues(id, FacialExpression.CHILD_SURPRISED, "I didn't mean to take it! I just forgot to pay."); stage = 101; break; case 101: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What? I'm trying to help your mum find Fluffs."); + interpreter.sendDialogues(player, FacialExpression.THINKING, "What? I'm trying to help your mum find Fluffs."); stage = 102; break; case 102: - interpreter.sendDialogues(id == 783 ? 781 : 783, null, "I might be able to help. Fluffs followed me to our secret", "play area and I haven't seen her since."); + interpreter.sendDialogues(id == 783 ? 781 : 783, FacialExpression.CHILD_SIDE_EYE, "Ohh...well, in that case I might be able to help. Fluffs", "followed me to my super secret hideout, I haven't seen", "her since. She's probably off eating small creatures", "somewhere."); stage = 103; break; case 103: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where is this play area?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Where is this secret hideout? I really need to find that", "cat for your mum."); stage = 104; break; case 104: - interpreter.sendDialogues(id == 783 ? 781 : 783, null, "If I told you that, it wouldn't be a secret."); + interpreter.sendDialogues(id == 783 ? 781 : 783, FacialExpression.CHILD_SIDE_EYE, "If I told you that, it wouldn't be a secret."); stage = 105; break; case 105: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What will make you tell me?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "What will make you tell me?"); stage = 106; break; case 106: - interpreter.sendDialogues(id == 783 ? 781 : 783, null, "Well...now you ask, I am a bit short on cash."); + interpreter.sendDialogues(id == 783 ? 781 : 783, FacialExpression.CHILD_SUSPICIOUS, "Well...now you ask, I am a bit short on cash."); stage = 107; break; case 107: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How much?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "How much?"); stage = 108; break; case 108: - interpreter.sendDialogues(id == 783 ? 781 : 783, null, "10 coins."); + interpreter.sendDialogues(id == 783 ? 781 : 783, FacialExpression.CHILD_SIDE_EYE, "10 coins."); stage = 109; break; case 109: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "10 coins?!"); + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_SURPRISED, "10 coins?!"); stage = 110; break; case 110: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "I'll handle this."); + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_NORMAL, "I'll handle this."); stage = 111; break; case 111: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "100 coins should cover it."); + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_FRIENDLY, "100 coins should cover it."); stage = 112; break; case 112: - interpreter.sendDialogues(player, null, "100 coins! Why should I pay you?"); + interpreter.sendDialogues(player, FacialExpression.ANGRY, "100 coins! Why should I pay you?"); stage = 113; break; case 113: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "You shouldn't, but we won't help otherwise. We never", "liked that cat anyway, so what do you say?"); + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_NORMAL, "You shouldn't, but we won't help otherwise. We never", "liked that cat anyway, so what do you say?"); stage = 114; break; case 114: @@ -174,17 +175,17 @@ public final class WiloughDialogue extends DialoguePlugin { case 115: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "I'm not paying you a penny."); + interpreter.sendDialogues(player, FacialExpression.ANGRY, "I'm not paying you a penny."); stage = 116; break; case 2: - interpreter.sendDialogues(player, null, "Okay then, I'll pay."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay then, I'll pay."); stage = 118; break; } break; case 116: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "Okay then, I'll find another way to make money."); + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_SIDE_EYE, "Okay then, I'll find another way to make money."); stage = 117; break; case 117: @@ -205,26 +206,26 @@ public final class WiloughDialogue extends DialoguePlugin { } break; case 119: - interpreter.sendDialogues(player, null, "There you go, now where did you see Fluffs?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "There you go, now where did you see Fluffs?"); stage = 120; break; case 120: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "We play at an abandoned lumber mill to the north east.", "Just beyond the Jolly Boar Inn. I saw Fluffs running", "around in there."); + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_NORMAL, "We play at an abandoned lumber mill to the north east.", "Just beyond the Jolly Boar Inn. I saw Fluffs running", "around in there."); stage = 121; break; case 121: - interpreter.sendDialogues(player, null, "Anything else?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Anything else?"); stage = 122; break; case 122: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "Well, you'll have to find the broken fence to get in. I'm", "sure you can manage that."); + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_SIDE_EYE, "Well, you'll have to find the broken fence to get in. I'm", "sure you can manage that."); stage = 123; break; case 123: end(); break; case 130: - interpreter.sendDialogues(id, null, "Weren't you listening? I saw the flea bag in the old", "lumber mill just north east of here. Just walk past the", "Jolly Boar Inn and you should find it."); + interpreter.sendDialogues(id, FacialExpression.CHILD_SIDE_EYE, "Weren't you listening? I saw the flea bag in the old", "lumber mill just north east of here. Just walk past the", "Jolly Boar Inn and you should find it."); stage = 131; break; case 131: diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/surok/AbyssalBook.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/AbyssalBook.kt index 76d8eefb3..677934f19 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/surok/AbyssalBook.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/AbyssalBook.kt @@ -144,7 +144,7 @@ class AbyssalBook : InteractionListener { BookLine("- if the barriers between", 57), BookLine("these dimensions are", 58), BookLine("sufficiently weakened,", 59), - BookLine("there ma exist the", 60), + BookLine("there may exist the", 60), BookLine("possibility of an alternative", 61), BookLine("method to proceed", 62), BookLine("with Operation:", 63), diff --git a/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt index 19a1f06aa..f49899210 100644 --- a/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt +++ b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt @@ -21,6 +21,7 @@ import core.game.diary.DiaryLevel import core.game.event.* import core.game.node.entity.player.link.SpellBookManager import core.game.node.entity.skill.Skills +import content.data.Quests class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { companion object { @@ -203,7 +204,7 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { ) } - if (player.questRepository.isComplete("Dragon Slayer")) { + if (player.questRepository.isComplete(Quests.DRAGON_SLAYER)) { if (event.target.id == NPCs.OZIACH_747 && event.option == "trade" && inBorders(player, OZIACH_SHOP)) { finishTask( player, diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/BennyNPC.java b/Server/src/main/content/region/misthalin/varrock/handlers/BennyNPC.java deleted file mode 100644 index 0ada0d65e..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/BennyNPC.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.region.misthalin.varrock.handlers; - -import core.game.node.entity.npc.AbstractNPC; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the representation of the benny npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BennyNPC extends AbstractNPC { - - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 5925 }; - - /** - * Represents the random messages for benny to display. - */ - private static final String[] MESSAGES = new String[] { "Read all about it!", "Varrock Herald, on sale here!", "Buy your Varrock Herald now!", "Extra! Extra! Read all about it!", "Varrock Herald, now only 50 gold!" }; - - /** - * Constructs a new {@code BennyNPC} {@code Object}. - */ - public BennyNPC() { - super(0, null); - } - - /** - * Constructs a new {@code BennyNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - private BennyNPC(int id, Location location) { - super(id, location); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new BennyNPC(id, location); - } - - @Override - public void tick() { - super.tick(); - if (RandomFunction.random(0, 12) == 5) { - sendChat(MESSAGES[RandomFunction.random(MESSAGES.length)]); - } - } - - @Override - public int[] getIds() { - return ID; - } - - @Override - public int getWalkRadius() { - return 6; - } -} diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/BennyNPC.kt b/Server/src/main/content/region/misthalin/varrock/handlers/BennyNPC.kt new file mode 100644 index 000000000..1e047b4d2 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/BennyNPC.kt @@ -0,0 +1,39 @@ +package content.region.misthalin.varrock.handlers + +import core.game.node.entity.npc.AbstractNPC +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +@Initializable +class BennyNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return BennyNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BENNY_5925) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + core.api.sendChat(this, messages.random()) + } + } + + override fun getWalkRadius(): Int { + return 6 + } + + companion object { + private val messages = arrayOf( + "Read all about it!", + "Varrock Herald, on sale here!", + "Buy your Varrock Herald now!", + "Extra! Extra! Read all about it!", + "Varrock Herald, now only 50 gold!" + ) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/BrassKeyDoorPlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/BrassKeyDoorPlugin.java deleted file mode 100644 index 0e74482b9..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/BrassKeyDoorPlugin.java +++ /dev/null @@ -1,43 +0,0 @@ -package content.region.misthalin.varrock.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.global.action.DoorActionHandler; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle the brass key door plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BrassKeyDoorPlugin extends OptionHandler { - - @Override - public boolean handle(Player player, Node node, String option) { - if (player.getInventory().contains(983, 1)) { - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - } else { - player.getPacketDispatch().sendMessage("This door is locked."); - return true; - } - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(1804).getHandlers().put("option:open", this); - return this; - } - - @Override - public Location getDestination(Node node, Node n) { - return DoorActionHandler.getDestination(((Player) node), ((Scenery) n)); - } - -} diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/ChampionsArenaPlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/ChampionsArenaPlugin.java deleted file mode 100644 index 7539754d3..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/ChampionsArenaPlugin.java +++ /dev/null @@ -1,37 +0,0 @@ -package content.region.misthalin.varrock.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the champions arena plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ChampionsArenaPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(10556).getHandlers().put("option:open", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - int id = node instanceof Scenery ? ((Scenery) node).getId() : ((NPC) node).getId(); - switch (id) { - case 10556: - player.getDialogueInterpreter().open(3050, true, true); - break; - } - return true; - } - -} diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/GuidorDoorPlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/GuidorDoorPlugin.java deleted file mode 100644 index fa4da00a0..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/GuidorDoorPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package content.region.misthalin.varrock.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the guidor door plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GuidorDoorPlugin extends OptionHandler { - - @Override - public boolean handle(Player player, Node node, String option) { - player.getDialogueInterpreter().open(342, true, true); - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(2032).getHandlers().put("option:open", this); - return this; - } - -} diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/LumberYardCratePlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/LumberYardCratePlugin.java index fc5c15e86..277d7d634 100644 --- a/Server/src/main/content/region/misthalin/varrock/handlers/LumberYardCratePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/LumberYardCratePlugin.java @@ -14,6 +14,7 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import content.data.Quests; /** * Represents the plugin used for handling a lumber yard crate. @@ -30,7 +31,7 @@ public final class LumberYardCratePlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); switch (option) { case "squeeze-under": Location dest = null; diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/MuseumGatePlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/MuseumGatePlugin.java deleted file mode 100644 index 1ce507bb3..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/MuseumGatePlugin.java +++ /dev/null @@ -1,36 +0,0 @@ -package content.region.misthalin.varrock.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.global.action.DoorActionHandler; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used for the museum gate plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MuseumGatePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(24536).getHandlers().put("option:open", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (player.getLocation().getY() >= 3447) { - player.getDialogueInterpreter().open(5941); - } else { - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - return true; - } - return true; - } -} diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/MuseumInteractionListener.kt b/Server/src/main/content/region/misthalin/varrock/handlers/MuseumInteractionListener.kt new file mode 100644 index 000000000..f72eed5d1 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/MuseumInteractionListener.kt @@ -0,0 +1,125 @@ +package content.region.misthalin.varrock.handlers + +import core.api.* +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class MuseumInteractionListener : InteractionListener { + override fun defineListeners() { + // Basement Stairs + addClimbDest(Location(3255, 3451, 0), Location(1759, 4958, 0)) + addClimbDest(Location(1758, 4959, 0), Location(3258, 3452, 0)) + + on(mapObject, IntType.SCENERY, "look-at", "take") { player, node -> + if (getUsedOption(player) == "take") { + if (!addItem(player, Items.MUSEUM_MAP_11184)) { + sendMessage(player, "You don't have enough space in your inventory.") + } + } else { + when (node.id) { + Scenery.MAP_24390 -> setAttribute(player, "iface:527:floor", "main") + Scenery.MAP_24391 -> setAttribute(player, "iface:527:floor", "second") + Scenery.MAP_24392 -> setAttribute(player, "iface:527:floor", "top") + } + openInterface(player, Components.VM_MUSEUM_MAP_527) + } + return@on true + } + + on(Items.MUSEUM_MAP_11184, IntType.ITEM, "look-at") { player, node -> + openInterface(player, Components.VM_MUSEUM_MAP_527) + return@on true + } + + on(Scenery.INFORMATION_BOOTH_24452, IntType.SCENERY, "look-at") { player, node -> + // TODO: I cannot find anything that shows what this does in 2009. + sendMessage(player, "Nothing interesting happens.") + return@on true + } + + on(doorsToDigsite, IntType.SCENERY, "open") { player, node -> + if (node.id == Scenery.GATE_24536) { + if (player.location.y <= 3446) { + handleMuseumDoor(player, node.asScenery()) + } else { + openDialogue(player, NPCs.MUSEUM_GUARD_5941) + } + return@on true + } else { + if (player.location.y >= 3442) { + handleMuseumDoor(player, node.asScenery()) + } else { + openDialogue(player, NPCs.MUSEUM_GUARD_5943) + } + } + return@on true + } + + on(Scenery.TOOLS_24535, IntType.SCENERY, "take") { player, node -> + sendDialogueOptions( + player, + "Which tool would you like?", + "Trowel", + "Rock pick", + "Specimen brush", + "Leather gloves", + "Leather boots" + ) + addDialogueAction(player) { _, button -> + val item = when (button) { + 2 -> Items.TROWEL_676 + 3 -> Items.ROCK_PICK_675 + 4 -> Items.SPECIMEN_BRUSH_670 + 5 -> Items.LEATHER_GLOVES_1059 + 6 -> Items.LEATHER_BOOTS_1061 + else -> return@addDialogueAction + } + val name = item.asItem().name.lowercase() + val word = if (name.startsWith("leather")) "pair of " else "" + + if (!addItem(player, item)) { + sendMessage(player, "You don't have enough space in your inventory.") + } else { + sendItemDialogue(player, item, "You take a $word$name from the rack.") + } + } + return@on true + } + + on(naturalHistoryPlaques, IntType.SCENERY, "study") { player, node -> + openInterface(player, 533) + return@on true + } + } + + companion object { + private val doorsToDigsite = intArrayOf(Scenery.GATE_24536, Scenery.DOOR_24565, Scenery.DOOR_24567) + private val mapObject = intArrayOf(Scenery.MAP_24390, Scenery.MAP_24391, Scenery.MAP_24392) + private val naturalHistoryPlaques = intArrayOf( + Scenery.PLAQUE_24605, Scenery.PLAQUE_24606, Scenery.PLAQUE_24607, Scenery.PLAQUE_24608, + Scenery.PLAQUE_24609, Scenery.PLAQUE_24610, Scenery.PLAQUE_24611, Scenery.PLAQUE_24612, + Scenery.PLAQUE_24613, Scenery.PLAQUE_24614, Scenery.PLAQUE_24615, Scenery.PLAQUE_24616, + Scenery.PLAQUE_24617, Scenery.PLAQUE_24618 + ) + + fun handleMuseumDoor(player: Player, door: core.game.node.scenery.Scenery?) { + val npc = if (door?.id == Scenery.GATE_24536) findLocalNPC(player, NPCs.MUSEUM_GUARD_5941) else findLocalNPC(player, NPCs.MUSEUM_GUARD_5943) + val animation = if (DoorActionHandler.getEndLocation(player, door).y > player.location.y) Animation(6391) else Animation(6392) + + if (npc != null) { + animate(npc, animation) + queueScript(player, animationDuration(animation)) { DoorActionHandler.handleAutowalkDoor(player, door) } + } else { + DoorActionHandler.handleAutowalkDoor(player, door) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/MuseumInterfaceListener.kt b/Server/src/main/content/region/misthalin/varrock/handlers/MuseumInterfaceListener.kt new file mode 100644 index 000000000..e31f4cefe --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/MuseumInterfaceListener.kt @@ -0,0 +1,91 @@ +package content.region.misthalin.varrock.handlers + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.Player +import org.rs09.consts.Components +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +class MuseumInterfaceListener : InterfaceListener { + override fun defineInterfaceListeners() { + onOpen(Components.VM_MUSEUM_MAP_527) { player, _ -> + showMapFloor(player, getAttribute(player, "iface:527:floor", "main")) + removeAttribute(player, "iface:527:floor") + return@onOpen true + } + + on(Components.VM_MUSEUM_MAP_527) { player, _, _, buttonID, _, _ -> + showMapFloor(player, when (buttonID) { + in mapButtonsToBasement -> "basement" + in mapButtonsToMainFloor -> "main" + in mapButtonsToSecondFloor -> "second" + in mapButtonsToTopFloor -> "top" + else -> return@on true + }) + return@on true + } + + onOpen(NATURAL_HISTORY_EXAM_533) { player, component -> + // The model for each display is confusing as hell. Some are objects and some are NPCs. + val model = getScenery(1763, 4937, 0)?.definition?.modelIds?.first() + player.packetDispatch.sendModelOnInterface(model!!, component.id, 3, 0) + + // Showing this child makes child 28 - 31 visible. + setComponentVisibility(player, component.id, 27, false) + + // The case number to display. + setInterfaceText(player, "1", component.id, 25) + + // The question text. + setInterfaceText(player, "When will the Natural History Quiz be implemented?", component.id, 28) + + // The choices. + setInterfaceText(player, "Never.", component.id, 29) + setInterfaceText(player, "In 2 days.", component.id, 30) + setInterfaceText(player, "After Barbarian Assault.", component.id, 31) + return@onOpen true + } + + on(NATURAL_HISTORY_EXAM_533) { player, component, opcode, buttonID, slot, itemID -> + if (buttonID in 29..31) { + closeInterface(player) + setVarbit(player, 3637, 1, false) + playAudio(player, Sounds.VM_GAIN_KUDOS_3653) + sendNPCDialogue(player, NPCs.ORLANDO_SMITH_5965, "Nice job, mate. That looks about right.") + } + return@on true + } + } + companion object { + private const val NATURAL_HISTORY_EXAM_533 = 533 + + private val mapButtonsToBasement = intArrayOf(41, 186) + private val mapButtonsToMainFloor = intArrayOf(117, 120, 187, 188) + private val mapButtonsToSecondFloor = intArrayOf(42, 44, 152, 153) + private val mapButtonsToTopFloor = intArrayOf(42, 44, 118, 119) + + private fun showMapFloor(player: Player, floor: String) { + when (floor) { + "basement" -> { + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 2, true) + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 7, false) + } + "main" -> { + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 3, true) + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 7, true) + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 2, false) + } + "second" -> { + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 2, true) + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 5, true) + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 3, false) + } + "top" -> { + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 3, true) + setComponentVisibility(player, Components.VM_MUSEUM_MAP_527, 5, false) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/MuseumMapArea.kt b/Server/src/main/content/region/misthalin/varrock/handlers/MuseumMapArea.kt new file mode 100644 index 000000000..cdb5dcb96 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/MuseumMapArea.kt @@ -0,0 +1,29 @@ +package content.region.misthalin.varrock.handlers + +import core.api.MapArea +import core.api.closeOverlay +import core.api.openOverlay +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Components + +class MuseumMapArea : MapArea { + override fun defineAreaBorders(): Array { + val vmArea = ZoneBorders(3253, 3442, 3267, 3455) + val vmBasementArea = ZoneBorders(1730, 4932, 1788, 4988) + return arrayOf(vmArea, vmBasementArea) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + openOverlay(entity.asPlayer(), Components.VM_KUDOS_532) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + closeOverlay(entity.asPlayer()) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/VarrockBrokenCart.java b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockBrokenCart.java deleted file mode 100644 index 22d6b9e7e..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/VarrockBrokenCart.java +++ /dev/null @@ -1,28 +0,0 @@ -package content.region.misthalin.varrock.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * @author 'Vexia - */ -@Initializable -public class VarrockBrokenCart extends OptionHandler { - - @Override - public boolean handle(Player player, Node node, String option) { - player.getDialogueInterpreter().open(70099, "You search the cart but are surprised to find very little there. It's a", "little odd for a travelling trader not to have anything to trade."); - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(23055).getHandlers().put("option:search", this); - return this; - } - -} diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/VarrockCensusInterface.kt b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockCensusInterface.kt new file mode 100644 index 000000000..6cb95f014 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockCensusInterface.kt @@ -0,0 +1,27 @@ +package content.region.misthalin.varrock.handlers + +import core.api.getVarbit +import core.api.setVarbit +import core.game.interaction.InterfaceListener + +class VarrockCensusInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(INTERFACE_ID) { player, _, _, buttonID, _, _ -> + when (buttonID) { + 2 -> setVarbit(player, VARBIT_ID, getVarbit(player, VARBIT_ID).plus(1)) + 3 -> setVarbit(player, VARBIT_ID, getVarbit(player, VARBIT_ID).minus(1)) + else -> return@on true + } + return@on true + } + + onClose(INTERFACE_ID) { player, _ -> + setVarbit(player, VARBIT_ID, 0) + return@onClose true + } + } + companion object { + const val INTERFACE_ID = 794 + const val VARBIT_ID = 5390 + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/VarrockInteractionListener.kt b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockInteractionListener.kt new file mode 100644 index 000000000..1610dafb6 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockInteractionListener.kt @@ -0,0 +1,91 @@ +package content.region.misthalin.varrock.handlers + +import content.region.misthalin.varrock.dialogue.KnockAtBankDoor +import core.api.* +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +class VarrockInteractionListener : InteractionListener { + override fun defineListeners() { + // Varrock Sewer Manhole + on(VARROCK_MANHOLE, IntType.SCENERY, "open", "close") { player, node -> + if (getUsedOption(player) == "open") { + playAudio(player, Sounds.MANHOLE_OPEN_75) + replaceScenery(node.asScenery(), Scenery.VARROCK_MANHOLE_OPEN_882, 100) + } else { + playAudio(player, Sounds.MANHOLE_CLOSE_74) + replaceScenery(node.asScenery(), Scenery.VARROCK_MANHOLE_CLOSED_881, -1) + } + return@on true + } + + // Phoenix Gang Hideout Plaque + on(Scenery.PLAQUE_23636, IntType.SCENERY, "read") { player, _ -> + openInterface(player, VTAM_IFACE) + return@on true + } + + // Varrock Census in the palace Library + on(Scenery.VARROCK_CENSUS_37209, IntType.SCENERY, "read") { player, _ -> + sendPlayerDialogue(player, "Hmm. The Varrock Census - year 160. That means it's nine years out of date.") + addDialogueAction(player) { _, buttonID -> + if (buttonID == 6) { + openInterface(player, VARROCK_CENSUS_IFACE) + } + } + return@on true + } + + // Broken Cart next to Rat Burgiss + on(Scenery.BROKEN_CART_23055, IntType.SCENERY, "search") { player, node -> + sendDialogue(player, "You search the cart but are surprised to find very little there. " + + "It's a little odd for a travelling trader not to have anything to trade.") + return@on true + } + + on(openOptionNodes, IntType.SCENERY, "open") { player, node -> + when (node.id) { + // Guidor's Bedroom Door + Scenery.BEDROOM_DOOR_2032 -> { + openDialogue(player, NPCs.GUIDORS_WIFE_342, true, true) + } + + // Guidor's Drawers + Scenery.DRAWERS_17466 -> { + sendMessage(player, "The drawers are locked shut.") + } + + // Brass Key Door to Edgeville Dungeon + Scenery.DOOR_1804 -> { + if (inInventory(player, Items.BRASS_KEY_983)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "This door is locked.") + } + } + } + return@on true + } + + // Varrock West Bank Door + on(Scenery.DOOR_24389, IntType.SCENERY, "knock-at") { player, node -> + openDialogue(player, KnockAtBankDoor()) + return@on true + } + + // TODO: Cooking Guild + // TODO: Fix Achievements + } + companion object { + private val VARROCK_MANHOLE = intArrayOf(Scenery.VARROCK_MANHOLE_CLOSED_881, Scenery.VARROCK_MANHOLE_OPEN_882) + private val openOptionNodes = intArrayOf(Scenery.BEDROOM_DOOR_2032, Scenery.DRAWERS_17466, Scenery.DOOR_1804) + private const val VTAM_IFACE = 531 + private const val VARROCK_CENSUS_IFACE = 794 + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/VarrockNodePlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockNodePlugin.java deleted file mode 100644 index eb2307284..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/VarrockNodePlugin.java +++ /dev/null @@ -1,313 +0,0 @@ -package content.region.misthalin.varrock.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.global.action.ClimbActionHandler; -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.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle node interactions in varrock. - * - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class VarrockNodePlugin extends OptionHandler { - - /** - * Represents the bronze axe item. - */ - private static final Item BRONZE_AXE = new Item(1351); - - /** - * Represents the spade item. - */ - private static final Item SPADE = new Item(952); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(24357).getHandlers().put("option:climb-up", this); - SceneryDefinition.forId(24359).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(5581).getHandlers().put("option:take-axe", this); - SceneryDefinition.forId(36974).getHandlers().put("option:take-axe", this); - SceneryDefinition.forId(24427).getHandlers().put("option:walk-up", this); - SceneryDefinition.forId(24428).getHandlers().put("option:walk-down", this); - SceneryDefinition.forId(1749).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(23636).getHandlers().put("option:read", this); - SceneryDefinition.forId(24389).getHandlers().put("option:knock-at", this); - SceneryDefinition.forId(9662).getHandlers().put("option:take", this); - SceneryDefinition.forId(29534).getHandlers().put("option:enter", this); - SceneryDefinition.forId(17985).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(24366).getHandlers().put("option:climb-up", this); - return this; - } - - @Override - public boolean handle(final Player player, Node node, String option) { - final int id = node instanceof Scenery ? ((Scenery) node).getId() : ((Item) node).getId(); - switch (id) { - case 24366: - ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, new Location(3237, 3459)); - return true; - case 29534: - player.getDialogueInterpreter().open(543543); - return true; - case 17985: - ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_DOWN, new Location(3204, 9910), "You enter the murky sewers."); - return true; - case 24389: - player.getDialogueInterpreter().open(KnockatDoorDialogue.ID, player.getLocation().getX() == 3182 ? 45 : 44); - break; - case 28094: - player.getDialogueInterpreter().sendDialogues(player, FacialExpression.THINKING, "I don't think I should go inside."); - break; - case 23636: - player.getInterfaceManager().open(new Component(531)); - break; - case 1749: - if (player.getLocation().getZ() == 2 && player.getLocation().getDistance(new Location(3096, 3433, 2)) < 4) { - ClimbActionHandler.climb(player, new Animation(827), Location.create(3097, 3432, 1)); - return true; - } else if (player.getLocation().getZ() == 1 && player.getLocation().getDistance(new Location(3095, 3433, 1)) < 4) { - ClimbActionHandler.climb(player, new Animation(827), Location.create(3096, 3432, 0)); - return true; - } - ClimbActionHandler.climbLadder(player, (Scenery) node, option); - return true; - case 5581: - case 36974: - if (!player.getInventory().add(BRONZE_AXE)) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - return true; - } - SceneryBuilder.replace(((Scenery) node), ((Scenery) node).transform(5582), 5000); - break; - case 24357: - if (player.getLocation().getDistance(Location.create(3188, 3358, 0)) < 3) { - ClimbActionHandler.climb(player, new Animation(828), Location.create(3188, 3354, 1)); - return true; - } - if (((Scenery) node).getLocation().equals(new Location(3156, 3435, 0))) { - ClimbActionHandler.climb(player, new Animation(828), Location.create(3155, 3435, 1)); - return true; - } - ClimbActionHandler.climbLadder(player, (Scenery) node, option); - return true; - - case 24359: - if (player.getLocation().getDistance(Location.create(3231, 3382, 1)) < 3) { - ClimbActionHandler.climb(player, null, Location.create(3231, 3386, 0)); - return true; - } - ClimbActionHandler.climbLadder(player, (Scenery) node, option); - return true; - - case 24427: //varrock museum stairs that lead upstairs - if (player.getLocation().getDistance(Location.create(1758, 4959, 0)) < 3) { - ClimbActionHandler.climb(player, new Animation(-1), Location.create(3258, 3452, 0)); - return true; - } - return true; - - case 24428: //varrock museum stairs that lead downstairs - if (player.getLocation().getDistance(Location.create(3255, 3451, 0)) < 4) { - ClimbActionHandler.climb(player, new Animation(-1), Location.create(1759, 4958, 0)); - return true; - } - return true; - case 9662: - if (!player.getInventory().hasSpaceFor(SPADE)) { - player.getPacketDispatch().sendMessage("Not enough inventory space."); - return true; - } - player.getInventory().add(SPADE); - SceneryBuilder.replace((Scenery) node, ((Scenery) node).transform(0), 250); - return true; - } - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - @Override - public boolean isWalk(final Player player, final Node node) { - return !(node instanceof Item); - } - - /** - * Represents the dialogue used for the knocking at a door in varrock bank. - * - * @author 'Vexia - * @version 1.0 - */ - public final class KnockatDoorDialogue extends DialoguePlugin { - - /** - * Represents the id of this dialogue. - */ - private static final int ID = 903042893; - - /** - * Represents the id to use. - */ - private int npcId; - - /** - * Constructs a new {@code KnockatDoorDialogue} {@code Object}. - */ - public KnockatDoorDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KnockatDoorDialogue} {@code Object}. - * - * @param player the player. - */ - public KnockatDoorDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KnockatDoorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npcId = (int) args[0]; - player("I don't think I'm ever going to be allowed in there."); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player.lock(3); - interpreter.sendPlainMessage(true, "Knock knock..."); - GameWorld.getPulser().submit(new Pulse(3, player) { - @Override - public boolean pulse() { - interpreter.sendDialogues(npcId, null, "Who's there?"); - stage = 1; - return true; - } - }); - break; - case 1: - options("I'm " + player.getUsername() + ". Please let me in.", "Boo.", "Kanga.", "Thank.", "Doctor."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - player("I'm " + player.getUsername() + ". Please let me in."); - stage = 10; - break; - case 2: - player("Boo."); - stage = 20; - break; - case 3: - player("Kanga."); - stage = 30; - break; - case 4: - player("Thank."); - stage = 40; - break; - case 5: - player("Doctor."); - stage = 50; - break; - } - break; - case 10: - interpreter.sendDialogues(npcId, null, "No. Staff only beyond this point.", "You can't come in here."); - stage = 11; - break; - case 11: - end(); - break; - case 20: - interpreter.sendDialogues(npcId, null, "Boo who?"); - stage = 21; - break; - case 21: - player("There's no need to cry!"); - stage = 22; - break; - case 22: - interpreter.sendDialogues(npcId, FacialExpression.FURIOUS, "What? I'm not... oh, just go away!"); - stage = 23; - break; - case 23: - end(); - break; - case 30: - interpreter.sendDialogues(npcId, null, "Kanga who?"); - stage = 31; - break; - case 31: - player("No, 'kangaroo'."); - stage = 32; - break; - case 32: - interpreter.sendDialogues(npcId, FacialExpression.FURIOUS, "Stop messing about and go away!"); - stage = 33; - break; - case 33: - end(); - break; - case 40: - interpreter.sendDialogues(npcId, null, "Thank who?"); - stage = 41; - break; - case 41: - player("You're welcome!"); - stage = 42; - break; - case 42: - interpreter.sendDialogues(npcId, FacialExpression.FURIOUS, "Stop it!"); - stage = 43; - break; - case 43: - end(); - break; - case 50: - interpreter.sendDialogues(npcId, FacialExpression.FURIOUS, "Doctor. wh.. hang on, I'm not falling for that one again!", "Go away."); - stage = 51; - break; - case 51: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[]{903042893}; - } - - } -} diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt b/Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt index 795b33ce1..22243ca0b 100644 --- a/Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt +++ b/Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt @@ -17,6 +17,7 @@ import org.json.simple.JSONObject import org.rs09.consts.Items import core.ServerStore import core.ServerStore.Companion.getInt +import content.data.Quests /** * Represents the plugin used for buying a battle staff from zeke. @@ -70,7 +71,7 @@ class ZaffPlugin : OptionHandler() { override fun open(vararg args: Any): Boolean { npc = args[0] as NPC - quest = player.questRepository.getQuest("What Lies Below") + quest = player.questRepository.getQuest(Quests.WHAT_LIES_BELOW) interpreter.sendDialogues( npc, core.game.dialogue.FacialExpression.HALF_GUILTY, @@ -391,16 +392,18 @@ class ZaffPlugin : OptionHandler() { ammount = getStoreFile().getInt(player.username.toLowerCase()) var amt = value as Int if(amt > maxStaffs - ammount) amt = maxStaffs - ammount + if(amt == 0){ + return@sendInputDialogue + } val coinage = amt * 7000 if(!inInventory(player, Items.COINS_995, coinage)){ sendDialogue(player, "You can't afford that many.") return@sendInputDialogue } - - if(amt == 0){ + if(!hasSpaceFor(player, Item(Items.BATTLESTAFF_1392, amt)) && amountInInventory(player, Items.COINS_995) > coinage){ + sendDialogue(player, "You don't have enough inventory space.") return@sendInputDialogue } - if(removeItem(player, Item(Items.COINS_995, coinage), Container.INVENTORY)){ addItem(player, Items.BATTLESTAFF_1392, amt) getStoreFile()[player.username.toLowerCase()] = amt + ammount diff --git a/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/AllFiredUp.kt b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/AllFiredUp.kt index f6f8d2c70..29f85806b 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/AllFiredUp.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/AllFiredUp.kt @@ -1,20 +1,21 @@ package content.region.misthalin.varrock.quest.allfiredup +import content.data.Quests +import content.minigame.allfiredup.AFUBeacon +import core.api.setVarbit import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items -import content.minigame.allfiredup.AFUBeacon -import core.api.* /** * Represents the "All Fired Up" quest. * @author Ceikry */ @Initializable -class AllFiredUp : Quest("All Fired Up", 157, 156, 1){ +class AllFiredUp : Quest(Quests.ALL_FIRED_UP, 157, 156, 1){ override fun newInstance(`object`: Any?): Quest { return this } @@ -23,52 +24,118 @@ class AllFiredUp : Quest("All Fired Up", 157, 156, 1){ super.drawJournal(player, stage) var line = 11 player ?: return - if(stage == 0){ - line(player, "I can start this quest by speaking to !!King Roald?? in Varrock Palace.", line++) + if (stage == 0) { + line(player, "I can start this quest by speaking to !!King Roald?? in !!Varrock??", line++) + line(player, "!!Palace??.", line++) line++ line(player, "To start this quest, I require:", line++) line(player, "!!43 Firemaking??", line++, player.skills.getLevel(Skills.FIREMAKING) >= 43) - line(player, "!!Completion of Priest in Peril??", line++, player.questRepository.isComplete("Priest in Peril")) - + line(player, "!!Completion of Priest in Peril??", line++, player.questRepository.isComplete(Quests.PRIEST_IN_PERIL)) + limitScrolling(player, line, true) } else { - if(stage >= 10){ - line(player, "!!King Roald?? needs me to test the !!beacons??. He has sent me", line++, stage >= 20) - line(player, "to speak with !!Blaze Sharpeye??, by the beacon near the !!River Salve??.", line++, stage >= 20) + line(player, "I have agreed to help King Roald test the beacon network", line++, true) + line(player, "that he hopes will serve as an early warning system,", line++, true) + line(player, "should Misthalin and Asgarnia come under attack from", line++, true) + line(player, "Morytania or the Wilderness.", line++, true) + + if (stage > 10) { + line(player, "I've spoken with the head fire-tender, Blaze Sharpeye", line++, true) + line(player, "who is stationed near the Temple of Paterdomus, at the", line++, true) + line(player, "source of the River Salve.", line++, true) + } else if (stage == 10) { + line(player, "!!King Roald?? asked me to talk to the head fire-tender !!Blaze??", line++, false) + line(player, "!!Sharpeye?? who is stationed near the !!Temple of??", line++, false) + line(player, "!!Paterdomus??, at the source of the !!River Salve??.", line++, false) } - if(stage >= 20){ - line(player, "Blaze Sharpeye asked me to light the !!beacon??. I'm going to need:", line++, stage >= 30) - line(player, "20 logs of a single kind", line++, stage >= 30) - line(player, "A tinderbox", line++, stage >= 30) + + if (stage > 20) { + // Replaced with stage 30 text. + } else if (stage == 20) { + line(player, "!!Blaze?? has asked me to light the nearby !!beacon??.", line++, false) + line(player, "To light the !!beacon??, I need to add !!twenty logs?? of the same", line++, false) + line(player, "type and then light the fire with a !!tinderbox??.", line++, false) } - if(stage == 30){ - line(player, "I lit the beacon. I should speak to !!Blaze Sharpeye?? again.", line++, stage >= 40) + + if (stage > 30) { + line(player, "I've lit the beacon near Blaze by loading it with twenty logs", line++, true) + line(player, "and lighting them with a tinderbox.", line++, true) + } else if (stage == 30) { + line(player, "!!Blaze?? has asked me to light the nearby !!beacon??.", line++, false) + line(player, "I've placed !!twenty logs?? on the !!beacon?? and lit the fire with", line++, false) + line(player, "my !!tinderbox??. Now that it's blazing brightly, perhaps I", line++, false) + line(player, "should speak with !!Blaze??.", line++, false) } - if(stage >= 40){ - line(player, "Blaze Sharpeye asked me to go and speak with !!Squire Fyre?? near the !!limestone quarry??.", line++, stage >= 50) + + if (stage > 40) { + // Replaced with stage 50 text. + } else if (stage == 40) { + line(player, "!!Blaze?? has now asked me to light the !!beacon?? tended by", line++, false) + line(player, "!!Squire Fyre??, which is located just west of the !!Rag and??", line++, false) + line(player, "!!Bone Man??'s hovel.", line++, false) + line(player, "I need permission from !!Squire Fyre?? to light the !!beacon??", line++, false) } - if(stage >= 50){ - line(player, "Squire Fyre permitted me to light the !!beacon??. I'm going to need:", line++, stage >= 60) - line(player, "20 logs of a single kind", line++, stage >= 60) - line(player, "A tinderbox", line++, stage >= 60) + + if (stage > 50) { + // Replaced with stage 60 text. + } else if (stage == 50) { + line(player, "!!Blaze?? has now asked me to light the !!beacon?? tended by", line++, false) + line(player, "!!Squire Fyre??, which is located just west of the !!Rag and??", line++, false) + line(player, "!!Bone Man??'s hovel.", line++, false) + line(player, "To light the !!beacon??, I need to add !!twenty logs?? of the same", line++, false) + line(player, "type and then light the fire with a !!tinderbox??.", line++, false) } - if(stage == 60){ - line(player, "I should return to !!Blaze Sharpeye??.", line++) + + if (stage > 60) { + line(player, "Blaze has now asked me to light the beacon tended by", line++, true) + line(player, "Squire Fyre, near the Rag and Bone Man's hovel. I've", line++, true) + line(player, "loaded it with logs and set it alight; it's now blazing", line++, true) + line(player, "brightly.", line++, true) + } else if (stage == 60) { + line(player, "!!Blaze?? has now asked me to light the !!beacon?? tended by", line++, false) + line(player, "!!Squire Fyre??, which is located just west of the !!Rag and??", line++, false) + line(player, "!!Bone Man??'s hovel.", line++, false) + line(player, "I've placed twenty logs on the !!beacon?? and lit the fire with", line++, false) + line(player, "my tinderbox. Now that it's blazing brightly, perhaps I", line++, false) + line(player, "should speak with Blaze.", line++, false) } - if(stage >= 70){ - line(player, "Blaze asked me to add more logs to the dying fire. I'll need:", line++, stage >= 80) - line(player, "5 logs of a single kind", line++, stage >= 80) - line(player, "A tinderbox", line++, stage >= 80) + + if (stage > 70) { + // Replaced with stage 80 text. + } else if (stage == 70) { + line(player, "!!Blaze?? has now asked me to maintain the nearby !!beacon??.", line++, false) + line(player, "To maintain the !!beacon??, I need to add !!five logs?? of the same", line++, false) + line(player, "type.", line++, false) } - if(stage == 80){ - line(player, "I should speak to !!Blaze?? again.", line++) + + if (stage > 80) { + line(player, "Blaze has explained how to maintain a beacon. When the", line++, true) + line(player, "fire begins to die out, five more logs can be added to", line++, true) + line(player, "restore a beacon to its blazing state. I've tended the", line++, true) + line(player, "beacon near Blaze and have reported back to him.", line++, true) + } else if (stage == 80) { + line(player, "!!Blaze?? has explained how to maintain a beacon. When the", line++, false) + line(player, "fire begins to die out, !!five more logs?? can be added to", line++, false) + line(player, "restore a beacon to its blazing state.", line++, false) + line(player, "!!Blaze?? has asked me to maintain the !!beacon?? nearest him.", line++, false) } - if(stage == 90){ - line(player, "I should report to !!King Roald??.", line++) + + if (stage > 90) { + // Replaced with stage 100 complete text. + } else if (stage == 90){ + line(player, "I need to talk to !!King Roald?? in !!Varrock Palace?? to claim my", line++, false) + line(player, "reward.", line++, false) } - if(stage == 100){ - line(player, "I helped King Roald test the beacon network", line++) - line(player, " and have gained full access to it.", line++) + + if (stage == 100) { + line(player, "I spoke to King Roald who thanked me for helping and", line++, true) + line(player, "rewarded me with 20,000gp and 5,500 Firemaking XP. He", line++, true) + line(player, "told me that if I'm able to light six, ten and all fourteen", line++, true) + line(player, "fires simultaneously, he'll have further rewards for me.", line++, true) + line++ + line++ + line(player,"QUEST COMPLETE!", line) } + limitScrolling(player, line, false) } } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/BlazeSharpeyeDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/BlazeSharpeyeDialogue.kt index 200749835..9f85e6603 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/BlazeSharpeyeDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/BlazeSharpeyeDialogue.kt @@ -5,6 +5,7 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable +import content.data.Quests @Initializable class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -14,7 +15,7 @@ class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player?.questRepository?.getStage("All Fired Up") ?: -1 + val qstage = player?.questRepository?.getStage(Quests.ALL_FIRED_UP) ?: -1 when(qstage){ 0 -> player.dialogueInterpreter.sendDialogue("He seems uninterested in talking.").also { stage = 1000 } 10 -> player("So, what's going on?").also { stage = 100 } @@ -48,7 +49,7 @@ class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { 112 -> npc("Our technique is super-secret, but quite effective. All","you do is put twenty logs of the same type on a","beacon and...").also { stage++ } 113 -> npc(FacialExpression.AMAZED,"SET IT ON FIRE WITH A TINDERBOX!").also { stage++ } 114 -> player("You really enjoy your job, don't you?").also { stage++ } - 115 -> npc("Yes. Yes I do. Now, why don't you go over there and","try lighting that beacon. Show us what you've got.").also { stage++; player.questRepository.getQuest("All Fired Up").setStage(player,20) } + 115 -> npc("Yes. Yes I do. Now, why don't you go over there and","try lighting that beacon. Show us what you've got.").also { stage++; player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player,20) } 116 -> options("Does it matter what type of log I use?","Okay.").also { stage++ } 117 -> when(buttonId){ 1 -> player("Does it matter what type of log I use?").also { stage = 150 } @@ -61,7 +62,7 @@ class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { 201 -> npc("Well, apparently, not for someone of your Firemaking","calibre and expertise. Now that you've got the hang of","things, we can get this show on the road.").also { stage++ } 202 -> npc("If you'd be so kind as to light the beacon to the west","and report back to me, I can make sure I can clearly","see its glow on the horizon.").also { stage++ } 203 -> npc("It's near the limestone quarry, north-east of Varrock,","west of the Rag and Bone Man's hovel.").also { stage++ } - 204 -> npc("My colleague, Squire Fyre, is tending that beacon.","She'll help you out if you run into any trouble.").also { stage = 116; player.questRepository.getQuest("All Fired Up").setStage(player,40) } + 204 -> npc("My colleague, Squire Fyre, is tending that beacon.","She'll help you out if you run into any trouble.").also { stage = 116; player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player,40) } //Stage = 60 300 -> player("Can you really see it from this far away?").also { stage++ } @@ -69,7 +70,7 @@ class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { 302 -> npc("This beacon, however, is struggling at the moment.","Do you see how the fire has died down?").also { stage++ } 303 -> player("Hmm, yes. The fire is a bit smaller and the logs look","rather charred.").also { stage++ } 304 -> npc("If a beacon's fire starts to die down, you can restore it","to its blazing glory by adding five logs.").also { stage++ } - 305 -> npc("You wouldn't mind topping this one up for me, would","you? Oh, how I love to see things burn!").also { stage++; player.questRepository.getQuest("All Fired Up").setStage(player, 70) } + 305 -> npc("You wouldn't mind topping this one up for me, would","you? Oh, how I love to see things burn!").also { stage++; player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player, 70) } 306 -> options("Oh, alright, then.","Don't you have logs of your own you can use?").also { stage++ } 307 -> when(buttonId){ 1 -> player("Oh, alright, then.").also { stage++ } @@ -94,7 +95,7 @@ class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { 410 -> npc("Imminently, I'm sure - we're just waiting for the word","from King Roald. Speaking of which, have you reported","back to him about the progress we've made?").also { stage++ } 411 -> player("Not yet, I'm afraid.").also { stage++ } 412 -> npc("Well, what are you waiting for? This is a serious","matter! I'm sure King Roald is on the edge of his","throne, waiting for the news.").also { stage++ } - 413 -> player("I'll get right on that.").also { stage = 1000; player.questRepository.getQuest("All Fired Up").setStage(player,90) } + 413 -> player("I'll get right on that.").also { stage = 1000; player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player,90) } 420 -> npc("Yes... YES HAHAHAHA FIRE").also { stage = 412 } 1000 -> end() diff --git a/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/KingRoaldAFUDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/KingRoaldAFUDialogue.kt index bd84874bc..4c5d4480a 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/KingRoaldAFUDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/KingRoaldAFUDialogue.kt @@ -4,6 +4,7 @@ import core.game.node.entity.skill.Skills import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests class KingRoaldAFUDialogue(val questStage: Int) : DialogueFile() { @@ -85,7 +86,7 @@ class KingRoaldAFUDialogue(val questStage: Int) : DialogueFile() { 17 -> { player("Thank you, Your Majesty. I'll seek out Blaze", "right away.") stage = END_DIALOGUE - player!!.questRepository.getQuest("All Fired Up").start(player) + player!!.questRepository.getQuest(Quests.ALL_FIRED_UP).start(player) } END_DIALOGUE -> end() @@ -103,7 +104,7 @@ class KingRoaldAFUDialogue(val questStage: Int) : DialogueFile() { 6 -> npc("There is much more to be done and this is but a", "pittance compared to what I'm willing to offer for", "further assistance!").also { stage++ } 7 -> { end() - player!!.questRepository.getQuest("All Fired Up").finish(player) + player!!.questRepository.getQuest(Quests.ALL_FIRED_UP).finish(player) } } } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/SquireFyreDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/SquireFyreDialogue.kt index ca33ddff3..606e784dc 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/SquireFyreDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/SquireFyreDialogue.kt @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import content.minigame.allfiredup.BeaconState import core.api.* +import content.data.Quests @Initializable class SquireFyreDialogue(player: Player? = null) : DialoguePlugin(player){ @@ -15,7 +16,7 @@ class SquireFyreDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player.questRepository.getQuest("All Fired Up").getStage(player) + val qstage = player.questRepository.getQuest(Quests.ALL_FIRED_UP).getStage(player) when(qstage){ 40 -> player("Hi there. I'm helping Blaze and King Roald test the","beacon network. Can you see it from here? Blaze said","you have pretty sharp eyes.").also { stage = 100 } else -> npc("Carry on, friend.").also { stage = 1000 } @@ -27,7 +28,7 @@ class SquireFyreDialogue(player: Player? = null) : DialoguePlugin(player){ when(stage){ 100 -> npc("Of course I can see it. I haven't spent my entire life","practising my seeing skills for nothing! I'm happy to","report that the fire near Blaze is burning brightly.").also { stage++ } 101 -> player("Terrific! Blaze has asked me to light this fire as well, so","he can see how things look from his vantage point.").also { stage++ } - 102 -> npc("Be my guest!").also { stage++; player.questRepository.getQuest("All Fired Up").setStage(player,50); setVarbit(player, 5146, BeaconState.DYING.ordinal) } + 102 -> npc("Be my guest!").also { stage++; player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player,50); setVarbit(player, 5146, BeaconState.DYING.ordinal) } 103 -> options("How do I light the beacon?","I suppose you don't have any logs I could have?","Okay, thanks.").also { stage++ } 104 -> when(buttonId){ 1 -> player("How do I light the beacon?").also { stage = 110 } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSCutsceneTrigger.kt b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSCutsceneTrigger.kt index 8ede2454c..d498fd07a 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSCutsceneTrigger.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSCutsceneTrigger.kt @@ -6,6 +6,7 @@ import core.game.activity.ActivityManager import core.game.node.entity.Entity import core.game.node.entity.player.Player import org.rs09.consts.Items +import content.data.Quests class DSCutsceneTrigger : MapArea { @@ -16,7 +17,7 @@ class DSCutsceneTrigger : MapArea { override fun areaEnter(entity: Entity) { if (entity !is Player) return - val quest = entity.questRepository.getQuest("Demon Slayer") + val quest = entity.questRepository.getQuest(Quests.DEMON_SLAYER) val alreadyInCutscene = getAttribute(entity, "demon-slayer:cutscene", false) val hasSilverlight = inInventory(entity, Items.SILVERLIGHT_2402) || inEquipment(entity, Items.SILVERLIGHT_2402) diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSlayerDrainPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSlayerDrainPlugin.java index b8a37841a..253484745 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSlayerDrainPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSlayerDrainPlugin.java @@ -11,6 +11,7 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** @@ -51,7 +52,7 @@ public final class DSlayerDrainPlugin extends UseWithHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - final Quest quest = player.getQuestRepository().getQuest("Demon Slayer"); + final Quest quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); if (player.getInventory().remove(BUCKET_OF_WATER)) { player.getInventory().add(BUCKET); player.animate(ANIMATION); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayer.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayer.java index 2fc51558f..0492bb2a9 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayer.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayer.java @@ -12,6 +12,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the demon slayer quest. @@ -50,7 +51,7 @@ public class DemonSlayer extends Quest { * Constructs a new {@Code DemonSlayer} {@Code Object} */ public DemonSlayer() { - super("Demon Slayer", 16, 15, 3, 222, 0, 1, 3); + super(Quests.DEMON_SLAYER, 16, 15, 3, 222, 0, 1, 3); } @Override @@ -196,7 +197,7 @@ public class DemonSlayer extends Quest { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Demon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); switch (quest.getStage(player)) { default: interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What are you doing up here? Only the palace guards", "are allowed up here."); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerCutscene.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerCutscene.java index 7a90c6fd0..7c46309f0 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerCutscene.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerCutscene.java @@ -30,6 +30,7 @@ import core.net.packet.out.CameraViewPacket; import core.net.packet.out.MinimapState; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents the cutscene during the combat of fighting delrith the demon. @@ -121,7 +122,7 @@ public final class DemonSlayerCutscene extends CutscenePlugin { return true; } final Player player = ((Player) entity); - final Quest quest = player.getQuestRepository().getQuest("Demon Slayer"); + final Quest quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); boolean in = player.getAttribute("demon-slayer:cutscene", false); if (quest.getStage(player) == 30 && !in && (player.getEquipment().containsItem(DemonSlayer.SILVERLIGHT) || player.getInventory().containsItem(DemonSlayer.SILVERLIGHT))) { ActivityManager.start(player, "Demon Slayer Cutscene", false); @@ -422,7 +423,7 @@ public final class DemonSlayerCutscene extends CutscenePlugin { cutscene.end(); cutscene.delrith.clear(); setVarp(player, 222, 5653570, true); - player.getQuestRepository().getQuest("Demon Slayer").finish(player); + player.getQuestRepository().getQuest(Quests.DEMON_SLAYER).finish(player); end(); return true; } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerPlugin.java index 1a051a67b..87740d6df 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerPlugin.java @@ -2,7 +2,6 @@ package content.region.misthalin.varrock.quest.demonslayer; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.global.action.ClimbActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; @@ -10,12 +9,11 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** @@ -37,9 +35,6 @@ public final class DemonSlayerPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(881).getHandlers().put("option:open", this); - SceneryDefinition.forId(882).getHandlers().put("option:close", this); - SceneryDefinition.forId(882).getHandlers().put("option:climb-down", this); SceneryDefinition.forId(DRAIN_ID).getHandlers().put("option:search", this); SceneryDefinition.forId(17429).getHandlers().put("option:take", this); NPCDefinition.forId(DemonSlayerCutscene.DELRITH).getHandlers().put("option:attack", this); @@ -49,7 +44,7 @@ public final class DemonSlayerPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Demon Slayer"); + final Quest quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); final int id = node instanceof Scenery ? ((Scenery) node).getId() : node instanceof Item ? ((Item) node).getId() : ((NPC) node).getId(); switch (id) { case 880: @@ -71,23 +66,6 @@ public final class DemonSlayerPlugin extends OptionHandler { player.sendMessage("You search the castle drain and find nothing of value."); } return true; - case 881: - SceneryBuilder.replace(((Scenery) node), ((Scenery) node).transform(882)); - break; - case 882: - switch (option) { - case "climb-down": - if (node.getLocation().equals(new Location(3237, 3458, 0))) { - ClimbActionHandler.climb(player, new Animation(828), SEWER_LOCATION); - } else { - ClimbActionHandler.climbLadder(player, (Scenery) node, option); - } - break; - case "close": - SceneryBuilder.replace(((Scenery) node), ((Scenery) node).transform(881)); - break; - } - break; case 17429: if (quest.getStage(player) == 20 && player.getInventory().add(DemonSlayer.FIRST_KEY)) { setVarp(player, 222, 4757762, true); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/GypsyArisDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/GypsyArisDialogue.java index 3e318c85c..7d0c85262 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/GypsyArisDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/GypsyArisDialogue.java @@ -17,6 +17,7 @@ import core.net.packet.PacketRepository; import core.net.packet.context.CameraContext; import core.net.packet.context.CameraContext.CameraType; import core.net.packet.out.CameraViewPacket; +import content.data.Quests; /** * Represents the dialogue which handles the transcript for the gypsy aris. @@ -75,7 +76,7 @@ public final class GypsyArisDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Demon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); switch (quest.getStage(player)) { case 100: npc("Greetings young one."); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/SirPyrsinDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/SirPyrsinDialogue.java index a0fc32221..8be176a64 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/SirPyrsinDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/SirPyrsinDialogue.java @@ -1,6 +1,5 @@ package content.region.misthalin.varrock.quest.demonslayer; -import content.region.misthalin.draynor.handlers.DraynorNodePlugin; import core.api.Container; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; @@ -16,6 +15,7 @@ import org.rs09.consts.Items; import static core.api.ContentAPIKt.*; import static core.tools.DialogueConstKt.END_DIALOGUE; import static core.tools.GlobalsKt.colorize; +import content.data.Quests; /** * Represents the dialogue which handles the Sir Prysin NPC. @@ -64,7 +64,7 @@ public class SirPyrsinDialogue extends DialoguePlugin { } else if (args[0] instanceof Integer) { id = ((int) args[0]); } - quest = player.getQuestRepository().getQuest("Demon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); switch (quest.getStage(player)) { case 30: npc(id, "Have you sorted that demon out yet?"); @@ -481,7 +481,7 @@ public class SirPyrsinDialogue extends DialoguePlugin { private final void handleDefault(int buttonId) { switch (stage) { case 0: - if(getQuestStage(player, "Demon Slayer") == 100) + if(getQuestStage(player, Quests.DEMON_SLAYER) == 100) options("I am a mighty adventurer. Who are you?", "I'm not sure, I was hoping you could tell me.", "Hey can you give me another Silverlight"); else options("I am a mighty adventurer. Who are you?", "I'm not sure, I was hoping you could tell me."); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/TraibornDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/TraibornDialogue.java index b16a415a2..5ea8e3622 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/TraibornDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/TraibornDialogue.java @@ -12,6 +12,7 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; +import content.data.Quests; /** * Represents the dialogue used to handle the Traiborn NPC. @@ -60,7 +61,7 @@ public class TraibornDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Demon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); switch (quest.getStage(player)) { case 20: if (player.getAttribute("demon-slayer:traiborn", false)) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/WallyCutscenePlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/WallyCutscenePlugin.java index 918451e1c..190989d52 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/WallyCutscenePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/WallyCutscenePlugin.java @@ -10,6 +10,7 @@ import core.net.packet.PacketRepository; import core.net.packet.context.CameraContext; import core.net.packet.context.CameraContext.CameraType; import core.net.packet.out.CameraViewPacket; +import content.data.Quests; /** * Represents the wally cutscene plugin. @@ -54,7 +55,7 @@ public class WallyCutscenePlugin extends CutscenePlugin { @Override public void fade() { - player.getQuestRepository().getQuest("Demon Slayer").start(player); + player.getQuestRepository().getQuest(Quests.DEMON_SLAYER).start(player); player.getDialogueInterpreter().open(882, Repository.findNPC(882), this); } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CabinBoyJenkins.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CabinBoyJenkins.java index 149fb9609..096bbba01 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CabinBoyJenkins.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CabinBoyJenkins.java @@ -5,6 +5,9 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; + +import static core.api.ContentAPIKt.getQuestStage; /** * Represents the cabin boy jenkins dialogue. @@ -12,12 +15,6 @@ import core.game.node.entity.player.link.quest.Quest; */ @Initializable public class CabinBoyJenkins extends DialoguePlugin { - - /** - * Represents the quest instance. - */ - private Quest quest; - /** * Constructs a new {@code CabinBoyJenkins} {@code Object}. */ @@ -43,8 +40,7 @@ public class CabinBoyJenkins extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Dragon Slayer"); - switch (quest.getStage(player)) { + switch (getQuestStage(player, Quests.DRAGON_SLAYER)) { case 20: npc("Ahoy! Whay d'ye think of yer ship then?"); stage = 0; @@ -60,7 +56,7 @@ public class CabinBoyJenkins extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { + switch (getQuestStage(player, Quests.DRAGON_SLAYER)) { case 40: case 30: switch (stage) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSMagicDoorPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSMagicDoorPlugin.java index e6c55249d..0501b06f0 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSMagicDoorPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSMagicDoorPlugin.java @@ -6,6 +6,7 @@ import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.world.map.Location; import core.plugin.Plugin; +import content.data.Quests; /** * Represents the dragon slayer magic door plugin. @@ -40,7 +41,7 @@ public final class DSMagicDoorPlugin extends UseWithHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) < 20) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) < 20) { return true; } if (player.getInventory().remove(event.getUsedItem())) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSNedNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSNedNPC.java index 846fc7f90..4040b3190 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSNedNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSNedNPC.java @@ -3,6 +3,7 @@ package content.region.misthalin.varrock.quest.dragonslayer; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the dragon slayer npc. @@ -39,7 +40,7 @@ public final class DSNedNPC extends AbstractNPC { @Override public boolean isHidden(final Player player) { - return player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) != 30 && player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) != 40; + return player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 30 && player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 40; } @Override diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.kt b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.kt index 8b6bb5ec5..3bb325be9 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.kt @@ -23,13 +23,14 @@ import core.integrations.discord.Discord import core.plugin.ClassScanner.definePlugins import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests /** * Represents the dragon slayer quest. * @author Vexia - Converted to Kotlin with use of event hooks by not-Vexia */ @Initializable -class DragonSlayer : Quest("Dragon Slayer", 18, 17, 2, 176, 0, 1, 10), LoginListener { +class DragonSlayer : Quest(Quests.DRAGON_SLAYER, 18, 17, 2, 176, 0, 1, 10), LoginListener { override fun newInstance(`object`: Any?): Quest { definePlugins( CrandorMapPlugin(), @@ -323,7 +324,7 @@ class DragonSlayer : Quest("Dragon Slayer", 18, 17, 2, 176, 0, 1, 10), LoginList } override fun login(player: Player) { - if (getQuestStage(player, this.name) == 20) { + if (getQuestStage(player, this.quest) == 20) { player.hook(Event.SpellCast, SpellCastHook) player.hook(Event.PickedUp, PickedUpHook) } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerCutscene.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerCutscene.java index 40044126c..b1a4fbc34 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerCutscene.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerCutscene.java @@ -28,6 +28,7 @@ import core.net.packet.out.MinimapState; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents the dragon slayer cutscene. @@ -103,7 +104,7 @@ public final class DragonSlayerCutscene extends CutscenePlugin { player.animate(ANIMATION); player.getDialogueInterpreter().close(); player.getDialogueInterpreter().sendDialogue("You are knocked unconscious and later awake on an ash-strewn", "beach."); - player.getQuestRepository().getQuest("Dragon Slayer").setStage(player, 40); + player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).setStage(player, 40); player.getSavedData().getQuestData().setDragonSlayerAttribute("repaired", false); setVarp(player, 177, 8257540); setVarp(player, 176, 8); @@ -292,7 +293,7 @@ public final class DragonSlayerCutscene extends CutscenePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Dragon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER); if (args.length > 1) { cutscene = ((DragonSlayerCutscene) args[1]); npc("Ah it's good to feel that salt spray on my face once", "again!"); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java index 85af8e2ed..f16658df0 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java @@ -25,6 +25,7 @@ import core.plugin.Plugin; import java.util.List; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** @@ -101,24 +102,14 @@ public final class DragonSlayerPlugin extends OptionHandler { SceneryDefinition.forId(25161).getHandlers().put("option:climb-over", this); NPCDefinition.forId(742).getHandlers().put("option:attack", this); NPCDefinition.forId(745).getHandlers().put("option:attack", this); - // guild - SceneryDefinition.forId(24357).getHandlers().put("option:climb-up", this); - SceneryDefinition.forId(10558).getHandlers().put("option:open", this); - SceneryDefinition.forId(10560).getHandlers().put("option:climb-up", this); return this; } @Override public boolean handle(final Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Dragon Slayer"); + final Quest quest = player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER); final int id = node instanceof Item ? ((Item) node).getId() : node instanceof Scenery ? ((Scenery) node).getId() : ((NPC) node).getId(); switch (id) { - case 10560: - ClimbActionHandler.climb(player, new Animation(828), Location.create(3191, 3355, 0)); - break; - case 10558: - ClimbActionHandler.climb(player, new Animation(-1), Location.create(3189, 9758, 0)); - return true; case 1755: if (player.getLocation().withinDistance(Location.create(2939, 9656, 0))) { ClimbActionHandler.climb(player, new Animation(828), Location.create(2939, 3256, 0)); @@ -127,20 +118,13 @@ public final class DragonSlayerPlugin extends OptionHandler { return true; } break; - case 24357: - if (player.getLocation().getDistance(Location.create(3188, 3358, 0)) < 3) { - ClimbActionHandler.climb(player, new Animation(828), Location.create(3188, 3354, 1)); - } else { - ClimbActionHandler.climbLadder(player, (Scenery) node, "climb-up"); - } - break; case 742: - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) == 40 && (player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD))) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) == 40 && (player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD))) { player.getPacketDispatch().sendMessage("You have already slain the dragon. Now you just need to return to Oziach for"); player.getPacketDispatch().sendMessage("your reward!"); return true; } - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) > 40) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) > 40) { player.getPacketDispatch().sendMessage("You have already slain Elvarg the dragon."); return true; } @@ -153,16 +137,16 @@ public final class DragonSlayerPlugin extends OptionHandler { movement.run(player, 10); return true; } - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) == 40 && (player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD))) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) == 40 && (player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD))) { player.getPacketDispatch().sendMessage("You have already slain the dragon. Now you just need to return to Oziach for"); player.getPacketDispatch().sendMessage("your reward!"); return true; } - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) > 40) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) > 40) { player.getPacketDispatch().sendMessage("You have already slain the dragon."); return true; } - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) == 40 && !player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD)) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) == 40 && !player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD)) { ForceMovement movement = new ForceMovement(player, player.getLocation(), player.getLocation().transform(player.getLocation().getX() == 2845 ? 2 : -2, 0, 0), new Animation(839)); movement.run(player, 10); if (player.getLocation().getX() <= 2845) { @@ -181,7 +165,7 @@ public final class DragonSlayerPlugin extends OptionHandler { player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 2); break; case 2606: - if (player.getLocation().getY() < 9600 && !player.getSavedData().getQuestData().getDragonSlayerAttribute("memorized") && player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) != 100) { + if (player.getLocation().getY() < 9600 && !player.getSavedData().getQuestData().getDragonSlayerAttribute("memorized") && player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 100) { player.getPacketDispatch().sendMessage("The door is securely locked."); } else { if (!player.getSavedData().getQuestData().getDragonSlayerAttribute("memorized")) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java index 047821ec9..fa25a9fdb 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java @@ -20,12 +20,12 @@ import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.tools.RandomFunction; import content.global.handlers.item.equipment.special.DragonfireSwingHandler; import static core.api.ContentAPIKt.calculateDragonfireMaxHit; +import content.data.Quests; /** @@ -154,14 +154,14 @@ public final class ElvargNPC extends AbstractNPC { return super.isAttackable(entity, style, message); } final Player player = (Player) entity; - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) == 40 && (player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD))) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) == 40 && (player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD))) { if(message) { player.getPacketDispatch().sendMessage("You have already slain the dragon. Now you just need to return to Oziach for"); player.getPacketDispatch().sendMessage("your reward!"); } return false; } - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) > 40) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) > 40) { if(message) { player.getPacketDispatch().sendMessage("You have already slain Elvarg."); } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/GuildmasterDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/GuildmasterDialogue.java index c96f8361a..a94d7ba5e 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/GuildmasterDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/GuildmasterDialogue.java @@ -5,6 +5,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; +import content.data.Quests; /** * Represents the guild master dialogue at the champions guild related to dragon slayer. @@ -46,7 +47,7 @@ public final class GuildmasterDialogue extends DialoguePlugin { if (player.getQuestRepository().getPoints() < 32) { return true; } - quest = player.getQuestRepository().getQuest("Dragon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER); npc("Greetings!"); if (quest.getStage(player) == 10) { stage = 0; @@ -225,7 +226,7 @@ public final class GuildmasterDialogue extends DialoguePlugin { stage = 112; break; case 112: - npc("Then, of course, you'll need to find a captain willy to", "sail to Crandor, and I'm not sure where you'd find one", "of them!"); + npc("Then, of course, you'll need to find a captain willing to", "sail to Crandor, and I'm not sure where you'd find one", "of them!"); stage = 113; break; case 113: @@ -286,7 +287,7 @@ public final class GuildmasterDialogue extends DialoguePlugin { handleDescription(buttonId); break; case 2: - player("I talked to Oziach and he have me a quest."); + player("I talked to Oziach and he gave me a quest."); stage = 3; break; } @@ -332,7 +333,7 @@ public final class GuildmasterDialogue extends DialoguePlugin { stage = 14; break; case 14: - npc("Some refuegees managed to escape in fishing boats.", "They landed on the coast, north of Rimmington, and", "set up camp but the dragon followed them and burned", "the camp to the ground."); + npc("Some refugees managed to escape in fishing boats.", "They landed on the coast, north of Rimmington, and", "set up camp but the dragon followed them and burned", "the camp to the ground."); stage = 15; break; case 15: diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDSDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDSDialogue.kt index 5cecdad45..7b59f9c70 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDSDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDSDialogue.kt @@ -1,9 +1,9 @@ package content.region.misthalin.varrock.quest.dragonslayer -import content.region.misthalin.varrock.quest.dragonslayer.DragonSlayer import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests private const val SHIP_DIALOGUE = 2000 class NedDSDialogue(val questStage: Int) : DialogueFile() { @@ -49,7 +49,7 @@ class NedDSDialogue(val questStage: Int) : DialogueFile() { 2006 -> { if (player!!.inventory.remove(DragonSlayer.CRANDOR_MAP)) { interpreter!!.sendItemMessage(DragonSlayer.CRANDOR_MAP.id, "You hand the map to Ned.") - player!!.questRepository.getQuest("Dragon Slayer").setStage(player, 30) + player!!.questRepository.getQuest(Quests.DRAGON_SLAYER).setStage(player, 30) stage++ } else stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDialogue.kt index 46d87047a..dcc331bf6 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDialogue.kt @@ -11,6 +11,7 @@ import content.region.desert.alkharid.quest.princealirescue.NedPARDialogue import content.region.misthalin.lumbridge.diary.NedDiaryDialogue import core.game.world.GameWorld.settings import core.tools.END_DIALOGUE +import content.data.Quests /** @@ -41,8 +42,8 @@ class NedDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(pl override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { 0 -> { - val dSlayerStage = player.questRepository.getStage("Dragon Slayer") - val parStage = player.questRepository.getStage("Prince Ali Rescue") + val dSlayerStage = player.questRepository.getStage(Quests.DRAGON_SLAYER) + val parStage = player.questRepository.getStage(Quests.PRINCE_ALI_RESCUE) showTopics( IfTopic("I'd like to talk about Dragon Slayer.", NedDSDialogue(dSlayerStage), dSlayerStage == 20 || dSlayerStage == 30), IfTopic("I'd like to talk about Prince Ali Rescue.", NedPARDialogue(parStage), parStage == 20 || parStage == 30 || parStage == 40 || parStage == 50), diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/OziachDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/OziachDialogue.java index c24850f15..faebde0bd 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/OziachDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/OziachDialogue.java @@ -5,6 +5,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue used to handle the oziach dialogue. @@ -43,7 +44,7 @@ public final class OziachDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Dragon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER); player.debug("" + quest.getStage(player)); switch (quest.getStage(player)) { case 100: @@ -261,7 +262,7 @@ public final class OziachDialogue extends DialoguePlugin { case 6: end(); int heads = player.getInventory().getAmount(DragonSlayer.ELVARG_HEAD); - if (player.getInventory().remove(new Item(DragonSlayer.ELVARG_HEAD.getId(),heads)) && !player.getQuestRepository().getQuest("Dragon Slayer").isCompleted(player)) { + if (player.getInventory().remove(new Item(DragonSlayer.ELVARG_HEAD.getId(),heads)) && !player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).isCompleted(player)) { quest.finish(player); } break; diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainDialogue.java index 9a69a3bd8..84feb6bfc 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.integrations.discord.Discord; +import content.data.Quests; /** * Represents the dialogue used to handle the wormbrain npc related to the @@ -51,7 +52,7 @@ public final class WormbrainDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Dragon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER); switch (quest.getStage(player)) { default: npc("Whut you want?"); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainNPC.java index 25c81de50..f755bd88f 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainNPC.java @@ -6,6 +6,7 @@ import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the worm brain npc. @@ -44,7 +45,7 @@ public final class WormbrainNPC extends AbstractNPC { public void finalizeDeath(final Entity killer) { super.finalizeDeath(killer); if (killer instanceof Player) { - if (((Player) killer).getQuestRepository().getQuest("Dragon Slayer").getStage(killer.asPlayer()) == 20 && !((Player) killer).getInventory().containsItem(DragonSlayer.WORMBRAIN_PIECE) && !((Player) killer).getBank().containsItem(DragonSlayer.WORMBRAIN_PIECE)) { + if (((Player) killer).getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(killer.asPlayer()) == 20 && !((Player) killer).getInventory().containsItem(DragonSlayer.WORMBRAIN_PIECE) && !((Player) killer).getBank().containsItem(DragonSlayer.WORMBRAIN_PIECE)) { GroundItemManager.create(DragonSlayer.WORMBRAIN_PIECE, getLocation(), ((Player) killer)); ((Player) killer).getPacketDispatch().sendMessage("Wormbrain drops a map piece on the floor."); } @@ -55,7 +56,7 @@ public final class WormbrainNPC extends AbstractNPC { public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (entity instanceof Player) { final Player player = (Player) entity; - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) != 20) { + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 20) { if(message) { player.getPacketDispatch().sendMessage("The goblin is already in prison. You have no reason to attack him."); } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ZombieRatNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ZombieRatNPC.java index 679922780..65a602673 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ZombieRatNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ZombieRatNPC.java @@ -8,6 +8,9 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.world.map.Location; import core.tools.RandomFunction; +import content.data.Quests; + +import static core.api.ContentAPIKt.getQuestStage; /** * Represents a zombie rat npc related to dragon slayer and witch's potion. @@ -52,12 +55,10 @@ public final class ZombieRatNPC extends AbstractNPC { super.finalizeDeath(killer); if (killer instanceof Player) { final Player p = ((Player) killer); - Quest quest = p.getQuestRepository().getQuest("Dragon Slayer"); if (RandomFunction.random(0, 4) == 2) { - GroundItemManager.create(DragonSlayer.RED_KEY, getLocation(), ((Player) killer)); + GroundItemManager.create(DragonSlayer.RED_KEY, getLocation(), p); } - quest = p.getQuestRepository().getQuest("Witch's Potion"); - if (quest.getStage(p) > 0 && quest.getStage(p) < 100) { + if (getQuestStage(p, Quests.WITCHS_POTION) > 0 && getQuestStage(p, Quests.WITCHS_POTION) < 100) { GroundItemManager.create(RAT_TAIL, getLocation(), p); } GroundItemManager.create(new Item(526), getLocation(), p); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/AvanDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/AvanDialogue.kt index 6b8c402a8..1961c4586 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/AvanDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/AvanDialogue.kt @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable class AvanDialogue (player: Player? = null): DialoguePlugin(player) { @@ -19,7 +20,7 @@ class AvanDialogue (player: Player? = null): DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player?.questRepository?.getStage("Family Crest") ?: -1 + val qstage = player?.questRepository?.getStage(Quests.FAMILY_CREST) ?: -1 if (qstage == 100) { options("Can you change my gauntlets for me?", "Nevermind") @@ -101,7 +102,7 @@ class AvanDialogue (player: Player? = null): DialoguePlugin(player) { 15 -> player("Well, I'll see what I can do.").also{ stage = 1000 - player.questRepository.getQuest("Family Crest").setStage(player, 14) + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 14) } 100 -> player("I'm still after that 'perfect gold'.").also { stage++ } @@ -128,7 +129,7 @@ class AvanDialogue (player: Player? = null): DialoguePlugin(player) { "with a red precious stone, and a perfect gold ring to match.").also { stage = 1000 } 300 -> sendDialogue("You hand Avan the perfect gold ring and necklace.").also{ - player.questRepository.getQuest("Family Crest").setStage(player, 16) + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 16) player.inventory.remove(Item(774), Item(773)) player.inventory.add(CREST_PIECE_AVAN) stage++ diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/BootDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/BootDialogue.kt index 8a29de6e1..89dd53a2f 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/BootDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/BootDialogue.kt @@ -5,6 +5,7 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable +import content.data.Quests @Initializable @@ -15,7 +16,7 @@ class BootDialogue (player: Player? = null): DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player?.questRepository?.getStage("Family Crest") ?: -1 + val qstage = player?.questRepository?.getStage(Quests.FAMILY_CREST) ?: -1 if(qstage < 14 || qstage > 14){ npc(FacialExpression.OLD_NORMAL,"Hello tall person.") @@ -61,7 +62,7 @@ class BootDialogue (player: Player? = null): DialoguePlugin(player){ 21 -> npc("I don't believe it's exactly easy to get to though...").also { stage = 1000 - player.questRepository.getQuest("Family Crest").setStage(player, 15) + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 15) } 1000 -> end() } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/CalebDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/CalebDialogue.kt index e25e5e60c..618bf40ec 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/CalebDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/CalebDialogue.kt @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable @@ -20,7 +21,7 @@ class CalebDialogue (player: Player? = null): DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player?.questRepository?.getStage("Family Crest") ?: -1 + val qstage = player?.questRepository?.getStage(Quests.FAMILY_CREST) ?: -1 if (qstage == 100) { options("Can you change my gauntlets for me?", "Nevermind") @@ -122,7 +123,7 @@ class CalebDialogue (player: Player? = null): DialoguePlugin(player) { 207 -> when(buttonId){ 1 -> npc("You will? It would help me a lot!").also{stage = 1000}.also{ - player.questRepository.getQuest("Family Crest").setStage(player, 11) + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 11) } 2 -> npc("It's a valuable family heirloom. " , @@ -141,7 +142,7 @@ class CalebDialogue (player: Player? = null): DialoguePlugin(player) { 301 -> sendDialogue("You exchange the fish for Caleb's piece of the crest.").also{stage++}.also{ player.inventory.remove(Item(315),Item(329), Item(361), Item(365), Item(373)) player.inventory.add(CREST_PIECE) - player.questRepository.getQuest("Family Crest").setStage(player, 12) + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 12) } 302 -> options("Uh... what happened to the rest of it?" , "Thank you very much!").also{stage++} diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt index fc2655329..1ca3002f0 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt @@ -1,4 +1,4 @@ -package plugin.quest.members.familycrest +package content.region.misthalin.varrock.quest.familycrest import core.game.node.entity.Entity @@ -10,10 +10,11 @@ import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneBuilder import core.plugin.Initializable import core.plugin.Plugin -import content.region.misthalin.varrock.quest.familycrest.ChronozonNPC -import core.game.node.item.Item +import core.api.getQuestStage +import core.api.hasAnItem import org.rs09.consts.Items import org.rs09.consts.NPCs +import content.data.Quests @Initializable @@ -23,7 +24,7 @@ class ChronozonCaveZone: MapZone("FC ChronozoneZone", true), Plugin { var chronozon = ChronozonNPC(NPCs.CHRONOZON_667, spawnLoc) override fun configure() { - register(ZoneBorders(3082, 9929, 3091, 9940)) + register(ZoneBorders(3079, 9927, 3095, 9944)) } override fun move(e: Entity?, from: Location?, to: Location?): Boolean { @@ -34,8 +35,9 @@ class ChronozonCaveZone: MapZone("FC ChronozoneZone", true), Plugin { if (e != null) { if (e.isPlayer) { val player = e as Player - if (player.questRepository.getQuest("Family Crest").getStage(e) in (19..99) && - !player.hasItem(Item(Items.CREST_PART_781))){ + if (getQuestStage(player,Quests.FAMILY_CREST) in (19..99) && + !hasAnItem(player, Items.CREST_PART_781).exists() + ){ // Chronozon is allowed to spawn (quest stage right and the player doesn't have the crest part) // Now check there is not one already if(!RegionManager.getLocalNpcs(spawnLoc, 5).contains(chronozon)){ @@ -50,6 +52,18 @@ class ChronozonCaveZone: MapZone("FC ChronozoneZone", true), Plugin { } return false } + + + override fun leave(e: Entity?, logout: Boolean): Boolean { + if (e!!.isPlayer){ + if (RegionManager.getLocalPlayers(spawnLoc, 5).size <= 0){ + // There are no other players close by + chronozon.clear() + } + } + return super.leave(e, logout) + } + override fun newInstance(arg: Unit?): Plugin { ZoneBuilder.configure(this) return this diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonNPC.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonNPC.kt index 6bdbc0d27..954e5b02e 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonNPC.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonNPC.kt @@ -7,6 +7,7 @@ import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.player.Player import core.game.world.map.Location import org.rs09.consts.NPCs +import content.data.Quests class ChronozonNPC(id: Int, location: Location?) : AbstractNPC(NPCs.CHRONOZON_667, Location(3086, 9936, 0)){ @@ -29,14 +30,6 @@ class ChronozonNPC(id: Int, location: Location?) : AbstractNPC(NPCs.CHRONOZON_66 return intArrayOf(NPCs.CHRONOZON_667) } - override fun handleTickActions() { - super.handleTickActions() - if (!targetplayer.isActive || targetplayer.getLocation().getDistance(getLocation()) > 15) { - clear() - } - - } - override fun checkImpact(state: BattleState?) { if (state != null) { if(amountOfAirDamageTaken == 0 || amountOfWaterDamageTaken == 0 || @@ -80,8 +73,10 @@ class ChronozonNPC(id: Int, location: Location?) : AbstractNPC(NPCs.CHRONOZON_66 override fun finalizeDeath(killer: Entity?) { if(killer == targetplayer) { - if (targetplayer.questRepository.getStage("Family Crest") != 20){ - targetplayer.questRepository.getQuest("Family Crest").setStage(targetplayer, 20) + if (targetplayer.questRepository.getStage(Quests.FAMILY_CREST) != 20){ + targetplayer.questRepository.getQuest(Quests.FAMILY_CREST).setStage(targetplayer, 20) + // Make sure to despawn Chronozon + this.clear() } } clear() diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/DimintheisDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/DimintheisDialogue.kt index 746f1ebfd..e5e2f9ba0 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/DimintheisDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/DimintheisDialogue.kt @@ -6,6 +6,7 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable @@ -18,19 +19,17 @@ class DimintheisDialogue(player: Player? = null): core.game.dialogue.DialoguePlu return DimintheisDialogue(player) } - private val questName = "Family Crest" - override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val questStage = getQuestStage(player, questName) - val questComplete = isQuestComplete(player, questName) + val questStage = getQuestStage(player, Quests.FAMILY_CREST) + val questComplete = isQuestComplete(player, Quests.FAMILY_CREST) if (questStage == 20 && inInventory(player, Items.FAMILY_CREST_782)) { player("I have retrieved your crest.").also{ stage = 5000 } return true } - val hasGauntlets = hasAnItem(player, Items.COOKING_GAUNTLETS_775, Items.GOLDSMITH_GAUNTLETS_776, Items.CHAOS_GAUNTLETS_777, Items.FAMILY_GAUNTLETS_778).container != null + val hasGauntlets = hasAnItem(player, arrayOf(Items.COOKING_GAUNTLETS_775, Items.GOLDSMITH_GAUNTLETS_776, Items.CHAOS_GAUNTLETS_777, Items.FAMILY_GAUNTLETS_778), true).container != null if (questComplete && hasGauntlets) { npc("Thank you for saving our family honour, ", @@ -134,7 +133,7 @@ class DimintheisDialogue(player: Player? = null): core.game.dialogue.DialoguePlu 1 -> npc("I thank you greatly adventurer!").also { stage++ } 2 -> npc("I realise it was a lot to ask of a stranger.").also { stage = 1000 } } - 2012 -> if(startQuest(player, questName)) { + 2012 -> if(startQuest(player, Quests.FAMILY_CREST)) { npc("If you find Caleb, or my other sons... please... ", "let them know their father still loves them...").also { stage = 1000 } } else { @@ -167,7 +166,7 @@ class DimintheisDialogue(player: Player? = null): core.game.dialogue.DialoguePlu "they should be able to imbue them with a skill for you.").also { stage = 1000 if (removeItem(player, Items.FAMILY_CREST_782)) { - finishQuest(player, questName) + finishQuest(player, Quests.FAMILY_CREST) } } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/FamilyCrest.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/FamilyCrest.kt index 0303ba3f8..2cedd6105 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/FamilyCrest.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/FamilyCrest.kt @@ -10,7 +10,7 @@ import core.game.node.entity.skill.Skills import core.plugin.Initializable import core.tools.Log import org.rs09.consts.Items -import core.tools.SystemLogger +import content.data.Quests /** * Represents the "Family Crest" quest. @@ -18,7 +18,7 @@ import core.tools.SystemLogger */ @Initializable -class FamilyCrest: Quest("Family Crest", 59, 58, 1, 148, 0, 1, 11) { +class FamilyCrest: Quest(Quests.FAMILY_CREST, 59, 58, 1, 148, 0, 1, 11) { override fun newInstance(`object`: Any?): Quest { return this diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonAntiPoisonInteraction.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonAntiPoisonInteraction.kt index 2ed748bab..9c12e517a 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonAntiPoisonInteraction.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonAntiPoisonInteraction.kt @@ -6,6 +6,7 @@ import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType +import content.data.Quests class JohnathonAntiPosionInteraction: InteractionListener { override fun defineListeners() { @@ -14,14 +15,14 @@ class JohnathonAntiPosionInteraction: InteractionListener { onUseWith(IntType.NPC, poisons, NPCs.JOHNATHON_668){ player, used, with -> val npc = with.asNpc() val antip = used.asItem() - val stage = getQuestStage(player, "Family Crest") + val stage = getQuestStage(player, Quests.FAMILY_CREST) val index = poisons.indexOf(used.id) val returnItem = if(index + 1 == poisons.size) Items.VIAL_229 else poisons[index + 1] if(stage == 17 && removeItem(player, antip)){ addItem(player, returnItem) - setQuestStage(player, "Family Crest", 18) + setQuestStage(player, Quests.FAMILY_CREST, 18) openDialogue(player, NPCs.JOHNATHON_668, npc) } else { sendMessage(player, "Nothing interesting happens.") diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonDialogue.kt index 8942d0ab8..c0e6d6f35 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonDialogue.kt @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable class JohnathonDialogue(player: Player? = null): DialoguePlugin(player) { @@ -18,7 +19,7 @@ class JohnathonDialogue(player: Player? = null): DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player?.questRepository?.getStage("Family Crest") ?: -1 + val qstage = player?.questRepository?.getStage(Quests.FAMILY_CREST) ?: -1 if (qstage == 100) { options("Can you change my gauntlets for me?", "Nevermind") @@ -53,7 +54,7 @@ class JohnathonDialogue(player: Player? = null): DialoguePlugin(player) { "too much... My head... " , "will not... stop spinning...").also { stage++ } 4 -> sendDialogue("Sweat is pouring down Jonathons' face.").also { stage = 1000 - player.questRepository.getQuest("Family Crest").setStage(player, 17) + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 17) } 100 -> npc("Ooooh... thank you... Wow! " , @@ -68,7 +69,7 @@ class JohnathonDialogue(player: Player? = null): DialoguePlugin(player) { "I lost a lot of equipment in our last battle when he " , "bested me and forced me away from his den. He probably still has it now.").also{ stage = 200 - player.questRepository.getQuest("Family Crest").setStage(player, 19) + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 19) } 200 -> options("So is this Chronozon hard to defeat?", "Where can I find Chronozon?", "So how did you end up getting poisoned?", "I will be on my way now.").also{stage++} diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/WitchavenLeverInteraction.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/WitchavenLeverInteraction.kt index d0b7f8541..af534f4d8 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/WitchavenLeverInteraction.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/WitchavenLeverInteraction.kt @@ -11,6 +11,7 @@ import core.net.packet.out.ConstructScenery import core.net.packet.out.UpdateAreaPosition import core.game.interaction.InteractionListener import core.game.interaction.IntType +import content.data.Quests fun doDoor(player: Player, scenery: Scenery) { val d = if(scenery.rotation == 0 || scenery.rotation == 3) { -1 } else { 0 } @@ -50,7 +51,7 @@ class WitchavenLeverInteraction : InteractionListener { override fun defineListeners() { on(LEVERS, IntType.SCENERY, "pull") { player, node -> val baseId = if(node.id % 2 == 0) { node.id - 1 } else { node.id } - if(player.questRepository.getQuest("Family Crest").getStage(player) == 0) { + if(player.questRepository.getQuest(Quests.FAMILY_CREST).getStage(player) == 0) { player.sendMessage("Nothing interesting happens.") } val old = player.getAttribute("family-crest:witchaven-lever:${baseId}", false) @@ -80,7 +81,7 @@ class WitchavenLeverInteraction : InteractionListener { val northA = player.getAttribute("family-crest:witchaven-lever:${NORTH_LEVER_A}", false) val northB = player.getAttribute("family-crest:witchaven-lever:${NORTH_LEVER_B}", false) val south = player.getAttribute("family-crest:witchaven-lever:${SOUTH_LEVER}", false) - val questComplete = player.questRepository.getQuest("Family Crest").getStage(player) >= 100 + val questComplete = player.questRepository.getQuest(Quests.FAMILY_CREST).getStage(player) >= 100 // Authentic door formulae from https://gitlab.com/open-runescape-classic/core/-/blob/develop/server/plugins/com/openrsc/server/plugins/authentic/quests/members/FamilyCrest.java#L575-657 val canPass = when(node.id) { NORTH_DOOR -> !northA && (south || northB) diff --git a/Server/src/main/content/region/misthalin/varrock/quest/gertrude/FluffNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/FluffNPC.java index 7fa4028e2..fd90e43a0 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/gertrude/FluffNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/FluffNPC.java @@ -4,6 +4,7 @@ import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the plugin used for the fluff npc. @@ -41,7 +42,7 @@ public final class FluffNPC extends AbstractNPC { @Override public boolean isHidden(final Player player) { - if (player.getQuestRepository().getQuest("Gertrude's Cat").getStage(player) < 20) { + if (player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT).getStage(player) < 20) { return true; } return player.getAttribute("hidefluff", 0L) > System.currentTimeMillis(); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/gertrude/GertrudesCat.java b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/GertrudesCat.java index 0551c0b26..c4eb02518 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/gertrude/GertrudesCat.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/GertrudesCat.java @@ -8,6 +8,9 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.addItemOrBank; +import content.data.Quests; + /** * Represents the gertrudes fortress quest. * @author 'Vexia @@ -19,7 +22,7 @@ public class GertrudesCat extends Quest { * Constructs a new {@code GertrudesCat} {@code Object}. */ public GertrudesCat() { - super("Gertrude's Cat", 67, 66, 1, 180, 0, 1, 100); + super(Quests.GERTRUDES_CAT, 67, 66, 1, 180, 0, 1, 100); } @Override @@ -79,12 +82,15 @@ public class GertrudesCat extends Quest { player.getPacketDispatch().sendString("1525 Cooking XP", 277, 10 + 2); player.getPacketDispatch().sendString("A chocolate cake", 277, 11 + 2); player.getPacketDispatch().sendString("A bowl of stew", 277, 12 + 2); - player.getPacketDispatch().sendString("Raise cats.", 277, 13 + 2); + player.getPacketDispatch().sendString("The ability to raise cats", 277, 13 + 2); player.getSkills().addExperience(Skills.COOKING, 1525); player.getPacketDispatch().sendItemZoomOnInterface(kitten.getId(), 240, 277, 3 + 2); setStage(player, 100); - player.getInventory().add(kitten); - player.getFamiliarManager().summon(kitten, true); + if (player.getFamiliarManager().hasFamiliar()) { + addItemOrBank(player, kitten.getId(), 1); + } else { + player.getFamiliarManager().summon(kitten, true, false); + } final Item cake = new Item(1897); final Item stew = new Item(2003); if (!player.getInventory().add(cake)) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/gertrude/LumberKittenNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/LumberKittenNPC.java index 9b1714bd4..51ab6f1b5 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/gertrude/LumberKittenNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/LumberKittenNPC.java @@ -7,6 +7,7 @@ import core.game.world.GameWorld; import core.game.world.map.Location; import core.plugin.Initializable; import core.tools.RandomFunction; +import content.data.Quests; /** * Represents the lumber kittens at the lumber yard. @@ -77,7 +78,7 @@ public final class LumberKittenNPC extends AbstractNPC { @Override public boolean isHidden(final Player player) { - Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); + Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); if (hidden) { return true; } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietDialogue.java index b8d37f6d0..7d6b190e4 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietDialogue.java @@ -15,6 +15,7 @@ import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; import core.game.world.repository.Repository; import core.game.world.update.flag.context.Animation; +import content.data.Quests; /** * Represents the dialogue of the juliet NPC. @@ -67,7 +68,7 @@ public final class JulietDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest("Romeo & Juliet"); + quest = player.getQuestRepository().getQuest(Quests.ROMEO_JULIET); npc = (NPC) args[0]; if (args.length > 1) { cutscene = (JulietCutscenePlugin) args[1]; @@ -123,7 +124,7 @@ public final class JulietDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Romeo & Juliet"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROMEO_JULIET); final NPC phil = cutscene != null ? cutscene.getPhillipia() : (NPC) Repository.findNPC(3325); final NPC dad = cutscene != null ? cutscene.getNPCS().get(2) : (NPC) Repository.findNPC(3324); switch (stage) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietNPC.java index 549692660..d87164ef7 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietNPC.java @@ -4,6 +4,7 @@ import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the juliet npc. @@ -41,7 +42,7 @@ public final class JulietNPC extends AbstractNPC { @Override public boolean isHidden(final Player player) { - return player.getQuestRepository().getQuest("Romeo & Juliet").getStage(player) > 60 && player.getQuestRepository().getQuest("Romeo & Juliet").getStage(player) < 100; + return player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).getStage(player) > 60 && player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).getStage(player) < 100; } @Override diff --git a/Server/src/main/content/region/misthalin/varrock/quest/romeo/RJCutscenePlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RJCutscenePlugin.java index ecb1d1d4d..17a33f20b 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/romeo/RJCutscenePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RJCutscenePlugin.java @@ -21,6 +21,7 @@ import core.net.packet.context.CameraContext; import core.net.packet.context.CameraContext.CameraType; import core.plugin.Initializable; import core.net.packet.out.CameraViewPacket; +import content.data.Quests; /** * Represents the romeo and juliet cutscene plugin. @@ -93,7 +94,7 @@ public final class RJCutscenePlugin extends CutscenePlugin { @Override public void fade() { - player.getQuestRepository().getQuest("Romeo & Juliet").finish(player); + player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).finish(player); } @Override @@ -169,7 +170,7 @@ public final class RJCutscenePlugin extends CutscenePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - Quest quest = player.getQuestRepository().getQuest("Romeo & Juliet"); + Quest quest = player.getQuestRepository().getQuest(Quests.ROMEO_JULIET); switch (stage) { case 0: interpreter.sendOptions("Select an Option", "No sorry. I haven't seen her.", "Perhaps I could help to find her for you?"); @@ -771,7 +772,7 @@ public final class RJCutscenePlugin extends CutscenePlugin { @Override public boolean open(Object... args) { - Quest quest = player.getQuestRepository().getQuest("Romeo & Juliet"); + Quest quest = player.getQuestRepository().getQuest(Quests.ROMEO_JULIET); npc = (NPC) args[0]; if (args.length > 1) { cutscene = (RJCutscenePlugin) args[1]; diff --git a/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoJuliet.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoJuliet.java index 744214e26..2e5945316 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoJuliet.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoJuliet.java @@ -3,6 +3,7 @@ package content.region.misthalin.varrock.quest.romeo; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the romeo and juliet quest. @@ -15,7 +16,7 @@ public class RomeoJuliet extends Quest { * Constructs a new {@code RomeoJuliet} {@code Object}. */ public RomeoJuliet() { - super("Romeo & Juliet", 26, 25, 5, 144, 0, 1, 100); + super(Quests.ROMEO_JULIET, 26, 25, 5, 144, 0, 1, 100); } @Override @@ -29,7 +30,8 @@ public class RomeoJuliet extends Quest { case 10: line(player, "I have agreed to find Juliet for Romeo and tell her how he", 4+ 7); line(player, "feels. For some reason he can't just do this himself.", 5+ 7); - line(player, BLUE + "All I need to do now is find " + RED + "Juliet.", 6+ 7); + // https://www.youtube.com/watch?v=ush_RVY4tvw + line(player, BLUE + "I should go and speak to " + RED + "Juliet" + BLUE + ", wherever she is?", 7+ 7); break; case 20: line(player, "I have agreed to find Juliet for Romeo and tell her how he", 4+ 7); @@ -113,6 +115,7 @@ public class RomeoJuliet extends Quest { line(player, BLUE + "I have to find " + RED + "Romeo" + BLUE + " and tell him what's happened.", 18+ 7); break; case 100: + // https://www.youtube.com/watch?v=m4bZ4GmHxRs line(player, "Romeo and Juliet can be together in peace.", 4+ 7); line(player, "I went to the Apothecary regarding making this cadava", 5+ 7); line(player, "potion, and he told me to bring him some cadava berries.", 6+ 7); @@ -121,14 +124,14 @@ public class RomeoJuliet extends Quest { line(player, "I told Romeo what was going to happen, but I'm not exactly", 9+ 7); line(player, "sure he understood what was happening. Ah well, I was", 10+ 7); line(player, "rewarded for all of my help regardless.", 11+ 7); - line(player, "QUEST COMPLETE!", 12+ 7); + line(player, "QUEST COMPLETE!", 13+ 7); break; } } @Override public void finish(Player player) { - if(player.getQuestRepository().getQuest("Romeo & Juliet").isCompleted(player)){ + if(player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).isCompleted(player)){ return; } super.finish(player); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoNPC.java index e520a3ce9..0034cddca 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoNPC.java @@ -55,7 +55,7 @@ public class RomeoNPC extends AbstractNPC { /*if (speakDelay < GameWorld.getTicks()) { speakDelay = GameWorld.getTicks() + 30; for (Player p : RegionManager.getLocalPlayers(this, 2)) { - if (!p.getInterfaceManager().isOpened() && RandomFunction.random(0, 8) == 2 && p.getQuestRepository().getQuest("Romeo & Juliet").getStage(p) == 0) { + if (!p.getInterfaceManager().isOpened() && RandomFunction.random(0, 8) == 2 && p.getQuestRepository().getQuest(Quests.ROMEO_JULIET).getStage(p) == 0) { if (p.getDialogueInterpreter().getDialogue() != null || p.getDialogueInterpreter().getDialogueStage() != null) { continue; } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CharlieTheTrampDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CharlieTheTrampDialogue.kt index 5b6d6ba3f..3ba076b35 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CharlieTheTrampDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CharlieTheTrampDialogue.kt @@ -6,6 +6,7 @@ import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs +import content.data.Quests /** * @author qmqz @@ -13,8 +14,6 @@ import org.rs09.consts.NPCs @Initializable class CharlieTheTrampDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - var q = "Shield of Arrav" - override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC npc(core.game.dialogue.FacialExpression.FRIENDLY,"Spare some change guv?").also { stage = 0 } @@ -70,11 +69,11 @@ class CharlieTheTrampDialogue(player: Player? = null) : core.game.dialogue.Dialo 282 -> npc(core.game.dialogue.FacialExpression.AFRAID, "But don't upset her, she's pretty dangerous.").also { stage++ } 283 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I also heard that Reldo the librarian knows more about them, go talk to him.").also { stage++ } 284 -> { - if (!player.questRepository.hasStarted(q)) { - player.questRepository.getQuest(q).start(player) - player.questRepository.getQuest(q).setStage(player,50) + if (!player.questRepository.hasStarted(Quests.SHIELD_OF_ARRAV)) { + player.questRepository.getQuest(Quests.SHIELD_OF_ARRAV).start(player) + player.questRepository.getQuest(Quests.SHIELD_OF_ARRAV).setStage(player,50) } else if (!ShieldofArrav.isBlackArm(player) && !ShieldofArrav.isPhoenix(player)) { - player.questRepository.getQuest(q).setStage(player, 50) + player.questRepository.getQuest(Quests.SHIELD_OF_ARRAV).setStage(player, 50) } end() } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenDialogue.kt index aa24c2ca2..67422ee21 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenDialogue.kt @@ -1,7 +1,6 @@ package content.region.misthalin.varrock.quest.shieldofarrav import content.region.desert.quest.thegolem.CuratorHaigHalenGolemDialogue -import content.region.misthalin.digsite.quest.thedigsite.TheDigSite import core.api.* import core.game.dialogue.* import core.game.node.entity.player.Player @@ -10,6 +9,7 @@ import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE import org.rs09.consts.Items import org.rs09.consts.NPCs +import content.data.Quests class CuratorHaigHalenDialogue (player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { @@ -18,7 +18,7 @@ class CuratorHaigHalenDialogue (player: Player? = null) : DialoguePlugin(player) if (player.getQuestRepository().points >= 50 && !player.achievementDiaryManager.hasCompletedTask(DiaryType.VARROCK, 0, 12)) { player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 12) } - if (getQuestStage(player, TheDigSite.questName) == 1 && inInventory(player, Items.UNSTAMPED_LETTER_682) ) { + if (getQuestStage(player, Quests.THE_DIG_SITE) == 1 && inInventory(player, Items.UNSTAMPED_LETTER_682) ) { stage = 11 // Couldn't do a dialogueFile for digsite as it needs to resume the topic after. } stage++ @@ -26,11 +26,12 @@ class CuratorHaigHalenDialogue (player: Player? = null) : DialoguePlugin(player) 1 -> showTopics( Topic(FacialExpression.FRIENDLY, "Have you any interesting news?", 2), Topic(FacialExpression.FRIENDLY, "Do you know where I could find any treasure?", 8), - IfTopic(FacialExpression.FRIENDLY, "I've lost the letter of recommendation.", 18, getQuestStage(player, TheDigSite.questName) == 2 && !inInventory(player, Items.SEALED_LETTER_683)), + IfTopic(FacialExpression.FRIENDLY, "I've lost the letter of recommendation.", 18, + getQuestStage(player, Quests.THE_DIG_SITE) == 2 && !inInventory(player, Items.SEALED_LETTER_683)), IfTopic("I have the Shield of Arrav", CuratorHaigHalenSOADialogue(), - getQuestStage(player, "Shield of Arrav") == 70, false), + getQuestStage(player, Quests.SHIELD_OF_ARRAV) == 70, false), IfTopic("I'm looking for a statuette recovered from the city of Uzer.", CuratorHaigHalenGolemDialogue(), - getQuestStage(player, "The Golem") == 3, false) + getQuestStage(player, Quests.THE_GOLEM) == 3, false) ) 2 -> npcl(FacialExpression.FRIENDLY, "Yes, we found a rather interesting island to the north of Morytania. We believe that it may be of archaeological significance.").also { stage++ } 3 -> playerl(FacialExpression.FRIENDLY, "Oh? That sounds interesting.").also { stage++ } @@ -61,8 +62,8 @@ class CuratorHaigHalenDialogue (player: Player? = null) : DialoguePlugin(player) stage++ } 16 -> playerl(FacialExpression.FRIENDLY, "Ok, I will. Thanks, see you later.").also { - if(getQuestStage(player, TheDigSite.questName) == 1) { - setQuestStage(player, TheDigSite.questName, 2) + if(getQuestStage(player, Quests.THE_DIG_SITE) == 1) { + setQuestStage(player, Quests.THE_DIG_SITE, 2) } stage = 1 } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/JohnnyBeardNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/JohnnyBeardNPC.java index 9208ad74b..f5be4a4b0 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/JohnnyBeardNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/JohnnyBeardNPC.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the npc to handle Johnny the beard npc. @@ -43,10 +44,12 @@ public final class JohnnyBeardNPC extends AbstractNPC { @Override public void finalizeDeath(final Entity killer) { super.finalizeDeath(killer); - final Player p = ((Player) killer); - final Quest quest = p.getQuestRepository().getQuest("Shield of Arrav"); - if (quest.getStage(p) == 60 && ShieldofArrav.isPhoenixMission(p) && !p.getInventory().containsItem(ShieldofArrav.INTEL_REPORT) && !p.getBank().containsItem(ShieldofArrav.INTEL_REPORT)) { - GroundItemManager.create(ShieldofArrav.INTEL_REPORT, getLocation(), p); + if (killer instanceof Player) { + final Player p = ((Player) killer); + final Quest quest = p.getQuestRepository().getQuest(Quests.SHIELD_OF_ARRAV); + if (quest.getStage(p) == 60 && ShieldofArrav.isPhoenixMission(p) && !p.getInventory().containsItem(ShieldofArrav.INTEL_REPORT) && !p.getBank().containsItem(ShieldofArrav.INTEL_REPORT)) { + GroundItemManager.create(ShieldofArrav.INTEL_REPORT, getLocation(), p); + } } } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KatrineDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KatrineDialogue.java index 101f9366a..53f90edcd 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KatrineDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KatrineDialogue.java @@ -1,10 +1,14 @@ package content.region.misthalin.varrock.quest.shieldofarrav; +import content.region.asgarnia.burthorpe.quest.heroesquest.KatrineDialogueFile; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; +import content.data.Quests; + +import static core.api.ContentAPIKt.openDialogue; /** * Represents the katrine NPC dialogue. @@ -48,11 +52,19 @@ public final class KatrineDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Shield of Arrav"); + quest = player.getQuestRepository().getQuest(Quests.SHIELD_OF_ARRAV); switch (quest.getStage(player)) { - case 80: - case 90: case 100: + if (ShieldofArrav.isBlackArm(player)) { + Quest heroesQuest = player.getQuestRepository().getQuest(Quests.HEROES_QUEST); + if (0 < heroesQuest.getStage(player) && heroesQuest.getStage(player) < 100) { + openDialogue(player, new KatrineDialogueFile(), npc); + break; + } + } + // Continues below if not during the Heroes' Quest + case 90: + case 80: case 70: if (ShieldofArrav.isPhoenix(player)) { npc("You've got some guts coming here, Phoenix guy!"); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KingRoaldArravDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KingRoaldArravDialogue.kt index 9135bd1d5..26238d76d 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KingRoaldArravDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KingRoaldArravDialogue.kt @@ -1,11 +1,11 @@ package content.region.misthalin.varrock.quest.shieldofarrav -import content.region.misthalin.varrock.quest.shieldofarrav.ShieldofArrav import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests private val CERTIFICATE = Item(769) @@ -46,7 +46,7 @@ class KingRoaldArravDialogue() : DialogueFile() { if (!player!!.inventory.add(Item(995, 600))) { GroundItemManager.create(Item(995, 600), player) } - player!!.questRepository.getQuest("Shield of Arrav").finish(player) + player!!.questRepository.getQuest(Quests.SHIELD_OF_ARRAV).finish(player) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ReldoDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ReldoDialogue.java index 8d2859688..91bee2817 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ReldoDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ReldoDialogue.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.link.diary.AchievementDiary; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; +import content.data.Quests; /** * Represents the dialogue to handle reldo. @@ -55,14 +56,14 @@ public class ReldoDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - knightSword = player.getQuestRepository().getQuest("The Knight's Sword"); - shieldArrav = player.getQuestRepository().getQuest("Shield of Arrav"); + knightSword = player.getQuestRepository().getQuest(Quests.THE_KNIGHTS_SWORD); + shieldArrav = player.getQuestRepository().getQuest(Quests.SHIELD_OF_ARRAV); if (args.length == 2 && ((String) args[1]).equals("book")) { player("Aha! 'The Shield of Arrav'! Exactly what I was looking", "for."); stage = 3; return true; } - if(player.getQuestRepository().getQuest("Lost Tribe").getStage(player) == 40 && player.getInventory().contains(Items.BROOCH_5008,1)){ + if(player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 40 && player.getInventory().contains(Items.BROOCH_5008,1)){ options("Hello stranger.","I have a question about my Achievement Diary.","Ask about the brooch."); } else { options("Hello stranger.", "I have a question about my Achievement Diary."); @@ -237,7 +238,7 @@ public class ReldoDialogue extends DialoguePlugin { break; case 2005: npc("The other day I filed a book about ancient goblin tribes.","It's somewhere on the west end of the library, I think.","Maybe that will be of some use."); - player.getQuestRepository().getQuest("Lost Tribe").setStage(player,42); + player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).setStage(player,42); stage++; break; case 2006: diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java index 2b51df2cf..80a581a22 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java @@ -23,6 +23,7 @@ import core.game.global.action.PickupHandler; import core.game.world.repository.Repository; import java.util.List; +import content.data.Quests; /** * Represents the shield of arrav plugin. @@ -59,7 +60,7 @@ public final class ShieldArravPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Shield of Arrav"); + final Quest quest = player.getQuestRepository().getQuest(Quests.SHIELD_OF_ARRAV); final int id = node instanceof Scenery ? ((Scenery) node).getId() : node instanceof Item ? ((Item) node).getId() : ((NPC) node).getId(); switch (id) { case 769: diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArrav.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArrav.java index ee91ad5d6..4b427035d 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArrav.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArrav.java @@ -6,6 +6,7 @@ import core.game.node.item.Item; import core.plugin.Initializable; import content.region.misthalin.varrock.dialogue.KingRoaldDialogue; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the shield of arrav quest. @@ -54,7 +55,7 @@ public class ShieldofArrav extends Quest { * Constructs a new {@Code ShieldofArrav} {@Code Object} */ public ShieldofArrav() { - super("Shield of Arrav", 29, 28, 1, 145, 0, 1, 7); + super(Quests.SHIELD_OF_ARRAV, 29, 28, 1, 145, 0, 1, 7); } @Override @@ -225,6 +226,22 @@ public class ShieldofArrav extends Quest { player.setAttribute("/save:black-arm-gang", true); } + /** + * Swaps the gang. + * @param player the player. + */ + public static void swapGang(final Player player) { + if(isPhoenix(player)) { + player.setAttribute("/save:black-arm-gang", true); + player.setAttribute("/save:phoenix-gang", false); + } else if(isBlackArm(player)) { + player.setAttribute("/save:black-arm-gang", false); + player.setAttribute("/save:phoenix-gang", true); + } else { + player.setAttribute("/save:phoenix-gang", true); + } + } + /** * Method used to check if the player is part of the phoenix gang. * @param player the player. diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArravBook.kt b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArravBook.kt index faebb66c3..10ec52e4e 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArravBook.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArravBook.kt @@ -4,11 +4,13 @@ import content.global.handlers.iface.BookInterface import content.global.handlers.iface.BookLine import content.global.handlers.iface.Page import content.global.handlers.iface.PageSet -import core.api.setAttribute import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.node.entity.player.Player import org.rs09.consts.Items +import content.data.Quests +import core.api.getQuestStage +import core.api.setQuestStage /** * Shield of Arrav Book @@ -18,7 +20,7 @@ import org.rs09.consts.Items */ class ShieldofArravBook : InteractionListener { companion object { - private val TITLE = "Shield of Arrav" + private val TITLE = "The Shield of Arrav" private val CONTENTS = arrayOf( PageSet( Page( @@ -86,8 +88,8 @@ class ShieldofArravBook : InteractionListener { private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) if (BookInterface.isLastPage(pageNum, CONTENTS.size)) { - if (player.questRepository.getQuest("Shield of Arrav").getStage(player) == 10) { - player.questRepository.getQuest("Shield of Arrav").setStage(player, 20) + if (getQuestStage(player, Quests.SHIELD_OF_ARRAV) == 10) { + setQuestStage(player, Quests.SHIELD_OF_ARRAV, 20) } } return true diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/StravenDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/StravenDialogue.java index 6b26f29c9..f12d9cef4 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/StravenDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/StravenDialogue.java @@ -5,6 +5,10 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; +import content.data.Quests; +import content.region.asgarnia.burthorpe.quest.heroesquest.StravenDialogueFile; + +import static core.api.ContentAPIKt.openDialogue; /** * Represents the dialogue which handles the straven NPC. @@ -43,9 +47,17 @@ public class StravenDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Shield of Arrav"); + quest = player.getQuestRepository().getQuest(Quests.SHIELD_OF_ARRAV); switch (quest.getStage(player)) { case 100: + if (ShieldofArrav.isPhoenix(player)) { + Quest heroesQuest = player.getQuestRepository().getQuest(Quests.HEROES_QUEST); + if (0 < heroesQuest.getStage(player) && heroesQuest.getStage(player) < 100) { + openDialogue(player, new StravenDialogueFile(), npc); + break; + } + } + // Continues below if not during the Heroes' Quest case 70: if (ShieldofArrav.isPhoenix(player)) { npc("Greetings fellow gang member."); @@ -321,7 +333,7 @@ public class StravenDialogue extends DialoguePlugin { stage = 47; break; case 47: - npc("Although having said that, a rival gang of ours, er,", "theirs, called the Black Arm Gang is supposedly metting", "a contact from Port Sarim today in the Blue Moon", "Inn."); + npc("Although having said that, a rival gang of ours, er,", "theirs, called the Black Arm Gang is supposedly meeting", "a contact from Port Sarim today in the Blue Moon", "Inn."); stage = 48; break; case 48: diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/WeaponsMasterDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/WeaponsMasterDialogue.java index c521eb492..e0f0f5fc6 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/WeaponsMasterDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/WeaponsMasterDialogue.java @@ -4,6 +4,7 @@ import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the dialogue which handles the weapons master. @@ -42,7 +43,7 @@ public final class WeaponsMasterDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Shield of Arrav"); + quest = player.getQuestRepository().getQuest(Quests.SHIELD_OF_ARRAV); switch (quest.getStage(player)) { default: if (args.length > 1) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/AnnaJonesDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/AnnaJonesDialogue.java index 6e68c8dca..645995cdb 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/AnnaJonesDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/AnnaJonesDialogue.java @@ -1,5 +1,6 @@ package content.region.misthalin.varrock.quest.whatliesbelow; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; @@ -40,7 +41,7 @@ public class AnnaJonesDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(WhatLiesBelow.NAME); + quest = player.getQuestRepository().getQuest(Quests.WHAT_LIES_BELOW); switch (quest.getStage(player)) { default: if (args.length >= 2) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/OutlawNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/OutlawNPC.java index e4c9bdc19..9649f5fa3 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/OutlawNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/OutlawNPC.java @@ -1,5 +1,6 @@ package content.region.misthalin.varrock.quest.whatliesbelow; +import content.data.Quests; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; @@ -39,7 +40,7 @@ public class OutlawNPC extends AbstractNPC { super.finalizeDeath(killer); if (killer instanceof Player) { Player p = killer.asPlayer(); - Quest quest = p.getQuestRepository().getQuest(WhatLiesBelow.NAME); + Quest quest = p.getQuestRepository().getQuest(Quests.WHAT_LIES_BELOW); if (quest.getStage(p) == 10) { int amount = p.getInventory().getAmount(WhatLiesBelow.RATS_PAPER) + p.getBank().getAmount(WhatLiesBelow.RATS_PAPER); if (amount < 5) { diff --git a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/RatBurgissDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/RatBurgissDialogue.java index 11b84906c..0a8e97bc6 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/RatBurgissDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/RatBurgissDialogue.java @@ -1,5 +1,6 @@ package content.region.misthalin.varrock.quest.whatliesbelow; +import content.data.Quests; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -52,7 +53,7 @@ public class RatBurgissDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(WhatLiesBelow.NAME); + quest = player.getQuestRepository().getQuest(Quests.WHAT_LIES_BELOW); options("Hello there!", "I have a question about my Achievement Diary."); stage = -1; return true; diff --git a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/SurokMagisDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/SurokMagisDialogue.java index 1f4cf375f..838900d75 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/SurokMagisDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/SurokMagisDialogue.java @@ -1,5 +1,6 @@ package content.region.misthalin.varrock.quest.whatliesbelow; +import content.data.Quests; import core.game.activity.ActivityManager; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; @@ -50,7 +51,7 @@ public class SurokMagisDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(WhatLiesBelow.NAME); + quest = player.getQuestRepository().getQuest(Quests.WHAT_LIES_BELOW); switch (quest.getStage(player)) { default: npc("Excuse me?"); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowCutscene.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowCutscene.java index 718d90602..d1c154c5d 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowCutscene.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowCutscene.java @@ -297,6 +297,7 @@ public class WLBelowCutscene extends CutscenePlugin { @Override public void configure() { region = DynamicRegion.create(12854); + region.setMusicId(250); setRegionBase(); registerRegion(region.getId()); } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowPlugin.java index 7f3b85182..09c27bcd9 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowPlugin.java @@ -1,5 +1,6 @@ package content.region.misthalin.varrock.quest.whatliesbelow; +import content.data.Quests; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; import core.game.node.entity.player.link.diary.DiaryType; @@ -56,7 +57,7 @@ public class WLBelowPlugin extends OptionHandler { @Override public boolean handle(final Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest(WhatLiesBelow.NAME); + final Quest quest = player.getQuestRepository().getQuest(Quests.WHAT_LIES_BELOW); switch (option) { case "summon": case "operate": diff --git a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WhatLiesBelow.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WhatLiesBelow.java index b31d5b058..0b0238179 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WhatLiesBelow.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WhatLiesBelow.java @@ -9,6 +9,7 @@ import core.game.node.item.Item; import core.plugin.ClassScanner; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * The what lies below quest. @@ -17,12 +18,6 @@ import static core.api.ContentAPIKt.*; */ @Initializable public class WhatLiesBelow extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "What Lies Below"; - /** * The bowl item. */ @@ -78,16 +73,6 @@ public class WhatLiesBelow extends Quest { */ public static final Item BEACON_RING = new Item(Items.BEACON_RING_11014); - /** - * The requirement messages. - */ - private static final String[] REQS = new String[] { - "Have level 35 Runecrafting.", - "Be able to defeat a level 47 enemy.", - "I need to have completed the Rune Mysteries quest.", - "Have a Mining level of 42 to use the Chaos Tunnel." - }; - /** * The requirements. */ @@ -97,7 +82,7 @@ public class WhatLiesBelow extends Quest { * Constructs a new {@Code WhatLiesBelow} {@Code Object} */ public WhatLiesBelow() { - super(NAME, 136, 135, 1); + super(Quests.WHAT_LIES_BELOW, 136, 135, 1); } @Override @@ -109,36 +94,81 @@ public class WhatLiesBelow extends Quest { @Override public void drawJournal(Player player, int stage) { super.drawJournal(player, stage); - switch (stage) { - case 0: - line(player, "I can start this quest by speaking to Rat Burgiss on theroad south of Varrock.Before I begin I will need to:" + getReqMessage(player), 11); - break; - case 10: - line(player, "Rat, a trader in Varrock, has asked me to help him with a task.I need to kill outlaws west of Varrock so that I can collect 5 of Rat's papers.", 11); - break; - case 20: - line(player, "Rat, a trader in Varrock, has asked me to help him with a task.I need to kill outlaws west of Varrock so that I can collect5 of Rat's papers.I have delivered Rat's folder to him. Perhaps Ishould speak to him again.I need to deliver Rat's letter to Surok Magisin Varrock.", 11); - break; - case 30: - case 40: - line(player, "Rat, a trader in Varrock, has asked me to help him with a task.Surok, a Wizard in Varrock, has asked me to complete a task for him.I need to kill the outlaws west of Varrock so that I can collect5 of Rat's papers.I have delivered Rat's folder to him. Perhaps Ishould speak to him again.I need to deliver Rat's letter to Surok Magis in Varrock. I need to talk to Surok about thesecret he has for me.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also needto find or buy an empty bowl.", 11); - break; - case 50: - line(player, "Rat, a trader in Varrock, has asked me to help him with a task.Surok, a Wizard in Varrock, has asked me to complete a task for him.I need to kill the outlaws west of Varrock so that I can collect5 of Rat's papers.I have delivered Rat's folder to him. Perhaps Ishould speak to him again.I need to deliver Rat's letter to Surok Magis in Varrock. I need to talk to Surok about thesecret he has for me.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to take the glowing wand I have created back to Surok in Varrockwith an empty bowl.I need to deliver Surok's letter to Rat who is waiting for me southof Varrock. I should speak to Rat again; he is waiting for me south of Varrock", 11); - break; - case 60: - line(player, "Rat, a trader in Varrock, has asked me to help him with a task.Surok, a Wizard in Varrock, has asked me to complete a task for him.I need to kill the outlaws west of Varrock so that I can collect5 of Rat's papers.I have delivered Rat's folder to him. Perhaps Ishould speak to him again.I need to deliver Rat's letter to Surok Magis in Varrock. I need to talk to Surok about thesecret he has for me.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to take the glowing wand I have created back to Surok in Varrockwith an empty bowl.I need to deliver Surok's letter to Rat who is waiting for me southof Varrock.I should speak to Rat again; he is waiting for me south of VarrockI need to speak to Zaff of Zaff's Staffs in Varrock.", 11); - break; - case 70: - line(player, "Rat, a trader in Varrock, has asked me to help him with a task.Surok, a Wizard in Varrock, has asked me to complete a task for him.I need to kill the outlaws west of Varrock so that I can collect5 of Rat's papers.I have delivered Rat's folder to him. Perhaps Ishould speak to him again.I need to deliver Rat's letter to Surok Magis in Varrock. I need to talk to Surok about thesecret he has for me.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to take the glowing wand I have created back to Surok in Varrockwith an empty bowl.I need to deliver Surok's letter to Rat who is waiting for me southof Varrock.I should speak to Rat again; he is waiting for me south of VarrockI need to speak to Zaff of Zaff's Staffs in Varrock.I need to tell Surok in Varrock that he is under arrest.", 11); - break; - case 80: - case 90: - line(player, "Rat, a trader in Varrock, has asked me to help him with a task.Surok, a Wizard in Varrock, has asked me to complete a task for him.I need to kill the outlaws west of Varrock so that I can collect5 of Rat's papers.I have delivered Rat's folder to him. Perhaps Ishould speak to him again.I need to deliver Rat's letter to Surok Magis in Varrock. I need to talk to Surok about thesecret he has for me.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to take the glowing wand I have created back to Surok in Varrockwith an empty bowl.I need to deliver Surok's letter to Rat who is waiting for me southof Varrock.I should speak to Rat again; he is waiting for me south of VarrockI need to speak to Zaff of Zaff's Staffs in Varrock.I need to tell Surok in Varrock that he is under arrest.I need to defeat King Roald in Varrock so that Zaff can remove themind-control spell.I need to tell Rat what has happened; he is waiting for mesouth of Varrock.", 11); - break; - case 100: - line(player, "Rat, a trader in Varrock, has asked me to help him with a task.Surok, a Wizard in Varrock, has asked me to complete a task for him.I need to kill the outlaws west of Varrock so that I can collect5 of Rat's papers.I have delivered Rat's folder to him. Perhaps Ishould speak to him again.I need to deliver Rat's letter to Surok Magis in Varrock. I need to talk to Surok about thesecret he has for me.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to infuse the metal wand with chaos runes at the Chaos Altar.I also need to find or buy an empty bowl.I need to take the glowing wand I have created back to Surok in Varrockwith an empty bowl.I need to deliver Surok's letter to Rat who is waiting for me southof Varrock.I should speak to Rat again; he is waiting for me south of VarrockI need to speak to Zaff of Zaff's Staffs in Varrock.I need to tell Surok in Varrock that he is under arrest.I need to defeat King Roald in Varrock so that Zaff can remove themind-control spell.I need to tell Rat what has happened; he is waiting for mesouth of Varrock.QUEST COMPLETE!I have been given information about the Chaos Tunnel.Zaff has given me the Beacon Ring.", 11); - break; + var line = 12; + + if(stage == 0){ + line(player, "I can start this quest by speaking to !!Rat Burgiss?? on the", line++); + line(player, "road south of !!Varrock??.", line++); + line(player, "Before I begin I will need to:", line++); + line(player, "Have level 35 !!Runecrafting??.", line++, getStatLevel(player, Skills.RUNECRAFTING) >= 35); + line(player, "Be able to defeat a !!level 47 enemy??.", line++); + line(player, "I need to have completed the !!Rune Mysteries?? quest.", line++, isQuestComplete(player, Quests.RUNE_MYSTERIES)); + line(player, "Have a !!Mining?? level of 42 to use the !!Chaos Tunnel??.", line++, getStatLevel(player, Skills.MINING) >= 42); + } else { + // These are somehow at the top with different stage when crossed out. + if (stage >= 10) { + line(player, "!!Rat??, a trader in Varrock, has asked me to help him with a", line++, stage >= 30); + line(player, "task.", line++, stage >= 30); + } + if (stage >= 30) { + line(player, "!!Surok??, a Wizard in Varrock, has asked me to complete a", line++, stage >= 50); + line(player, "task for him.", line++, stage >= 50); + } + // End + + if (stage >= 10) { + line(player, "I need to kill !!outlaws?? west of Varrock so that I can collect 5", line++, stage >= 20); + line(player, "of Rat's !!papers??.", line++, stage >= 20); + if (inInventory(player, Items.FULL_FOLDER_11007, 1)) { + line(player, "I should take the !!full folder?? back to Rat.", line++); + } + } + if (stage >= 20) { + line(player, "I have delivered Rat's folder to him. Perhaps I should", line++, stage >= 30); + line(player, "should speak to him again.", line++, stage >= 30); + // Should be separated stages + line(player, "I need to deliver !!Rat's?? letter to !!Surok Magis?? in !!Varrock??.", line++, stage >= 30); + // Should be separated stages + line(player, "I need to talk to !!Surok?? about the secret he has for me.", line++, stage >= 30); + } + if (stage >= 30) { + line(player, "I need to infuse the !!metal wand?? with !!chaos runes?? at the", line++, stage >= 50); + line(player, "!!Chaos Altar??. I also need to find or buy an empty !!bowl??.", line++, stage >= 50); + } + if (stage >= 50) { + line(player, "I need to take the !!glowing wand?? I have created back to", line++, true); + line(player, "!!Surok?? in Varrock along with an empty !!bowl??.", line++, true); + // Should be separated stages + line(player, "I need to deliver !!Surok's letter?? to !!Rat?? who is waiting for me", line++, true); + line(player, "south of Varrock.", line++, true); + // Should be separated stages + line(player, "I should speak to !!Rat?? again; he is waiting for me south of", line++, stage >= 60); + line(player, "Varrock.", line++, stage >= 60); + } + if (stage >= 60) { + line(player, "I need to speak to !!Zaff?? of !!Zaff's Staffs?? in Varrock.", line++, stage >= 70); + } + if (stage >= 70) { + line(player, "I need to tell !!Surok?? in Varrock that he is under arrest.", line++, stage >= 80); + } + if (stage >= 80) { + line(player, "I need to defeat !!King Roald?? in Varrock so that !!Zaff?? can", line++, true); + line(player, "remove the mind-control spell.", line++, true); + // Should be separated stages + line(player, "I need to tell !!Rat?? what has happened; he is waiting for me", line++, stage >= 100); + line(player, "south of Varrock.", line++, stage >= 100); + } + if (stage >= 100) { + line++; + line++; + line(player,"QUEST COMPLETE!", line++); + line++; + line(player, "I have been given information about the !!Chaos Tunnel??.", line++); + line(player, "Zaff has given me the !!Beacon Ring??.", line++); + line++; + line(player, "I have also been given !!8,000 Runecrafting XP, 2000??.", line++); + line(player, "!!Defence XP?? and !!1 Quest Point??.", line++); + } } } @@ -161,29 +191,13 @@ public class WhatLiesBelow extends Quest { player.getQuestRepository().syncronizeTab(player); } - /** - * Gets the req message. - * @return the message. - */ - public String getReqMessage(Player player) { - hasRequirements(player); - String s = ""; - for (int i = 0; i < requirements.length; i++) { - String l = REQS[i]; - if (requirements[i]) { - l = l.replace("", "").replace("", "").trim(); - } - s += (requirements[i] ? "" : "") + l + ""; - } - return s; - } @Override public boolean hasRequirements(Player player) { requirements[0] = player.getSkills().getStaticLevel(Skills.RUNECRAFTING) >= 35; requirements[1] = false; requirements[3] = player.getSkills().getStaticLevel(Skills.MINING) >= 42; - requirements[2] = player.getQuestRepository().isComplete("Rune Mysteries"); + requirements[2] = player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES); return requirements[0] && requirements[2] && requirements[3]; } @@ -198,7 +212,7 @@ public class WhatLiesBelow extends Quest { } else if (stage > 0 && stage < 100) { return new int[] { id, 1 }; } - setVarp(player, 1181, (1 << 8) + (1 << 9), true); + setVarp(player, 1181, (1 << 8) + (1 << 9), true); return new int[] { id, 502 }; } diff --git a/Server/src/main/content/region/misthalin/wiztower/dialogue/TraibornDialogue.java b/Server/src/main/content/region/misthalin/wiztower/dialogue/TraibornDialogue.java index 37c40a125..4f4200c9e 100644 --- a/Server/src/main/content/region/misthalin/wiztower/dialogue/TraibornDialogue.java +++ b/Server/src/main/content/region/misthalin/wiztower/dialogue/TraibornDialogue.java @@ -14,6 +14,7 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import content.region.misthalin.varrock.quest.demonslayer.DemonSlayer; +import content.data.Quests; /** * Represents the dialogue used to handle the Traiborn NPC. @@ -68,7 +69,7 @@ public class TraibornDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Demon Slayer"); + quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); switch (quest.getStage(player)) { case 20: if (player.getAttribute("demon-slayer:traiborn", false)) { diff --git a/Server/src/main/content/region/misthalin/wiztower/handlers/TalismanMapInterface.kt b/Server/src/main/content/region/misthalin/wiztower/handlers/TalismanMapInterface.kt new file mode 100644 index 000000000..b97893d88 --- /dev/null +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/TalismanMapInterface.kt @@ -0,0 +1,21 @@ +package content.region.misthalin.wiztower.handlers + +import core.api.* +import core.game.interaction.InteractionListener +import org.rs09.consts.Scenery +import org.rs09.consts.Components + +// http://youtu.be/T62dugdfzSQ +/** Map for talismans in Wizard Tower basement. I display everything, because let's be real, you can find this online... */ +class TalismanMapInterface : InteractionListener { + override fun defineListeners() { + on(intArrayOf(Scenery.MAP_38421, Scenery.MAP_38422), SCENERY, "study") { player, node -> + openInterface(player, Components.RCGUILD_MAP_780) + // Air talisman to Death talisman + for(i in 35..48) { + setComponentVisibility(player, Components.RCGUILD_MAP_780, i, false) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/wiztower/handlers/WizardTowerPlugin.java b/Server/src/main/content/region/misthalin/wiztower/handlers/WizardTowerPlugin.java index 2cf368836..01b970fd8 100644 --- a/Server/src/main/content/region/misthalin/wiztower/handlers/WizardTowerPlugin.java +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/WizardTowerPlugin.java @@ -10,7 +10,6 @@ import core.game.global.action.ClimbActionHandler; import core.game.global.action.DoorActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; -import core.game.node.entity.Entity; import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; @@ -39,6 +38,7 @@ import kotlin.Unit; import content.global.travel.EssenceTeleport; import core.game.world.GameWorld; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the plugins used related to the wizard tower. @@ -81,7 +81,7 @@ public final class WizardTowerPlugin extends OptionHandler { public boolean handle(Player player, Node node, String option) { switch (option) { case "teleport": - if (!player.getQuestRepository().isComplete("Rune Mysteries")) { + if (!player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES)) { player.getPacketDispatch().sendMessage("You need to have completed the Rune Mysteries Quest to use this feature."); return true; } @@ -311,7 +311,7 @@ public final class WizardTowerPlugin extends OptionHandler { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - Quest quest = player.getQuestRepository().getQuest("Imp Catcher"); + Quest quest = player.getQuestRepository().getQuest(Quests.IMP_CATCHER); switch (quest.getStage(player)) { case 0: player("Give me a quest!"); @@ -331,7 +331,7 @@ public final class WizardTowerPlugin extends OptionHandler { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Imp Catcher"); + final Quest quest = player.getQuestRepository().getQuest(Quests.IMP_CATCHER); switch (quest.getStage(player)) { case 0: switch (stage) { @@ -1041,7 +1041,7 @@ public final class WizardTowerPlugin extends OptionHandler { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Rune Mysteries"); + final Quest quest = player.getQuestRepository().getQuest(Quests.RUNE_MYSTERIES); switch (stage) { case 0: if (quest.getStage(player) == 100) { @@ -1542,7 +1542,7 @@ public final class WizardTowerPlugin extends OptionHandler { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("Rune Mysteries"); + final Quest quest = player.getQuestRepository().getQuest(Quests.RUNE_MYSTERIES); if (quest.getStage(player) == 40) { npc("My gratitude to you adventurer for bringing me these", "research notes. I notice that you brought the head", "wizard a special talisman that was the key to our finally", "unlocking the puzzle."); stage = 900; @@ -1559,7 +1559,7 @@ public final class WizardTowerPlugin extends OptionHandler { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Rune Mysteries"); + final Quest quest = player.getQuestRepository().getQuest(Quests.RUNE_MYSTERIES); switch (stage) { case 0: if (quest.getStage(player) == 30) { @@ -1577,7 +1577,7 @@ public final class WizardTowerPlugin extends OptionHandler { stage = 950; return true; } - if (!player.getQuestRepository().isComplete("Rune Mysteries")) { + if (!player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES)) { options("Yes please!", "Oh, it's a rune shop. No thank you, then."); stage = 100; } else { diff --git a/Server/src/main/content/region/misthalin/wiztower/quest/ImpCatcher.java b/Server/src/main/content/region/misthalin/wiztower/quest/ImpCatcher.java index a24c4d6d6..d077a59f1 100644 --- a/Server/src/main/content/region/misthalin/wiztower/quest/ImpCatcher.java +++ b/Server/src/main/content/region/misthalin/wiztower/quest/ImpCatcher.java @@ -10,6 +10,7 @@ import core.game.node.scenery.SceneryBuilder; import core.game.world.map.Location; import core.plugin.Initializable; import core.game.world.map.RegionManager; +import content.data.Quests; /** * Represents the imp catcher quest. @@ -48,7 +49,7 @@ public class ImpCatcher extends Quest { * Constructs a new {@Code ImpCatcher} {@Code Object} */ public ImpCatcher() { - super("Imp Catcher", 21, 20, 1, 160, 0, 1, 2); + super(Quests.IMP_CATCHER, 21, 20, 1, 160, 0, 1, 2); } @Override diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/BrosDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/BrosDialogue.java deleted file mode 100644 index 416cdc7f6..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/BrosDialogue.java +++ /dev/null @@ -1,61 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used for the bros np.c - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BrosDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BrosDialogue} {@code Object}. - */ - public BrosDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BrosDialogue} {@code Object}. - * @param player the player. - */ - public BrosDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BrosDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Out of my way, punk"); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6026, 6032 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/GregoryDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/GregoryDialogue.java deleted file mode 100644 index 20dd7f484..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/GregoryDialogue.java +++ /dev/null @@ -1,61 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used for the gregory npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GregoryDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GregoryDialogue} {@code Object}. - */ - public GregoryDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GregoryDialogue} {@code Object}. - * @param player the player. - */ - public GregoryDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GregoryDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "If I were as ugly as you I would not dare to show my", "face in public!"); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6033, 6027, 6043, 6036, 6040, 6038, 6045 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/HumanWerewolfDialogue.kt b/Server/src/main/content/region/morytania/canifis/dialogue/HumanWerewolfDialogue.kt new file mode 100644 index 000000000..9763d0ae3 --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/dialogue/HumanWerewolfDialogue.kt @@ -0,0 +1,66 @@ +package content.region.morytania.canifis.dialogue + +import core.api.anyInEquipment +import core.api.toIntArray +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items + +@Initializable +class HumanWerewolfDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage){ + START_DIALOGUE -> { + // There are 10 random different messages for all of the werewolves in human form + // If you have the Ring of Charos they think you're a werewolf and talk differently + if (anyInEquipment(player, Items.RING_OF_CHAROS_4202, Items.RING_OF_CHAROSA_6465)){ + // Nice talks + when ((1..10).random()){ + 1 -> npcl(FacialExpression.HAPPY, "I bet you have wonderful paws.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.NEUTRAL, "A very miserable day, altogether... enjoy it while it lasts.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.ASKING, "If you catch anyone promise me you'll share.").also { stage = END_DIALOGUE } + 4 -> npcl(FacialExpression.ASKING, "I haven't smelt you around here before...").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.FRIENDLY, "You smell familiar...").also { stage = END_DIALOGUE } + 6 -> npcl(FacialExpression.ASKING, "Seen any humans around here? I'm v-e-r-y hungry.").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.FRIENDLY, "You look to me like someone with a healthy taste for blood.").also { stage = END_DIALOGUE } + 8 -> npcl(FacialExpression.FRIENDLY, "Good day to you, my friend.").also { stage = END_DIALOGUE } + 9 -> npcl(FacialExpression.ASKING, "Fancy going up to the castle for a bit of a snack?").also { stage = END_DIALOGUE } + 10 -> npcl(FacialExpression.NEUTRAL, "Give me a moment, I have a bit of someone stuck in my teeth...").also { stage = END_DIALOGUE } + } + } + else { + // Mean talks + when ((1..10).random()){ + 1 -> npcl(FacialExpression.ANNOYED, "If I were as ugly as you I would not dare to show my face in public!").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.ANGRY, "Out of my way, punk.").also { stage = END_DIALOGUE } + // The only one that has a path + 3 -> npcl(FacialExpression.ASKING, "Hmm... you smell strange...").also { stage++ } + 4 -> npcl(FacialExpression.ANGRY, "Leave me alone.").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.ANNOYED, "Don't talk to me again if you value your life!").also { stage = END_DIALOGUE } + 6 -> npcl(FacialExpression.ANNOYED, "Get lost!").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.ANNOYED, "I don't have anything to give you so leave me alone, mendicant.").also { stage = END_DIALOGUE } + 8 -> npcl(FacialExpression.ANGRY, "Have you no manners?").also { stage = END_DIALOGUE } + 9 -> npcl(FacialExpression.ANNOYED, "I don't have time for this right now.").also { stage = END_DIALOGUE } + 10 -> npcl(FacialExpression.ANGRY, "I have no interest in talking to a pathetic meat bag like yourself.").also{ stage = END_DIALOGUE} + } + } + } + // There's one path that the player can respond to (3 without the ring) + 1 -> playerl(FacialExpression.ASKING, "Strange how?").also { stage++ } + 2 -> npcl(FacialExpression.EVIL_LAUGH, "Like a human!").also { stage++ } + 3 -> playerl(FacialExpression.PANICKED, "Oh! Er... I just ate one is why!").also { stage = END_DIALOGUE } + else -> { + end() + } + } + return true + } + + override fun getIds(): IntArray { + return (6026..6046).toIntArray() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/IrinaDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/IrinaDialogue.java deleted file mode 100644 index 730f38a6e..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/IrinaDialogue.java +++ /dev/null @@ -1,61 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used for the irina npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class IrinaDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code IrinaDialogue} {@code Object}. - */ - public IrinaDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code IrinaDialogue} {@code Object}. - * @param player the player. - */ - public IrinaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new IrinaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Out of my way, punk."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6035 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/JosephDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/JosephDialogue.java deleted file mode 100644 index 95b68dc2c..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/JosephDialogue.java +++ /dev/null @@ -1,61 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used for the joseph npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class JosephDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code JosephDialogue} {@code Object}. - */ - public JosephDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code JosephDialogue} {@code Object}. - * @param player the player. - */ - public JosephDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new JosephDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I have no interest in talking to a pathetic meat bag like", "yourself."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6029 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/MalakDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/MalakDialogue.java deleted file mode 100644 index b4e1d290f..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/MalakDialogue.java +++ /dev/null @@ -1,57 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue used for malak. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public class MalakDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code MalakDialogue} {@code Object}. - */ - public MalakDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MalakDialogue} {@code Object}. - * @param player the player. - */ - public MalakDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MalakDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Away from me, dog.", "I have business to discuss with the barkeeper."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - end(); - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1920 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/NikitiaDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/NikitiaDialogue.java deleted file mode 100644 index 404f49f74..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/NikitiaDialogue.java +++ /dev/null @@ -1,48 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the NikitiaDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class NikitiaDialogue extends DialoguePlugin { - - public NikitiaDialogue() { - - } - - public NikitiaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new NikitiaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't talk to me again if you value your life!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - end(); - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6042 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.kt b/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.kt index 97b81b311..41b7314a2 100644 --- a/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.kt +++ b/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.kt @@ -10,10 +10,9 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE import org.rs09.consts.Items import org.rs09.consts.NPCs +import content.data.Quests /** * Roavar dialogue. @@ -37,8 +36,8 @@ class RoavarDialogue (player: Player? = null) : DialoguePlugin(player) { 1 -> showTopics( Topic(FacialExpression.HALF_GUILTY, "Can I buy a beer?", 10, false), - Topic(FacialExpression.HALF_GUILTY, "Can I hear some gossip", 20, false), - IfTopic(FacialExpression.HALF_GUILTY, "Can I buy something to eat?", RoavarDialogueFile(1), player.getQuestRepository().getQuest("Creature of Fenkenstrain").getStage(player) == 2, false), + Topic(FacialExpression.HALF_GUILTY, "Can I hear some gossip?", 20, false), + IfTopic(FacialExpression.HALF_GUILTY, "Can I buy something to eat?", RoavarDialogueFile(1), player.getQuestRepository().getQuest(Quests.CREATURE_OF_FENKENSTRAIN).getStage(player) == 2, false), Topic(FacialExpression.HALF_GUILTY, "Nothing thanks.", 40, false) ) @@ -82,7 +81,7 @@ class RoavarDialogue (player: Player? = null) : DialoguePlugin(player) { } 21 -> end() - 30 -> stage = if (inInventory(player, 2963, 1)) { + 30 -> stage = if (inInventory(player, Items.SILVER_SICKLEB_2963, 1)) { npc(FacialExpression.HALF_GUILTY, "I don't have a spare lying around, sorry friend.", "Hopefully you'll find something else that can protect you", "against ghasts!") 31 } else { @@ -102,7 +101,7 @@ class RoavarDialogue (player: Player? = null) : DialoguePlugin(player) { npc(FacialExpression.HALF_GUILTY, "Oh, nevermind. It seems your backpack is full.") } else { sendDialogue(player, "The bartender hands you a silver sickle.") - addItem(player, 2963) + addItemOrDrop(player, Items.SILVER_SICKLEB_2963) } stage = 31 } diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/SbottDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/SbottDialogue.java deleted file mode 100644 index f053085ca..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/SbottDialogue.java +++ /dev/null @@ -1,103 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.plugin.Initializable; -import content.global.skill.crafting.TanningProduct; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Handles the SbottDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class SbottDialogue extends DialoguePlugin { - - public SbottDialogue() { - - } - - public SbottDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SbottDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello stranger. Would you like to me to tan any hides for", "you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - // interpreter.sendDialogues(npc, FacialExpression.NORMAL, - // "Soft leather - 2 gp per hide","Hard leather - 5 gp per hide","Snakeskins - 25 gp per hide","Dragon leather - 45 gp per hide."); - interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Soft leather - 1 gp per hide", "Hard leather - 3 gp per hide", "Snakeskins - 20 gp per hide", "Dragon leather - 20 gp per hide."); - stage = 1; - break; - case 1: - player.getInventory().refresh(); - Item items[] = player.getInventory().toArray(); - for (int i = 0; i < items.length; i++) { - if (items[i] == null) { - continue; - } - if (TanningProduct.forItemId(items[i].getId()) != null) { - interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "I see you have brought me some hides.", "Would you like me to tan them for you?"); - stage = 100; - return true; - } - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks, I haven't any hides."); - stage = 2; - break; - case 2: - end(); - break; - case 100: - interpreter.sendOptions("Select an Option", "Yes please.", "No thanks."); - stage = 101; - break; - case 101: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please."); - stage = 210; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No thanks."); - stage = 200; - break; - } - break; - case 210: - end(); - TanningProduct.open(player, 2824); - break; - case 200: - interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Very well, sir, as you wish."); - stage = 201; - break; - case 201: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1041 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/SbottDialogue.kt b/Server/src/main/content/region/morytania/canifis/dialogue/SbottDialogue.kt new file mode 100644 index 000000000..b315f4b01 --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/dialogue/SbottDialogue.kt @@ -0,0 +1,67 @@ +package content.region.morytania.canifis.dialogue + +import content.global.skill.crafting.TanningProduct +import core.api.inInventory +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * Handles Sbott's dialogue. + */ +@Initializable +class SbottDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HAPPY, "Hello stranger. Would you like to me to tan any hides for you?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + //0 -> npc(FacialExpression.HAPPY, "Soft leather - 2 gp per hide", "Hard leather - 5 gp per hide", "Snakeskins - 25 gp per hide", "Dragon leather - 45 gp per hide.").also { stage++ } + 0 -> npc(FacialExpression.HAPPY, "Soft leather - 1 gp per hide", "Hard leather - 3 gp per hide", "Snakeskins - 20 gp per hide", "Dragon leather - 20 gp per hide.").also { stage++ } + 1 -> { + var hasHides = false + + for (tanningProduct in TanningProduct.values()) { + if (inInventory(player, tanningProduct.item)) { + hasHides = true + break + } + } + + if(hasHides) { + npcl(FacialExpression.FRIENDLY, "I see you have brought me some hides. Would you like me to tan them for you?").also { stage = 10 } + } else { + playerl(FacialExpression.HALF_GUILTY, "No thanks, I haven't any hides.").also { stage = END_DIALOGUE } + } + } + + 10 -> options("Yes please.", "No thanks.").also { stage++ } + + 11 -> when (buttonId) { + 1 -> playerl(FacialExpression.HAPPY, "Yes please.").also { stage = 12 } + 2 -> playerl(FacialExpression.NEUTRAL, "No thanks.").also { stage = 13 } + } + + 12 -> end().also { TanningProduct.open(player, NPCs.SBOTT_1041) } + 13 -> npcl(FacialExpression.FRIENDLY, "Very well, @g[sir,madam], as you wish.").also { stage = END_DIALOGUE } + } + + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return SbottDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SBOTT_1041) + } +} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/SofiyaDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/SofiyaDialogue.java deleted file mode 100644 index 7735eab73..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/SofiyaDialogue.java +++ /dev/null @@ -1,52 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the SofiyaDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class SofiyaDialogue extends DialoguePlugin { - - public SofiyaDialogue() { - - } - - public SofiyaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SofiyaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Out of my way punk."); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6039, 6030, 6037 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/SvetlanaDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/SvetlanaDialogue.java deleted file mode 100644 index 0d72329fe..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/SvetlanaDialogue.java +++ /dev/null @@ -1,48 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the SvetlanaDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class SvetlanaDialogue extends DialoguePlugin { - - public SvetlanaDialogue() { - - } - - public SvetlanaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SvetlanaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmm... you smell strange..."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - end(); - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6034 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/TaxidermistDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/TaxidermistDialogue.java deleted file mode 100644 index eedae1210..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/TaxidermistDialogue.java +++ /dev/null @@ -1,76 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the TaxidermistDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class TaxidermistDialogue extends DialoguePlugin { - - public TaxidermistDialogue() { - - } - - public TaxidermistDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new TaxidermistDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh, hello. Have you got something you want", "preserving?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes please", "Not right now"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Not right now."); - stage = 20; - break; - - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Give it to me to look at then."); - stage = 11; - break; - case 11: - end(); - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4246 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/TaxidermistDialogue.kt b/Server/src/main/content/region/morytania/canifis/dialogue/TaxidermistDialogue.kt new file mode 100644 index 000000000..abaefb1d0 --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/dialogue/TaxidermistDialogue.kt @@ -0,0 +1,43 @@ +package content.region.morytania.canifis.dialogue + +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +open class TaxidermistDialogue(player: Player? = null) : DialoguePlugin(player) { + companion object{ + const val YES = 10 + const val NO = 20 + const val WHAT = 30 + } + + override fun open(vararg args: Any?): Boolean { + npcl(FacialExpression.HAPPY, "Oh, hello. Have you got something you want preserving?").also { stage++ } + return true + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE + 1 -> showTopics( + Topic("Yes please.", YES), + Topic("Not right now.", NO), + Topic("What?", WHAT) + ) + + YES -> npcl(FacialExpression.HAPPY, "Give it to me to look at then.").also { stage = END_DIALOGUE } + NO -> npcl(FacialExpression.ANNOYED, "Well, you go kill things so I can stuff them, eh?").also { stage = END_DIALOGUE } + WHAT -> npcl(FacialExpression.NEUTRAL, " If you bring me a monster head or a very big fish, I can preserve it for you so you can mount it in your house.").also { stage++ } + WHAT + 1 -> npcl(FacialExpression.HAPPY, "I hear there are all sorts of exotic creatures in the Slayer Tower -- I'd like a chance to stuff one of them!").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TAXIDERMIST_4246) + } +} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/YadvigaDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/YadvigaDialogue.java deleted file mode 100644 index a78bbdbc6..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/YadvigaDialogue.java +++ /dev/null @@ -1,61 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the yadviga dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class YadvigaDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code YadvigaDialogue} {@code Object}. - */ - public YadvigaDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code YadvigaDialogue} {@code Object}. - * @param player the player. - */ - public YadvigaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new YadvigaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Get lost!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6041 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/YuriDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/YuriDialogue.java deleted file mode 100644 index 0920c953d..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/YuriDialogue.java +++ /dev/null @@ -1,57 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue used for the yuri npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class YuriDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code YuriDialogue} {@code Object}. - */ - public YuriDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code YuriDialogue} {@code Object}. - * @param player the player. - */ - public YuriDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new YuriDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmm... you smell strange..."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - end(); - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6028 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/handlers/CanafisWereWolfPlugin.java b/Server/src/main/content/region/morytania/canifis/handlers/CanafisWereWolfPlugin.java deleted file mode 100644 index 76ad461e0..000000000 --- a/Server/src/main/content/region/morytania/canifis/handlers/CanafisWereWolfPlugin.java +++ /dev/null @@ -1,66 +0,0 @@ -package content.region.morytania.canifis.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle the attacking of a werwolf. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CanafisWereWolfPlugin extends OptionHandler { - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(6543); - - /** - * Represents the id to transform into. - */ - private static final int TRANSFORM_ID = 6006; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i = 6026; i < 6046; i++) { - NPCDefinition.forId(i).getHandlers().put("option:attack", this); - } - return this; - } - - @Override - public boolean handle(final Player player, Node node, String option) { - if (!player.getEquipment().contains(2952, 1)) { - player.lock(2); - final NPC n = (NPC) node; - final NPC newN = NPC.create(TRANSFORM_ID, n.getLocation()); - newN.init(); - newN.animate(ANIMATION); - n.clear(); - newN.lock(2); - newN.setRespawn(false); - newN.setAttribute("original", n.getId()); - newN.setAttribute("loc", n.getProperties().getSpawnLocation()); - GameWorld.getPulser().submit(new Pulse(2) { - @Override - public boolean pulse() { - newN.getProperties().getCombatPulse().attack(player); - return true; - } - }); - } else { - player.getProperties().getCombatPulse().attack(node); - } - return true; - } - -} diff --git a/Server/src/main/content/region/morytania/canifis/handlers/CanafisWerewolfNPC.kt b/Server/src/main/content/region/morytania/canifis/handlers/CanafisWerewolfNPC.kt new file mode 100644 index 000000000..51a82b834 --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/handlers/CanafisWerewolfNPC.kt @@ -0,0 +1,61 @@ +package content.region.morytania.canifis.handlers + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.DeathTask +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items + +class WerewolfBehavior : NPCBehavior(*HUMAN_NPCS) { + companion object { + // There are 20 humans that can turn into werewolves. They are all in series, so a range toIntArray() is easier. + private val HUMAN_NPCS = (6026 .. 6045).toIntArray() + private val WEREWOLF_NPCS = (6006 .. 6025).toIntArray() + private val HUMAN_OUT_ANIMATION = Animation(6554) + private val WEREWOLF_IN_ANIMATION = Animation(6543) // This is not used as there is a corresponding gfx. + private val WEREWOLF_IN_GFXS = (1079 .. 1098).toIntArray() // Play each werewolf's gfx with the animation. + } + + override fun afterDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if(DeathTask.isDead(self)){ + // Don't transform if you are killed + return + } + if (attacker is Player) { + if (!inEquipment(attacker, Items.WOLFBANE_2952, 1) && self.id in HUMAN_NPCS) { + delayAttack(self, 3) + delayAttack(attacker, 3) + lock(self, 3) + queueScript(self, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + visualize(self, HUMAN_OUT_ANIMATION, WEREWOLF_IN_GFXS[self.id - 6026]) + return@queueScript delayScript(self, WEREWOLF_IN_ANIMATION.duration) + } + 1 -> { + transformNpc(self, WEREWOLF_NPCS[self.id - 6026], 200) + return@queueScript delayScript(self, 1) + } + 2 -> { + self.properties.combatPulse.attack(attacker) + return@queueScript stopExecuting(self) + } + else -> return@queueScript stopExecuting(self) + } + } + } + } + } + + override fun onRespawn(self: NPC) { + if (self.id in WEREWOLF_NPCS){ + self.reTransform() + } + super.onRespawn(self) + } +} diff --git a/Server/src/main/content/region/morytania/canifis/handlers/TaxidermistInteraction.kt b/Server/src/main/content/region/morytania/canifis/handlers/TaxidermistInteraction.kt new file mode 100644 index 000000000..d7ead3a88 --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/handlers/TaxidermistInteraction.kt @@ -0,0 +1,100 @@ +package content.region.morytania.canifis.handlers + +import content.region.morytania.canifis.dialogue.TaxidermistDialogue +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class TaxidermistInteraction : InteractionListener { + + enum class StuffableItem(val toStuff : Int, val product : Int, val message : String, val cost : Int){ + BASS(Items.BIG_BASS_7989, Items.BIG_BASS_7990, "That's a mighty fine sea bass you've caught there.", 1000), + SWORDFISH(Items.BIG_SWORDFISH_7991, Items.BIG_SWORDFISH_7992, "Don't point that thing at me!", 2500), + SHARK(Items.BIG_SHARK_7993,Items.BIG_SHARK_7994, "That's quite a fearsome shark! You've done everyone a service by removing it from the sea!", 5000), + CRAWLING_HAND(Items.CRAWLING_HAND_7975,Items.CRAWLING_HAND_7982, "That's a very fine crawling hand.", 1000), + COCKATRICE(Items.COCKATRICE_HEAD_7976, Items.COCKATRICE_HEAD_7983, "A cockatrice! Beautiful, isn't it? Look at the plumage!", 2000), + BASILISK( Items.BASILISK_HEAD_7977, Items.BASILISK_HEAD_7984, "My, he's a scary-looking fellow, isn't he? He'll look good on your wall!",4000), + KURASK(Items.KURASK_HEAD_7978, Items.KURASK_HEAD_7985, "A kurask? Splendid! Look at those horns!", 6000), + ABYSSAL(Items.ABYSSAL_HEAD_7979, Items.ABYSSAL_HEAD_7986, "Goodness, an abyssal demon!", 12000), + KBD(Items.KBD_HEADS_7980, Items.KBD_HEADS_7987, " This must be a King Black Dragon!", 50000), + KQ(Items.KQ_HEAD_7981, Items.KQ_HEAD_7988, " That must be the biggest kalphite I've ever seen!", 50000) + } + class TaxidermistTradeDialogue : DialogueFile() { + companion object{ + const val TRADE = 10 + const val YES = 20 + const val NO = 30 + const val POOR = 40 + } + + lateinit var stuffableItem : StuffableItem + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.HAPPY, stuffableItem.message).also { + stage = when(stuffableItem){ + StuffableItem.ABYSSAL -> stage + 1 + StuffableItem.KBD -> stage + 2 + StuffableItem.KQ -> stage + 3 + else -> TRADE + } + } + START_DIALOGUE + 1 -> npcl(FacialExpression.HAPPY, "See how it's still glowing? I'll have to use some magic to preserve that.").also { stage = TRADE } + START_DIALOGUE + 2 -> npcl(FacialExpression.HAPPY, "I'll have to get out my heavy duty tools -- this skin's as tough as iron!").also { stage = TRADE } + START_DIALOGUE + 3 -> npcl(FacialExpression.HAPPY, "Preserving insects is always tricky. I'll have to be careful...").also { stage = TRADE } + + TRADE -> npcl(FacialExpression.HAPPY, "I can preserve that for you for ${stuffableItem.cost} coins.").also { stage++ } + TRADE + 1 -> showTopics( + Topic("Yes please.", YES), + Topic("No thanks.", NO) + ) + YES -> { + if (inInventory(player!!, Items.COINS_995, stuffableItem.cost)){ + if (removeItem(player!!, stuffableItem.toStuff) && removeItem(player!!, Item(Items.COINS_995, stuffableItem.cost))){ + addItem(player!!, stuffableItem.product) + npcl(FacialExpression.HAPPY, "There you go!").also { stage = END_DIALOGUE } + } + } + else { + playerl(FacialExpression.SAD, "But I don't have enough money on me.").also { stage = POOR } + } + } + + POOR -> npcl(FacialExpression.ANNOYED, "Don't waste my time, then!").also { stage = END_DIALOGUE } + + NO -> npcl(FacialExpression.NEUTRAL, "All right, come back if you change your mind, eh?").also { stage = END_DIALOGUE } + } + } + + } + + override fun defineListeners() { + + onUseAnyWith(IntType.NPC, NPCs.TAXIDERMIST_4246) { player, used, with -> + val item = used.id + StuffableItem.values().map { if(it.toStuff == item){ + val dialogueFile = TaxidermistTradeDialogue() + dialogueFile.stuffableItem = it + openDialogue(player, dialogueFile, with as NPC) + return@onUseAnyWith true + } + } + + when (item) { + // all fish + Items.RAW_CHICKEN_2138 -> sendNPCDialogue(player, with.id, "Killing a chicken is hardly worth boasting about!") + else -> sendNPCDialogue(player, with.id, "Don't be silly, I can't preserve that!") + } + return@onUseAnyWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/canifis/handlers/WerewolfNPC.java b/Server/src/main/content/region/morytania/canifis/handlers/WerewolfNPC.java deleted file mode 100644 index 3884bbf56..000000000 --- a/Server/src/main/content/region/morytania/canifis/handlers/WerewolfNPC.java +++ /dev/null @@ -1,50 +0,0 @@ -package content.region.morytania.canifis.handlers; - -import core.game.node.entity.Entity; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Represents the tutorial chicken npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class WerewolfNPC extends AbstractNPC { - - /** - * Constructs a new {@code WerewolfNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public WerewolfNPC(int id, Location location) { - super(id, location, true); - } - - /** - * Constructs a new {@code WerewolfNPC} {@code Object}. - */ - public WerewolfNPC() { - super(0, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new WerewolfNPC(id, location); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - NPC nn = NPC.create(getAttribute("original", 6026), getAttribute("loc", getLocation())); - nn.init(); - } - - @Override - public int[] getIds() { - return new int[] { 6006 }; - } - -} diff --git a/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt b/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt index 6748596f5..cb80df540 100644 --- a/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt +++ b/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt @@ -70,8 +70,9 @@ class MortMyreGhastNPC : AbstractNPC { val consumable = Consumables.getConsumableById(i.id) if(consumable != null && consumable.consumable is Food) { hasFood = true - removeItem(player, i, Container.INVENTORY) - addItem(player, Items.ROTTEN_FOOD_2959) + if (removeItem(player, i, Container.INVENTORY)) { + addItem(player, Items.ROTTEN_FOOD_2959) + } sendMessage(player, "You feel something attacking your backpack, and smell a terrible stench.") break } diff --git a/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt b/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt index 19429c4bd..5edfa55f7 100644 --- a/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt +++ b/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt @@ -11,6 +11,7 @@ import org.rs09.consts.NPCs import core.game.bots.AIPlayer import core.game.dialogue.DialogueFile import core.game.world.GameWorld +import content.data.Quests class MorytaniaArea : MapArea { override fun defineAreaBorders(): Array { @@ -22,7 +23,7 @@ class MorytaniaArea : MapArea { override fun areaEnter(entity: Entity) { if (entity is Player && entity !is AIPlayer && ( - !isQuestComplete(entity, "Priest in Peril") || //not allowed to be anywhere in Morytania + !isQuestComplete(entity, Quests.PRIEST_IN_PERIL) || //not allowed to be anywhere in Morytania defineAreaBorders()[1].insideBorder(entity) //Werewolf agility course is not implemented )) { kickThemOut(entity) diff --git a/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt b/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt index ccce9cbaf..0cd82feda 100644 --- a/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt +++ b/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt @@ -13,6 +13,7 @@ import org.rs09.consts.Scenery import core.game.interaction.InteractionListener import core.game.interaction.IntType import kotlin.random.Random +import content.data.Quests /** * File to be used for anything Morytania related. @@ -43,7 +44,7 @@ class MorytaniaListeners : InteractionListener { findLocalNPC(player, NPCs.ULIZIUS_1054)?.sendChat("Oh my! You're still alive!", 2) } } else { - if (player.questRepository.hasStarted("Nature Spirit")) { + if (player.questRepository.hasStarted(Quests.NATURE_SPIRIT)) { core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } else { sendNPCDialogue( diff --git a/Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java index 23d8093fe..40f395cf4 100644 --- a/Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java @@ -82,13 +82,13 @@ public final class GhostDiscipleDialogue extends DialoguePlugin { stage++; break; case 1: - options("What is the Ectofunuts?", "Where do I get ectoplasm from?", "How do I grind bones?", "How do I receive Ectotokens?", "Thanks for your time."); + options("What is the Ectofuntus?", "Where do I get ectoplasm from?", "How do I grind bones?", "How do I receive Ectotokens?", "Thanks for your time."); stage++; break; case 2: switch (buttonId) { case 1: - player("What is the Ectofunuts?"); + player("What is the Ectofuntus?"); stage = 10; break; case 2: diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/BookcaseDialogueFile.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/BookcaseDialogueFile.kt index a5d0843da..7a07c5a44 100644 --- a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/BookcaseDialogueFile.kt +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/BookcaseDialogueFile.kt @@ -1,5 +1,6 @@ package content.region.morytania.quest.creatureoffenkenstrain +import content.data.Quests import content.global.handlers.iface.BookInterface import content.global.handlers.iface.BookLine import content.global.handlers.iface.Page @@ -46,7 +47,7 @@ class BookcaseEastDialogueFile : DialogueFile() { 4 -> sendDialogue(player!!, "The book is appallingly dull.").also { stage = END_DIALOGUE } } 2 -> { - if (getQuestStage(player!!, CreatureOfFenkenstrain.questName) == 2) { + if (getQuestStage(player!!, Quests.CREATURE_OF_FENKENSTRAIN) == 2) { sendItemDialogue(player!!, Items.OBSIDIAN_AMULET_4188, "You find an obsidian amulet in the secret compartment.").also { addItemOrDrop(player!!, Items.OBSIDIAN_AMULET_4188, 1) stage = END_DIALOGUE @@ -67,7 +68,7 @@ class BookcaseEastDialogueFile : DialogueFile() { class ChimneySweepingOnABudgetBook { companion object { - private val TITLE = "Chimney Sweeping on a Budget " + private val TITLE = "Chimney Sweeping on a Budget" val CONTENTS = arrayOf( PageSet( Page( @@ -102,4 +103,4 @@ class ChimneySweepingOnABudgetBook { return true } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrain.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrain.kt index 3e3b3137b..dd7dccc9b 100644 --- a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrain.kt +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrain.kt @@ -6,8 +6,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.plugin.Initializable import org.rs09.consts.Items - -val CREATURE_OF_FENKENSTRAIN = "Creature of Fenkenstrain" +import content.data.Quests /** * Creature of Fenkenstrain Quest @@ -27,10 +26,9 @@ val CREATURE_OF_FENKENSTRAIN = "Creature of Fenkenstrain" * 100 - Stoped Fenkenstrain by stealing ring of charos */ @Initializable -class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, 0, 1, 9) { +class CreatureOfFenkenstrain : Quest(Quests.CREATURE_OF_FENKENSTRAIN, 41, 40, 2, 399, 0, 1, 9) { companion object { - const val questName = "Creature of Fenkenstrain" const val attributeArms = "/save:quest:creatureoffenkenstrain-arms" const val attributeLegs = "/save:quest:creatureoffenkenstrain-legs" const val attributeTorso = "/save:quest:creatureoffenkenstrain-torso" @@ -46,7 +44,7 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, var line = 12 var stage = getStage(player) - var started = getQuestStage(player, questName) > 0 + var started = getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) > 0 if(!started){ line(player, "I can start this quest by reading the signpost in the", line++, false) @@ -56,8 +54,9 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, line(player, "Level 20 Crafting", line++, hasLevelStat(player, Skills.CRAFTING, 20)) line(player, "Level 25 Theiving", line++, hasLevelStat(player, Skills.THIEVING, 25)) line(player, "I also need to have completed the following quests:", line++, false) - line(player, "Priest in Peril", line++, isQuestComplete(player, "Priest in Peril")) - line(player, "Restless Ghost", line++, isQuestComplete(player, "The Restless Ghost")) + line(player, "Priest in Peril", line++, isQuestComplete(player, Quests.PRIEST_IN_PERIL)) + line(player, "Restless Ghost", line++, isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) + limitScrolling(player, line, true) } else { line(player, "I read the signpost in Canifis, which tells of a butler", line++, true) line(player, "position that is available at the castle to the northeast.", line++, true) @@ -67,12 +66,13 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, } else if (stage >= 1) { line(player, "I should go up to the castle and speak to !!Dr Fenkenstrain??", line++, false) } - line++ if (stage >= 3) { - line(player, "I gave a torso, some arms and legs, and a head to Fenkenstrain,", line++, true) - line(player, "who then wanted a needle and 5 lots of thread, so that he could", line++, true) - line(player, "sew the bodyparts together and create his creature.", line++, true) + line(player, "I gave a torso, some arms and legs, and a head to", line++, true) + line(player, "Fenkenstrain, who then wanted a needle and 5 lots of", line++, true) + line(player, "thread, so that he could sew the bodyparts together and", line++, true) + line(player, "create his creature.", line++, true) } else if (stage >= 2) { + line++ line(player, "I need to find these body parts for !!Fenkenstrain??:", line++, false) line(player, "a pair of !!arms??", line++, false) line(player, "a pair legs !!legs??", line++, false) @@ -84,7 +84,6 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, line(player, "elsewhere, so perhaps I should look at the graves in the", line++, false) line(player, "local area", line++, false) } - line++ if (stage >= 4) { line(player, "I brought Fenkenstrain a needle and 5 quantities of", line++, true) line(player, "thread.", line++, true) @@ -92,15 +91,13 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, line(player, "I need to bring !!Fenkenstrain?? a !!needle?? and !!5 quantities??", line++, false) line(player, "!!of thread??.", line++, false) } - line++ if (stage >= 5) { line(player, "I repaired the lightning conductor, and Fenkenstrain", line++, true) line(player, "brought the Creature to life.", line++, true) } else if (stage >= 4) { - line(player, "I need to repair the !!lightning conductor?? on the", line++, false) - line(player, "!!balcony?? above.", line++, false) + line(player, "!!Fenkenstrain?? has ordered me to repair the lightning", line++, false) + line(player, "conductor.", line++, false) } - line++ if (stage == 5) { line(player, "!!Fenkenstrain?? wants to talk to me.", line++, false) line++ @@ -115,10 +112,9 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, line(player, "The !!Creature?? went on a rampage, and !!Fenkenstrain?? wants", line++, false) line(player, "me to go up the !!Tower?? to destroy it.", line++, false) } - line++ if (stage >= 8) { - line(player, "I stole Fenkenstrain's Ring of Charos, and he released me from", line++, true) - line(player, "his service.", line++, true) + line(player, "I stole Fenkenstrain's Ring of Charos, and he released", line++, true) + line(player, "me from his service.", line++, true) } else if (stage >= 7) { line(player, "I must find a way to stop Fenkenstrain's experiments.", line++, false) } @@ -126,6 +122,7 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, line++ line(player,"QUEST COMPLETE!", line) } + limitScrolling(player, line, false) } } @@ -133,8 +130,8 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, return arrayOf( hasLevelStat(player, Skills.CRAFTING, 20), hasLevelStat(player, Skills.THIEVING, 25), - isQuestComplete(player, "Priest in Peril"), - isQuestComplete(player, "The Restless Ghost"), + isQuestComplete(player, Quests.PRIEST_IN_PERIL), + isQuestComplete(player, Quests.THE_RESTLESS_GHOST), ).all { it } } @@ -172,10 +169,10 @@ class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 2, 399, override fun updateVarps(player: Player) { // This is a bit of a hack. I didn't manage to align the quest with the varp, // so I had to include both stage 3 and 4 to varp value 3 to show the creature. - if(getQuestStage(player, questName) == 4) { + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 4) { setVarp(player, fenkenstrainVarp, 3, true) } - if(getQuestStage(player, questName) >= 8) { + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) >= 8) { setVarp(player, fenkenstrainVarp, 8, true) } } diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrainListeners.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrainListeners.kt index a79c8940e..7a0aea3bc 100644 --- a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrainListeners.kt +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrainListeners.kt @@ -1,15 +1,12 @@ package content.region.morytania.quest.creatureoffenkenstrain -import content.global.travel.canoe.CanoeListener +import content.data.Quests import core.api.* import core.game.dialogue.FacialExpression import core.game.global.action.DoorActionHandler import core.game.global.action.PickupHandler import core.game.interaction.InteractionListener -import core.game.node.Node -import core.game.node.entity.player.Player import core.game.node.item.GroundItem -import core.game.node.item.Item import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.update.flag.context.Animation @@ -64,7 +61,7 @@ class CreatureOfFenkenstrainListeners : InteractionListener { // 1: Reading Signpost to start the quest on(Items.NULL_5164, SCENERY, "read") { player, _ -> - if (getQuestStage(player, CreatureOfFenkenstrain.questName) < 7 ) { + if (getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) < 7 ) { sendDialogueLines( player, "The signpost has a note pinned onto it. The note says:", @@ -79,8 +76,11 @@ class CreatureOfFenkenstrainListeners : InteractionListener { "'AAARRGGGHHHHH!!!!!'", ) } - if(getQuest(player, CreatureOfFenkenstrain.questName).hasRequirements(player) && getQuestStage(player, CreatureOfFenkenstrain.questName) == 0) { - setQuestStage(player, CreatureOfFenkenstrain.questName, 1) + if(getQuest(player, Quests.CREATURE_OF_FENKENSTRAIN).hasRequirements(player) && getQuestStage( + player, + Quests.CREATURE_OF_FENKENSTRAIN + ) == 0) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 1) } return@on true } @@ -199,7 +199,7 @@ class CreatureOfFenkenstrainListeners : InteractionListener { on(Items.NULL_5167, SCENERY, "search") { player, node -> val scenery = node.asScenery() if(getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedMemorial, false) || - getQuestStage(player, CreatureOfFenkenstrain.questName) > 2) { + getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) > 2) { animateScenery(player, scenery, 1620) var dest: Location? = null if(scenery.location.equals(Location(3505, 3571))) { @@ -230,7 +230,7 @@ class CreatureOfFenkenstrainListeners : InteractionListener { on(Items.NULL_5167, SCENERY, "push") { player, node -> val scenery = node.asScenery() if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedMemorial, false) || - getQuestStage(player, CreatureOfFenkenstrain.questName) > 2) { + getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) > 2) { animateScenery(player, scenery, 1620) var dest: Location? = null if(scenery.location.equals(Location(3505, 3571))) { @@ -256,7 +256,7 @@ class CreatureOfFenkenstrainListeners : InteractionListener { // 5: Garden Shed Door on(Scenery.DOOR_5174, SCENERY, "open") { player, node -> if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, false) || - getQuestStage(player, CreatureOfFenkenstrain.questName) >= 5) { + getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) >= 5) { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } else if (inInventory(player, Items.SHED_KEY_4186)) { if (removeItem(player, Items.SHED_KEY_4186)) { @@ -272,7 +272,7 @@ class CreatureOfFenkenstrainListeners : InteractionListener { // 5: Garden Shed Door onUseWith(SCENERY, Items.SHED_KEY_4186, Scenery.DOOR_5174) { player, used, with -> if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, false) || - getQuestStage(player, CreatureOfFenkenstrain.questName) >= 5) { + getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) >= 5) { DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) } else if (removeItem(player, used)) { DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) @@ -346,8 +346,8 @@ class CreatureOfFenkenstrainListeners : InteractionListener { return@on true } if (removeItem(player, Items.CONDUCTOR_4201)) { - if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 4) { - setQuestStage(player, CreatureOfFenkenstrain.questName, 5) + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 4) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 5) } sendDialogue(player, "You repair the lightning conductor not one moment too soon - a tremendous bold of lightning melts the new lightning conductor, and power blazes throughout the castle, if only briefly.") val scenery = node.asScenery() @@ -359,7 +359,10 @@ class CreatureOfFenkenstrainListeners : InteractionListener { // 6: Enter jail above on(Scenery.DOOR_5172, SCENERY, "open") { player, node -> - if (inInventory(player, Items.TOWER_KEY_4185) || getQuestStage(player, CreatureOfFenkenstrain.questName) > 7) { + if (inInventory(player, Items.TOWER_KEY_4185) || getQuestStage( + player, + Quests.CREATURE_OF_FENKENSTRAIN + ) > 7) { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) } else { sendMessage(player, "The door is locked.") @@ -377,10 +380,10 @@ class CreatureOfFenkenstrainListeners : InteractionListener { // 7: Pickpocket Ring of Charos from Fenkenstrain on(NPCs.DR_FENKENSTRAIN_1670, NPC, "pickpocket") { player, node -> - if (getQuestStage(player, CreatureOfFenkenstrain.questName) == 7) { + if (getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 7) { sendMessage(player, "You steal the Ring of Charos from Fenkenstrain.") - finishQuest(player, CreatureOfFenkenstrain.questName) - } else if (getQuestStage(player, CreatureOfFenkenstrain.questName) > 7 && hasAnItem(player, Items.RING_OF_CHAROS_4202).container == null) { + finishQuest(player, Quests.CREATURE_OF_FENKENSTRAIN) + } else if (getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) > 7 && hasAnItem(player, Items.RING_OF_CHAROS_4202).container == null) { // Allow Fenkenstrain to be pickpocketed beyond the quest if the ring is lost. addItemOrDrop(player, Items.RING_OF_CHAROS_4202, 1) sendMessage(player, "You steal the Ring of Charos from Fenkenstrain.") diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/DrFenkenstrainDialogue.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/DrFenkenstrainDialogue.kt index a4fcea45d..c6e848f86 100644 --- a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/DrFenkenstrainDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/DrFenkenstrainDialogue.kt @@ -1,5 +1,6 @@ package content.region.morytania.quest.creatureoffenkenstrain +import content.data.Quests import core.api.* import core.game.dialogue.* import core.game.node.entity.player.Player @@ -65,13 +66,13 @@ class DrFenkenstrainDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages(CREATURE_OF_FENKENSTRAIN, 0) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 0) .npcl("Have you come to apply for the job?") .playerl(FacialExpression.THINKING, "What job?") .npcl("I've posted a note on the signpost in Canifis about it. Go take a look at it first.") .end() - b.onQuestStages(CREATURE_OF_FENKENSTRAIN, 1) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 1) .npcl("Have you come to apply for the job?") .options().let { optionBuilder -> val continuePath = b.placeholder() @@ -127,12 +128,12 @@ class DrFenkenstrainDialogueFile : DialogueBuilderFile() { .npcl("I need you to get me enough dead body parts for me to stitch together a complete body, which I plan to bring to life.") .playerl("Right...okay...if you insist.") .endWith { _, player -> - if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 1) { - setQuestStage(player, CreatureOfFenkenstrain.questName, 2) + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 1) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 2) } } - b.onQuestStages(CreatureOfFenkenstrain.questName, 2) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 2) .options().let { optionBuilder -> val continuePath = b.placeholder() @@ -184,12 +185,12 @@ class DrFenkenstrainDialogueFile : DialogueBuilderFile() { .npcl("Oh bother! I haven't got a needle or thread!") .npcl("Go and get me a needle, and I'll need 5 lots of thread.") .endWith { _, player -> - if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 2) { - setQuestStage(player, CreatureOfFenkenstrain.questName, 3) + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 2) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 3) } } - b.onQuestStages(CreatureOfFenkenstrain.questName, 3) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 3) .npcl("Where are my needle and thread, @name?") // Dialogue path to look for 1 needle. .let{ builder -> return@let hasPart(builder, Item(Items.NEEDLE_1733, 1), CreatureOfFenkenstrain.attributeNeedle, "Ah, a needle. Wonderful.") } @@ -219,19 +220,19 @@ class DrFenkenstrainDialogueFile : DialogueBuilderFile() { .playerl("Repair the lightning conductor, right. Can I have a break, soon? By law I'm entitled to 15 minutes every-") .npcl("Repair the conductor and BEGONE!!") .endWith { _, player -> - if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 3) { - setQuestStage(player, CreatureOfFenkenstrain.questName, 4) + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 3) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 4) } } - b.onQuestStages(CreatureOfFenkenstrain.questName, 4) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 4) .playerl(FacialExpression.THINKING, "How do I repair the lighting conductor?") .npcl("Oh, it would be easier to do it myself! If you find a conductor mould you should be able to cast a new one.") .npcl("Remember this, @name, my experiment will only work with a conductor made from silver.") .end() - b.onQuestStages(CreatureOfFenkenstrain.questName, 5) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 5) .playerl("So did it work, then?") .npcl("Yes, I'm afraid it did, @name - all too well.") .playerl(FacialExpression.SUSPICIOUS, "I can't see it anywhere.") @@ -244,19 +245,19 @@ class DrFenkenstrainDialogueFile : DialogueBuilderFile() { .playerl(FacialExpression.SUSPICIOUS, "What do you want me to do about it?") .npcl("Destroy it!!! Take the key to the Tower and take back the life I never should have granted!!!") .endWith() { df, player -> - if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 5) { - setQuestStage(player, CreatureOfFenkenstrain.questName, 6) + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 5) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 6) } addItemOrDrop(player, Items.TOWER_KEY_4185) } - b.onQuestStages(CreatureOfFenkenstrain.questName, 6) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 6) .npcl("So have you destroyed it?!!?") .playerl("Not yet.") .npcl("Please, hurry - save me!!!!") .end() - b.onQuestStages(CreatureOfFenkenstrain.questName, 7) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 7) .npcl("So have you destroyed it?!!?") .playerl("Never, now that he has told me the truth!") .npcl("Oh my, oh my, this is exactly what I feared!") @@ -265,7 +266,7 @@ class DrFenkenstrainDialogueFile : DialogueBuilderFile() { .npcl("No! I refuse to release you! You must help me build another creature to destroy this dreadful mistake!!") .end() - b.onQuestStages(CreatureOfFenkenstrain.questName, 8, 100) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 8, 100) .npcl("theyrecomingtogetme theyrecomingtogetme...") .playerl("It is all you deserve. Lord Rologarth is master of this castle once more. Let him protect you - if he wants to.") .npcl("theyrecomingtogetme theyrecomingtogetme...") diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostDialogue.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostDialogue.kt index 712ab5100..70387d2c8 100644 --- a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostDialogue.kt @@ -1,12 +1,10 @@ package content.region.morytania.quest.creatureoffenkenstrain +import content.data.Quests import core.api.* import core.game.dialogue.* import core.game.node.entity.player.Player -import core.game.node.item.Item import core.plugin.Initializable -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE import org.rs09.consts.Items import org.rs09.consts.NPCs import java.util.* @@ -41,7 +39,7 @@ class GardenerGhostDialogueFile : DialogueBuilderFile() { b.onPredicate { player -> !player.equipment.containsAtLeastOneItem(Items.GHOSTSPEAK_AMULET_552) } .npcl("Wooo wooo wooooo.") .end() - b.onQuestStages(CreatureOfFenkenstrain.questName, *(0 .. 8).toIntArray(), 100) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, *(0..8).toIntArray(), 100) .options().let { optionBuilder -> optionBuilder.option("Tell me about Fenkenstrain.") .playerl("Can you tell me anything about Fenkenstrain?") @@ -57,14 +55,20 @@ class GardenerGhostDialogueFile : DialogueBuilderFile() { .npcl("Don't worry yerself. I'm not worried about bein' dead. Worse things could happen, I suppose.") .npcl("One thing I do know is, there ain't no lord of the castle anymore, 'cept for old Fenky. Makes ya think a bit, don't it?") .end() - optionBuilder.optionIf("Do you know where the key to the shed is?") { player -> return@optionIf getQuestStage(player, CreatureOfFenkenstrain.questName) == 4 } + optionBuilder.optionIf("Do you know where the key to the shed is?") { player -> return@optionIf getQuestStage( + player, + Quests.CREATURE_OF_FENKENSTRAIN + ) == 4 } .item(Items.GHOSTSPEAK_AMULET_552, "You feel power emanate from the Amulet of Ghostspeak", "and the air around you vibrates with the ghostly voice", "of the headless gardener.") .npcl("Got it right 'ere in my pocket. Here you go.") .iteml(4186, "The headless gardener hands you a rusty key.") .endWith { _, player -> addItemOrDrop(player, Items.SHED_KEY_4186) } - optionBuilder.optionIf("Do you know where I can find a lightning conductor mould is?") { player -> return@optionIf getQuestStage(player, CreatureOfFenkenstrain.questName) == 4 } + optionBuilder.optionIf("Do you know where I can find a lightning conductor mould is?") { player -> return@optionIf getQuestStage( + player, + Quests.CREATURE_OF_FENKENSTRAIN + ) == 4 } .item(Items.GHOSTSPEAK_AMULET_552, "You feel power emanate from the Amulet of Ghostspeak", "and the air around you vibrates with the ghostly voice", "of the headless gardener.") .npcl("A conductor mould, you say? Let me see...") .npcl("There used to be a bloke 'ere, sort of an 'andyman 'e was. Did everything 'round the place - fixed what was broke, swept the chimneys and the like. He would 'ave had a mould, I imagine.") @@ -80,7 +84,10 @@ class GardenerGhostDialogueFile : DialogueBuilderFile() { .playerl("Would you show me where the place was?") .npcl("Well, oi s'pose oi've got ten minutes to spare.") .endWith { df, player -> (df.npc!! as GardenerGhostNPC).startFollowing(player) } - optionBuilder.optionIf("What's your name?") { player -> return@optionIf getQuestStage(player, CreatureOfFenkenstrain.questName) < 4 } + optionBuilder.optionIf("What's your name?") { player -> return@optionIf getQuestStage( + player, + Quests.CREATURE_OF_FENKENSTRAIN + ) < 4 } .playerl("What's your name?") .item(Items.GHOSTSPEAK_AMULET_552, "You feel power emanate from the Amulet of Ghostspeak", "and the air around you vibrates with the ghostly voice", "of the headless gardener.") .npcl("Me name? It's been a moivellous long while, mate, since I had any use for such a thing as a name.") diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/LordRologarthDialogue.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/LordRologarthDialogue.kt index de32db2a3..6fd066de5 100644 --- a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/LordRologarthDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/LordRologarthDialogue.kt @@ -1,11 +1,11 @@ package content.region.morytania.quest.creatureoffenkenstrain +import content.data.Quests import core.api.* import core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs @@ -27,7 +27,7 @@ class LordRologarthDialogue (player: Player? = null) : DialoguePlugin(player) { class LordRologarthDialogueFile : DialogueBuilderFile() { override fun create(b: DialogueBuilder) { - b.onQuestStages(CreatureOfFenkenstrain.questName, 6) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 6) .playerl("I am commanded to destroy you, creature!") .npcl("Oh that's *hic* not very *hic* nice ...") .playerl("Are you feeling ok?") @@ -49,11 +49,11 @@ class LordRologarthDialogueFile : DialogueBuilderFile() { .playerl("That's it - I'm leaving this dreadful place, whether I get paid or not. Is there anything I can do for you before I leave?") .npcl("Only one - please stop Fenkenstrain from carrying on his experiments, once and for all, so that no other poor soul has to endure suffering such as that of my people and I.") .endWith() { df, player -> - if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 6) { - setQuestStage(player, CreatureOfFenkenstrain.questName, 7) + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 6) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 7) } } - b.onQuestStages(CreatureOfFenkenstrain.questName, 7) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 7) .playerl(FacialExpression.THINKING, "Do you know how I can stop Fenkenstrain's experiments?") .npcl("Take the Ring of Charos from him.") .playerl(FacialExpression.THINKING, "What is this ring?") @@ -61,7 +61,7 @@ class LordRologarthDialogueFile : DialogueBuilderFile() { .npcl("The Ring of Charos has many powers, but Fenkenstrain has bent them to his down evil purposes. Without the power of the ring, he will not be able to raise the dead from their sleep.") .npcl("It has one other, extremely important use - it confuses the werewolves' senses, making them believe that they smell one of their own kind. Without the ring, Fenkenstrain will be at their mercy.") .end() - b.onQuestStages(CreatureOfFenkenstrain.questName, 8, 100) + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 8, 100) .npcl("How goes it, friend?") .playerl("I stole the Ring of Charos from Fenkenstrain.") .npcl("I saw him climb up into the Tower to hide. It doesn't matter - soon the werewolves will come for him, and his experiments will be forever ceased.") diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt index 4ff9d60c4..8bfa2330f 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt @@ -11,11 +11,12 @@ import core.game.dialogue.DialogueFile import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE import org.rs09.consts.Sounds +import content.data.Quests class NSDrezelDialogue : DialogueFile() { var questStage = 0 override fun handle(componentID: Int, buttonID: Int) { - questStage = player!!.questRepository.getStage("Nature Spirit") + questStage = player!!.questRepository.getStage(Quests.NATURE_SPIRIT) if(questStage <= 5){ when(stage){ @@ -51,12 +52,12 @@ class NSDrezelDialogue : DialogueFile() { if(questStage == 0){ repeat(3) { addItemOrDrop(player!!, Items.MEAT_PIE_2327, 1) } repeat(3) { addItemOrDrop(player!!, Items.APPLE_PIE_2323, 1) } - player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, 5) + player!!.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player!!, 5) } stage++ } 23 -> npcl(FacialExpression.NEUTRAL, "Please take this food to Filliman, he'll probably appreciate a bit of cooked food. Now, he's never revealed where he lives in the swamps but I guess he'd be to the south, search for him won't you?").also { stage++ } - 24 -> playerl(FacialExpression.FRIENDLY, "I'll do my very best, don't worry, if he's in there and he's still alive I'll definitely find him.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest("Nature Spirit").start(player!!) } + 24 -> playerl(FacialExpression.FRIENDLY, "I'll do my very best, don't worry, if he's in there and he's still alive I'll definitely find him.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest(Quests.NATURE_SPIRIT).start(player!!) } } } @@ -85,7 +86,7 @@ class NSDrezelDialogue : DialogueFile() { } else if(questStage == 40){ - npcl(FacialExpression.NEUTRAL, "There you go my friend, you're now blessed. It's funny, now I look at you, there seems to be something of the faith about you. Anyway, good luck with your quest!").also { stage = END_DIALOGUE; player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, 45) } + npcl(FacialExpression.NEUTRAL, "There you go my friend, you're now blessed. It's funny, now I look at you, there seems to be something of the faith about you. Anyway, good luck with your quest!").also { stage = END_DIALOGUE; player!!.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player!!, 45) } } else { @@ -106,7 +107,7 @@ private class BlessingPulse(val drezel: NPC, val player: Player) : Pulse(){ when(ticks){ 0 -> animate(drezel, 1162).also { spawnProjectile(drezel, player, 268); playAudio(player, Sounds.PRAYER_RECHARGE_2674) } 2 -> visualize(player, Animation(645), Graphics(267, 100)) - 4 -> unlock(player).also { player.questRepository.getQuest("Nature Spirit").setStage(player, 40); return true } + 4 -> unlock(player).also { player.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player, 40); return true } } ticks++ return false diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt index c8e5a1b4a..b580aa4f3 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt @@ -17,9 +17,9 @@ import core.game.shops.Shops import core.game.interaction.InteractionListener import core.game.interaction.IntType import content.region.morytania.handlers.MortMyreGhastNPC -import core.tools.SystemLogger import core.tools.END_DIALOGUE import core.tools.Log +import content.data.Quests class NSListeners : InteractionListener { @@ -63,7 +63,7 @@ class NSListeners : InteractionListener { } on(GROTTO_ENTRANCE, IntType.SCENERY, "enter"){ player, node -> - val questStage = player.questRepository.getQuest("Nature Spirit").getStage(player) + val questStage = player.questRepository.getQuest(Quests.NATURE_SPIRIT).getStage(player) if(questStage < 55) { val npc = core.game.node.entity.npc.NPC.create(NPCs.FILLIMAN_TARLOCK_1050, Location.create(3440, 3336, 0)) npc.init() @@ -76,7 +76,7 @@ class NSListeners : InteractionListener { } on(GROTTO_ALTAR, IntType.SCENERY, "search"){ player, node -> - val stage = player.questRepository.getStage("Nature Spirit") + val stage = player.questRepository.getStage(Quests.NATURE_SPIRIT) if(stage == 55){ openDialogue(player, FillimanCompletionDialogue(), NPC(NPCs.FILLIMAN_TARLOCK_1050)) return@on true @@ -101,7 +101,7 @@ class NSListeners : InteractionListener { } on(WISHING_WELL, IntType.SCENERY, "make-wish"){ player, node -> - if(player.questRepository.isComplete("Nature Spirit") && player.questRepository.isComplete("Wolf Whistle")) + if(player.questRepository.isComplete(Quests.NATURE_SPIRIT) && player.questRepository.isComplete(Quests.WOLF_WHISTLE)) Shops.openId(player, 241) else sendDialogue(player, "You can't do that yet.") @@ -132,8 +132,7 @@ class NSListeners : InteractionListener { } on(SPELLCARD, IntType.ITEM, "cast"){ player, node -> - if(NSUtils.castBloom(player)){ - removeItem(player, node.asItem(), Container.INVENTORY) + if (NSUtils.castBloom(player) && removeItem(player, node.asItem(), Container.INVENTORY)) { addItem(player, Items.A_USED_SPELL_2969) } return@on true @@ -141,7 +140,7 @@ class NSListeners : InteractionListener { on(intArrayOf(DRUID_POUCH, DRUID_POUCH_EMPTY), IntType.ITEM, "fill"){ player, node -> - if(player.questRepository.getStage("Nature Spirit") >= 75) { + if(player.questRepository.getStage(Quests.NATURE_SPIRIT) >= 75) { if (amountInInventory(player, PEAR) >= 3) { if (node.id != Items.DRUID_POUCH_2958) { removeItem(player, node, Container.INVENTORY) @@ -183,7 +182,7 @@ class NSListeners : InteractionListener { } onUseWith(IntType.NPC, Items.SECATEURS_5329, NPCs.NATURE_SPIRIT_1051) {player, used, with -> - if (!hasRequirement(player, "Fairytale I - Growing Pains")) + if (!hasRequirement(player, Quests.FAIRYTALE_I_GROWING_PAINS)) return@onUseWith true if (amountInInventory(player, Items.COINS_995) < 40000) { sendDialogue(player, "You need 40,000 coins to do this.") @@ -253,7 +252,7 @@ class CompleteSpellPulse(val player: Player) : Pulse(2){ override fun pulse(): Boolean { when(counter++){ 0 -> repeat(6) { spawnProjectile(locations[it], dest, 268, 0, 1000, 0, 40, 20) } - 1 -> player.questRepository.getQuest("Nature Spirit").setStage(player, 60) + 1 -> player.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player, 60) 2 -> player.teleport(player.location.transform(0,0,1)) 3 -> openDialogue(player, NPCs.NATURE_SPIRIT_1051, findLocalNPC(player, NPCs.NATURE_SPIRIT_1051) as NPC).also { unlock(player); return true } } diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt index cfdd8ea17..7559ff085 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt @@ -6,7 +6,6 @@ import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.player.link.quest.Quest import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.update.flag.context.Graphics @@ -14,6 +13,7 @@ import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests @Initializable class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -25,7 +25,7 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - val quest = player.questRepository.getQuest("Nature Spirit") + val quest = player.questRepository.getQuest(Quests.NATURE_SPIRIT) questStage = quest.getStage(player) if(questStage > 10 && !inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ @@ -127,7 +127,7 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { playerl(FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } } else end() 74 -> npcl(FacialExpression.NEUTRAL, "Sure, but please look after this one.").also { stage++ } - 75 -> sendDialogue("The spirit of Filliman Tarlock gives you","another bloom spell.").also { addItem(player, Items.DRUIDIC_SPELL_2968); stage = END_DIALOGUE } + 75 -> sendDialogue("The spirit of Filliman Tarlock gives you","another bloom spell.").also { addItemOrDrop(player, Items.DRUIDIC_SPELL_2968); stage = END_DIALOGUE } //has fungus 80 -> sendDialogue("You show the fungus to Filliman.").also { stage++ } @@ -190,7 +190,7 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { npcl(FacialExpression.NEUTRAL, "No, you've already got one!").also { stage = END_DIALOGUE } } else { npcl(FacialExpression.NEUTRAL, "Sure, but look after this one.") - addItem(player, Items.DRUIDIC_SPELL_2968) + addItemOrDrop(player, Items.DRUIDIC_SPELL_2968) stage = END_DIALOGUE } @@ -234,7 +234,7 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { } fun setQuest(stage: Int){ - player.questRepository.getQuest("Nature Spirit").setStage(player, stage) + player.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player, stage) } } \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt index 03263c6c4..1a59aa8df 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt @@ -58,7 +58,7 @@ object NSUtils { if(pouchAmt == 1) shouldAddEmptyPouch = true if(pouchAmt > 0 && removeItem(player, Items.DRUID_POUCH_2958, Container.INVENTORY)){ if(shouldAddEmptyPouch){ - addItem(player, Items.DRUID_POUCH_2957) + addItemOrDrop(player, Items.DRUID_POUCH_2957) } spawnProjectile(player, attacker, 268) submitWorldPulse(object : Pulse(){ diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritDialogue.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritDialogue.kt index fe4913223..bdf92adea 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritDialogue.kt @@ -12,11 +12,12 @@ import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs import core.tools.END_DIALOGUE +import content.data.Quests @Initializable class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ - val questStage = player?.questRepository?.getStage("Nature Spirit") ?: 0 + val questStage = player?.questRepository?.getStage(Quests.NATURE_SPIRIT) ?: 0 override fun newInstance(player: Player?): DialoguePlugin { return NatureSpiritDialogue(player) } @@ -114,7 +115,7 @@ class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ //killed all dem buggers bruv 350 -> npcl(FacialExpression.NEUTRAL, "Many thanks my friend, you have completed your quest!").also { stage++ } - 351 -> end().also { player.questRepository.getQuest("Nature Spirit").finish(player) } + 351 -> end().also { player.questRepository.getQuest(Quests.NATURE_SPIRIT).finish(player) } } return true @@ -146,7 +147,7 @@ class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ if(removeItem(player, Items.SILVER_SICKLE_2961, Container.INVENTORY)){ addItem(player, Items.SILVER_SICKLEB_2963) unlock(player) - player.questRepository.getQuest("Nature Spirit").setStage(player, 70) + player.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player, 70) openDialogue(player, NPCs.NATURE_SPIRIT_1051, findLocalNPC(player, NPCs.NATURE_SPIRIT_1051) as NPC) sendMessage(player, "Your sickle has been blessed! You can bless a new sickle by dipping it into the grotto waters.") } @@ -158,6 +159,6 @@ class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ } fun setQuest(stage: Int){ - player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, stage) + player!!.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player!!, stage) } } \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritQuest.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritQuest.kt index 026a4c04e..87d7107b0 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritQuest.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritQuest.kt @@ -6,9 +6,10 @@ import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests @Initializable -class NatureSpiritQuest : Quest("Nature Spirit", 95, 94, 2, 307, 0, 1, 110 ) { +class NatureSpiritQuest : Quest(Quests.NATURE_SPIRIT, 95, 94, 2, 307, 0, 1, 110 ) { override fun newInstance(`object`: Any?): Quest { return this } @@ -16,92 +17,179 @@ class NatureSpiritQuest : Quest("Nature Spirit", 95, 94, 2, 307, 0, 1, 110 ) { override fun drawJournal(player: Player?, stage: Int) { super.drawJournal(player, stage) player ?: return - var line = 11 + var line = 12 if(stage == 0){ - line(player, "I can start this quest by speaking to !!Drezel?? in the !!temple of Saradomin??.", line++) - } else { - if(stage >= 10){ - line(player, "After talking to Drezel in the temple of Saradomin I've",line++, true) - line(player,"agreed to look for a Druid called Filliman Tarlock.", line++, true) + line(player, "I can start this quest by speaking to !!Drezel?? in the temple.", line++) + line(player, /* The "to" is [sic] */"to !!Saradomin?? at the mouth of the river !!Salve??.", line++) + line(player, "I first need to complete :", line++) + line(player, "!!The Restless Ghost.??", line++, isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) + line(player, "!!Priest in Peril.??", line++, isQuestComplete(player, Quests.PRIEST_IN_PERIL)) + if (isQuestComplete(player, Quests.THE_RESTLESS_GHOST) && isQuestComplete(player, Quests.PRIEST_IN_PERIL)) { + line(player, "I've completed all the quest requirements.", line++) } + line(player, "In order to complete this quest !!level 18 crafting?? would be", line++, getStatLevel(player, Skills.CRAFTING) >= 18) + line(player, "an advantage.", line++, getStatLevel(player, Skills.CRAFTING) >= 18) + if (getStatLevel(player, Skills.CRAFTING) >= 18) { + line(player, "I have a suitable crafting level for this quest.", line++) + } + if (isQuestComplete(player, Quests.THE_RESTLESS_GHOST) && isQuestComplete(player, Quests.PRIEST_IN_PERIL) && getStatLevel(player, Skills.CRAFTING) >= 18) { + line(player, "I have all the requirements for this quest.", line++) + } + } else if (stage < 100) { + line(player, "After talking to Drezel in the temple of Saradomin I've", line++, true) + line(player, "agreed to look for a Druid called Filliman Tarlock.", line++, true) - if(stage == 10){ - line(player, "I need to look for !!Filliman Tarlock?? in the !!Swamps?? of Mort",line++) + if (stage >= 15) { + line(player, "I've found a spirit in the swamp which I think might be", line++, true) + line(player, "Filliman Tarlock.", line++, true) + } else if (stage >= 10) { + line(player, "I need to look for !!Filliman Tarlock?? in the !!Swamps?? of Mort", line++) line(player, "Myre. I should be wary of !!Ghasts??.", line++) } - if(stage == 15){ - line(player, "I located a !!spirit?? in the swamp. I believe he's", line++, false) - line(player, "!!Filliman Tarlock?? but I can't understand him.",line++, false) + if (stage >= 20) { + line(player, "I've communicated with Fillman using the amulet of", line++, true) + line(player, "ghostspeak.", line++, true) + } else if (stage >= 15) { + // Questionable + line(player, "I located a !!spirit?? in the swamp. I believe he's", line++) + line(player, "!!Filliman Tarlock?? but I can't understand him.", line++) } - if(stage == 20){ - line(player, "I located !!Filliman Tarlock?? in the swamp. I believe he's",line++) - line(player, "dead but he doesn't believe me. I need to convince him.", line++) + if (stage >= 25) { + line(player, "I managed to convince Fillman that he's a ghost.", line++, true) + } else if (stage >= 20) { + line(player, "I think I need to convince this poor fellow !!Tarlock?? that he's", line++) + line(player, "actually !!dead??!", line++) } - if(stage >= 25){ - line(player, "I located Filliman Tarlock in the swamp and managed to",line++,true) - line(player, "convince him that he is in fact a ghost. ", line++, true) + if (stage >= 30) { + line(player, "Fillman is looking for his journal to help him plan what his", line++, true) + line(player, "next step is.", line++, true) + line(player, "I've given Filliman his journal. I wonder what he plans to do", line++, true) + line(player, "now?", line++, true) + } else if (stage >= 25){ + line(player, "Fillman is looking for his !!journal?? to help him plan what his", line++, true) + line(player, "next step is.", line++, true) + // Questionable +// line(player, "Filliman needs his !!journal?? to figure out what to do",line++) +// line(player, "next. He mentioned something about a !!knot??.", line++) } - if(stage == 25){ - line(player, "Filliman needs his !!journal?? to figure out what to do",line++) - line(player, "next. He mentioned something about a !!knot??.", line++) + if (stage >= 35) { + line(player, "I've agreed to help Fillman become a nature spirit.", line++, true) + line(player, "I need to find 'something from nature', 'something of", line++, true) + line(player, "faith' and 'something of the spirit-to-become freely", line++, true) + line(player, "given'.", line++, true) + } else if (stage >= 30) { + // Derived by squinting hard + line(player, "!!Filliman?? might need !!my help?? with his !!plan??.", line++) + // Questionable +// line(player, "I should speak to !!Filliman Tarlock?? to see what I can", line++) +// line(player, "do to help.", line++) } - if(stage >= 30){ - line(player, "I recovered Filliman's journal for him.", line++, true) + if (stage >= 40) { + line(player, "Filliman gave me a 'bloom' spell to cast in the swamp.", line++, true) + line(player, "With the bloom spell I can collect 'Something of nature.'", line++, true) + line(player, "I've been blessed at the temple by Drezel.", line++, true) + } else if (stage >= 35) { + line(player, "!!Filliman?? gave me a '!!bloom??' spell but I need to be !!blessed?? at", line++) + line(player, "the !!temple?? before I can cast it. I am supposed to collect", line++) + line(player, "'!!something from nature??'.", line++) } - if(stage == 30) { - line(player, "I should speak to !!Filliman Tarlock?? to see what I can",line++) - line(player, "do to help.", line++) + if (stage >= 45) { + // Disappears. + } else if (stage >= 40) { + line(player, "I should return to !!Filliman?? to see what I need to do.", line++) } - if(stage >= 40){ - line(player, "I've gone and gotten blessed by Drezel.", line++, true) + if (stage in 45 until 55){ + if (NSUtils.hasPlacedFungus(player)) { + line(player, "I've cast the bloom spell in the swamp.", line++, true) + line(player, "I collected a Mort Myre Fungi.", line++, true) + line(player, "I think I have collected 'something of nature'.", line++, true) + } else if (inInventory(player, Items.MORT_MYRE_FUNGUS_2970)) { + line(player, "I've cast the bloom spell in the swamp.", line++, true) + line(player, "I collected a Mort Myre Fungi.", line++, true) + line(player, "I have a !!Mort Myre Fungi??, I hope this is what !!Fillman??",line++) + line(player, "wanted.",line++) + } else { + // Questionable +// if(stage == 50){ +// line(player, "I know for a fact the fungus is !!something of Nature??.", line++, false) +// } + line(player, "I need to collect '!!something of nature??'.", line++) + } + + // Just stand on the damn thing. + line(player, "I need to find '!!something with faith??'.",line++, false) + + if (NSUtils.hasPlacedCard(player)) { + line(player, "The spell scroll was absorbed into the spirit stone I think I", line++, true) + line(player, "have collected 'something of spirit-to-become freely", line++, true) + line(player, "given.'", line++, true) + } else { + line(player, "I need to find :",line++) + line(player, "'!!something of the spirit-to-be freely given??.'", line++) + } } - if(stage >= 35) { - line(player, "I've agreed to help Filliman become a Nature Spirit.",line++, true) + if (stage >= 55) { + line(player, "I managed to get all the required items that Fillman asked.", line++, true) + line(player, "for. He says that he can cast the spell now which will", line++, true) + line(player, "transform him into a Nature Spirit.", line++, true) } - if(stage == 35){ - line(player, "The first thing Filliman needs me to do is go and get",line++) - line(player, "blessed by !!Drezel?? in the temple of Saradomin.",line++) + if (stage >= 60) { + line(player, "I entered Fillimans grotto as he asked me to.", line++, true) // no apostrophe is sic + line(player, "Filliman has turned into a nature spirit, it was an", line++, true) + line(player, "impressive transformation!", line++, true) + line(player, "Filliman says he can help me to defeat the ghasts.", line++, true) + } else if (stage >= 55) { + // Questionable + line(player, "!!Filliman?? has asked me to enter his !!grotto??.", line++) } - if (stage == 40){ - line(player, "I should return to !!Filliman?? to see what I need to do.", line++, false) + if (stage >= 70) { + line(player, "Filliman has blessed the silver sickle for me.", line++, true) + // --- Should be separate stage, but we don't have it. + // line(player, "I need to use the !!sickle?? to make the swamp bloom.", line++) + line(player, "I cast the bloom spell in the swamp.", line++, true) + // --- Should be separate stage, but we don't have it. + // line(player, "I need to collect some !!bloomed items?? from the swamp", line++) + // line(player, "and put them into a druid pouch.", line++) + line(player, "I collected some bloomed items from the swamp an put", line++, true) + line(player, "them into a druid pouch.", line++, true) + } else if (stage >= 60) { + // Questionable + line(player, "I need to bring a silver sickle for !!Filliman?? to bless.", line++) } - - if(stage in 45 until 55){ - line(player, "In order to help Filliman I need to find 3 things:", line++, false) - line(player, "Something of !!Faith??.",line++, false) - line(player, "Something of !!Nature??.", line++, stage >= 50) - line(player, "Something of the !!spirit-to-be freely given??.", line++, false) - } - - if(stage == 50){ - line(player, "I know for a fact the fungus is !!something of Nature??.", line++, false) - } - - if(stage >= 55){ - line(player, "I've helped Filliman complete the spell.", line++, true) - } - - if(stage == 55){ - line(player, "Filliman has asked me to meet him back inside the !!grotto??.", line++, false) - } - - if(stage == 75){ - line(player, "I need to go and kill !!3 Ghasts?? for Filliman.", line++, false) - } - - if(stage >= 100){ - line(player,"%%QUEST COMPLETE!&&",line++) + // We don't have this stage. +// if (stage >= 80) { +// line(player, "The druid pouch made a ghast appear which I attacked and", line++, true) +// line(player, "killed.", line++, true) +// line(player, "I've killed two ghasts now.", line++, true) +// line(player, "I've killed three ghasts now.", line++, true) +// line(player, "I should tell !!Filliman?? that I've killed the !!three ghasts??.", line++, true) +// } else + if (stage >= 75){ + line(player, "!!Filliman?? asked me to kill !!three Ghasts??.", line++, false) } + } else { + // The final text is a summary of the quest. + line(player, "Drezel, a priest of Saradomin, asked me to look for the", line++, true) + line(player, "druid Filliman Tarlock in the swamps of Mort Myre. However", line++, true) + line(player, "Filliman had been slain and appeared as a ghost. After", line++, true) + line(player, "persuading Filliman that he was in fact dead I helped him to", line++, true) + line(player, "make a transformation into a Nature Spirit.", line++, true) + line++ + line(player, "In return for this help Filliman blessed a silver sickle and", line++, true) + line(player, "showed me how to defeat the ghasts of Mort Myre.", line++, true) + line(player, "He also gave me some kill experience in crafting,", line++, true) + line(player, "hitpoints and defence.", line++, true) + line(player, "%%QUEST COMPLETE!&&",line++) } } diff --git a/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java b/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java index 844cc01c2..b8d7628f1 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java +++ b/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java @@ -6,6 +6,7 @@ import core.plugin.Initializable; import core.game.node.entity.player.Player; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles the quarter master dialogue. @@ -42,7 +43,7 @@ public final class QuarterMasterDialogue extends DialoguePlugin { public boolean open(Object... args) { npc = (NPC) args[0]; npc("Hi, would you like to see my wares?"); - if (!hasRequirement(player, "Regicide")) { + if (!hasRequirement(player, Quests.REGICIDE)) { end(); return true; } diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/ElunedDialogue.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/ElunedDialogue.java index 283f98467..67b9ddad7 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/ElunedDialogue.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/ElunedDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import org.rs09.consts.Items; +import content.data.Quests; /** * Handles Eluned's Dialogue for Roving Elves. @@ -28,7 +29,7 @@ public class ElunedDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); switch (stage) { case 500: end(); @@ -180,7 +181,7 @@ public class ElunedDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); if (quest.getStage(player) == 10) { interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hey there... Islwyn said you may be able to help me.", "He told me you know how to consecrate ground for an", "elven burial. I need to reconsecrate Glarial's resting", "place."); stage = 1; diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java index b24862a1f..2d111e7cd 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; +import content.data.Quests; /** * Handles Islwyn's dialogue for Roving Elves. @@ -29,7 +30,7 @@ public class IslwynDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); switch (stage) { case 500: end(); @@ -171,7 +172,7 @@ public class IslwynDialogue extends DialoguePlugin { stage = 21; break; case 21: - interpreter.sendDoubleItemMessage(RovingElves.CRYSTAL_BOW_FULL, RovingElves.CRYSTAL_SHIELD_FULL, "Islwyn shows you a crystal bow and a crystal shield."); + interpreter.sendDoubleItemMessage(RovingElves.CRYSTAL_BOW_FULL.getId(), RovingElves.CRYSTAL_SHIELD_FULL.getId(), "Islwyn shows you a crystal bow and a crystal shield."); stage = 22; break; case 22: @@ -355,8 +356,8 @@ public class IslwynDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); - final Quest waterfall = player.getQuestRepository().getQuest("Waterfall"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); + final Quest waterfall = player.getQuestRepository().getQuest(Quests.WATERFALL_QUEST); if (quest.getStage(player) == 0 && waterfall.isCompleted(player)) { interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there."); stage = 0; diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java index 842f3cc9b..82685174f 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java @@ -13,6 +13,7 @@ import core.game.node.item.Item; import core.game.world.map.Location; import core.plugin.Plugin; import core.plugin.ClassScanner; +import content.data.Quests; /** * The level 84 Moss Giant in Glarial's tomb. @@ -47,7 +48,7 @@ public final class MossGiantGuardianNPC extends AbstractNPC { super.finalizeDeath(killer); if (killer instanceof Player) { final Player player = (Player) killer; - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); if (quest.getStage(player) == 15 && !player.getInventory().contains(RovingElves.CONSECRATION_SEED.getId(), 1)) { player.getPacketDispatch().sendMessages("A small grey seed drops on the ground."); GroundItemManager.create(new Item(RovingElves.CONSECRATION_SEED.getId()), getLocation(), player); diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java index 52546a877..91d68130e 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.ClassScanner; +import content.data.Quests; /** * The Roving Elves quest. @@ -43,7 +44,7 @@ public class RovingElves extends Quest { * Constructs a new {@Code RovingElves} {@Code Object} */ public RovingElves() { - super("Roving Elves", 105, 104, 1, 402, 0, 1, 6); + super(Quests.ROVING_ELVES, 105, 104, 1, 402, 0, 1, 6); } @Override diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java index e24874c99..144b624c1 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.List; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles all the agility obstacles for Roving Elves. @@ -96,7 +97,7 @@ public final class RovingElvesObstacles extends OptionHandler { switch (node.getId()) { case 8742: - if (!hasRequirement(player, "Mourning's End Part I")) + if (!hasRequirement(player, Quests.MOURNINGS_END_PART_I)) return true; player.teleport(player.getLocation().transform(EAST_WEST, 2)); break; diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesPlugin.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesPlugin.java index 243e4823a..38616e8d4 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesPlugin.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesPlugin.java @@ -12,6 +12,7 @@ import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; +import content.data.Quests; /** * Master plugin file for Roving Elves. @@ -39,7 +40,7 @@ public final class RovingElvesPlugin extends OptionHandler { @SuppressWarnings("static-access") @Override public boolean handle(final Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); if (quest == null) { player.sendMessage("Error! RovingElves quest cannot be found, please contact an admin!"); return true; diff --git a/Server/src/main/content/region/wilderness/handlers/BorkNPC.java b/Server/src/main/content/region/wilderness/handlers/BorkNPC.java index 1b603410b..c0a28f080 100644 --- a/Server/src/main/content/region/wilderness/handlers/BorkNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/BorkNPC.java @@ -532,6 +532,7 @@ public class BorkNPC extends AbstractNPC { public void configure() { region = DynamicRegion.create(12374); region.setMulticombat(true); + region.setMusicId(488); setRegionBase(); registerRegion(region.getId()); } diff --git a/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java b/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java index e25b2405e..6d1f06cfc 100644 --- a/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java +++ b/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java @@ -28,6 +28,7 @@ import core.plugin.ClassScanner; import core.tools.RandomFunction; import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles the chaos tunnels. @@ -233,7 +234,7 @@ public final class ChaosTunnelZone extends MapZone implements Plugin { */ private void teleport(Player player, Scenery object) { if (object.getLocation().getX() == 3142 && object.getLocation().getY() == 5545) { - if (hasRequirement(player, "What Lies Below")) + if (hasRequirement(player, Quests.WHAT_LIES_BELOW)) commenceBorkBattle(player); return; } @@ -266,7 +267,7 @@ public final class ChaosTunnelZone extends MapZone implements Plugin { * @param player The player. */ private void commenceBorkBattle(Player player) { - if (ServerStore.getBoolean(getStoreFile(), player.getUsername().toLowerCase()) && GameWorld.getSettings().isHosted()) { + if (ServerStore.getBoolean(getStoreFile(), player.getUsername().toLowerCase(), false) && GameWorld.getSettings().isHosted()) { player.getPacketDispatch().sendMessage("The portal's magic is too weak to teleport you right now."); return; } diff --git a/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java b/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java index ea1849876..67ad9c876 100644 --- a/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java @@ -2,15 +2,11 @@ package content.region.wilderness.handlers; import content.data.BossKillCounter; 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.*; import core.game.node.entity.combat.ImpactHandler.HitsplatType; -import core.game.node.entity.combat.MultiSwingHandler; import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.combat.equipment.Weapon; import core.game.node.entity.impl.Projectile; -import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; import core.game.node.entity.npc.NPCBehavior; import core.game.node.entity.player.Player; @@ -22,7 +18,6 @@ import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; -import core.plugin.Plugin; import core.tools.RandomFunction; import org.rs09.consts.NPCs; @@ -32,7 +27,6 @@ import java.util.List; /** * Handles the Corporeal beast NPC. * @author Emperor - * */ @Initializable public final class CorporealBeastNPC extends NPCBehavior { @@ -41,19 +35,24 @@ public final class CorporealBeastNPC extends NPCBehavior { * The combat handler. */ private final MultiSwingHandler combatHandler = new CombatHandler(); - + /** * The dark energy core NPC. */ public NPC darkEnergyCore; - + + /** + * Whether to force a dark core spawn roll on our next swing (only done if we just got hit >= 32 damage). + */ + public boolean forceCoreRoll = false; + /** * Constructs a new {@code CorporealBeastNPC} {@code Object}. */ public CorporealBeastNPC() { - super(new int[]{NPCs.CORPOREAL_BEAST_8133}); + super(new int[] { NPCs.CORPOREAL_BEAST_8133 }); } - + @Override public void onCreation(NPC self) { self.configureBossData(); @@ -64,27 +63,31 @@ public final class CorporealBeastNPC extends NPCBehavior { return combatHandler; } - @Override - public void beforeDamageReceived(NPC self, Entity attacker, BattleState state) { - if(state.getStyle() == CombatStyle.MELEE || state.getStyle() == CombatStyle.RANGE) { - Weapon w = state.getWeapon(); - String name = w != null ? w.getName() : ""; - if(w == null || name.toLowerCase().indexOf("spear") == -1) { - if(state.getEstimatedHit() > 0) { - state.setEstimatedHit(state.getEstimatedHit()/2); - } - if(state.getSecondaryHit() > 0) { - state.setSecondaryHit(state.getSecondaryHit()/2); - } - } - } - if(state.getEstimatedHit() > 100) { - state.setEstimatedHit(100); - } - if(state.getSecondaryHit() > 100) { - state.setSecondaryHit(100); - } - } + @Override + public void beforeDamageReceived(NPC self, Entity attacker, BattleState state) { + if (state.getStyle() == CombatStyle.MELEE || state.getStyle() == CombatStyle.RANGE) { + Weapon w = state.getWeapon(); + String name = w != null ? w.getName() : ""; + if (w == null || name.toLowerCase().indexOf("spear") == -1) { + if (state.getEstimatedHit() > 0) { + state.setEstimatedHit(state.getEstimatedHit() / 2); + } + if (state.getSecondaryHit() > 0) { + state.setSecondaryHit(state.getSecondaryHit() / 2); + } + } + } + if (state.getEstimatedHit() >= 32) { + CorporealBeastNPC corp = (CorporealBeastNPC) self.behavior; + corp.forceCoreRoll = true; + } + if (state.getEstimatedHit() > 100) { + state.setEstimatedHit(100); + } + if (state.getSecondaryHit() > 100) { + state.setSecondaryHit(100); + } + } @Override public void onDeathFinished(NPC self, Entity killer) { @@ -94,41 +97,46 @@ public final class CorporealBeastNPC extends NPCBehavior { darkEnergyCore = null; } } - + /** * Handles the Corporeal beast's combat. * @author Emperor - * */ static class CombatHandler extends MultiSwingHandler { - /** * Constructs a new {@code CombatHandler} {@code Object}. */ public CombatHandler() { super( - //Melee (crush) - new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10057)).setMaximumHit(52), - //Melee (slash) - new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10058)).setMaximumHit(51), - //Magic (drain skill) - new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1823, 60, 36, 41, 46)).setMaximumHit(55), - //Magic (location based) - new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1824, 60, 36, 41, 46)).setMaximumHit(42), - //Magic (hit through prayer) - new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1825, 60, 36, 41, 46)).setMaximumHit(66) - ); + //Melee (crush) + new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10057)).setMaximumHit(51), + //Melee (slash) + new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10058)).setMaximumHit(51), + //Magic (drain skill, blocked by prayer) + new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1823, 60, 36, 41, 46)).setMaximumHit(55), + //Magic (location-based, hits through prayer) + new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1824, 60, 36, 41, 46)).setMaximumHit(42), + //Magic (hits through prayer) + new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1825, 60, 36, 41, 46)).setMaximumHit(65) + ); } @Override public int swing(Entity entity, Entity victim, BattleState state) { - spawnDarkCore(entity, (CorporealBeastNPC)((NPC) entity).behavior, victim); + // If we're below the right HP threshold, roll a chance to spawn the dark core + CorporealBeastNPC corp = (CorporealBeastNPC) ((NPC) entity).behavior; + double thresh = entity.getSkills().getMaximumLifepoints() * (0.3 + (entity.getViewport().getCurrentPlane().getPlayers().size() * 0.05)); + if (corp.forceCoreRoll || entity.getSkills().getLifepoints() < thresh) { + rollDarkCore(entity, corp, victim); + corp.forceCoreRoll = false; + } + // If we can stomp, do that for our turn if (doStompAttack(entity)) { entity.getProperties().getCombatPulse().setNextAttack(entity.getProperties().getAttackSpeed()); return -1; } - //Location based attack. + // Location-based attack. if (super.getNext().getProjectile() != null && super.getNext().getProjectile().getProjectileId() == 1824) { setCurrent(getNext()); CombatStyle style = getCurrent().getStyle(); @@ -142,18 +150,17 @@ public final class CorporealBeastNPC extends NPCBehavior { } return super.swing(entity, victim, state); } - + /** - * Spawns a dark core. + * Rolls a 1/8 chance to spawn a dark core. * @param npc The corporeal beast NPC. * @param victim The victim. */ - private void spawnDarkCore(Entity corp, final CorporealBeastNPC npc, Entity victim) { - if (npc.darkEnergyCore != null && npc.darkEnergyCore.isActive()) { + private void rollDarkCore(Entity corp, final CorporealBeastNPC npc, Entity victim) { + if (npc.darkEnergyCore != null && npc.darkEnergyCore.isActive() && !DeathTask.isDead(npc.darkEnergyCore)) { return; } - double max = corp.getSkills().getMaximumLifepoints() * (0.3 + (corp.getViewport().getCurrentPlane().getPlayers().size() * 0.05)); - if (corp.getSkills().getLifepoints() > max) { + if (!RandomFunction.roll(8)) { return; } Location l = RegionManager.getTeleportLocation(victim.getLocation(), 3); @@ -163,8 +170,8 @@ public final class CorporealBeastNPC extends NPCBehavior { GameWorld.getPulser().submit(new Pulse(2, corp) { @Override public boolean pulse() { - if (npc.darkEnergyCore == null) - return true; + if (npc.darkEnergyCore == null) + return true; npc.darkEnergyCore.init(); return true; } @@ -184,6 +191,7 @@ public final class CorporealBeastNPC extends NPCBehavior { boolean secondStage = false; List players = RegionManager.getLocalPlayers(entity); Location[] locations = null; + @Override public boolean pulse() { if (!secondStage) { @@ -215,11 +223,14 @@ public final class CorporealBeastNPC extends NPCBehavior { locations = null; return true; } + private void hit(Player p) { - int max = p.hasProtectionPrayer(CombatStyle.MAGIC) ? 13 : 42; int hit = 0; if (isAccurateImpact(entity, p)) { - hit = RandomFunction.random(max); + hit = RandomFunction.random(42); + if (p.hasProtectionPrayer(CombatStyle.MAGIC)) { + hit = (int) (hit * 0.6); + } } p.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC); } @@ -252,7 +263,7 @@ public final class CorporealBeastNPC extends NPCBehavior { } return false; } - + @Override public void adjustBattleState(Entity entity, Entity victim, BattleState state) { super.adjustBattleState(entity, victim, state); @@ -262,10 +273,9 @@ public final class CorporealBeastNPC extends NPCBehavior { int skill = random == 0 ? Skills.PRAYER : random == 1 ? Skills.MAGIC : Skills.SUMMONING; int drain = 1 + RandomFunction.random(6); if ((skill == Skills.PRAYER ? victim.getSkills().getPrayerPoints() : victim.getSkills().getLevel(skill)) < 1) { - victim.getImpactHandler().manualHit(entity, drain, HitsplatType.NORMAL,2); + victim.getImpactHandler().manualHit(entity, drain, HitsplatType.NORMAL, 2); ((Player) victim).getPacketDispatch().sendMessage("Your Hitpoints have been slightly drained!"); - } - else { + } else { if (skill == Skills.PRAYER) { victim.getSkills().decrementPrayerPoints(drain); } else { @@ -278,11 +288,13 @@ public final class CorporealBeastNPC extends NPCBehavior { } } } - + @Override protected int getFormattedHit(Entity entity, Entity victim, BattleState state, int hit) { if (getCurrent().getProjectile() == null || getCurrent().getProjectile().getProjectileId() != 1825) { hit = (int) entity.getFormattedHit(state, hit); + } else if (victim.hasProtectionPrayer(CombatStyle.MAGIC)) { + hit = (int) (hit * 0.6); } return formatHit(victim, hit); } diff --git a/Server/src/main/content/region/wilderness/handlers/CorporealBeastWarningInterface.kt b/Server/src/main/content/region/wilderness/handlers/CorporealBeastWarningInterface.kt index 83b64acc2..5568d5460 100644 --- a/Server/src/main/content/region/wilderness/handlers/CorporealBeastWarningInterface.kt +++ b/Server/src/main/content/region/wilderness/handlers/CorporealBeastWarningInterface.kt @@ -5,6 +5,7 @@ import core.game.node.entity.player.Player import core.game.interaction.InterfaceListener import core.game.world.GameWorld import core.api.* +import content.data.Quests /** * Handles the corporeal beast warning interface @@ -16,7 +17,7 @@ class CorporealBeastWarningInterface : InterfaceListener { override fun defineInterfaceListeners() { on(COMPONENT_ID,17){player,component,_,_,_,_ -> - if (!hasRequirement(player, "Summer's End")) + if (!hasRequirement(player, Quests.SUMMERS_END)) return@on true if(player.getAttribute("corp-beast-cave-delay",0) <= GameWorld.ticks) { player.properties.teleportLocation = player.location.transform(4, 0, 0).also { close(player,component) } diff --git a/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java b/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java index bcc8930cd..0695955ba 100644 --- a/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java @@ -9,6 +9,8 @@ import core.game.node.entity.player.Player; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; +import core.game.world.map.path.Path; +import core.game.world.map.path.Pathfinder; import core.plugin.Initializable; import core.tools.RandomFunction; @@ -16,8 +18,7 @@ import static core.api.ContentAPIKt.*; /** * Handles the Dark Energy Core NPC. - * @author Emperor - * + * @author Emperor, Player Name */ @Initializable public final class DarkEnergyCoreNPC extends AbstractNPC { @@ -31,21 +32,22 @@ public final class DarkEnergyCoreNPC extends AbstractNPC { * The amount of ticks. */ private int ticks = 0; - + /** * The amount of failed attacks. */ private int fails = 0; - + /** * Constructs a new {@code DarkEnergyCoreNPC} {@code Object}. */ public DarkEnergyCoreNPC() { this(8127, null); } - + /** * Constructs a new {@code DarkEnergyCoreNPC} {@code Object}. + * * @param id The NPC id. * @param location The location. */ @@ -59,14 +61,15 @@ public final class DarkEnergyCoreNPC extends AbstractNPC { if (objects.length > 0) { core.master = (NPC) objects[0]; } + core.setRespawn(false); return core; } - + @Override public boolean canStartCombat(Entity victim) { return false; //No combat needed. } - + @Override public void handleTickActions() { ticks++; @@ -91,8 +94,11 @@ public final class DarkEnergyCoreNPC extends AbstractNPC { if (jump) { Entity victim = master.getProperties().getCombatPulse().getVictim(); if (++fails >= 3 && victim != null && victim.getViewport().getCurrentPlane() == getViewport().getCurrentPlane()) { - jump(victim.getLocation()); - fails = 0; + Path path = Pathfinder.find(getLocation(), victim.getLocation(), 1); + if (path.isSuccessful() || !path.isMoveNear()) { + jump(victim.getLocation()); + fails = 0; + } } } else { fails = 0; @@ -119,7 +125,6 @@ public final class DarkEnergyCoreNPC extends AbstractNPC { @Override public int[] getIds() { - return new int[] { 8127 }; + return new int[]{8127}; } - } diff --git a/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt b/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt deleted file mode 100644 index 929375a65..000000000 --- a/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt +++ /dev/null @@ -1,152 +0,0 @@ -package content.region.wilderness.handlers - -import content.region.wilderness.handlers.revenants.RevenantController -import content.region.wilderness.handlers.revenants.RevenantNPC -import content.region.wilderness.handlers.revenants.RevenantType -import core.api.* -import core.game.node.entity.Entity -import core.game.node.entity.combat.CombatStyle -import core.game.node.entity.combat.DeathTask -import core.game.node.entity.npc.NPC -import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.game.system.command.Privilege -import core.game.system.timer.PersistTimer -import core.game.system.timer.impl.Disease -import core.game.world.map.path.Pathfinder -import core.game.world.map.zone.impl.WildernessZone -import core.game.world.update.flag.context.Graphics -import core.tools.RandomFunction -import core.tools.colorize -import org.json.simple.JSONObject - -object DeepWildyThreat { - @JvmStatic fun getThreat (player: Player) : Int { - return getOrStartTimer(player).ticksLeft - } - - @JvmStatic fun adjustThreat (player: Player, amount: Int) { - val timer = getOrStartTimer(player) - timer.ticksLeft += amount - if (timer.ticksLeft >= 2500 && timer.lastMessage < 2000) { - sendMessage(player, colorize("%RYou sense a great wrath upon you.")) - timer.lastMessage = 2000 - } else if (timer.ticksLeft >= 1000 && timer.lastMessage < 1000) { - sendMessage(player, colorize("%RYou sense watchful eyes upon you.")) - timer.lastMessage = 1000 - } else if (timer.ticksLeft >= 500 && timer.lastMessage < 500) { - sendMessage(player, colorize("%RYou sense a dark presence.")) - timer.lastMessage = 500 - } - } -} - -class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands { - var ticksLeft = 0 - var lastMessage = 0 - var currentRev: NPC? = null - var forceSpawn = false - - override fun run(entity: Entity): Boolean { - if (ticksLeft-- <= 0) return false - if (ticksLeft > 3000) ticksLeft = 3000 - if (ticksLeft % 5 != 0) return true - if (entity !is Player) return false - if (!entity.skullManager.isWilderness) return true - - val rollchance = - if (ticksLeft >= 2500) 10 - else if (ticksLeft >= 2000) 200 - else if (ticksLeft >= 1500) 400 - else if (ticksLeft >= 1000) 800 - else if (ticksLeft >= 500) 1500 - else 2_000_000 - - if ((currentRev == null || DeathTask.isDead(currentRev) || !currentRev!!.isActive) && (forceSpawn || RandomFunction.roll(rollchance))) { - forceSpawn = false - val type = RevenantType.getClosestHigherOrEqual(entity.properties.currentCombatLevel) - val npc = NPC.create(type.ids.random(), entity.location) - npc.isRespawn = false - npc.behavior = RevGuardianBehavior() - npc.init() - npc.setAttribute("dw-threat-target", entity) - RevenantController.unregisterRevenant(npc as RevenantNPC, false) - currentRev = npc - } else if (currentRev != null && !currentRev!!.location.withinDistance(entity.location, 25) && currentRev!!.properties.teleportLocation == null) { - poofClear(currentRev!!) - currentRev = null - } - - return true - } - - override fun save(root: JSONObject, entity: Entity) { - root["threat-time-remaining"] = ticksLeft.toString() - root["threat-forceSpawn"] = (currentRev != null).toString() - } - - override fun parse(root: JSONObject, entity: Entity) { - ticksLeft = root["threat-time-remaining"]?.toString()?.toIntOrNull() ?: 0 - forceSpawn = root["threat-forceSpawn"]?.toString()?.toBoolean() ?: false - } - - override fun defineCommands() { - define("dwthreat", Privilege.ADMIN, "", "") {player, _ -> - val timer = getOrStartTimer(player) - notify(player, "Current Threat: ${timer.ticksLeft}") - } - } -} - -class RevGuardianBehavior : NPCBehavior() { - val deathMessages = arrayOf("Curses upon thee!", "Rot in blight!", "Suffer my wrath!", "Nevermore!", "May ye be undone!") - var chats = arrayOf("Leave this place!", "Suffer!", "Death to thee!", "Flee, coward!", "Leave my resting place!", "Let me rest in peace!", "Thou belongeth to me!") - - override fun tick(self: NPC): Boolean { - val target = getAttribute(self, "dw-threat-target", null) ?: return true - if (!target.isActive || DeathTask.isDead(target)) { - self.clear() - return true - } - if (target.properties.teleportLocation != null && self.properties.teleportLocation == null) { - if (WildernessZone.isInZone(target.properties.teleportLocation)) - self.properties.teleportLocation = target.properties.teleportLocation - } - self.attack(target) - return true - } - - override fun onCreation(self: NPC) { - Graphics.send(Graphics(86), self.location) - sendChat(self, chats.random()) - } - - override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { - val target = getAttribute(self, "dw-threat-target", null) - if (attacker != target) { - if (shouldSendMessage && attacker is Player) - sendMessage(attacker, "This revenant is focused on someone else.") - return false - } - return super.canBeAttackedBy(self, attacker, style, shouldSendMessage) - } - - override fun onDeathStarted(self: NPC, killer: Entity) { - val target = getAttribute(self, "dw-threat-target", null) ?: return - sendChat(self, deathMessages.random()) - val disease = getOrStartTimer(target, 25) - disease.hitsLeft = 25 - } - - override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { - val target = getAttribute(self, "dw-threat-target", null) ?: return - if (killer != target) drops.clear() - val timer = getOrStartTimer(target) - timer.ticksLeft = 0 - } - - override fun getPathfinderOverride(self: NPC): Pathfinder? { - return Pathfinder.SMART - } -} diff --git a/Server/src/main/content/region/wilderness/handlers/KingBlackDragonArea.kt b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonArea.kt new file mode 100644 index 000000000..7940a2468 --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonArea.kt @@ -0,0 +1,14 @@ +package content.region.wilderness.handlers + +import core.api.* +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction + +class KingBlackDragonArea : MapArea { + override fun defineAreaBorders() : Array { + return arrayOf(ZoneBorders(2256, 4680, 2287, 4711, 0, true)) + } + override fun getRestrictions() : Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java index 4c93fdf39..20b1c8719 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java @@ -52,6 +52,10 @@ public final class WildernessDitchPlugin extends OptionHandler { player.faceLocation(node.getLocation()); Scenery ditch = (Scenery) node; player.setAttribute("wildy_ditch", ditch); + /* + + Comment out the annoying ditch warning until the doomsayer has been implemented so that players can disable it properly. + if(!player.isArtificial()) { if (ditch.getRotation() % 2 == 0) { if (player.getLocation().getY() <= node.getLocation().getY()) { @@ -65,6 +69,7 @@ public final class WildernessDitchPlugin extends OptionHandler { } } } + */ WildernessInterfacePlugin.handleDitch(player); } diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt b/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt index d65982858..2f0ed6a1f 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt +++ b/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt @@ -1,9 +1,10 @@ package content.region.wilderness.handlers +import content.region.wilderness.handlers.WildernessGateHandler.GateDialogue +import core.ServerConstants import core.api.* import core.game.interaction.* import core.game.node.entity.player.Player -import core.game.node.scenery.Scenery import core.game.node.Node import core.game.global.action.DoorActionHandler import core.game.dialogue.* @@ -16,39 +17,57 @@ class WildernessGateHandler : InteractionListener { on (gates, IntType.SCENERY, "open", handler = ::handleGate) } - private fun handleGate (player: Player, node: Node) : Boolean { - if (player.location.y > 3890) { - val isEntering = !player.skullManager.isDeepWilderness() - - if (isEntering) - openDialogue(player, GateDialogue(node.asScenery())) - else { - if (player.properties.combatPulse.isInCombat) + private fun handleGate(player: Player, node: Node) : Boolean { + if (player.location.y > 3890 && ServerConstants.ENHANCED_DEEP_WILDERNESS) { + val isEntering = !player.skullManager.isDeepWilderness + if (isEntering) { + fun enter(player: Player) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + player.skullManager.isDeepWilderness = true + } + enterDeepWilderness(player, ::enter, "Beyond this gate you enter the deep wilderness!") + } else { + if (player.properties.combatPulse.isInCombat) { sendMessage(player, "You cannot leave while you are under attack.") - else { + } else { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) player.skullManager.isDeepWilderness = false } } - } else DoorActionHandler.handleAutowalkDoor (player, node.asScenery()) + } else DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) return true } - class GateDialogue (val gate: Scenery) : DialogueFile() { + class GateDialogue(val callback: (Player) -> Unit, val firstLine: String) : DialogueFile() { override fun handle (interfaceId: Int, buttonId: Int) { when (stage) { - 0 -> sendDialogueLines(player!!, "WARNING!", "Beyond this gate you enter the deep wilderness!", "Anyone will be able to attack you without consequence!", "You WILL NOT be able to leave during combat!").also { stage++ } - 1 -> showTopics ( + 0 -> sendDialogueLines(player!!, "WARNING!", firstLine, "Anyone will be able to attack you without consequence!", "You WILL NOT be able to leave during combat!").also { stage++ } + 1 -> sendDialogueLines(player!!, "While you are there, you will be skulled:","you will lose all your items if you die!","The skull will go away when you leave the deep wilderness.").also { stage++ } + 2 -> sendDialogueLines(player!!, "If you are risking sufficient value, the skull will become red.","This increases your chances of receiving special loot from killing","revenants and the Chaos Elemental!").also { stage++ } + 3 -> showTopics( Topic(FacialExpression.NEUTRAL, "I wish to proceed.", 10, true), - Topic(FacialExpression.NEUTRAL, "Nevermind.", END_DIALOGUE, true) + Topic(FacialExpression.NEUTRAL, "I wish to proceed, and don't show this warning again.", 11, true), + Topic(FacialExpression.NEUTRAL, "Never mind.", END_DIALOGUE, true) ) - 10 -> { + 10 -> { end() - DoorActionHandler.handleAutowalkDoor (player!!, gate) - player!!.skullManager.isDeepWilderness = true + callback(player!!) + } + 11 -> { + player!!.setAttribute("/save:skip-deep-wilderness-warning", true) + end() + callback(player!!) } } } } } + +fun enterDeepWilderness(player: Player, callback: (Player) -> Unit, firstLine: String) { + if (player.getAttribute("/save:skip-deep-wilderness-warning",false)) { + callback(player) + } else { + openDialogue(player, GateDialogue(callback, firstLine)) + } +} diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java index 40597b85b..26bb09c1a 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java @@ -16,6 +16,7 @@ import core.plugin.Plugin; import org.rs09.consts.Sounds; import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents a plugin used to handle wilderness nodes. @@ -49,7 +50,7 @@ public final class WildernessPlugin extends OptionHandler { ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, Location.create(3239, 3606, 0), "You climb up the ladder to the surface."); break; case 39188: - if (!hasRequirement(player, "Defender of Varrock")) + if (!hasRequirement(player, Quests.DEFENDER_OF_VARROCK)) break; ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_DOWN, Location.create(3241, 9991, 0), "You descend into the cavern below."); break; diff --git a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantCombatHandler.java b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantCombatHandler.java index 01040043e..0c509175f 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantCombatHandler.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantCombatHandler.java @@ -74,8 +74,10 @@ public class RevenantCombatHandler extends MultiSwingHandler { } } } - if (!isPoisoned(victim) && (WildernessZone.getWilderness(entity) >= 50 || entity.getId() == 6998)) { - applyPoison(victim, entity, 6); + if (entity.getId() == 6998) { + applyPoison(victim, entity, 40); + } else if (WildernessZone.getWilderness(entity) >= 50) { + applyPoison(victim, entity, 30); } super.impact(entity, victim, state); } diff --git a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java index c63797294..6e0d0e6fb 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java @@ -26,7 +26,7 @@ import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles a revenant NPC. - * @author Ceikry-ish (mostly Vexia code still) + * @author Ceikry-ish (mostly Vexia code still), Player Name */ @Initializable public class RevenantNPC extends AbstractNPC { @@ -88,6 +88,7 @@ public class RevenantNPC extends AbstractNPC { configureBonuses(); super.configure(); this.swingHandler = new RevenantCombatHandler(getProperties().getAttackAnimation(), getProperties().getMagicAnimation(), getProperties().getRangeAnimation()); + setAttribute("food-items", 20); } @Override @@ -116,16 +117,23 @@ public class RevenantNPC extends AbstractNPC { public void tick() { skills.pulse(); getWalkingQueue().update(); - if (this.getViewport().getRegion().isActive()) + if (this.getViewport().getRegion().isActive()) { getUpdateMasks().prepare(this); - if (!DeathTask.isDead(this) && getSkills().getLifepoints() <= (getSkills().getStaticLevel(Skills.HITPOINTS) / 2) && getAttribute("eat-delay", 0) < GameWorld.getTicks()) { - lock(3); - getProperties().getCombatPulse().delayNextAttack(3); - getSkills().heal(10); - for (Player p : RegionManager.getLocalPlayers(this)) { - playAudio(p, Sounds.EAT_2393); + } + if (!DeathTask.isDead(this)) { + int curhp = getSkills().getLifepoints(); + int maxhp = getSkills().getStaticLevel(Skills.HITPOINTS); + int fooditems = getAttribute("food-items", 0); + if (curhp < maxhp / 2 && fooditems > 0 && getAttribute("eat-delay", 0) < GameWorld.getTicks()) { + lock(3); + getProperties().getCombatPulse().delayNextAttack(3); + getSkills().heal(maxhp / 6); + for (Player p : RegionManager.getLocalPlayers(this)) { + playAudio(p, Sounds.EAT_2393); + } + setAttribute("eat-delay", GameWorld.getTicks() + 6); + setAttribute("food-items", fooditems - 1); } - setAttribute("eat-delay", GameWorld.getTicks() + 6); } behavior.tick(this); if (aggressiveHandler != null && aggressiveHandler.selectTarget()) { @@ -196,9 +204,9 @@ public class RevenantNPC extends AbstractNPC { public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (entity instanceof Player) { if (!hasAcceptableCombatLevel(entity.asPlayer()) && !entity.asPlayer().isAdmin()) { - if(message) { - entity.asPlayer().sendMessage("The level difference between you and your opponent is too great."); - } + if (message) { + entity.asPlayer().sendMessage("The level difference between you and your opponent is too great."); + } return false; } } diff --git a/Server/src/main/core/Server.kt b/Server/src/main/core/Server.kt index fd128d949..80f29a8cf 100644 --- a/Server/src/main/core/Server.kt +++ b/Server/src/main/core/Server.kt @@ -3,20 +3,21 @@ package core import core.api.log import core.game.system.SystemManager import core.game.system.SystemState -import core.net.NioReactor -import core.tools.TimeStamp -import kotlinx.coroutines.* -import core.tools.SystemLogger import core.game.system.config.ServerConfigParser import core.game.world.GameWorld -import core.game.world.repository.Repository +import core.net.NioReactor import core.tools.Log +import core.tools.NetworkReachability +import core.tools.TimeStamp +import kotlinx.coroutines.* import java.io.File import java.io.FileWriter import java.lang.management.ManagementFactory import java.lang.management.ThreadMXBean import java.net.BindException +import java.net.URL import java.util.* +import kotlin.math.max import kotlin.system.exitProcess @@ -43,6 +44,8 @@ object Server { @JvmStatic var reactor: NioReactor? = null + var networkReachability = NetworkReachability.Reachable + /** * The main method, in this method we load background utilities such as * cache and our world, then end with starting networking. @@ -95,6 +98,11 @@ object Server { GlobalScope.launch { delay(20000) while (running) { + val timeStart = System.currentTimeMillis() + if (!checkConnectivity()) + networkReachability = NetworkReachability.Unreachable + else + networkReachability = NetworkReachability.Reachable if (System.currentTimeMillis() - lastHeartbeat > 7200 && running) { log(this::class.java, Log.ERR, "Triggering reboot due to heartbeat timeout") log(this::class.java, Log.ERR, "Creating thread dump...") @@ -115,12 +123,36 @@ object Server { if (!SystemManager.isTerminated()) exitProcess(0) } - delay(625) + val timeNow = System.currentTimeMillis() + delay(max(0L, 625 - (timeNow - timeStart))) } } } } + private fun checkConnectivity(): Boolean + { + //Has to be done this way because you can't actually ping in Java unless you run the whole thing as root + val urls = ServerConstants.CONNECTIVITY_CHECK_URL.split(",") + var timeout = ServerConstants.CONNECTIVITY_TIMEOUT + if (timeout * urls.size > 5000) //Limit timeout down to 5000ms so other watchdog functions continue as expected. + timeout = 5000 / urls.size + for (targetUrl in urls) { + try { + val url = URL(targetUrl) + val conn = url.openConnection() + conn.connectTimeout = timeout + conn.connect() + conn.getInputStream().close() + return true + } catch (e: Exception) { + log(this::class.java, Log.WARN, "${targetUrl} failed to respond. Are we offline?") + continue + } + } + return false + } + @JvmStatic fun heartbeat() { lastHeartbeat = System.currentTimeMillis() diff --git a/Server/src/main/core/ServerConstants.backup b/Server/src/main/core/ServerConstants.backup deleted file mode 100644 index 3c4d50610..000000000 --- a/Server/src/main/core/ServerConstants.backup +++ /dev/null @@ -1,217 +0,0 @@ -package core - -import core.game.system.SystemShutdownHook -import core.game.system.mysql.SQLManager -import core.game.world.map.Location -import core.tools.mysql.Database -import core.tools.secondsToTicks -import org.json.simple.JSONObject -import java.io.File -import java.math.BigInteger - -/** - * A class holding various variables for the server. - * @author Ceikry - */ -class ServerConstants { - companion object { - @JvmField - var SHUTDOWN_HOOK: Thread = Thread(SystemShutdownHook()) - - @JvmField - var DATA_PATH: String? = null - - //path to the cache - @JvmField - var CACHE_PATH: String? = null - - //path for the server store (obsolete, but kept for the sake of system sanity.) - @JvmField - var STORE_PATH: String? = null - - //path for player saves - @JvmField - var PLAYER_SAVE_PATH: String? = null - - @JvmField - var PLAYER_ATTRIBUTE_PATH = "ish"; - - //path to the various config files, such as npc_spawns.json - var CONFIG_PATH: String? = null - - @JvmField - var GRAND_EXCHANGE_DATA_PATH: String? = null - - @JvmField - var RDT_DATA_PATH: String? = null - - @JvmField - var OBJECT_PARSER_PATH: String? = null - - @JvmField - var SCRIPTS_PATH: String? = null - - @JvmField - var DIALOGUE_SCRIPTS_PATH: String? = null - - @JvmField - var LOGS_PATH: String? = null - - @JvmField - var BOT_DATA_PATH: String? = null - - //the max number of players. - @JvmField - var MAX_PLAYERS = 0 - - //the max number of NPCs - @JvmField - var MAX_NPCS = 0 - - //the location where new players are placed on login. - @JvmField - var START_LOCATION: Location? = null - - //Location for all home teleports/respawn location - @JvmField - var HOME_LOCATION: Location? = null - - //the name for the database - @JvmField - var DATABASE_NAME: String? = null - - //username for the database - @JvmField - var DATABASE_USER: String? = null - - //password for the database - @JvmField - var DATABASE_PASS: String? = null - - //address for the database - @JvmField - var DATABASE_ADDRESS: String? = null - - @JvmField - var DATABASE_PORT: String? = null - - @JvmField - var WRITE_LOGS: Boolean = false - - @JvmField - var BANK_SIZE: Int = 496 - - @JvmField - var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour - - @JvmField - var GE_AUTOSTOCK_ENABLED = false - - //location names for the ::to command. - val TELEPORT_DESTINATIONS = arrayOf( - arrayOf(Location.create(2974, 4383, 2), "corp", "corporal", "corporeal"), - arrayOf(Location.create(2659, 2649, 0), "pc", "pest control", "pest"), - arrayOf(Location.create(3293, 3184, 0), "al kharid", "alkharid", "kharid"), - arrayOf(Location.create(3222, 3217, 0), "lumbridge", "lumby"), - arrayOf(Location.create(3110, 3168, 0), "wizard tower", "wizards tower", "tower", "wizards"), - arrayOf(Location.create(3083, 3249, 0), "draynor", "draynor village"), - arrayOf(Location.create(3019, 3244, 0), "port sarim", "sarim"), - arrayOf(Location.create(2956, 3209, 0), "rimmington"), - arrayOf(Location.create(2965, 3380, 0), "fally", "falador"), - arrayOf(Location.create(2895, 3436, 0), "taverley"), - arrayOf(Location.create(3080, 3423, 0), "barbarian village", "barb"), - arrayOf(Location.create(3213, 3428, 0), "varrock"), - arrayOf(Location.create(3164, 3485, 0), "grand exchange", "ge"), - arrayOf(Location.create(2917, 3175, 0), "karamja"), - arrayOf(Location.create(2450, 5165, 0), "tzhaar"), - arrayOf(Location.create(2795, 3177, 0), "brimhaven"), - arrayOf(Location.create(2849, 2961, 0), "shilo village", "shilo"), - arrayOf(Location.create(2605, 3093, 0), "yanille"), - arrayOf(Location.create(2663, 3305, 0), "ardougne", "ardy"), - arrayOf(Location.create(2450, 3422, 0), "gnome stronghold", "gnome"), - arrayOf(Location.create(2730, 3485, 0), "camelot", "cammy", "seers"), - arrayOf(Location.create(2805, 3435, 0), "catherby"), - arrayOf(Location.create(2659, 3657, 0), "rellekka"), - arrayOf(Location.create(2890, 3676, 0), "trollheim"), - arrayOf(Location.create(2914, 3746, 0), "godwars", "gwd", "god wars"), - arrayOf(Location.create(3180, 3684, 0), "bounty hunter", "bh"), - arrayOf(Location.create(3272, 3687, 0), "clan wars", "clw"), - arrayOf(Location.create(3090, 3957, 0), "mage arena", "mage", "magearena", "arena"), - arrayOf(Location.create(3069, 10257, 0), "king black dragon", "kbd"), - arrayOf(Location.create(3359, 3416, 0), "digsite"), - arrayOf(Location.create(3488, 3489, 0), "canifis"), - arrayOf(Location.create(3428, 3526, 0), "slayer tower", "slayer"), - arrayOf(Location.create(3502, 9483, 2), "kalphite queen", "kq", "kalphite hive", "kalphite"), - arrayOf(Location.create(3233, 2913, 0), "pyramid"), - arrayOf(Location.create(3419, 2917, 0), "nardah"), - arrayOf(Location.create(3482, 3090, 0), "uzer"), - arrayOf(Location.create(3358, 2970, 0), "pollnivneach", "poln"), - arrayOf(Location.create(3305, 2788, 0), "sophanem"), - arrayOf(Location.create(2898, 3544, 0), "burthorpe", "burthorp"), - arrayOf(Location.create(3088, 3491, 0), "edge", "edgeville"), - arrayOf(Location.create(3169, 3034, 0), "bedabin"), - arrayOf(Location.create(3565, 3289, 0), "barrows"), - arrayOf(Location.create(3016, 3513, 0), "bkf", "black knights fortress"), - arrayOf(Location.create(3052, 3481, 0), "monastery") - ) - - @JvmField - var DATABASE: Database? = null - - //if SQL is enabled - @JvmField - var MYSQL = true - - //the server name - @JvmField - var SERVER_NAME: String = "" - - //The RSA_KEY for the server. - @JvmField - var EXPONENT = BigInteger("52317200263721308660411803146360972546561037484450290559823448967617618536819222494429186211525706853703641369936136465589036631055945454547936148730495933263344792588795811788941129493188907621550836988152620502378278134421731002382361670176785306598134280732756356458964850508114958769985438054979422820241") - - //The MODULUS for the server. - @JvmField - var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051") - - /** - * Parses a JSONObject and retrieves the values for all settings in this file. - * @author Ceikry - * @param data : The JSONObject to parse. - */ - fun parse(data: JSONObject) { - MAX_PLAYERS = data["max_players"].toString().toInt() - MAX_NPCS = data["max_npcs"].toString().toInt() - - START_LOCATION = JSONUtils.parseLocation(data["new_player_location"].toString()) - HOME_LOCATION = JSONUtils.parseLocation(data["home_location"].toString()) - - DATA_PATH = JSONUtils.parsePath(data["data_path"].toString()) - CACHE_PATH = JSONUtils.parsePath(data["cache_path"].toString()) - STORE_PATH = JSONUtils.parsePath(data["store_path"].toString()) - PLAYER_SAVE_PATH = JSONUtils.parsePath(data["save_path"].toString()) - CONFIG_PATH = JSONUtils.parsePath(data["configs_path"].toString()) - PLAYER_ATTRIBUTE_PATH = PLAYER_SAVE_PATH + "attributes" + File.separator - GRAND_EXCHANGE_DATA_PATH = JSONUtils.parsePath(data["grand_exchange_data_path"].toString()) - BOT_DATA_PATH = JSONUtils.parsePath(data["bot_data_path"].toString()) - RDT_DATA_PATH = JSONUtils.parsePath(data["rare_drop_table_path"].toString()) - OBJECT_PARSER_PATH = JSONUtils.parsePath(data["object_parser_path"].toString()) - SCRIPTS_PATH = JSONUtils.parsePath(data["scripts_path"].toString()) - DIALOGUE_SCRIPTS_PATH = JSONUtils.parsePath(data["dialogue_scripts_path"].toString()) - if(data.containsKey("logs_path")){ - LOGS_PATH = data["logs_path"].toString() - } - if(data.containsKey("writeLogs")){ - WRITE_LOGS = data["writeLogs"] as Boolean - } - - DATABASE_NAME = data["database_name"].toString() - DATABASE_USER = data["database_username"].toString() - DATABASE_PASS = data["database_password"].toString() - DATABASE_ADDRESS = data["database_address"].toString() - DATABASE_PORT = data["database_port"].toString() - - DATABASE = Database(DATABASE_ADDRESS, DATABASE_NAME, DATABASE_USER, DATABASE_PASS) - } - } -} \ No newline at end of file diff --git a/Server/src/main/core/ServerConstants.kt b/Server/src/main/core/ServerConstants.kt index c15622a83..2959ccb0c 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -7,6 +7,7 @@ import core.tools.mysql.Database import core.tools.secondsToTicks import rs09.game.content.activity.castlewars.CastleWars import java.math.BigInteger +import java.util.* /** * A class holding various variables for the server. @@ -16,6 +17,9 @@ class ServerConstants { companion object { var NOAUTH_DEFAULT_ADMIN: Boolean = true + @JvmField + var CURRENT_SAVEFILE_VERSION = 4 + @JvmField var DAILY_ACCOUNT_LIMIT = 3 @@ -157,7 +161,7 @@ class ServerConstants { var LOG_CUTSCENE = true @JvmField - var RULES_AND_INFO_ENABLED = true + var RULES_AND_INFO_ENABLED = false @JvmField var WATCHDOG_ENABLED = true @@ -235,6 +239,8 @@ class ServerConstants { arrayOf(Location.create(2660, 3158, 0), "fishing trawler", "trawler"), arrayOf(Location.create(2800, 3667, 0), "mountain camp"), arrayOf(Location.create(2575, 3250, 0), "clocktower"), + arrayOf(Location.create(2544, 3759, 0), "waterbirth"), + arrayOf(Location.create(2899, 4450, 0), "dks"), arrayOf(CastleWars.lobbyBankArea.randomLoc, "cwars", "castle wars", "castle war", "castlewars", "castlewar", "castle") ) @@ -286,13 +292,16 @@ class ServerConstants { var DRAGON_AXE_USE_OSRS_SPEC = false @JvmField - var ENABLE_GLOBALCHAT = false + var ENABLE_GLOBAL_CHAT = false @JvmField var MAX_PATHFIND_DISTANCE = 25 @JvmField - var IRONMAN_ICONS = false + var XP_RATES = false + + @JvmField + var IRONMAN = false @JvmField var PLAYER_STOCK_CLEAR_INTERVAL = 1 @@ -304,24 +313,60 @@ class ServerConstants { var BOTSTOCK_LIMIT = 5000 @JvmField - var BETTER_AGILITY_PYRAMID_GP = true + var BETTER_AGILITY_PYRAMID_GP = false @JvmField - var BETTER_DFS = true + var BETTER_DFS = false @JvmField - var NEW_PLAYER_ANNOUNCEMENT = true + var NEW_PLAYER_ANNOUNCEMENT = false @JvmField - var HOLIDAY_EVENT_RANDOMS = true + var INAUTHENTIC_CANDLELIGHT_RANDOM = false @JvmField - var FORCE_HALLOWEEN_RANDOMS = false + var HOLIDAY_EVENT_RANDOMS = false @JvmField - var FORCE_CHRISTMAS_RANDOMS = false + var FORCE_HALLOWEEN_EVENTS = false @JvmField - var RUNECRAFTING_FORMULA_REVISION = 581 + var FORCE_CHRISTMAS_EVENTS = false + + @JvmField + var FORCE_EASTER_EVENTS = false + + @JvmField + var RUNECRAFTING_FORMULA_REVISION = 530 + + @JvmField + var ENHANCED_DEEP_WILDERNESS = false + + @JvmField + var WILDERNESS_EXCLUSIVE_LOOT = false + + @JvmField + var SHOOTING_STAR_RING = false + + @JvmField + var RING_OF_WEALTH_TELEPORT = false + + @JvmField + var SECOND_BANK = false + + @JvmField + var PLAYER_COMMANDS = false + + @JvmField + var BOOSTED_TRAWLER_REWARDS = false + + @JvmField + var STARTUP_MOMENT = Calendar.getInstance() + + @JvmField + var CONNECTIVITY_CHECK_URL = "https://google.com,https://2009scape.org" + + @JvmField + var CONNECTIVITY_TIMEOUT = 500 } } diff --git a/Server/src/main/core/ServerStore.kt b/Server/src/main/core/ServerStore.kt index ad2678584..52df7fb52 100644 --- a/Server/src/main/core/ServerStore.kt +++ b/Server/src/main/core/ServerStore.kt @@ -113,18 +113,18 @@ class ServerStore : PersistWorld { } @JvmStatic - fun JSONObject.getString(key: String): String { - return this[key] as? String ?: "nothing" + fun JSONObject.getString(key: String, default: String = "nothing"): String { + return this[key] as? String ?: default } @JvmStatic - fun JSONObject.getLong(key: String): Long { - return this[key] as? Long ?: 0L + fun JSONObject.getLong(key: String, default: Long = 0L): Long { + return this[key] as? Long ?: default } @JvmStatic - fun JSONObject.getBoolean(key: String): Boolean { - return this[key] as? Boolean ?: false + fun JSONObject.getBoolean(key: String, default: Boolean = false): Boolean { + return this[key] as? Boolean ?: default } fun List.toJSONArray(): JSONArray{ diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index f79e89ba7..8185d5079 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -1,36 +1,64 @@ package core.api import com.moandjiezana.toml.Toml +import content.data.Quests +import content.data.consumables.* +import content.data.skill.SkillingTool +import content.global.handlers.iface.ge.StockMarket +import content.global.skill.slayer.SlayerEquipmentFlags +import content.global.skill.slayer.SlayerFlags +import content.global.skill.slayer.SlayerManager +import content.global.skill.slayer.Tasks +import content.global.skill.summoning.familiar.BurdenBeast +import core.ServerConstants +import core.api.utils.PlayerStatsCounter +import core.api.utils.Vector +import core.cache.def.impl.AnimationDefinition import core.cache.def.impl.ItemDefinition import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.VarbitDefinition +import core.game.activity.Cutscene import core.game.component.Component +import core.game.consumable.* import core.game.container.impl.EquipmentContainer +import core.game.dialogue.DialogueFile +import core.game.dialogue.SkillDialogueHandler +import core.game.ge.GrandExchangeRecords +import core.game.interaction.* import core.game.node.Node import core.game.node.entity.Entity +import core.game.node.entity.combat.CombatSwingHandler import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.impl.Animator import core.game.node.entity.impl.ForceMovement import core.game.node.entity.impl.Projectile import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player +import core.game.node.entity.player.info.LogType +import core.game.node.entity.player.info.PlayerMonitor import core.game.node.entity.player.link.HintIconManager import core.game.node.entity.player.link.IronmanMode import core.game.node.entity.player.link.TeleportManager import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.player.link.emote.Emotes -import core.game.node.entity.player.link.quest.QuestRepository import core.game.node.entity.player.link.prayer.PrayerType +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.player.link.quest.QuestRepository import core.game.node.entity.skill.Skills -import content.data.skill.SkillingTool -import content.global.skill.slayer.Tasks -import content.global.skill.summoning.familiar.BurdenBeast import core.game.node.item.GroundItem import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder +import core.game.requirement.* +import core.game.shops.Shops +import core.game.system.config.ItemConfigParser +import core.game.system.config.ServerConfigParser import core.game.system.task.Pulse +import core.game.system.timer.* +import core.game.system.timer.impl.* +import core.game.world.GameWorld +import core.game.world.GameWorld.Pulser import core.game.world.map.Direction import core.game.world.map.Location import core.game.world.map.RegionManager @@ -39,48 +67,21 @@ import core.game.world.map.path.Pathfinder import core.game.world.map.zone.MapZone import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneBuilder +import core.game.world.repository.Repository +import core.game.world.update.flag.* import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag +import core.game.world.update.flag.context.* +import core.net.packet.PacketRepository +import core.net.packet.context.DefaultContext +import core.net.packet.context.MusicContext +import core.net.packet.out.AudioPacket +import core.net.packet.out.MusicPacket +import core.tools.* import org.rs09.consts.Items import org.rs09.consts.NPCs -import core.game.dialogue.DialogueFile -import core.game.dialogue.SkillDialogueHandler -import core.api.utils.GlobalKillCounter -import core.game.shops.Shops -import core.game.ge.GrandExchangeRecords -import core.game.interaction.InteractionListeners -import content.global.handlers.iface.ge.StockMarket -import content.global.skill.slayer.SlayerManager -import content.data.consumables.* -import core.game.activity.Cutscene -import core.game.interaction.* -import core.game.node.entity.player.info.LogType -import core.game.node.entity.player.info.PlayerMonitor -import core.tools.SystemLogger -import core.game.system.config.ItemConfigParser -import core.game.system.config.ServerConfigParser -import core.game.world.GameWorld -import core.game.world.GameWorld.Pulser -import core.game.world.repository.Repository -import core.game.consumable.* -import core.ServerConstants -import core.api.utils.Vector -import core.cache.def.impl.AnimationDefinition -import core.game.node.entity.player.link.quest.Quest -import core.tools.* -import core.game.world.update.flag.* -import core.game.world.update.flag.context.* -import core.game.requirement.* -import core.net.packet.PacketRepository -import core.net.packet.context.MusicContext -import core.net.packet.out.MusicPacket -import core.game.system.timer.* -import core.game.system.timer.impl.* -import core.game.node.entity.combat.CombatSwingHandler -import core.net.packet.context.DefaultContext -import core.net.packet.out.AudioPacket import org.rs09.consts.Sounds -import java.util.regex.* import java.io.* +import java.util.regex.* import kotlin.math.* /** @@ -195,6 +196,18 @@ fun inEquipment(player: Player, id: Int, amount: Int = 1): Boolean { return amountInEquipment(player, id) >= amount } +/** + * Check if any item is in a player's inventory + * @param player the player + * @param ids the set of item ids to check + * @return true if the player has at least one of the items in their inventory, false if none are present + */ +fun anyInInventory(player: Player, vararg ids: Int): Boolean { + return ids.any{ id -> + inInventory(player, id) + } +} + /** * Check if an item exists in a player's equipment or inventory * @param player the player whose equipment to check @@ -249,14 +262,40 @@ class ContainerisedItem(val container: core.game.container.Container?, val itemI } /** - * Check if player has any of the specified item IDs equipped, in inventory, or in banks - * Returns a ContainerisedItem containing the container and the item ID if found, otherwise ContainerisedItem(null, -1) if not found + * Check if player has the specified item ID equipped, in inventory, or in their bank + * @param id The item ID to check + * @return A ContainerisedItem containing the container and the item ID if found, otherwise ContainerisedItem(null, -1) if not found */ -fun hasAnItem(player: Player, vararg ids: Int): ContainerisedItem { - for (searchSpace in arrayOf(player.inventory, player.equipment, player.bankPrimary, player.bankSecondary)) { +fun hasAnItem(player: Player, id: Int): ContainerisedItem { + return hasAnItem(player, arrayOf(id), false) +} + +/** + * Check if player has the specified item ID equipped, in inventory, or in their bank + * @param id The item ID to check + * @param checkSecondBank Whether to check the player's second bank. + * @return A ContainerisedItem containing the container and the item ID if found, otherwise ContainerisedItem(null, -1) if not found + */ +fun hasAnItem(player: Player, id: Int, checkSecondBank: Boolean): ContainerisedItem { + return hasAnItem(player, arrayOf(id), checkSecondBank) +} + +/** + * Check if player has any of the specified item IDs equipped, in inventory, or in their bank + * @param ids An array of item IDs to check + * @param checkSecondBank Whether to check the player's second bank. + * @return A ContainerisedItem containing the container and the item ID if found, otherwise ContainerisedItem(null, -1) if not found + */ +fun hasAnItem(player: Player, ids: Array, checkSecondBank: Boolean): ContainerisedItem { + val searchSpace = if (checkSecondBank) { + arrayOf(player.inventory, player.equipment, player.bankPrimary, player.bankSecondary) + } else { + arrayOf(player.inventory, player.equipment, player.bankPrimary) + } + for (container in searchSpace) { for (id in ids) { - if (searchSpace.containItems(id)) { - return ContainerisedItem(searchSpace, id) + if (container.containItems(id)) { + return ContainerisedItem(container, id) } } } @@ -305,6 +344,24 @@ fun removeItem(player: Player, item: T, container: Container = Container.INV } } +/** + * Remove items in a players inventory + * Checks that the player has enough first + * @param player + * @param items the items to remove + */ +fun removeItemsIfPlayerHasEnough(player: Player, vararg items: Item): Boolean { + for (item in items) { + if (amountInInventory(player, item.id) >= 1) continue + return false + } + for (item in items) { + if (removeItem(player, item)) continue + return false + } + return true +} + /** * Add an item to the given player's given container * @param player the player whose container to modify @@ -328,6 +385,7 @@ fun addItem(player: Player, id: Int, amount: Int = 1, container: Container = Con * @param player the player whose container to modify * @param slot the slot to use * @param item the item to replace the slot with + * @param currentItem the current item that is being replaced * @param container the Container to modify * @return the item that was previously in the slot, or null if none. */ @@ -360,6 +418,48 @@ fun replaceSlot(player: Player, slot: Int, item: Item, currentItem: Item? = null return null } +/** + * Replaces all items a player owns anywhere (equipment, inventory, bank, second bank) + * @param player the player whose inventory to remove the item from + * @param itemId the item ID to replace + * @param replaceId the replacement item ID + * @author Player Name + */ +fun replaceAllItems(player: Player, itemId: Int, replaceId: Int) { + val item = Item(itemId) + val containers = if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar.isBurdenBeast()) { + arrayOf(player.inventory, player.equipment, player.bankPrimary, player.bankSecondary, (player.familiarManager.familiar as BurdenBeast).container) + } else { + arrayOf(player.inventory, player.equipment, player.bankPrimary, player.bankSecondary) + } + for (container in containers) { + val hasItems = container.getAll(item) + if (!item.definition.isStackable && container != player.bankPrimary && container != player.bankSecondary) { + // just replace + for (target in hasItems) { + val newItem = Item(replaceId, target.amount) + container.replace(newItem, target.slot, true) + } + } else { + // add to existing stack if possible + if (hasItems.size > 0) { + val target = hasItems[0] + var count = 0 + for (x in hasItems) { + count += x.amount + } + val newItem = Item(replaceId, count) + container.replace(newItem, target.slot, true) + } + if (hasItems.size > 1) { + for (i in 1 until hasItems.size) { + container.remove(hasItems[i], hasItems[i].slot, true) + } + } + } + } +} + /** * Add an item with a variable quantity or drop it if a player does not have enough space * @param player the player whose inventory to add to @@ -378,6 +478,26 @@ fun addItemOrDrop(player: Player, id: Int, amount: Int = 1) { } } +/** + * Add an item with a variable quantity or bank it if a player does not have enough space, or drop it if that still doesn't work + * @param player the player whose inventory to add to + * @param id the ID of the item to add to the player's inventory + * @param amount the amount of the ID to add to the player's inventory, defaults to 1 + */ +fun addItemOrBank(player: Player, id: Int, amount: Int = 1) { + val item = Item(id, amount) + if (!player.inventory.add(item)) { + if (player.ironmanManager.mode != IronmanMode.ULTIMATE && player.bankPrimary.add(item)) { + sendMessage(player, colorize("%RThe ${item.name} has been sent to your bank.")) + } else if (player.ironmanManager.mode != IronmanMode.ULTIMATE && player.bankSecondary.add(item)) { + sendMessage(player, colorize("%RThe ${item.name} has been sent to your secondary bank.")) + } else { + GroundItemManager.create(item, player) + sendMessage(player, colorize("%RAs your inventory and bank account(s) are all full, the ${item.name} has been placed on the ground under your feet. Don't forget to grab it. (Also consider cleaning out some stuff, maybe? I mean, Jesus!)")) + } + } +} + /** * Clears an NPC with the "poof" smoke graphics commonly seen with random event NPCs. * @param npc the NPC object to initialize @@ -901,6 +1021,14 @@ fun openDialogue(player: Player, dialogue: Any, vararg args: Any) { } } +/** + * Closes any opened dialogue. + */ +fun closeDialogue(player: Player) { + player.dialogueInterpreter.close() + player.interfaceManager.closeChatbox() +} + /** * Gets an NPC with the given ID from the repository. * @param id the ID of the NPC to locate @@ -1227,10 +1355,19 @@ fun getVarbit (player: Player, varbitId: Int) : Int { @JvmOverloads fun setVarp (player: Player, varpIndex: Int, value: Int, save: Boolean = false) { player.varpMap[varpIndex] = value - player.saveVarp[varpIndex] = save + if (player.saveVarp[varpIndex] != true && save) + player.saveVarp[varpIndex] = true //only set if we're choosing to save. Prevents accidental unsaving. if you REALLY want to unsave a varp, use unsaveVarp. player.packetDispatch.sendVarp(varpIndex, value) } +fun saveVarp (player: Player, varpIndex: Int) { + player.saveVarp[varpIndex] = true +} + +fun unsaveVarp (player: Player, varpIndex: Int) { + player.saveVarp.remove(varpIndex) +} + @JvmOverloads fun setVarbit (player: Player, def: VarbitDefinition, value: Int, save: Boolean = false) { val mask = def.mask @@ -1251,6 +1388,11 @@ fun setVarbit (player: Player, varbitId: Int, value: Int, save: Boolean = false) setVarbit (player, def, value, save) } +fun setVarc (player: Player, varc: Int, value: Int) +{ + player.packetDispatch.sendVarcUpdate(varc.toShort(), value) +} + fun reinitVarps (player: Player) { for ((index, value) in player.varpMap) { setVarp(player, index, value, player.saveVarp[index] ?: false) @@ -1583,14 +1725,25 @@ fun closeTabInterface(player: Player) { player.interfaceManager.closeSingleTab() } +/** + * Closes any open (both chat and non-chat) interfaces for the player + * @param player the player to close the interfaces for + */ +fun closeAllInterfaces(player: Player) { + player.interfaceManager.close() + player.interfaceManager.closeChatbox() + player.dialogueInterpreter.close() +} + /** * Sends a dialogue that uses the player's chathead. * @param player the player to send the dialogue to * @param msg the message to send. * @param expr the FacialExpression to use. An enum exists for these called FacialExpression. Defaults to FacialExpression.FRIENDLY + * @param hide should the continue button be hidden? */ -fun sendPlayerDialogue(player: Player, msg: String, expr: core.game.dialogue.FacialExpression = core.game.dialogue.FacialExpression.FRIENDLY) { - player.dialogueInterpreter.sendDialogues(player, expr, *splitLines(msg)) +fun sendPlayerDialogue(player: Player, msg: String, expr: core.game.dialogue.FacialExpression = core.game.dialogue.FacialExpression.FRIENDLY, hide: Boolean = false) { + player.dialogueInterpreter.sendDialogues(player, expr, hide, *splitLines(msg)) } /** @@ -1604,14 +1757,28 @@ fun sendPlayerOnInterface(player: Player, iface: Int, child: Int) { } /** - * Sends a dialogue that uses the player's chathead. + * Sends a dialogue that uses the npc's chathead. * @param player the player to send the dialogue to * @param npc the ID of the NPC to use for the chathead * @param msg the message to send. * @param expr the FacialExpression to use. An enum exists for these called FacialExpression. Defaults to FacialExpression.FRIENDLY + * @param hide should the continue button be hidden? */ -fun sendNPCDialogue(player: Player, npc: Int, msg: String, expr: core.game.dialogue.FacialExpression = core.game.dialogue.FacialExpression.FRIENDLY) { - player.dialogueInterpreter.sendDialogues(npc, expr, *splitLines(msg)) +fun sendNPCDialogue(player: Player, npc: Int, msg: String, expr: core.game.dialogue.FacialExpression = core.game.dialogue.FacialExpression.FRIENDLY, + hide: Boolean = false) { + player.dialogueInterpreter.sendDialogues(npc, expr, hide, *splitLines(msg)) +} + +/** + * Sends a dialogue that uses the npc's chathead. + * @param player the player to send the dialogue to + * @param npc the ID of the NPC to use for the chathead + * @param expr the FacialExpression to use. An enum exists for these called FacialExpression. + * @param msg the message to send. + */ +fun sendNPCDialogueLines(player: Player, npc: Int, expr: core.game.dialogue.FacialExpression, hideContinue: Boolean, vararg msgs: String) { + val dialogueComponent = player.dialogueInterpreter.sendDialogues(npc, expr, *msgs) + player.packetDispatch.sendInterfaceConfig(dialogueComponent.id, msgs.size + 4, hideContinue) } /** @@ -1692,8 +1859,11 @@ fun sendItemDialogue(player: Player, item: Any, message: String) { * @param item2 the ID of the second item to show * @param message the text to display */ -fun sendDoubleItemDialogue(player: Player, item1: Int, item2: Int, message: String) { - player.dialogueInterpreter.sendDoubleItemMessage(item1, item2, message) +fun sendDoubleItemDialogue(player: Player, item1: Any, item2: Any, message: String) { + when (item1) { + is Item -> player.dialogueInterpreter.sendDoubleItemMessage(item1, item2 as Item, *splitLines(message)) + is Int -> player.dialogueInterpreter.sendDoubleItemMessage(item1, item2 as Int, *splitLines(message)) + } } /** @@ -1767,7 +1937,7 @@ fun runTask(entity: Entity, delay: Int = 0, repeatTimes: Int = 1, task: () -> Un entity.pulseManager.run(object : Pulse(delay) { override fun pulse(): Boolean { task.invoke() - return --cycles == 0 + return --cycles <= 0 } }) } @@ -1784,29 +1954,33 @@ fun getQuestPoints(player: Player): Int { /** * Gets the stage for the given quest for the given player */ -fun getQuestStage(player: Player, quest: String): Int { +fun getQuestStage(player: Player, quest: Quests): Int { return player.questRepository.getStage(quest) } /** * Sets the stage for the given quest for the given player */ -fun setQuestStage(player: Player, quest: String, stage: Int) { +fun setQuestStage(player: Player, quest: Quests, stage: Int) { player.questRepository.setStage(QuestRepository.getQuests()[quest]!!, stage) player.questRepository.syncronizeTab(player) } /** - * Check if a quest is in progress + * Check if a quests' stage for a given player is (inclusively) between the provided start and end stage */ -fun isQuestInProgress(player: Player, quest: String, startStage: Int, endStage: Int): Boolean { +fun isQuestInProgress(player: Player, quest: Quests, startStage: Int, endStage: Int): Boolean { return player.questRepository.getStage(quest) in startStage..endStage } +fun isQuestStarted(player: Player, quest: Quests): Boolean { + return getQuestStage(player, quest) > 0 +} + /** * Check if a quest is complete */ -fun isQuestComplete(player: Player, quest: String): Boolean { +fun isQuestComplete(player: Player, quest: Quests): Boolean { return player.questRepository.getStage(quest) == 100 } @@ -1816,7 +1990,7 @@ fun isQuestComplete(player: Player, quest: String): Boolean { * @param quest The quest name string * @return the quest object */ -fun getQuest(player: Player, quest: String): Quest { +fun getQuest(player: Player, quest: Quests): Quest { return player.questRepository.getQuest(quest) } @@ -1824,7 +1998,7 @@ fun getQuest(player: Player, quest: String): Quest { /** * Check if a player meets the requirements to start a quest, and then starts it if they do. Returns success bool */ -fun startQuest(player: Player, quest: String): Boolean { +fun startQuest(player: Player, quest: Quests): Boolean { val quest = player.questRepository.getQuest(quest) val canStart = quest.hasRequirements(player) if (!canStart) return false @@ -1835,7 +2009,7 @@ fun startQuest(player: Player, quest: String): Boolean { /** * Finishes a quest, gives rewards, marks as completed, etc */ -fun finishQuest(player: Player, quest: String) { +fun finishQuest(player: Player, quest: Quests) { player.questRepository.getQuest(quest).finish(player) } @@ -1932,7 +2106,7 @@ fun runcs2 (player: Player, scriptId: Int, vararg arguments: Any) { * @param callback a callback to handle the selection. The parameters passed to the callback are the slot in the inventory of the selected item, and the 0-9 index of the option clicked. **/ @JvmOverloads -fun sendItemSelect (player: Player, vararg options: String, keepAlive: Boolean = false, callback: (slot: Int, optionIndex: Int) -> Unit) { +fun sendItemSelect(player: Player, vararg options: String, keepAlive: Boolean = false, callback: (slot: Int, optionIndex: Int) -> Unit) { player.interfaceManager.openSingleTab(Component(12)) val scriptArgs = arrayOf ((12 shl 16) + 18, 93, 4, 7, 0, -1, "", "", "", "", "", "", "", "", "") for (i in 0 until min(9, options.size)) @@ -1949,11 +2123,11 @@ fun sendItemSelect (player: Player, vararg options: String, keepAlive: Boolean = fun announceIfRare(player: Player, item: Item) { if (item.definition.getConfiguration(ItemConfigParser.RARE_ITEM, false)) { sendNews("${player.username} has just received: ${item.amount} x ${item.name}.") - GlobalKillCounter.incrementRareDrop(player, item) + PlayerStatsCounter.incrementRareDrop(player, item) } } -fun hasRequirement (player: Player, req: QuestReq, message: Boolean = true) : Boolean { +fun hasRequirement(player: Player, req: QuestReq, message: Boolean = true) : Boolean { var (isMet, unmetReqs) = req.evaluate(player) val messageList = ArrayList() @@ -1974,9 +2148,9 @@ fun hasRequirement (player: Player, req: QuestReq, message: Boolean = true) : Bo if (isMet) return true if (unmetReqs.size == 2 && unmetReqs[0] is QuestReq) { - messageList.add ("This requires completion of ${(unmetReqs[0] as QuestReq).questReq.questName} to access.") + messageList.add ("This requires completion of ${(unmetReqs[0] as QuestReq).questReq.quest} to access.") } else { - messageList.add ("You need the pre-reqs for ${req.questReq.questName} to access this.") + messageList.add ("You need the pre-reqs for ${req.questReq.quest} to access this.") messageList.add ("Please check the page in your quest journal for more info.") } @@ -1988,8 +2162,8 @@ fun hasRequirement (player: Player, req: QuestReq, message: Boolean = true) : Bo } @JvmOverloads -fun hasRequirement (player: Player, quest: String, message: Boolean = true) : Boolean { - val questReq = QuestRequirements.values().filter { it.questName.equals(quest, true) }.firstOrNull() ?: return false +fun hasRequirement(player: Player, quest: Quests, message: Boolean = true) : Boolean { + val questReq = QuestRequirements.values().firstOrNull { it.quest == quest } ?: return false return hasRequirement(player, QuestReq(questReq), message) } @@ -2030,6 +2204,10 @@ fun dumpContainer(player: Player, container: core.game.container.Container): Int var dumpedCount = 0 run beginDepositing@{ + if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { + return@beginDepositing + } + container.toArray().filterNotNull().forEach { item -> if (!bank.hasSpaceFor(item)) { sendMessage(player, "You have no more space in your bank.") @@ -2045,10 +2223,13 @@ fun dumpContainer(player: Player, container: core.game.container.Container): Int sendMessage(player, "A magical force prevents you from removing your ${item.name}.") return@forEach } + if (SlayerEquipmentFlags.isSlayerEq(item.id)) { + SlayerEquipmentFlags.updateFlags(player) + } } container.remove(item) - bank.add(unnote(item)) + bank.add(unnote(item), false) dumpedCount++ } } @@ -2296,7 +2477,11 @@ fun getSlayerTip(player: Player): Array { * @return the task flags as Int. */ fun getSlayerTaskFlags(player: Player): Int { - return SlayerManager.getInstance(player).flags.taskFlags + return getSlayerFlags(player).taskFlags +} + +fun getSlayerFlags(player: Player): SlayerFlags { + return SlayerManager.getInstance(player).flags } /** @@ -2681,15 +2866,15 @@ fun getCredits(player: Player) : Int { } /** - * Asserts that a quest is required, and sends the player "You must have completed the $questName quest $message" + * Asserts that a quest is required, and sends the player "You must have completed the $quest quest $message" * @param player the player we are checking - * @param questName the name of the quest we are checking for - * @param message the text appended to "You must have completed the $questName quest ..." if the quest is not complete. + * @param quest the quest we are checking for + * @param message the text appended to "You must have completed the $quest quest ..." if the quest is not complete. * @return whether or not the quest has been completed */ -fun requireQuest(player: Player, questName: String, message: String) : Boolean { - if (!isQuestComplete(player, questName)) { - sendMessage(player, "You must have completed the $questName quest $message") +fun requireQuest(player: Player, quest: Quests, message: String) : Boolean { + if (!isQuestComplete(player, quest)) { + sendMessage(player, "You must have completed the $quest quest $message") return false } return true @@ -2824,10 +3009,14 @@ fun queueScript(entity: Entity, delay: Int = 1, strength: QueueStrength = QueueS * @param entity the entity whose clock we are updating * @param clock the clock we are updating. Please use [core.game.interaction.Clocks] for this argument. * @param ticks the number of ticks to delay by + * @param delayScript whether to delay the script by the number of ticks passed in. * @return always returns false so this can be used as a script return value. **/ -fun delayClock(entity: Entity, clock: Int, ticks: Int) : Boolean { +fun delayClock(entity: Entity, clock: Int, ticks: Int, delayScript: Boolean = false) : Boolean { entity.clocks[clock] = getWorldTicks() + ticks + if (delayScript) { + return delayScript(entity, ticks) + } return false } @@ -2847,6 +3036,10 @@ fun delayAttack(entity: Entity, ticks: Int) { } fun stun(entity: Entity, ticks: Int) { + stun(entity, ticks, true) +} + +fun stun(entity: Entity, ticks: Int, sendMessage: Boolean) { entity.walkingQueue.reset() entity.pulseManager.clear() entity.locks.lockMovement(ticks) @@ -2855,7 +3048,9 @@ fun stun(entity: Entity, ticks: Int) { if (entity is Player) { playAudio(entity.asPlayer(), Sounds.STUNNED_2727) entity.animate(Animation(424, Animator.Priority.VERY_HIGH)) - sendMessage(entity, "You have been stunned!") + if (sendMessage) { + sendMessage(entity, "You have been stunned!") + } } } @@ -2874,11 +3069,18 @@ fun applyPoison (entity: Entity, source: Entity, severity: Int) { if(hasTimerActive(entity)) { return } + if(entity.isPoisonImmune()) { + return + } val existingTimer = getTimer(entity) if (existingTimer != null) { - existingTimer.severity = severity - existingTimer.damageSource = source + if (existingTimer.severity > severity) { + return + } else { + existingTimer.severity = severity + existingTimer.damageSource = source + } } else { registerTimer(entity, spawnTimer(source, severity)) } @@ -2988,7 +3190,7 @@ fun calculateDragonfireMaxHit(entity: Entity, maxDamage: Int, wyvern: Boolean = if (entity is Player) { hasShield = hasDragonfireShieldProtection(entity, wyvern) - hasPotion = !wyvern && getAttribute(entity, "fire:immune", 0) >= getWorldTicks() + hasPotion = !wyvern && hasTimerActive(entity) hasPrayer = entity.prayer.get(PrayerType.PROTECT_FROM_MAGIC) if (sendMessage) { diff --git a/Server/src/main/core/api/God.kt b/Server/src/main/core/api/God.kt index 58c880a97..70eb2b0fd 100644 --- a/Server/src/main/core/api/God.kt +++ b/Server/src/main/core/api/God.kt @@ -20,14 +20,11 @@ enum class God(vararg val validItems: Int) { Items.BANDOS_TASSETS_11726, ), SARADOMIN( - Items.ANCIENT_SYMBOL_11181, - Items.GILDED_KITESHIELD_3489, + Items.DAMAGED_BOOK_3839, Items.HOLY_BOOK_3840, Items.HOLY_SYMBOL_1718, - Items.HOLY_SYMBOL_4682, Items.MONKS_ROBE_542, Items.MONKS_ROBE_544, - Items.RUNE_HERALDIC_HELM_8488, Items.SARADOMIN_BRACERS_10384, Items.SARADOMIN_CAPE_2412, Items.SARADOMIN_CHAPS_10388, @@ -43,26 +40,16 @@ enum class God(vararg val validItems: Int) { Items.SARADOMIN_PLATEBODY_2661, Items.SARADOMIN_PLATELEGS_2663, Items.SARADOMIN_PLATESKIRT_3479, - Items.SARADOMIN_PLATESKIRT_3675, Items.SARADOMIN_ROBE_LEGS_10464, Items.SARADOMIN_ROBE_TOP_10458, Items.SARADOMIN_STAFF_2415, Items.SARADOMIN_STOLE_10470, Items.SARADOMIN_SWORD_11730, - Items.SARADOMIN_SYMBOL_8055, - Items.STEEL_HERALDIC_HELM_8706, ), ZAMORAK( - Items.DAGONHAI_HAT_14499, - Items.DAGONHAI_ROBE_TOP_14497, - Items.DAGONHAI_ROBE_BOTTOM_14501, Items.DAMAGED_BOOK_3841, - Items.RUNE_HERALDIC_HELM_8494, - Items.STEEL_HERALDIC_HELM_8712, Items.UNHOLY_BOOK_3842, Items.UNHOLY_SYMBOL_1724, - Items.UNHOLY_SYMBOL_3852, - Items.UNHOLY_SYMBOL_4683, Items.ZAMORAK_BRACERS_10368, Items.ZAMORAK_CAPE_2414, Items.ZAMORAK_CHAPS_10372, @@ -81,17 +68,17 @@ enum class God(vararg val validItems: Int) { Items.ZAMORAK_PLATEBODY_2653, Items.ZAMORAK_PLATELEGS_2655, Items.ZAMORAK_PLATESKIRT_3478, - Items.ZAMORAK_PLATESKIRT_3674, Items.ZAMORAK_ROBE_LEGS_10468, Items.ZAMORAK_ROBE_TOP_10460, Items.ZAMORAK_ROBE_TOP_10786, Items.ZAMORAK_STAFF_2417, Items.ZAMORAK_STOLE_10474, - Items.ZAMORAK_SYMBOL_8056, + Items.ZAMORAK_ROBE_1033, + Items.ZAMORAK_ROBE_1035, ), GUTHIX( - Items.STEEL_HERALDIC_HELM_8692, - Items.RUNE_HERALDIC_HELM_8474, + Items.DAMAGED_BOOK_3843, + Items.BOOK_OF_BALANCE_3844, Items.GUTHIX_BRACERS_10376, Items.GUTHIX_CAPE_10720, Items.GUTHIX_CHAPS_10380, @@ -100,30 +87,18 @@ enum class God(vararg val validItems: Int) { Items.GUTHIX_CROZIER_10442, Items.GUTHIX_DRAGONHIDE_10378, Items.GUTHIX_DRAGONHIDE_10794, - Items.GUTHIX_FULL_HELM_13833, Items.GUTHIX_FULL_HELM_2673, - Items.GUTHIX_KITESHIELD_13834, Items.GUTHIX_KITESHIELD_2675, Items.GUTHIX_MITRE_10454, Items.GUTHIX_MJOLNIR_6760, Items.GUTHIX_PLATEBODY_10780, Items.GUTHIX_PLATEBODY_2669, - Items.GUTHIX_PLATEBODY_13830, Items.GUTHIX_PLATELEGS_2671, - Items.GUTHIX_PLATELEGS_13831, - Items.GUTHIX_PLATESKIRT_13832, Items.GUTHIX_PLATESKIRT_3676, Items.GUTHIX_ROBE_LEGS_10466, Items.GUTHIX_ROBE_TOP_10462, Items.GUTHIX_ROBE_TOP_10788, Items.GUTHIX_STAFF_2416, Items.GUTHIX_STOLE_10472, - Items.GUTHIX_SYMBOL_8057 - ), - ZAROS( - Items.ANCIENT_STAFF_13406, - Items.ANCIENT_STAFF_4675, - Items.ANCIENT_MACE_11061, - Items.ANCIENT_BOOK_7633, ) } diff --git a/Server/src/main/core/api/utils/GlobalKillCounter.kt b/Server/src/main/core/api/utils/GlobalKillCounter.kt deleted file mode 100644 index eaecfe982..000000000 --- a/Server/src/main/core/api/utils/GlobalKillCounter.kt +++ /dev/null @@ -1,120 +0,0 @@ -package core.api.utils - -import core.api.ShutdownListener -import core.api.StartupListener -import core.game.node.entity.player.Player -import core.tools.SystemLogger -import java.io.File -import java.io.FileReader -import java.io.FileWriter -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import core.ServerConstants -import core.api.log -import core.game.node.item.Item -import core.tools.Log -import core.tools.SystemLogger.logShutdown -import core.tools.SystemLogger.logStartup - -class GlobalKillCounter : StartupListener, ShutdownListener { - override fun startup() { - logStartup("Parsing Global Kill Counts") - val file = File(ServerConstants.DATA_PATH + File.separator + "global_kill_stats.json") - if(!file.exists()) { - return - } - - val reader = FileReader(file) - val parser = JSONParser() - try { - val data = parser.parse(reader) as JSONObject - val tmp_kills = data.get("kills") - populate(kills, tmp_kills) - val tmp_rare_drops = data.get("rare_drops") - populate(rare_drops, tmp_rare_drops) - } catch (e: Exception){ - log(this::class.java, Log.ERR, "Failed parsing ${file.name} - stack trace below.") - e.printStackTrace() - } - } - - override fun shutdown() { - logShutdown("Saving Global Kill Counts") - val data = JSONObject() - data.put("kills", saveField(kills)) - data.put("rare_drops", saveField(rare_drops)) - val file = File(ServerConstants.DATA_PATH + File.separator + "global_kill_stats.json") - FileWriter(file).use { it.write(data.toJSONString()); it.flush(); it.close() } - } - - companion object { - val kills: HashMap> = HashMap() - val rare_drops: HashMap> = HashMap() - - @JvmStatic - fun populate(field: HashMap>, obj: Any?) { - if(obj != null && obj is JSONObject) { - for((player, tmp_kc) in obj.asIterable()) { - if(player is String) { - val kc: HashMap = HashMap() - for((npc_id, count) in (tmp_kc as JSONObject).asIterable()) { - kc.put(java.lang.Long.parseLong(npc_id as String), count as Long) - } - field.put(player, kc) - } - } - } - } - - @JvmStatic - fun saveField(field: HashMap>): JSONObject { - val tmp_kills = JSONObject() - for((player, kc) in field.asIterable()) { - val tmp_kc = JSONObject() - for((id, count) in kc.asIterable()) { - tmp_kc.put(id, count) - } - tmp_kills.put(player, tmp_kc) - } - return tmp_kills - } - - @JvmStatic - fun save() { - - } - - @JvmStatic - fun incrementKills(player: Player, npc_id: Int) { - val player_kills = kills.getOrPut(player.username, { HashMap() }) - val old_amount = player_kills.getOrElse(npc_id.toLong(), { 0 }) - player_kills.put(npc_id.toLong(), 1 + old_amount) - } - - @JvmStatic - fun incrementRareDrop(player: Player, item: Item) { - val player_drops = rare_drops.getOrPut(player.username, { HashMap() }) - val old_amount = player_drops.getOrElse(item.id.toLong(), { 0 }) - player_drops.put(item.id.toLong(), item.amount + old_amount) - } - - @JvmStatic - fun getKills(player: Player, npc_id: Int): Long { - return kills.getOrElse(player.username, { HashMap() }).getOrElse(npc_id.toLong(), { 0 }) - } - - @JvmStatic - fun getKills(player: Player, npc_ids: IntArray): Long { - var sum: Long = 0 - for(npc_id in npc_ids) { - sum += getKills(player, npc_id) - } - return sum - } - - @JvmStatic - fun getRareDrops(player: Player, item_id: Int): Long { - return rare_drops.getOrElse(player.username, { HashMap() }).getOrElse(item_id.toLong(), { 0 }) - } - } -} diff --git a/Server/src/main/core/api/utils/Permadeath.kt b/Server/src/main/core/api/utils/Permadeath.kt new file mode 100644 index 000000000..a49d575cf --- /dev/null +++ b/Server/src/main/core/api/utils/Permadeath.kt @@ -0,0 +1,135 @@ +package core.api.utils + +import content.global.skill.construction.HouseLocation +import content.minigame.blastfurnace.BFPlayerState +import content.minigame.blastfurnace.BlastFurnace +import core.ServerConstants +import core.api.isUsingSecondaryBankAccount +import core.api.teleport +import core.api.toggleBankAccount +import core.game.node.entity.player.Player +import core.game.node.entity.player.VarpManager +import core.game.node.entity.player.info.login.PlayerSaver +import core.game.node.entity.player.link.IronmanMode +import core.game.node.entity.player.link.SavedData +import core.game.node.entity.player.link.SpellBookManager +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.player.link.quest.QuestRepository +import core.game.node.entity.skill.Skills +import core.game.node.item.GroundItem +import core.game.node.item.GroundItemManager +import core.game.world.map.Location +import java.util.ArrayList + +fun permadeath(target: Player) { + teleport(target, Location.create(3094, 3107, 0)) + + // Core + target.inventory.clear() + target.bank.clear() + target.bankSecondary.clear() + for (i in target.bankPrimary.tabStartSlot.indices) { + target.bankPrimary.tabStartSlot[i] = 0 + } + for (i in target.bankSecondary.tabStartSlot.indices) { + target.bankSecondary.tabStartSlot[i] = 0 + } + if (isUsingSecondaryBankAccount(target)) { + toggleBankAccount(target) + } + target.equipment.clear() + target.varpManager = VarpManager(target) + target.varpMap.clear() + target.saveVarp.clear() + target.timers.clearTimers() + + // Skills + target.skills = Skills(target) + + // Settings can be kept + + // Quests + target.questRepository = QuestRepository(target) + + // Appearance doesn't matter because you're going to tutorial island anyway + + // Spellbook + target.spellBookManager.setSpellBook(SpellBookManager.SpellBook.MODERN) + + // Saved data + target.savedData = SavedData(target) + + // Autocast + target.properties.autocastSpell = null + + // Player monitor is a no-op + + // Music player + target.musicPlayer.clearUnlocked() + + // Familiar manager + if (target.familiarManager.hasFamiliar()) { + target.familiarManager.dismiss() + } + val petKeys = target.familiarManager.petDetails.keys.toList() + for (key in petKeys) { + target.familiarManager.removeDetails(key) + } + + // Bank pin data + target.bankPinManager.doCancelPin() + + // House data + target.houseManager.createNewHouseAt(HouseLocation.NOWHERE) + + // Achievements + for (type in DiaryType.values()) { + val diary = target.achievementDiaryManager.getDiary(type) + for (level in 0 until diary.levelStarted.size) { + for (task in 0 until diary.taskCompleted[level].size) { + diary.resetTask(target, level, task) + } + } + } + + // Xp rate + target.skills.experienceMultiplier = 1.0 + + // Ironman data + target.ironmanManager.mode = IronmanMode.NONE + + // Emote data + target.emoteManager.emotes.clear() + + // Stat manager is a no-op + + // Attributes + target.clearAttributes() + + // Pouches + for (pouch in target.pouchManager.pouches.values) { + pouch.container.clear() + pouch.currentCap = pouch.capacity + pouch.charges = pouch.maxCharges + pouch.remakeContainer() + } + + // Destroy any dropped items to prevent droptrading to yourself after death + val droppedItems = ArrayList(); + for (item in GroundItemManager.getItems()) { + if (item.dropperUid == target.details.uid) { + droppedItems.add(item) + } + } + for (item in droppedItems) { + GroundItemManager.destroy(item) + } + + // grep -R savePlayer: jobs, treasure trails, brawling gloves, slayer manager, barcrawl, ge history will simply not get saved if we don't run the hooks + // Only the Blast Furnace needs to be reset explicitly + BlastFurnace.playerStates[target.details.uid] = BFPlayerState(target) + + // Sayonara + PlayerSaver(target).save() + target.clear() +} diff --git a/Server/src/main/core/api/utils/PlayerStatsCounter.kt b/Server/src/main/core/api/utils/PlayerStatsCounter.kt new file mode 100644 index 000000000..3337c3de7 --- /dev/null +++ b/Server/src/main/core/api/utils/PlayerStatsCounter.kt @@ -0,0 +1,253 @@ +package core.api.utils + +import core.api.StartupListener +import core.game.node.entity.player.Player +import java.io.File +import java.io.FileReader +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import core.ServerConstants +import core.api.log +import core.game.node.item.Item +import core.game.world.GameWorld +import core.integrations.sqlite.SQLiteProvider +import core.tools.Log +import core.tools.SystemLogger.logStartup +import kotlin.io.path.Path + +class PlayerStatsCounter( + private val dbPath: String = Path(ServerConstants.DATA_PATH ?: "", "playerstats", "player_stats.db").toString() +) : StartupListener { + + override fun startup() { + logStartup("Loading Player Stats") + + db = SQLiteProvider(dbPath, expectedTables) + db.initTables() + + if (!tableHasData()) { // TODO: Remove check, porter and raw inserts once SQLite tracking is proven and the live server has been updated + portLegacyKillCounterJsonToSQLite() + } + } + + companion object { + lateinit var db: SQLiteProvider + + private fun resolveUIDFromPlayerUsername(playerUsername: String): Int { + return GameWorld.accountStorage.getAccountInfo(playerUsername).uid + } + + private fun portLegacyKillCounterJsonToSQLite() { + val file = File(Path(ServerConstants.DATA_PATH ?: "", "global_kill_stats.json").toString()) + if (!file.exists()) { + return + } + + val reader = FileReader(file) + val parser = JSONParser() + try { + val data = parser.parse(reader) as JSONObject + val json_kills = data.get("kills") + if (json_kills != null && json_kills is JSONObject) { + var progress = 1 + val totalPlayers = json_kills.size + for ((player, killStats) in json_kills.asIterable()) { + log( + PlayerStatsCounter::class.java, + Log.INFO, + "Porting kill counters for player $progress/$totalPlayers" + ) + if (player is String) { + val playerUid = resolveUIDFromPlayerUsername(player.replace(" ", "_")) + log( + PlayerStatsCounter::class.java, + Log.INFO, + "Player $player ($playerUid)" + ) + for ((npc_id, count) in (killStats as JSONObject).asIterable()) { + log( + PlayerStatsCounter::class.java, + Log.INFO, + "Inserting kill for $player ($playerUid, $npc_id, $count)" + ) + incrementKills( + playerUid, + (npc_id as String).toInt(), + count as Long + ) + } + } + progress++ + } + } + val json_rare_drops = data.get("rare_drops") + if (json_rare_drops != null && json_rare_drops is JSONObject) { + var progress = 1 + val totalPlayers = json_rare_drops.size + for ((player, rareDrops) in json_rare_drops.asIterable()) { + log( + PlayerStatsCounter::class.java, + Log.INFO, + "Porting rare drops for player $progress/$totalPlayers" + ) + if (player is String) { + val playerUid = resolveUIDFromPlayerUsername(player.replace(" ", "_")) + log( + PlayerStatsCounter::class.java, + Log.INFO, + "Player $player ($playerUid)" + ) + for ((item_id, count) in (rareDrops as JSONObject).asIterable()) { + log( + PlayerStatsCounter::class.java, + Log.INFO, + "Inserting rare drop for $player ($playerUid, $item_id, $count)" + ) + incrementRareDrop( + playerUid, + (item_id as String).toInt(), + count as Long + ) + } + } + progress++ + } + } + } catch (e: Exception) { + log(this::class.java, Log.ERR, "Failed parsing ${file.name} - stack trace below.") + e.printStackTrace() + } + } + + private val killsTableDefinition = """ + CREATE TABLE kills( + player_uid INTEGER, + entity_id INTEGER, + kills INTEGER, + PRIMARY KEY(player_uid, entity_id)) + """.trimIndent() + + private val rareDropsTableDefinition = """ + CREATE TABLE rare_drops( + player_uid INTEGER, + item_id INTEGER, + amount INTEGER, + PRIMARY KEY (player_uid, item_id)) + """.trimIndent() + + private val getKillsRowCountSql = """ + SELECT COUNT(1) FROM kills; + """.trimIndent() + + private val insertOrIncrementKillSql = """ + INSERT INTO kills (player_uid, entity_id, kills) + VALUES (?, ?, ?) + ON CONFLICT(player_uid, entity_id) DO UPDATE SET kills = kills + ?; + """.trimIndent() + + private val insertOrIncrementRareDropSql = """ + INSERT INTO rare_drops (player_uid, item_id, amount) + VALUES (?, ?, ?) + ON CONFLICT(player_uid, item_id) DO UPDATE SET amount = amount + ?; + """.trimIndent() + + private val getRareDropsSql = """ + SELECT amount FROM rare_drops WHERE player_uid=? AND item_id=? + """.trimIndent() + + private fun createGetKillsSql(countOfEntitiesToSearchFor: Int): String { + if (countOfEntitiesToSearchFor < 1) { + throw Exception("Should be at least 1") + } + val entitySearchParameterMarkers = "?" + ",?".repeat(countOfEntitiesToSearchFor - 1) + return """ + SELECT SUM(kills) FROM kills WHERE player_uid=? AND entity_id IN ($entitySearchParameterMarkers) + """.trimIndent() + } + + private val expectedTables = hashMapOf( + "kills" to killsTableDefinition, + "rare_drops" to rareDropsTableDefinition, + ) + + private fun tableHasData(): Boolean { + var hasData = false + db.run { conn -> + val statement = conn.prepareStatement(getKillsRowCountSql) + val result = statement.executeQuery() + if (result.next()) { + val rowCount = result.getInt(1) + hasData = rowCount > 0 + } + } + return hasData + } + + @JvmStatic + private fun incrementKills(playerUid: Int, npcId: Int, kills: Long) { + db.run { conn -> + val statement = conn.prepareStatement(insertOrIncrementKillSql) + statement.setInt(1, playerUid) + statement.setInt(2, npcId) + statement.setLong(3, kills) + statement.setLong(4, kills) + statement.execute() + } + } + + @JvmStatic + fun incrementKills(player: Player, npcId: Int) { + incrementKills(player.details.uid, npcId, 1) + } + + @JvmStatic + private fun incrementRareDrop(playerUid: Int, itemId: Int, amount: Long) { + db.run { conn -> + val statement = conn.prepareStatement(insertOrIncrementRareDropSql) + statement.setInt(1, playerUid) + statement.setInt(2, itemId) + statement.setLong(3, amount) + statement.setLong(4, amount) + statement.execute() + } + } + + @JvmStatic + fun incrementRareDrop(player: Player, item: Item) { + incrementRareDrop(player.details.uid, item.id, item.amount.toLong()) + } + + @JvmStatic + fun getKills(player: Player, npcIds: IntArray): Long { + var kills: Long = 0 + db.run { conn -> + val statement = conn.prepareStatement(createGetKillsSql(npcIds.size)) + statement.setInt(1, player.details.uid) + for (npcIdParamIndex in npcIds.indices) { + // +2 because the statement parameterIndexes start at 1 and the first param is the player uid + statement.setInt(npcIdParamIndex + 2, npcIds[npcIdParamIndex]) + } + val result = statement.executeQuery() + if (result.next()) { + kills = result.getLong(1) + } + } + return kills + } + + @JvmStatic + fun getRareDrops(player: Player, itemId: Int): Long { + var rareDropsForItem: Long = 0 + db.run { conn -> + val statement = conn.prepareStatement(getRareDropsSql) + statement.setInt(1, player.details.uid) + statement.setInt(2, itemId) + val result = statement.executeQuery() + if (result.next()) { + rareDropsForItem = result.getLong(1) + } + } + return rareDropsForItem + } + } +} diff --git a/Server/src/main/core/api/utils/WeightBasedTable.kt b/Server/src/main/core/api/utils/WeightBasedTable.kt index 79466bffe..a890d4d15 100644 --- a/Server/src/main/core/api/utils/WeightBasedTable.kt +++ b/Server/src/main/core/api/utils/WeightBasedTable.kt @@ -42,27 +42,48 @@ open class WeightBasedTable : ArrayList() { return roll(receiver, 1) } - open fun roll(receiver: Entity? = null, times: Int = 1): ArrayList{ - val items = ArrayList((guaranteedItems.size + 1) * times) + open fun roll(receiver: Entity?, times: Int = 1): ArrayList { + val weightedItemsToDrop = ArrayList() - for (i in 0 until times) { - items.addAll(guaranteedItems) + repeat(times) { + // Guaranteed items + for (item in guaranteedItems) { + if (isSpecialSlot(item.id)) { + val rolls = RandomFunction.random(item.minAmt, item.maxAmt) + repeat(rolls) { + weightedItemsToDrop.add(item) + } + } else { + // Add just one instance with amount set by getItem() + weightedItemsToDrop.add(item) + } + } - if (size == 1) { - items.add(get(0)) - } else if (isNotEmpty()) { + // Weighted roll (one per table roll) + if (isNotEmpty()) { var rngWeight = RandomFunction.randomDouble(totalWeight) for (item in this) { rngWeight -= item.weight if (rngWeight <= 0) { - items.add(item) + if (isSpecialSlot(item.id)) { + val rolls = RandomFunction.random(item.minAmt, item.maxAmt) + repeat(rolls) { + weightedItemsToDrop.add(item) + } + } else { + weightedItemsToDrop.add(item) + } break } } } } - return convertWeightedItems(items, receiver) + return convertWeightedItems(weightedItemsToDrop, receiver) + } + + private fun isSpecialSlot(id: Int): Boolean { + return id == SLOT_HDT || id == SLOT_USDT // extend this if a drop table is not dropping multiple times } fun convertWeightedItems(weightedItems: ArrayList, receiver: Entity?): ArrayList { diff --git a/Server/src/main/core/cache/def/impl/ClothDefinition.java b/Server/src/main/core/cache/def/impl/ClothDefinition.java index 27c63cc53..c100c16ef 100644 --- a/Server/src/main/core/cache/def/impl/ClothDefinition.java +++ b/Server/src/main/core/cache/def/impl/ClothDefinition.java @@ -1,7 +1,6 @@ package core.cache.def.impl; import java.nio.ByteBuffer; -import java.util.Arrays; import core.ServerConstants; import core.cache.Cache; diff --git a/Server/src/main/core/cache/def/impl/ItemDefinition.java b/Server/src/main/core/cache/def/impl/ItemDefinition.java index 60baf51ea..b7c17e5ec 100644 --- a/Server/src/main/core/cache/def/impl/ItemDefinition.java +++ b/Server/src/main/core/cache/def/impl/ItemDefinition.java @@ -1,6 +1,5 @@ package core.cache.def.impl; -import core.ServerConstants; import core.api.EquipmentSlot; import core.cache.Cache; import core.cache.def.Definition; @@ -12,17 +11,12 @@ import core.game.node.entity.skill.Skills; import core.game.node.item.Item; import core.game.node.item.ItemPlugin; import core.game.world.GameWorld; -import core.net.packet.PacketRepository; -import core.net.packet.out.WeightUpdate; -import core.plugin.Plugin; import core.tools.Log; import core.tools.StringUtils; -import core.tools.SystemLogger; import core.game.system.config.ItemConfigParser; import org.rs09.consts.Items; import java.nio.ByteBuffer; -import java.text.DecimalFormat; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -33,7 +27,6 @@ import static core.api.ContentAPIKt.log; /** * Represents an item's definitions. - * @author Jagex * @author Emperor */ public class ItemDefinition extends Definition { @@ -611,8 +604,12 @@ public class ItemDefinition extends Definition { * @return {@code True} if so. */ public static boolean canEnterEntrana(Player player) { - Container[] container = new Container[] { player.getInventory(), player.getEquipment() }; - for (Container c : container) { + Container[] containers = new Container[] { player.getInventory(), player.getEquipment() }; + if (player.getFamiliarManager().hasFamiliar() && !player.getFamiliarManager().hasPet()) { + return false; + } + + for (Container c : containers) { for (Item i : c.toArray()) { if (i == null) { continue; @@ -651,6 +648,7 @@ public class ItemDefinition extends Definition { Items.BOOK_OF_BALANCE_3844, Items.DAMAGED_BOOK_3843, Items.WIZARD_BOOTS_2579, + Items.COMBAT_BRACELET_11126, Items.COMBAT_BRACELET1_11124, Items.COMBAT_BRACELET2_11122, Items.COMBAT_BRACELET3_11120, @@ -666,8 +664,28 @@ public class ItemDefinition extends Definition { Items.HAM_HOOD_4302, Items.HAM_CLOAK_4304, Items.HAM_LOGO_4306, - Items.GLOVES_4308, - Items.BOOTS_4310 + Items.ZAMORAK_ROBE_1033, + Items.ZAMORAK_ROBE_1035, + Items.PRIEST_GOWN_426, + Items.PRIEST_GOWN_428, + Items.DRUIDS_ROBE_538, + Items.DRUIDS_ROBE_540, + Items.FLOWERS_2460, + Items.FLOWERS_2462, + Items.FLOWERS_2464, + Items.FLOWERS_2466, + Items.FLOWERS_2468, + Items.FLOWERS_2470, + Items.FLOWERS_2472, + Items.FLOWERS_2474, + Items.FLOWERS_2476, + Items.FIXED_DEVICE_6082, + Items.GHOSTLY_BOOTS_6106, + Items.GHOSTLY_ROBE_6107, + Items.GHOSTLY_ROBE_6108, + Items.GHOSTLY_HOOD_6109, + Items.GHOSTLY_GLOVES_6110, + Items.GHOSTLY_CLOAK_6111 )); private static final HashSet entranaBannedItems = new HashSet(Arrays.asList( /**Items.BUTTERFLY_NET_10010, easing the restriction until barehanded implementation**/ @@ -676,15 +694,10 @@ public class ItemDefinition extends Definition { Items.CANNON_BASE_6, Items.CANNON_STAND_8, Items.CANNON_FURNACE_12, - Items.COOKING_GAUNTLETS_775, - Items.CHAOS_GAUNTLETS_777, - Items.GOLDSMITH_GAUNTLETS_776, - Items.KARAMJA_GLOVES_1_11136, - Items.KARAMJA_GLOVES_2_11138, - Items.KARAMJA_GLOVES_3_11140, - Items.VYREWATCH_TOP_9634, - Items.VYREWATCH_LEGS_9636, - Items.VYREWATCH_SHOES_9638 + Items.ZAMORAK_STOLE_10474, + Items.EXPLORERS_RING_1_13560, + Items.EXPLORERS_RING_2_13561, + Items.EXPLORERS_RING_3_13562 )); @@ -699,11 +712,11 @@ public class ItemDefinition extends Definition { if (entranaBannedItems.contains(getId())) { return false; } - if (equipSlot(getId()) == EquipmentSlot.AMMO) { + if (equipSlot(getId()) == EquipmentSlot.AMMO || equipSlot(getId()) == EquipmentSlot.NECK || equipSlot(getId()) == EquipmentSlot.RING) { return true; } - if (getName().toLowerCase().startsWith("ring") || getName().toLowerCase().startsWith("amulet")) { - return true; + if (hasAction("summon")) { + return false; } int[] bonuses = getConfiguration(ItemConfigParser.BONUS); return bonuses == null || Arrays.stream(bonuses).allMatch(x -> x == 0); @@ -1543,27 +1556,6 @@ public class ItemDefinition extends Definition { */ private static final String[] BONUS_NAMES = { "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Summoning: ", "Strength: ", "Prayer: " }; - /** - * Updates the equipment stats interface. - * @param player The player to update for. - */ - public static void statsUpdate(Player player) { - if (!player.getAttribute("equip_stats_open", false)) { - return; - } - int index = 0; - int[] bonuses = player.getProperties().getBonuses(); - for (int i = 36; i < 50; i++) { - if (i == 47) { - continue; - } - int bonus = bonuses[index]; - String bonusValue = bonus > -1 ? ("+" + bonus) : Integer.toString(bonus); - player.getPacketDispatch().sendString(BONUS_NAMES[index++] + bonusValue, 667, i); - } - player.getPacketDispatch().sendString("Attack bonus", 667, 34); - } - /** * Checks if it has a plugin. * @return {@code True} if so. diff --git a/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java b/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java index f68060e46..881e4040d 100644 --- a/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java +++ b/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java @@ -2,7 +2,6 @@ package core.cache.def.impl; import core.cache.Cache; import core.tools.Log; -import core.tools.SystemLogger; import core.game.world.GameWorld; import java.lang.reflect.Array; diff --git a/Server/src/main/core/cache/def/impl/SceneryDefinition.java b/Server/src/main/core/cache/def/impl/SceneryDefinition.java index 60ba83f99..6172d7828 100644 --- a/Server/src/main/core/cache/def/impl/SceneryDefinition.java +++ b/Server/src/main/core/cache/def/impl/SceneryDefinition.java @@ -7,7 +7,6 @@ import core.game.interaction.OptionHandler; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.tools.Log; -import core.tools.SystemLogger; import core.game.world.GameWorld; import java.nio.ByteBuffer; diff --git a/Server/src/main/core/game/activity/Cutscene.kt b/Server/src/main/core/game/activity/Cutscene.kt index 68c4bba85..a75dcb4b5 100644 --- a/Server/src/main/core/game/activity/Cutscene.kt +++ b/Server/src/main/core/game/activity/Cutscene.kt @@ -57,6 +57,15 @@ abstract class Cutscene(val player: Player) { base = region.baseLocation } + /** + * Immediately closes the player's overlay. + */ + fun closeOverlay() + { + logCutscene("Close ${player.username}'s overlay.") + player.interfaceManager.closeOverlay() + } + /** * Fade the player's view to black. This process can be safely assumed to take about 8 ticks to complete. */ @@ -113,14 +122,38 @@ abstract class Cutscene(val player: Player) { * @param expression the FacialExpression the NPC should use * @param message the message to send * @param onContinue (optional) a method that runs when the dialogue is "continued." Increments the cutscene stage by default. + * @param hide Should the continue button be hidden? */ - fun dialogueUpdate(npcId: Int, expression: core.game.dialogue.FacialExpression, message: String, onContinue: () -> Unit = {incrementStage()}) + fun dialogueUpdate(npcId: Int, expression: core.game.dialogue.FacialExpression, message: String, onContinue: () -> Unit = {incrementStage()}, hide: Boolean = false) { logCutscene("Sending NPC dialogue update.") - sendNPCDialogue(player, npcId, message, expression) + sendNPCDialogue(player, npcId, message, expression, hide) player.dialogueInterpreter.addAction { _,_ -> onContinue.invoke() } } + /** + * Sends a dialogue to the player using the given NPC ID, which updates the cutscene stage by default when continued. + * @param npcId the ID of the NPC to send a dialogue for + * @param expression the FacialExpression the NPC should use + * @param message the message to send + * @param onContinue (optional) a method that runs when the dialogue is "continued." Increments the cutscene stage by default. + */ + fun dialogueLinesUpdate(npcId: Int, expression: core.game.dialogue.FacialExpression, vararg message: String, onContinue: () -> Unit = {incrementStage()}) + { + logCutscene("Sending NPC dialogue lines update.") + sendNPCDialogueLines(player, npcId, expression, true, *message) + player.dialogueInterpreter.addAction { _,_ -> onContinue.invoke() } + } + + /** + * Forces the dialogue to close. + */ + fun dialogueClose() + { + logCutscene("Sending dialogue close.") + closeDialogue(player) + } + /** * Sends a non-NPC dialogue to the player, which updates the cutscene stage by default when continued * @param message the message to send @@ -133,6 +166,18 @@ abstract class Cutscene(val player: Player) { player.dialogueInterpreter.addAction {_,_ -> onContinue.invoke()} } + /** + * Sends a non-NPC dialogue to the player, which updates the cutscene stage by default when continued + * @param message the message to send + * @param onContinue (optional) a method that runs when the dialogue is "continued." Increments the cutscene stage by default. + */ + fun dialogueLinesUpdate(vararg message: String, onContinue: () -> Unit = {incrementStage()}) + { + logCutscene("Sending standard dialogue lines update.") + sendDialogueLines(player, *message) + player.dialogueInterpreter.addAction {_,_ -> onContinue.invoke()} + } + /** * Sends a player dialogue, which updates the cutscene stage by default when continued * @param expression the FacialExpression to use @@ -223,13 +268,19 @@ abstract class Cutscene(val player: Player) { logCutscene("Added NPC $id at location LOCAL[$regionX,$regionY,$plane] GLOBAL[${npc.location.x},${npc.location.y},$plane]") } - fun start() + fun start(){ + start(true) + } + + fun start(hideMiniMap: Boolean) { logCutscene("Starting cutscene for ${player.username}.") region = RegionManager.forId(player.location.regionId) base = RegionManager.forId(player.location.regionId).baseLocation setup() - PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + if (hideMiniMap) { + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + } runStage(player.getCutsceneStage()) setAttribute(player, ATTRIBUTE_CUTSCENE, this) setAttribute(player, ATTRIBUTE_CUTSCENE_STAGE, 0) @@ -242,22 +293,21 @@ abstract class Cutscene(val player: Player) { AntiMacro.pause(player) } + /** - * Ends this cutscene, fading the screen to black, teleporting the player to the exit location, and then fading it back in and executing the endActions passed to this method. + * Ends this cutscene, teleporting the player to the exit location, and then fading it back in and executing the endActions passed to this method. * @param endActions (optional) a method that executes when the cutscene fully completes */ - fun end(endActions: (() -> Unit)? = null) + fun endWithoutFade(endActions: (() -> Unit)? = null) { ended = true - fadeToBlack() GameWorld.Pulser.submit(object : Pulse(){ var tick: Int = 0 override fun pulse(): Boolean { when(tick++) { - 8 -> player.properties.teleportLocation = exitLocation - 9 -> fadeFromBlack() - 16 -> { + 0 -> player.properties.teleportLocation = exitLocation + 1 -> { return true } } @@ -288,6 +338,54 @@ abstract class Cutscene(val player: Player) { }) } + /** + * Ends this cutscene, fading the screen to black, teleporting the player to the exit location, and then fading it back in and executing the endActions passed to this method. + * @param fade (optional) should the cutscene fade to black? + * @param endActions (optional) a method that executes when the cutscene fully completes + */ + fun end(fade: Boolean = true, endActions: (() -> Unit)? = null) + { + ended = true + if (fade) fadeToBlack() + GameWorld.Pulser.submit(object : Pulse(){ + var tick: Int = 0 + override fun pulse(): Boolean { + if (fade) { + when (tick++) { + 8 -> player.properties.teleportLocation = exitLocation + 9 -> fadeFromBlack() + 16 -> return true + else -> return false + } + } + else player.properties.teleportLocation = exitLocation + return true + } + + override fun stop() { + super.stop() + player ?: return + player.removeAttribute(ATTRIBUTE_CUTSCENE) + player.removeAttribute(ATTRIBUTE_CUTSCENE_STAGE) + player.properties.isSafeZone = false + player.properties.safeRespawn = ServerConstants.HOME_LOCATION + player.interfaceManager.restoreTabs() + player.unlock() + clearNPCs() + player.unhook(CUTSCENE_DEATH_HOOK) + player.logoutListeners.remove("cutscene") + AntiMacro.unpause(player) + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + try { + endActions?.invoke() + } catch (e: Exception) { + log(this::class.java, Log.ERR, "There's some bad nasty code in ${this::class.java.simpleName} end actions!") + e.printStackTrace() + } + } + }) + } + /** * Moves the camera to the specified regionX and regionY * @param regionX the region-local X coordinate to move the camera to (0-63) diff --git a/Server/src/main/core/game/bots/CombatBotAssembler.kt b/Server/src/main/core/game/bots/CombatBotAssembler.kt index 7655d98e9..ebbed1402 100644 --- a/Server/src/main/core/game/bots/CombatBotAssembler.kt +++ b/Server/src/main/core/game/bots/CombatBotAssembler.kt @@ -53,7 +53,7 @@ class CombatBotAssembler { val bot = CombatBot(location) generateStats(bot, tier, Skills.RANGE, Skills.DEFENCE) - gearRangedBot(bot, crossbow ?: Random().nextInt() % 2 == 0) + gearRangedBot(bot, (crossbow ?: (Random().nextInt() % 2)) == 0) return bot } @@ -82,7 +82,7 @@ class CombatBotAssembler { fun MeleeAdventurer(tier: Tier, location: Location): CombatBot { val bot = CombatBot(location) var max = 0 - val level = RandomFunction.random(25, 65).also {max = 99 } + val level = RandomFunction.random(25, 69).also {max = 99 } generateStats(bot,tier,Skills.ATTACK, Skills.STRENGTH, Skills.DEFENCE) bot.skills.setStaticLevel(Skills.HITPOINTS, level) bot.skills.setStaticLevel(Skills.ATTACK, level + 5) @@ -114,7 +114,7 @@ class CombatBotAssembler { fun RangeAdventurer(tier: Tier, location: Location): CombatBot { val bot = CombatBot(location) var max = 0 - val level = RandomFunction.random(35, 65).also {max = 75 } + val level = RandomFunction.random(35, 69).also {max = 75 } generateStats(bot,tier,Skills.ATTACK, Skills.STRENGTH) bot.skills.setStaticLevel(Skills.HITPOINTS, level) bot.skills.setStaticLevel(Skills.DEFENCE, level) diff --git a/Server/src/main/core/game/bots/GeneralBotCreator.kt b/Server/src/main/core/game/bots/GeneralBotCreator.kt index 361247a36..7e3cb5478 100644 --- a/Server/src/main/core/game/bots/GeneralBotCreator.kt +++ b/Server/src/main/core/game/bots/GeneralBotCreator.kt @@ -98,7 +98,7 @@ class GeneralBotCreator { }*/ if(!botScript.running) return true //has to be separated this way or it double-submits the respawn pulse. - if (botPulsesTriggeredThisTick++ >= 50) + if (botPulsesTriggeredThisTick++ >= 75) return false val idleRoll = RandomFunction.random(10) diff --git a/Server/src/main/core/game/bots/Script.java b/Server/src/main/core/game/bots/Script.java index cda961420..9c5da330a 100644 --- a/Server/src/main/core/game/bots/Script.java +++ b/Server/src/main/core/game/bots/Script.java @@ -1,5 +1,6 @@ package core.game.bots; +import content.data.Quests; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -13,7 +14,7 @@ public abstract class Script { public ArrayList inventory = new ArrayList<>(20); public ArrayList equipment = new ArrayList<>(20); public Map skills = new HashMap<>(); - public ArrayList quests = new ArrayList<>(20); + public ArrayList quests = new ArrayList<>(20); public Player bot; @@ -31,7 +32,7 @@ public abstract class Script { for (Map.Entry skill : skills.entrySet()) { setLevel(skill.getKey(), skill.getValue()); } - for (String quest : quests) { + for (Quests quest : quests) { bot.getQuestRepository().setStage(bot.getQuestRepository().getQuest(quest), 100); } for (Item i : equipment) { diff --git a/Server/src/main/core/game/bots/ScriptAPI.kt b/Server/src/main/core/game/bots/ScriptAPI.kt index 85ac568c1..5a3306e4c 100644 --- a/Server/src/main/core/game/bots/ScriptAPI.kt +++ b/Server/src/main/core/game/bots/ScriptAPI.kt @@ -54,6 +54,7 @@ import kotlin.math.pow import kotlin.math.sqrt import core.ServerConstants import core.api.utils.Vector +import kotlin.random.Random class ScriptAPI(private val bot: Player) { val GRAPHICSUP = Graphics(1576) @@ -396,6 +397,20 @@ class ScriptAPI(private val bot: Player) { } } + /** + * @param location the location you want the coordinates randomized for. + * @param xMin the minimum range value X coordinates should be randomized by, must be xMin <= xMax ex: -1 min 1 max. + * @param xMax the maximum range value X coordinates should be randomized by, must be xMin <= xMax ex: -1 min 1 max. + * @param yMin the minimum range value Y coordinates should be randomized by, must be yMin <= yMax ex: -1 min 1 max. + * @param yMax the maximum range value Y coordinates should be randomized by, must be yMin <= yMax ex: -1 min 1 max. + * @param staticZ this value is static and does not change from what is given, must be actual Z value of location. + * @author Kermit + */ + fun randomizeLocationInRanges(location: Location, xMin: Int, xMax: Int, yMin: Int, yMax: Int, staticZ: Int): Location { + val newX = location.x + Random.nextInt(xMin, xMax) + val newY = location.y + Random.nextInt(yMin, yMax) + return Location(newX, newY, staticZ) + } /** * The iterator for long-distance walking. Limited by doors and large obstacles like mountains. @@ -553,7 +568,7 @@ class ScriptAPI(private val bot: Player) { 1517 -> continue 1519 -> continue 1521 -> continue - else -> Repository.sendNews(SERVER_GE_NAME + " just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") + else -> sendNews(SERVER_GE_NAME + " just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.lowercase() + " on the GE.") } } bot.bank.remove(item) @@ -562,7 +577,35 @@ class ScriptAPI(private val bot: Player) { return true } } - bot.pulseManager.run(toCounterPulseAll()) + if (ge != null) { + bot.pulseManager.run(toCounterPulseAll()) + } + } + + /** + * Function to bank all items that are not excluded at a nearby bank. + * @author Kermit & Ceikry + */ + fun depositAtBank(){ + val bank: Scenery? = getNearestNode("Bank booth", true) as Scenery? + class BankingPulse : MovementPulse(bot, bank, DestinationFlag.OBJECT) { + override fun pulse(): Boolean { + bot.faceLocation(bank?.location) + for (item in bot.inventory.toArray()) { + item ?: continue + when (item.id) { + Items.RUNE_AXE_1359, Items.TINDERBOX_590, Items.ADAMANT_PICKAXE_1271, Items.COINS_995 -> continue + } + bot.bank.add(item) + bot.inventory.remove(item) + } +// log(this::class.java, Log.FINE, "${bot.username} Just finished banking at ${bot.location} || Bank contents: ${bot.bank}") + return true + } + } + if (bank != null) { + bot.pulseManager.run(BankingPulse()) + } } /** diff --git a/Server/src/main/core/game/container/Container.java b/Server/src/main/core/game/container/Container.java index f0d512069..5cecb6905 100644 --- a/Server/src/main/core/game/container/Container.java +++ b/Server/src/main/core/game/container/Container.java @@ -8,7 +8,6 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.rs09.consts.Items; -import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -221,7 +220,7 @@ public class Container { public boolean add(Item item, boolean fireListener, int preferredSlot) { item = item.copy(); int maximum = getMaximumAdd(item); - if (maximum == 0 || (item.getDefinition().isStackable() && item.getAmount() == 0)) { + if (maximum == 0) { return false; } if (preferredSlot > -1 && items[preferredSlot] != null) { @@ -560,30 +559,6 @@ public class Container { return -1; } - /** - * Parses the container data from the byte buffer. - * - * @param buffer The byte buffer. - * @return The total value of all items (G.E price > Store price > High - * alchemy price). - */ - public int parse(ByteBuffer buffer) { - int slot; - int total = 0; - while ((slot = buffer.getShort()) != -1) { - int id = buffer.getShort() & 0xFFFF; - int amount = buffer.getInt(); - int charge = buffer.getInt(); - if (id >= ItemDefinition.getDefinitions().size() || slot >= items.length || slot < 0) { - continue; - } - Item item = items[slot] = new Item(id, amount, charge); - item.setIndex(slot); - total += item.getValue(); - } - return total; - } - public void parse(JSONArray itemArray){ AtomicInteger total = new AtomicInteger(0); itemArray.forEach(item -> { @@ -601,30 +576,6 @@ public class Container { }); } - /** - * Saves the item data on the byte buffer. - * - * @param buffer The byte buffer. - * @return The total value of all items (G.E price > Store price > High - * alchemy price). - */ - public long save(ByteBuffer buffer) { - long totalValue = 0; - for (int i = 0; i < items.length; i++) { - Item item = items[i]; - if (item == null) { - continue; - } - buffer.putShort((short) i); - buffer.putShort((short) item.getId()); - buffer.putInt(item.getAmount()); - buffer.putInt(item.getCharge()); - totalValue += item.getValue(); - } - buffer.putShort((short) -1); - return totalValue; - } - /** * Copies the container to this container. * diff --git a/Server/src/main/core/game/container/impl/BankContainer.java b/Server/src/main/core/game/container/impl/BankContainer.java index 7c7bd1ddc..5f8a51215 100644 --- a/Server/src/main/core/game/container/impl/BankContainer.java +++ b/Server/src/main/core/game/container/impl/BankContainer.java @@ -2,6 +2,7 @@ package core.game.container.impl; import core.api.IfaceSettingsBuilder; import core.game.container.access.InterfaceContainer; +import kotlin.Unit; import kotlin.ranges.IntRange; import org.rs09.consts.Vars; import core.ServerConstants; @@ -129,15 +130,12 @@ public final class BankContainer extends Container { }); player.getInterfaceManager().openSingleTab(new Component(763)); super.refresh(); - player.getInventory().getListeners().add(listener); player.getInventory().refresh(); - setVarp(player, 1249, lastAmountX); + player.getInventory().getListeners().add(listener); + setVarp(player, 1249, lastAmountX); int settings = new IfaceSettingsBuilder().enableOptions(new IntRange(0, 5)).enableExamine().enableSlotSwitch().build(); player.getPacketDispatch().sendIfaceSettings(settings, 0, 763, 0, 27); - player.getPacketDispatch().sendRunScript(1451, ""); open = true; - setTabConfigurations(); - sendBankSpace(); } public void open(Player player) { @@ -155,38 +153,16 @@ public final class BankContainer extends Container { BankContainer.this.close(); return true; }); - refresh(listener); player.getInterfaceManager().openSingleTab(new Component(763)); - player.getInventory().getListeners().add(player.getBank().listener); + refresh(listener); player.getInventory().refresh(); - setVarp(player, 1249, lastAmountX); + player.getInventory().getListeners().add(player.getBank().listener); + setVarp(player, 1249, lastAmountX); player.getPacketDispatch().sendIfaceSettings(1278, 73, 762, 0, SIZE); int settings = new IfaceSettingsBuilder().enableOptions(new IntRange(0,5)).enableExamine().enableSlotSwitch().build(); player.getPacketDispatch().sendIfaceSettings(settings, 0, 763, 0, 27); player.getPacketDispatch().sendRunScript(1451, ""); open = true; - this.player.getBank().setTabConfigurations(player); - - } - - @Override - public long save(ByteBuffer buffer) { - buffer.putInt(lastAmountX); - buffer.put((byte) tabStartSlot.length); - for (int j : tabStartSlot) { - buffer.putShort((short) j); - } - return super.save(buffer); - } - - @Override - public int parse(ByteBuffer buffer) { - lastAmountX = buffer.getInt(); - int length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - tabStartSlot[i] = buffer.getShort(); - } - return super.parse(buffer); } /** @@ -242,7 +218,7 @@ public final class BankContainer extends Container { } } - if (player.getInventory().remove(item, slot, true)) { + if (player.getInventory().remove(item, slot, false)) { int preferredSlot = -1; if (tabIndex != 0 && tabIndex != 10 && !super.contains(add.getId(), 1)) { preferredSlot = tabStartSlot[tabIndex] + getItemsInTab(tabIndex); @@ -250,21 +226,9 @@ public final class BankContainer extends Container { increaseTabStartSlots(tabIndex); } super.add(add, true, preferredSlot); - setTabConfigurations(); + player.getInventory().update(); } } - - /** - * Re-opens the bank interface. - */ - public void reopen() { - if (!open) { - return; - } - player.getInterfaceManager().close(); - open(); - refresh(); - } /** * Takes a item from the bank container and adds one to the inventory @@ -300,17 +264,14 @@ public final class BankContainer extends Container { add = item; } if (super.remove(item, slot, false)) { - player.getInventory().add(add); + player.getInventory().add(add, false); } - int tabId = getTabByItemSlot(slot); if (get(slot) == null) { + int tabId = getTabByItemSlot(slot); decreaseTabStartSlots(tabId); - } - setTabConfigurations(); - shift(); - if (player.getAttribute("search", false)) { - reopen(); - } + shift(); + } else update(); + player.getInventory().update(); } /** @@ -319,7 +280,7 @@ public final class BankContainer extends Container { */ public void updateLastAmountX(int amount) { this.lastAmountX = amount; - setVarp(player, 1249, amount); + setVarp(player, 1249, amount); } /** @@ -386,8 +347,7 @@ public final class BankContainer extends Container { * Sends the bank space values on the interface. */ public void sendBankSpace() { - player.getPacketDispatch().sendString(Integer.toString(capacity() - freeSlots()), 762, 97); - player.getPacketDispatch().sendString(Integer.toString(capacity()), 762, 98); + setVarc(player, 192, capacity() - freeSlots()); } /** @@ -411,47 +371,17 @@ public final class BankContainer extends Container { replace(tempTabItems[i], slot, false); } refresh(); //We only refresh once. - setTabConfigurations(); } /** * Sets the tab configs. */ public void setTabConfigurations() { - int value = getItemsInTab(1); - value += getItemsInTab(2) << 10; - value += getItemsInTab(3) << 20; - setVarp(player, 1246, value); - value = getItemsInTab(4); - value += getItemsInTab(5) << 10; - value += getItemsInTab(6) << 20; - setVarp(player, 1247, value); - value = -2013265920; - value += (134217728 * (tabIndex == 10 ? 0 : tabIndex)); - value += getItemsInTab(7); - value += getItemsInTab(8) << 10; - setVarp(player, 1248, value); + for (int i = 0; i < 8; i++) { + setVarbit(player, 4885 + i, getItemsInTab(i + 1)); + } } - - /** - * Sets the tab configs. - */ - public void setTabConfigurations(Player player) { - int value = getItemsInTab(1); - value += getItemsInTab(2) << 10; - value += getItemsInTab(3) << 20; - setVarp(player, 1246, value); - value = getItemsInTab(4); - value += getItemsInTab(5) << 10; - value += getItemsInTab(6) << 20; - setVarp(player, 1247, value); - value = -2013265920; - value += (134217728 * (tabIndex == 10 ? 0 : tabIndex)); - value += getItemsInTab(7); - value += getItemsInTab(8) << 10; - setVarp(player, 1248, value); - } - + /** * Gets the amount of items in one tab. * @param tabId The tab index. @@ -483,7 +413,7 @@ public final class BankContainer extends Container { * @return If items have to be noted {@code true}. */ public boolean isNoteItems() { - return getVarbit(player, Vars.VARBIT_IFACE_BANK_NOTE_MODE) == 1; + return getVarbit(player, Vars.VARBIT_IFACE_BANK_NOTE_MODE) == 1; } /** @@ -491,7 +421,7 @@ public final class BankContainer extends Container { * @param noteItems If items have to be noted {@code true}. */ public void setNoteItems(boolean noteItems) { - setVarbit(player, Vars.VARBIT_IFACE_BANK_NOTE_MODE, noteItems ? 1 : 0, true); + setVarbit(player, Vars.VARBIT_IFACE_BANK_NOTE_MODE, noteItems ? 1 : 0, true); } /** @@ -515,14 +445,9 @@ public final class BankContainer extends Container { * @param tabIndex The tabIndex to set. */ public void setTabIndex(int tabIndex) { - this.tabIndex = tabIndex; - - /* - * Kludge to update the interface - * after dumping all to prevent - * "invisible" items in slots. - */ - update(true); + this.tabIndex = tabIndex == 0 ? 10 : tabIndex; + setVarbit(player, 4893, tabIndex + 1); + setAttribute(player, "bank:lasttab", tabIndex); } /** @@ -530,7 +455,7 @@ public final class BankContainer extends Container { * @param insertItems The insert items value. */ public void setInsertItems(boolean insertItems) { - setVarbit(player, Vars.VARBIT_IFACE_BANK_INSERT_MODE, insertItems ? 1 : 0, true); + setVarbit(player, Vars.VARBIT_IFACE_BANK_INSERT_MODE, insertItems ? 1 : 0, true); } /** @@ -538,7 +463,7 @@ public final class BankContainer extends Container { * @return {@code True} if inserting items mode is enabled. */ public boolean isInsertItems() { - return getVarbit(player, Vars.VARBIT_IFACE_BANK_INSERT_MODE) == 1; + return getVarbit(player, Vars.VARBIT_IFACE_BANK_INSERT_MODE) == 1; } /** @@ -572,22 +497,22 @@ public final class BankContainer extends Container { public void update(Container c, ContainerEvent event) { if (c instanceof BankContainer) { PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 762, 64000, 95, event.getItems(), false, event.getSlots())); + player.getBank().setTabConfigurations(); + player.getBank().sendBankSpace(); } else { PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 763, 64000, 93, event.getItems(), false, event.getSlots())); } - player.getBank().setTabConfigurations(); - player.getBank().sendBankSpace(); } @Override public void refresh(Container c) { if (c instanceof BankContainer) { PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 762, 64000, 95, c.toArray(), c.capacity(), false)); + player.getBank().setTabConfigurations(); + player.getBank().sendBankSpace(); } else { PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 763, 64000, 93, c.toArray(), 28, false)); } - player.getBank().setTabConfigurations(); - player.getBank().sendBankSpace(); } } } diff --git a/Server/src/main/core/game/container/impl/EquipmentContainer.java b/Server/src/main/core/game/container/impl/EquipmentContainer.java index 8ecc54bf5..d2eb70398 100644 --- a/Server/src/main/core/game/container/impl/EquipmentContainer.java +++ b/Server/src/main/core/game/container/impl/EquipmentContainer.java @@ -16,6 +16,7 @@ import core.plugin.Plugin; import org.jetbrains.annotations.Nullable; import core.game.interaction.InteractionListeners; import core.game.system.config.ItemConfigParser; +import org.rs09.consts.Components; import java.util.ArrayList; @@ -303,11 +304,11 @@ public final class EquipmentContainer extends Container { player.removeAttribute("dfs_spec"); player.getProperties().getCombatPulse().setHandler(null); if (!player.getSettings().isSpecialToggled()) { - setVarp(player, 301, 0); + setVarp(player, 301, 0); } } player.getAppearance().setAnimations(); - player.updateAppearance(); + player.updateAppearance(); player.getSettings().updateWeight(); updateBonuses(player); } @@ -323,9 +324,6 @@ public final class EquipmentContainer extends Container { if (item != null) { int[] bonus = item.getDefinition().getConfiguration(ItemConfigParser.BONUS, new int[15]); for (int i = 0; i < bonus.length; i++) { - if (i == 14 && bonuses[i] != 0) { - continue; - } bonuses[i] += bonus[i]; } } @@ -352,7 +350,9 @@ public final class EquipmentContainer extends Container { bonuses[10] += increase; } player.getProperties().setBonuses(bonuses); - update(player); + if (player.getInterfaceManager().hasMainComponent(Components.EQUIP_SCREEN2_667)) { + update(player); + } } /** @@ -360,9 +360,6 @@ public final class EquipmentContainer extends Container { * @param player The player to update for. */ public static void update(Player player) { - if (!player.getInterfaceManager().hasMainComponent(667)) { - return; - } int index = 0; int[] bonuses = player.getProperties().getBonuses(); for (int i = 36; i < 50; i++) { @@ -373,6 +370,7 @@ public final class EquipmentContainer extends Container { String bonusValue = bonus > -1 ? ("+" + bonus) : Integer.toString(bonus); player.getPacketDispatch().sendString(BONUS_NAMES[index++] + bonusValue, 667, i); } + player.getSettings().updateWeight(); player.getPacketDispatch().sendString("Attack bonus", 667, 34); } } diff --git a/Server/src/main/core/game/dialogue/DialogueBuilder.kt b/Server/src/main/core/game/dialogue/DialogueBuilder.kt index 454ea29a5..5b1f542c5 100644 --- a/Server/src/main/core/game/dialogue/DialogueBuilder.kt +++ b/Server/src/main/core/game/dialogue/DialogueBuilder.kt @@ -1,11 +1,12 @@ package core.game.dialogue +import content.data.Quests import core.api.splitLines import core.game.node.entity.player.Player import core.tools.END_DIALOGUE import java.util.regex.Pattern -val DEBUG_DIALOGUE = true +val DEBUG_DIALOGUE = false val NUMBER_PATTERN1 = Pattern.compile("^(\\d+) \\[label", Pattern.MULTILINE) val NUMBER_PATTERN2 = Pattern.compile("(\\d+) -> (\\d+)") @@ -295,6 +296,14 @@ class DialogueBuilder(var target: DialogueBuilderFile, var clauseIndex: Int = -1 } } + /** + * The first if-statement to a dialogue. At minimum, you must have "b.onPredicate { _ -> true }" + * PLEASE BE CAREFUL ABOUT HAVING COMPLEX PREDICATE. + * If at any point during a dialogue that the predicate is not satisfied, + * it will block further dialogue progression and any dialogue will suddenly disappear. + * e.g. onPredicate(x==2) but during dialogue you set x=3, dialogue after it will disappear. + * Think of this as a repeated filter at every dialogue step. + */ fun onPredicate(predicate: (player: Player) -> Boolean): DialogueBuilder { target.data.add(DialogueClause(predicate, ArrayList())) clauseIndex = target.data.size - 1 @@ -303,7 +312,7 @@ class DialogueBuilder(var target: DialogueBuilderFile, var clauseIndex: Int = -1 fun defaultDialogue(): DialogueBuilder { return onPredicate({ _ -> return@onPredicate true}) } - fun onQuestStages(name: String, vararg stages: Int): DialogueBuilder { + fun onQuestStages(name: Quests, vararg stages: Int): DialogueBuilder { return onPredicate() { player -> val questStage = player.questRepository.getStage(name) return@onPredicate stages.contains(questStage) diff --git a/Server/src/main/core/game/dialogue/DialogueFile.kt b/Server/src/main/core/game/dialogue/DialogueFile.kt index 92beaf0a6..c73e3dbab 100644 --- a/Server/src/main/core/game/dialogue/DialogueFile.kt +++ b/Server/src/main/core/game/dialogue/DialogueFile.kt @@ -108,8 +108,8 @@ abstract class DialogueFile { interpreter!!.sendDialogues(entity, expression, *messages) } - open fun options(vararg options: String?) { - interpreter!!.sendOptions("Select an Option", *options) + open fun options(vararg options: String?, title: String = "Select an Option") { + interpreter!!.sendOptions(title, *options) } /** @@ -151,7 +151,7 @@ abstract class DialogueFile { player?.dialogueInterpreter?.sendDialogue(*messages) } - fun showTopics(vararg topics: Topic<*>): Boolean { + fun showTopics(vararg topics: Topic<*>, title: String = "Select an Option"): Boolean { val validTopics = ArrayList() topics.filter { if(it is IfTopic) it.showCondition else true }.forEach { topic -> interpreter!!.activeTopics.add(topic) @@ -161,7 +161,7 @@ abstract class DialogueFile { return true } else if (validTopics.size == 1) { - val topic = topics[0] + val topic = topics.filter { it.text == validTopics[0] }[0] if(topic.toStage is DialogueFile) { val topicFile = topic.toStage as DialogueFile interpreter!!.dialogue.loadFile(topicFile) @@ -172,7 +172,7 @@ abstract class DialogueFile { interpreter!!.activeTopics.clear() return false } - else { options(*validTopics.toTypedArray()) + else { options(*validTopics.toTypedArray(), title = title) return false } } diff --git a/Server/src/main/core/game/dialogue/DialogueInterpreter.java b/Server/src/main/core/game/dialogue/DialogueInterpreter.java index 06a93a303..a64838e33 100644 --- a/Server/src/main/core/game/dialogue/DialogueInterpreter.java +++ b/Server/src/main/core/game/dialogue/DialogueInterpreter.java @@ -10,6 +10,7 @@ import core.game.node.entity.Entity; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import core.game.world.GameWorld; import core.net.packet.PacketRepository; import core.net.packet.context.ChildPositionContext; import core.net.packet.context.ContainerContext; @@ -325,61 +326,38 @@ public final class DialogueInterpreter { * @param itemId The item id. */ public Component sendItemMessage(int itemId, String... messages) { - if(1 <= messages.length && messages.length < 4) { - ArrayList packedMessages = new ArrayList(); - for(int i = 0; i < messages.length/2; i++) { - packedMessages.add(messages[i] + "
" + messages[i+1]); - } - if(messages.length % 2 == 1) { - packedMessages.add(messages[messages.length-1]); - } - - int interfaceId = 241 + (packedMessages.size() - 1); - player.getInterfaceManager().openChatbox(interfaceId); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, interfaceId, 1); - ItemDefinition itemDef = ItemDefinition.forId(itemId); - player.getPacketDispatch().sendAngleOnInterface(interfaceId, 1, itemDef.getModelZoom() / 2, itemDef.getModelRotationX(), itemDef.getModelRotationY()); - player.getPacketDispatch().sendString("", interfaceId, 3); - for(int i = 0; i < packedMessages.size(); i++) { - //System.out.printf("sendItemMessage[%d]: %s\n", i, packedMessages[i]); - player.getPacketDispatch().sendString(packedMessages.get(i), interfaceId, 4+i); - } - } else { - int interfaceId = 131; - //int interfaceId = 173; - //int interfaceId = 519; - //int interfaceId = 757; - //int interfaceId = 760; - player.getInterfaceManager().openChatbox(interfaceId); - String message = messages[0]; - for (int i = 1; i < messages.length; i++) { - message += "
" + messages[i]; - } - switch(interfaceId) { - case 131: - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, 131, 2); - break; - case 173: - player.getPacketDispatch().sendString(message, 173, 4); - player.getPacketDispatch().sendString("", 173, 3); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, 173, 1); - break; - case 519: - player.getPacketDispatch().sendString(message, 519, 1); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, 519, 0); - break; - case 757: - player.getPacketDispatch().sendString(message, 757, 2); - player.getPacketDispatch().sendString("", 757, 1); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, 757, 0); - break; - case 760: - player.getPacketDispatch().sendString(message, 760, 0); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, 760, 1); - break; - } + // Select interface based on number of messages - 241 (1 line) to 244 (4 lines) + int interfaceId = 240 + messages.length; + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, false); + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 2, false); + // Hide or empty the title, since double item messages do not have them. (Child 3) + player.getPacketDispatch().sendString("", interfaceId, 3); + // Loop and print messages on Child 4,5,6,7 based on messages count. + for(int i = 0; i < messages.length; i++) { + player.getPacketDispatch().sendString(messages[i], interfaceId, 4+i); } + // Set the first Item (child 1) + ItemDefinition itemDef = ItemDefinition.forId(itemId); + player.getPacketDispatch().sendItemOnInterface(itemId, 1, interfaceId, 2); + player.getPacketDispatch().sendAngleOnInterface(interfaceId, 2, itemDef.getModelZoom() / 2, itemDef.getModelRotationX(), itemDef.getModelRotationY()); + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 45, 46); + // Hide the second item which seems to be used for double items (child 1) + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, true); + // Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title) + player.getInterfaceManager().openChatbox(interfaceId); + // These are old interfaces which made no sense to use them. +// player.getPacketDispatch().sendString(message, 131, 1); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 131, 2); +// player.getPacketDispatch().sendString(message, 173, 4); +// player.getPacketDispatch().sendString("", 173, 3); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 173, 1); +// player.getPacketDispatch().sendString(message, 519, 1); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 519, 0); +// player.getPacketDispatch().sendString(message, 757, 2); +// player.getPacketDispatch().sendString("", 757, 1); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 757, 0); +// player.getPacketDispatch().sendString(message, 760, 0); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 760, 1); return player.getInterfaceManager().getChatbox(); } @@ -394,23 +372,40 @@ public final class DialogueInterpreter { * Send a message with an item next to it. * @param message The message. */ - public Component sendDoubleItemMessage(int first, int second, String message) { - player.getInterfaceManager().openChatbox(131); - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(first, 1, 131, 0); - player.getPacketDispatch().sendItemOnInterface(second, 1, 131, 2); - return player.getInterfaceManager().getChatbox(); + public Component sendDoubleItemMessage(int first, int second, String... message) { + return sendDoubleItemMessage(new Item(first), new Item(second), message); } /** - * Send a message with an item next to it. - * @param message The message. + * Send a message with two items next to it. + * Note that interface 241 to 244 contains 2 childs for images, which is used for sendDoubleItemMessage. + * @param first The first item to display. + * @param second The second item to display. + * @param messages The array of messages, one message per line. */ - public Component sendDoubleItemMessage(Item first, Item second, String message) { - player.getInterfaceManager().openChatbox(131); - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(first.getId(), first.getAmount(), 131, 0); - player.getPacketDispatch().sendItemOnInterface(second.getId(), second.getAmount(), 131, 2); + public Component sendDoubleItemMessage(Item first, Item second, String... messages) { + // Select interface based on number of messages - 241 (1 line) to 244 (4 lines) + int interfaceId = 240 + messages.length; + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, false); + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 2, false); + // Hide or empty the title, since double item messages do not have them. + player.getPacketDispatch().sendString("", interfaceId, 3); + // Loop and print messages on child 4,5,6,7 based on messages count. + for(int i = 0; i < messages.length; i++) { + player.getPacketDispatch().sendString(messages[i], interfaceId, 4+i); + } + // Set the first item + ItemDefinition itemDef = ItemDefinition.forId(first.getId()); + player.getPacketDispatch().sendItemOnInterface(first.getId(), first.getAmount(), interfaceId, 1); + player.getPacketDispatch().sendAngleOnInterface(interfaceId, 1, (int)(itemDef.getModelZoom() / 1.5), itemDef.getModelRotationX(), itemDef.getModelRotationY()); + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 1, 40, 40); + // Set the second items + ItemDefinition itemDef2 = ItemDefinition.forId(second.getId()); + player.getPacketDispatch().sendItemOnInterface(second.getId(), second.getAmount(), interfaceId, 2); + player.getPacketDispatch().sendAngleOnInterface(interfaceId, 2, (int)(itemDef2.getModelZoom() / 1.5), itemDef2.getModelRotationX(), itemDef2.getModelRotationY()); + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 60, 65); + // Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title) + player.getInterfaceManager().openChatbox(interfaceId); return player.getInterfaceManager().getChatbox(); } @@ -433,7 +428,7 @@ public final class DialogueInterpreter { * @return The chatbox component. */ public Component sendDialogues(Entity entity, int expression, String... messages) { - return sendDialogues(entity instanceof Player ? -1 : ((NPC) entity).getShownNPC(player).getId(), expression, messages); + return sendDialogues(entity instanceof Player ? -1 : ((NPC) entity).getShownNPC(player).getId(), expression, false, messages); } /** @@ -441,12 +436,11 @@ public final class DialogueInterpreter { * @param npcId The npc id. * @param expression The entity's facial expression. * @param messages The messages. - * @param hide the continue. + * @param hide should the continue button be hidden? * @return The chatbox component. */ public Component sendDialogues(int npcId, FacialExpression expression, boolean hide, String... messages) { - sendDialogues(npcId, expression == null ? -1 : expression.getAnimationId(), messages); - return player.getInterfaceManager().getChatbox(); + return sendDialogues(npcId, expression == null ? -1 : expression.getAnimationId(), hide, messages); } /** @@ -457,34 +451,18 @@ public final class DialogueInterpreter { * @return The chatbox component. */ public Component sendDialogues(Entity entity, FacialExpression expression, boolean hide, String... messages) { - sendDialogues(entity, expression, messages); - player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), 3, hide); - return player.getInterfaceManager().getChatbox(); + return sendDialogues(entity.getId(), expression == null ? -1 : expression.getAnimationId(), hide, messages); } /** * Send dialogues based on the amount of specified messages. * @param expression The entity's facial expression. * @param messages The messages. - * @param hide the continue. + * @param hide should the continue button be hidden? * @return The chatbox component. */ public Component sendDialogues(Entity entity, int expression, boolean hide, String... messages) { - sendDialogues(entity, expression, messages); - player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), 3, hide); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send dialogues based on the amount of specified messages. - * @param expression The entity's facial expression. - * @param messages The messages. - * @return The chatbox component. - */ - public Component sendDialogues(int npcId, int expression, boolean hide, String... messages) { - sendDialogues(npcId, expression, messages); - player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), 3, hide); - return player.getInterfaceManager().getChatbox(); + return sendDialogues(entity.getId(), expression, hide, messages); } /** @@ -495,12 +473,13 @@ public final class DialogueInterpreter { * @return The chatbox component. */ public Component sendDialogues(int npcId, FacialExpression expression, String... messages) { - return sendDialogues(npcId, expression == null ? -1 : expression.getAnimationId(), messages); + return sendDialogues(npcId, expression == null ? -1 : expression.getAnimationId(),false, messages); } static Pattern GENDERED_SUBSTITUTION = Pattern.compile("@g\\[([^,]*),([^\\]]*)\\]"); public static String doSubstitutions(Player player, String msg) { msg = msg.replace("@name", player.getUsername()); + msg = msg.replace("@servername", GameWorld.getSettings().getName()); StringBuilder sb = new StringBuilder(); Matcher m = GENDERED_SUBSTITUTION.matcher(msg); int index = player.isMale() ? 1 : 2; @@ -510,7 +489,6 @@ public final class DialogueInterpreter { m.appendTail(sb); return sb.toString(); } - /** * Send dialogues based on the amount of specified messages. * @param npcId The npc id. @@ -519,29 +497,48 @@ public final class DialogueInterpreter { * @return The chatbox component. */ public Component sendDialogues(int npcId, int expression, String... messages) { + return sendDialogues(npcId, expression, false, messages); + } + + /** + * Send dialogues based on the amount of specified messages. + * @param npcId The npc id. + * @param expression The entity's facial expression. + * @param messages The messages. + * @param hide should the continue button be hidden? + * @return The chatbox component. + */ + public Component sendDialogues(int npcId, int expression, boolean hide, String... messages) { if (messages.length < 1 || messages.length > 4) { System.err.println("Invalid amount of messages: " + messages.length); return null; } boolean npc = npcId > -1; int interfaceId = (npc ? 240 : 63) + messages.length; + interfaceId += hide ? 4 : 0; if (expression == -1) { expression = FacialExpression.HALF_GUILTY.getAnimationId(); } + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, true); + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 2, false); player.getPacketDispatch().sendAnimationInterface(expression, interfaceId, 2); + player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 1); if (npc) { player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 1); + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 45, 45); player.getPacketDispatch().sendNpcOnInterface(npcId, interfaceId, 2); player.getPacketDispatch().sendString(NPCDefinition.forId(npcId).getName(), interfaceId, 3); } else { + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 426, 45); // 423 is 47 * 9 player.getPacketDispatch().sendPlayerOnInterface(interfaceId, 2); player.getPacketDispatch().sendString(player.getUsername(), interfaceId, 3); } for (int i = 0; i < messages.length; i++) { - player.getPacketDispatch().sendString(doSubstitutions(player, messages[i].toString()), interfaceId, (i + 4)); + player.getPacketDispatch().sendString(doSubstitutions(player, messages[i]), interfaceId, (i + 4)); } - player.getInterfaceManager().openChatbox(interfaceId); player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), 3, false); + // Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title) + player.getInterfaceManager().openChatbox(interfaceId); return player.getInterfaceManager().getChatbox(); } @@ -561,6 +558,7 @@ public final class DialogueInterpreter { for (int i = 0; i < options.length; i++) { player.getPacketDispatch().sendString(options[i].toString(), interfaceId, i + 2); } + // Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title) player.getInterfaceManager().openChatbox(interfaceId); return player.getInterfaceManager().getChatbox(); } diff --git a/Server/src/main/core/game/dialogue/DialogueLabeller.kt b/Server/src/main/core/game/dialogue/DialogueLabeller.kt new file mode 100644 index 000000000..8259474a0 --- /dev/null +++ b/Server/src/main/core/game/dialogue/DialogueLabeller.kt @@ -0,0 +1,395 @@ +package core.game.dialogue + +import core.api.Event +import core.api.InputType +import core.api.face +import core.api.openDialogue +import core.api.splitLines +import core.game.component.Component +import core.game.component.Component.setUnclosable +import core.game.event.DialogueCloseEvent +import core.game.event.EventHook +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item + +/** Alias FacialExpression to ChatAnim for compatibility. */ +typealias ChatAnim = FacialExpression +/** Alias InputType to InputType for compatibility. */ +typealias InputType = InputType +/** Create container class DialogueOption for [DialogueLabeller.options] */ +class DialogueOption( + val goto: String, // Required: Label to go to if player selects option. + val option: String, // Required: Printed text on the option list. + val spokenText: String = option, // Optional: Option selected will be spoken text unless provided here. + val expression: ChatAnim = ChatAnim.NEUTRAL, // Optional: Player expression to show. + val skipPlayer: Boolean = false, // Optional: Skips the player spoken text (if wildly different) + val optionIf: ((player: Player, npc: NPC) -> Boolean)? = null // Optional: Function to show/not show option. +) + +/** + * DialogueLabeller is another way to organize a dialogue file. + * It shares the same dialogue layout as another project for portability. + * - Uses [stage] is for the current loop and [super.stage] as the "next" stage + * - Have [dialogueCounter] assign stages and guards for each option/player/npc dialogue + * - Assign [exec] and [goto] the same [dialogueCounter] to execute all in one pass + * - Save [label] to a HashMap to refer to when using [goto] + * - Loops again when a [goto] is encountered, ends when no [stageHit] during a stage + * + * ! WARNING: DO NOT use functions in DialogueFile as it will cause unexpected behavior. + * + * ! HELP: There are snippets below that you can copy and modify to use as part of your code. + */ +abstract class DialogueLabeller : DialogueFile() { + + companion object { + /** + * Makes NPC stop in its tracks and look at player. + * An alternative to setting up DialoguePlugin(player) to be used in InteractionListener. + */ + fun open(player: Player, dialogue: Any, npc: NPC) { + face(npc, player.location) + npc.setDialoguePlayer(player) // This prevents random walking in [NPC.java handleTickActions()] + npc.getWalkingQueue().reset() + npc.getPulseManager().clear() + openDialogue(player, dialogue, npc) + } + } + + /** Maps labels to stage numbers, to make jumps. */ + val labelStageMap = HashMap () + /** Assigns the number to each dialogue line. */ + var dialogueCounter = 0 + /** Set [stage] to start off (1 when there is an initial label). */ + var startingStage: Int? = null + /** Keeps the current stage for the whole cycle. */ + override var stage = -1 + /** Assigns the number to each stage. */ + var stageHit = false + /** Jump to next stage number when hitting a goto. */ + var jumpTo: Int? = null + /** ButtonID on every click */ + var buttonID: Int? = null + /** ButtonID when user clicks on an option */ + var optButton: Int? = null + /** Input value after a user enters a value */ + var optInput: Any? = null + + /** Implement this function instead of overriding handle. */ + abstract fun addConversation() + + /** Helper functions to create an individual stage for each of the dialogue stages. */ + private fun assignIndividualStage(callback: () -> Component?, unclosable: Boolean) { + if (startingStage == null) { startingStage = 0 } + if (stage == dialogueCounter && jumpTo == null) { // Run this stage when the stage equals to the dialogueCounter of this dialogue + val component = callback() // CALLBACK FUNCTION + if (unclosable && component != null) { + setUnclosable(player, component) + } + super.stage++ // Increment the stage to the next stage (only applies after a pass) + stageHit = true // Flag that the stage was hit, so that it doesn't close the dialogue + } + dialogueCounter++ // Increment the dialogueCounter to assign each line of dialogue + } + + /** Formats a vararg messages or falls back to message to an array for spread function. */ + private fun formatMessages(messages: Array?, message: String = ""): Array { + return if (messages == null || messages.isEmpty()) { + splitLines(message) + } else if (messages.size > 1) { + messages + } else { + splitLines(messages[0]) // A single line message is similar to a splitLine returning 1 line. + } + } + + /** Does absolutely nothing but to make it look like the other API. */ + fun assignToIds(npcid: Int) { /* super.npc = NPC(npcid) */ } + + /** Marks the start of a series of dialogue that can be jumped to using a [goto]. */ + fun label(label: String, nesting: () -> Unit = {}) { + if (startingStage == null) { startingStage = 1 } + dialogueCounter++ + labelStageMap[label] = dialogueCounter + nesting() + } + + /** Jumps to a [label] after a series of dialogue. */ + fun goto(label: String) { + if (stage == dialogueCounter && jumpTo == null) { + jumpTo = labelStageMap[label] + } + } + + /** Jumps to a [label] inside an [exec]. */ + fun loadLabel(player: Player, label: String) { + goto(label) + } + + /** + * Executes the callback between stages and can be used for branching with [loadLabel]. + * You can chain as many exec as you like since they read sequentially. + * You can also read [options] and [input] values here via [optButton] and [optInput]. + */ + fun exec(callback: (player: Player, npc: NPC) -> Unit) { + if (startingStage == null) { startingStage = 0 } + if (stage == dialogueCounter && jumpTo == null) { + callback(player!!, npc!!) + } + } + + /** Manual stage. For custom creation of an individual stage. Must call interpreter in some form. **/ + fun manual(unclosable: Boolean = false, callback: (player: Player, npc: NPC) -> Component?) { + assignIndividualStage({ return@assignIndividualStage callback(player!!, npc!!) }, unclosable) + } + + /** Dialogue player/playerl. Shows player chathead with text. **/ + fun player(chatAnim: ChatAnim = ChatAnim.NEUTRAL, vararg messages: String, unclosable: Boolean = false) { + assignIndividualStage({ return@assignIndividualStage interpreter!!.sendDialogues(player, chatAnim, *formatMessages(messages)) }, unclosable) + } + /** Dialogue player/playerl. Shows player chathead with text. **/ + fun player(vararg messages: String) { player(ChatAnim.NEUTRAL, *messages) } + @Deprecated("Use player() instead.", ReplaceWith("player(chatAnim, *messages)")) + fun playerl(chatAnim: ChatAnim = ChatAnim.NEUTRAL, vararg messages: String) { throw Exception("Deprecated DialogueLabel: Use player() instead.") } + @Deprecated("Use player() instead.", ReplaceWith("player(*messages)")) + fun playerl(vararg messages: String) { throw Exception("Deprecated DialogueLabel: Use player() instead.") } + + /** Dialogue npc/npcl. Shows npc chathead with text. **/ + fun npc(chatAnim: ChatAnim = ChatAnim.NEUTRAL, vararg messages: String, unclosable: Boolean = false) { + val callback = callback@{ return@callback interpreter!!.sendDialogues(npc, chatAnim, *formatMessages(messages)) } + assignIndividualStage(callback, unclosable) + } + /** Dialogue npc/npcl. Shows npcId chathead with text. **/ + fun npc(chatAnim: ChatAnim = ChatAnim.NEUTRAL, npcId: Int = npc!!.id, vararg messages: String, unclosable: Boolean = false) { + assignIndividualStage({ return@assignIndividualStage interpreter!!.sendDialogues(NPC(npcId), chatAnim, *formatMessages(messages)) }, unclosable) + } + /** Dialogue npc/npcl. Shows npcId chathead with text. **/ + fun npc(npcId: Int = npc!!.id, vararg messages: String, unclosable: Boolean = false) { + assignIndividualStage({ return@assignIndividualStage interpreter!!.sendDialogues(NPC(npcId), ChatAnim.NEUTRAL, *formatMessages(messages)) }, unclosable) + } + /** Dialogue npc/npcl. Shows npc chathead with text. **/ + fun npc(vararg messages: String) { npc(ChatAnim.NEUTRAL, *messages) } + @Deprecated("Use npc() instead.", ReplaceWith("npc(chatAnim, *messages)")) + fun npcl(chatAnim: ChatAnim = ChatAnim.NEUTRAL, vararg messages: String) { throw Exception("Deprecated DialogueLabel: Use npc() instead.") } + @Deprecated("Use npc() instead.", ReplaceWith("npc(*messages)")) + fun npcl(vararg messages: String) { throw Exception("Deprecated DialogueLabel: Use npc() instead.") } + + /** Dialogue item/iteml. Shows item with text. **/ + fun item(item: Item, vararg messages: String, message: String = "", unclosable: Boolean = false) { + val callback = callback@{ return@callback interpreter!!.sendItemMessage(item, *formatMessages(messages, message)) } + assignIndividualStage(callback, unclosable) + } + @Deprecated("Use item() instead.", ReplaceWith("item(item, *messages)")) + fun iteml(item: Item, vararg messages: String) { throw Exception("Deprecated DialogueLabel: Use item() instead.") } + + /** Dialogue overloaded doubleItem/doubleIteml. Shows two items with text. **/ + fun item(item: Item, item2: Item, vararg messages: String, message: String = "", unclosable: Boolean = false) { + assignIndividualStage({ return@assignIndividualStage interpreter!!.sendDoubleItemMessage(item, item2, *formatMessages(messages, message)) }, unclosable) + } + + /** Dialogue line/linel. Simply shows text. **/ + fun line(vararg messages: String, unclosable: Boolean = false) { + val callback = callback@{ return@callback interpreter!!.sendDialogue(*messages) } + assignIndividualStage(callback, unclosable) + } + @Deprecated("Use line() instead.", ReplaceWith("line(*messages)")) + fun linel(vararg messages: String) { throw Exception("Deprecated DialogueLabel: Use line() instead.") } + + /** Dialogue option. Shows the option dialogue with choices for the user to select. **/ + fun options(vararg options: DialogueOption, title: String = "Select an Option", unclosable: Boolean = false) { + // Filter out options that aren't shown. + val filteredOptions = options.filter{ if (it.optionIf != null) { it.optionIf.invoke(player!!, npc!!) } else { true } } + // Stage Part 1: Options List Dialogue + val callback = callback@{ return@callback interpreter!!.sendOptions(title, *filteredOptions.map{ it.option }.toTypedArray()) } + assignIndividualStage(callback, unclosable) + // Stage Part 2: Show spoken text. + val opt = if (buttonID != null && buttonID in 1..filteredOptions.size) { filteredOptions[buttonID!! - 1] } else { null } + assignIndividualStage({ + var component: Component? = null + if (opt?.skipPlayer == true) { + jumpTo = stage + 1 + } else { + component = interpreter!!.sendDialogues(player, opt?.expression ?: ChatAnim.NEUTRAL, *(splitLines(opt?.spokenText ?: " "))) + } + optButton = buttonID // transfer the buttonID to a temp memory for the next stage + return@assignIndividualStage component + }, unclosable) + // Stage Part 3: Jump To goto + if (stage == dialogueCounter && optButton != null && optButton in 1..filteredOptions.size) { + jumpTo = labelStageMap[filteredOptions[optButton!! - 1].goto] + } + dialogueCounter++ + } + @Deprecated("Use options(DialogueOption()) and not options(string).", ReplaceWith("options(DialogueOption(options))")) + override fun options(vararg options: String?, title: String) { throw Exception("Deprecated DialogueLabel: Use options(DialogueOption()) and not options(string).") } + + /** Dialogue input. Shows the input dialogue with an input box for the user to type in. Read [optInput] for the value. **/ + fun input(type: InputType, prompt: String = "Enter the amount") { + assignIndividualStage({ + // These are similar to calling sendInputDialogue + when (type) { + InputType.AMOUNT -> interpreter!!.sendInput(true, prompt) + InputType.NUMERIC -> interpreter!!.sendInput(false, prompt) + InputType.STRING_SHORT -> interpreter!!.sendInput(true, prompt) // Only 12 letters + InputType.STRING_LONG -> interpreter!!.sendLongInput(prompt) // Very long text, can overflow. + InputType.MESSAGE -> interpreter!!.sendMessageInput(prompt) + } + if (type == InputType.AMOUNT) { + player!!.setAttribute("parseamount", true) + } + // This runscript is the same runscript as the one in ContentAPI sendInputDialogue + player!!.setAttribute("runscript") { value: Any -> + optInput = value + // The next line is a hack. Because this prompt overlays the actual chat box, we trigger the next dialogue with a call to handle. + interpreter!!.handle(player!!.interfaceManager.chatbox.id, 2) + } + player!!.setAttribute("input-type", type) + return@assignIndividualStage null + }, false) + } + /** Dialogue input. Shows the input dialogue with an input box for the user to type in. Read [optInput] in an [exec] function for the value. **/ + fun input(numeric: Boolean, prompt: String = "Enter the amount") { input( if (numeric) { InputType.NUMERIC } else { InputType.STRING_SHORT }, prompt) } + + /** Runs arbitrary code when the dialogue closes, once. **/ + fun afterClose(callback: (player: Player) -> Unit) { + val hook = object : EventHook { + override fun process(entity: Entity, event: DialogueCloseEvent) { + val you = entity as Player + you.unhook(this) + callback(you) + } + } + player!!.hook(Event.DialogueClosed, hook) + } + + /** Calls another dialogue file. Always use this to open another dialogue file instead of calling openDialogue() in exec{} due to interfaces clashing. **/ + fun open(player: Player, dialogue: Any, vararg args: Any) { + val callback = callback@{ + core.api.openDialogue(player, dialogue, *args) + return@callback null + } + assignIndividualStage(callback, false) + } + + /** WARNING: DIALOGUE LABELLER WILL BREAK IN CERTAIN FUNCTIONS. USE open() instead. */ + fun openDialogue(player: Player, dialogue: Any, vararg args: Any) { + core.api.openDialogue(player, dialogue, *args) + } + + /** Hook onto the handle function of DialogueFile. This function gets called every loop with a super.stage. */ + override fun handle(componentID: Int, buttonID: Int) { + this.buttonID = buttonID + startingStage = null + /** This -1 stage is to read labels into a hashmap and to find the starting stage. */ + if (stage == -1) { + dialogueCounter = 0 + addConversation() // Force all labels to be recorded into hashmap. + super.stage = startingStage ?: 0 + } + for (i in 0..10) { // Limit to 10 jumpTo PER DIALOGUE LINE to prevent infinite looping/ping-ponging jumps. + if (jumpTo != null) { // If jumpTo is set, set the super.stage stage as the new jumpTo stage. + super.stage = jumpTo as Int + jumpTo = null + } + stageHit = false + stage = super.stage // [stage] is for the current loop. [super.stage] is treated as the "next" stage. + dialogueCounter = 0 + addConversation() // MAIN CALLBACK FUNCTION + // If there is no jumpTo set, or jumpTo is set to the same stage as this(infinite loop), exit. + if (jumpTo == null || jumpTo == stage) { + break + } + } + // If a dialogue stage is not hit, end the dialogue. + if (!stageHit) { + end() + } + } +} + +/* +// COPY PASTA SNIPPETS SECTION FOR QUICK BOILERPLATES + +// STANDARD: Initializing with DialoguePlugin. +@Initializable +class DonieDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return DonieDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DonieDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DONIE_2238) + } +} +class DonieDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.DONIE_2238) + + npc(ChatAnim.FRIENDLY, "Hello there, can I help you?") + goto("nowhere") + } +} + +// NEW! (EXPERIMENTAL): Initializing with InteractionListener. +class SomeDudeDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.PRIEST_OF_GUTHIX_8555, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, SomeDudeDialogueLabellerFile(), node as NPC) + return@on true + } + } +} + +// Exec with quest stage branches. +exec { player, npc -> + when(getQuestStage(player, SomeQuest.questName)) { + 100 -> loadLabel(player, "SomeQuestStage100") + 10, 20 -> loadLabel(player, "SomeQuestStage10") + else -> { + loadLabel(player, "SomeQuestStage0") + } + } +} + +// Exec to move quest stage up. +exec { player, npc -> + if (getQuestStage(player, SomeQuest.questName) == 0) { + setQuestStage(player, SomeQuest.questName, 10) + } +} + +// Exec to add item, set attribute. +exec { player, npc -> + if (removeItem(player, Items.ITEM_1)) { + addItemOrDrop(player, Items.ITEM_2) + } + if (getAttribute(player, attributeSomething, null) == null) { + setAttribute(player, attributeSomething, player.location) + } +} + +// Open to another dialogue file. +npc("I'm going to another file.") +open(player!!, SomeDialogueFile2(), npc!!) +... +class SomeDialogueFile2 : DialogueLabeller() { + override fun addConversation() { + npc("This is another file.") + } +} + +// Options with all the different controls. +options( + DialogueOption("Label1", "Line 1 Say", expression=ChatAnim.THINKING), + DialogueOption("Label2", "Line 2 Huh", skipPlayer=true), + DialogueOption("Label3", "Line 3 Blah", spokenText="I say something else", expression=ChatAnim.FRIENDLY), + DialogueOption("Label4", "Line 4 What") { player, npc -> + return@DialogueOption false // Don't show + } +) + +*/ \ No newline at end of file diff --git a/Server/src/main/core/game/dialogue/DialoguePlugin.java b/Server/src/main/core/game/dialogue/DialoguePlugin.java index 635b41c2b..18d17dd37 100644 --- a/Server/src/main/core/game/dialogue/DialoguePlugin.java +++ b/Server/src/main/core/game/dialogue/DialoguePlugin.java @@ -276,8 +276,8 @@ public abstract class DialoguePlugin implements Plugin { * Method used to send options. * @param options the options. */ - public void options(final String... options) { - interpreter.sendOptions("Select an Option", options); + public Component options(final String... options) { + return interpreter.sendOptions("Select an Option", options); } /** diff --git a/Server/src/main/core/game/dialogue/FacialExpression.java b/Server/src/main/core/game/dialogue/FacialExpression.java index e3011a5a3..6d415cf0f 100644 --- a/Server/src/main/core/game/dialogue/FacialExpression.java +++ b/Server/src/main/core/game/dialogue/FacialExpression.java @@ -90,6 +90,9 @@ public enum FacialExpression { //9855-9857 are like disgusted? does it just repeat after this? //Child Chathead? + CHILD_ANGRY(7168), + CHILD_SIDE_EYE(7169), + CHILD_RECALLING(7170), CHILD_EVIL_LAUGH(7171), CHILD_FRIENDLY(7172), CHILD_NORMAL(7173), @@ -98,9 +101,31 @@ public enum FacialExpression { CHILD_THINKING(7176), CHILD_SAD(7177), CHILD_GUILTY(7178), - CHILD_SUSPICIOUS(7179); //TODO: More? + CHILD_SUSPICIOUS(7179), + CHILD_SURPRISED(7180), + ; //TODO: More? + /* + * From some sources: here's a potential list of chatheads. + * // 667 + * Chat animation group: 1540; linked animations: [7, 8, 9, 6824] + * Chat animation group: 1489; linked animations: [225, 6550, 6551, 6552, 6553, 6555, 8372, 8373, 8374, 8375, 8581, 8582, 9178, 9179, 9180, 9181, 9183, 9187, 9189, 9190, 9192, 9202] + * Chat animation group: 82; linked animations: [554, 555, 556, 557, 562, 563, 564, 565, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 610, 611, 612, 613, 614, 615, 616, 617] + * Chat animation group: 84; linked animations: [558, 559, 560, 561] + * Chat animation group: 80; linked animations: [584, 585, 586, 587] + * Chat animation group: 78; linked animations: [3874] + * Chat animation group: 77; linked animations: [4119, 4120, 4121, 4122] + * Chat animation group: 1124; linked animations: [4843, 4844, 4845, 4846, 8388, 8389, 8390, 8391, 8392, 8393, 8394, 8403, 8404, 8405, 8406, 8898, 8899, 8900] + * Chat animation group: 1309; linked animations: [5661, 5662, 5663, 5665] + * Chat animation group: 1421; linked animations: [6244, 6245, 6246, 7636, 7637, 7638, 7639, 8380, 8381, 8382, 8383, 8475, 8476, 8477, 8478] + * Chat animation group: 1627; linked animations: [7168, 7169, 7170, 7171, 7172, 7173, 7176, 7177, 7178, 7179, 7180, 8824] + * Chat animation group: 1698; linked animations: [7539, 7540, 7541, 7542, 8447, 8448, 8449, 8450] + * Chat animation group: 1885; linked animations: [8395, 8396, 8397, 8398] + * Chat animation group: 1882; linked animations: [8411, 8412, 8413, 8414] + * Chat animation group: 1887; linked animations: [8443, 8444, 8445, 8446, 9315, 9316, 9317, 9318, 9319] + * Chat animation group: 1906; linked animations: [8579, 8580, 8583, 8584, 8585, 8656, 8657, 8659, 8660, 8661, 8662] + */ /** * The animation id. diff --git a/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt b/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt index 58e692b23..acc07905c 100644 --- a/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt +++ b/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt @@ -19,25 +19,11 @@ open class SkillDialogueHandler( /** * Represents the skill dialogue type. */ - val type: SkillDialogue?, vararg data: Any) { - /** - * Gets the player. - * @return The player. - */ - - /** - * Gets the type. - * @return The type. - */ - - /** - * Gets the passed data. - * @return the data. - */ - /** - * Represents the object data passed through. - */ - val data: Array + val type: SkillDialogue?, + /** + * The items that can be created through this dialog. + */ + vararg val items: Item) { /** * Method used to open a skill dialogue. @@ -70,7 +56,7 @@ open class SkillDialogueHandler( * @return the amount. */ open fun getAll(index: Int): Int { - return player.inventory.getAmount(data[0] as Item) + return player.inventory.getAmount(items[0]) } /** @@ -107,7 +93,7 @@ open class SkillDialogueHandler( private val length: Int) { ONE_OPTION(309, 5, 1) { override fun display(player: Player, handler: SkillDialogueHandler) { - val item = handler.data[0] as Item + val item = handler.items[0] player.packetDispatch.sendString("



" + item.name, 309, 6) player.packetDispatch.sendItemZoomOnInterface(item.id, 160, 309, 2) PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 309, 6, 60, 20)) @@ -125,7 +111,7 @@ open class SkillDialogueHandler( }, MAKE_SET_ONE_OPTION(582, 4, 1) { override fun display(player: Player, handler: SkillDialogueHandler) { - val item = handler.data[0] as Item + val item = handler.items[0] // Send item + item name to interface player.packetDispatch.sendItemZoomOnInterface(item.id, 160, 582, 2) @@ -163,8 +149,8 @@ open class SkillDialogueHandler( override fun display(player: Player, handler: SkillDialogueHandler) { var item: Item player.interfaceManager.openChatbox(306) - for (i in handler.data.indices) { - item = handler.data[i] as Item + for (i in handler.items.indices) { + item = handler.items[i] player.packetDispatch.sendString("



" + handler.getName(item), 303, 7 + i) player.packetDispatch.sendItemZoomOnInterface(item.id, 160, 303, 2 + i) } @@ -190,11 +176,11 @@ open class SkillDialogueHandler( }, THREE_OPTION(304, 8, 3) { override fun display(player: Player, handler: SkillDialogueHandler) { - var item: Item? = null + var item: Item? for (i in 0..2) { - item = handler.data[i] as Item + item = handler.items[i] player.packetDispatch.sendItemZoomOnInterface(item.id, 135, 304, 2 + i) - player.packetDispatch.sendString("



" + item!!.name, 304, 304 - 296 + i * 4) + player.packetDispatch.sendString("



" + item.name, 304, 304 - 296 + i * 4) } } @@ -219,10 +205,10 @@ open class SkillDialogueHandler( }, FOUR_OPTION(305, 9, 4) { override fun display(player: Player, handler: SkillDialogueHandler) { - var item: Item? = null + var item: Item? for (i in 0..3) { - item = handler.data[i] as Item - player.packetDispatch.sendItemZoomOnInterface(item!!.id, 135, 305, 2 + i) + item = handler.items[i] + player.packetDispatch.sendItemZoomOnInterface(item.id, 135, 305, 2 + i) player.packetDispatch.sendString("



" + item.name, 305, 305 - 296 + i * 4) } } @@ -255,8 +241,8 @@ open class SkillDialogueHandler( override fun display(player: Player, handler: SkillDialogueHandler) { var item: Item player.interfaceManager.openChatbox(306) - for (i in handler.data.indices) { - item = handler.data[i] as Item + for (i in handler.items.indices) { + item = handler.items[i] player.packetDispatch.sendString("



" + handler.getName(item), 306, 10 + 4 * i) player.packetDispatch.sendItemZoomOnInterface(item.id, 160, 306, 2 + i) PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 306, 2 + i, positions[i][0], positions[i][1])) @@ -361,14 +347,4 @@ open class SkillDialogueHandler( */ const val SKILL_DIALOGUE = 3 shl 16 } - - /** - * Constructs a new `SkillDialogueHandler` `Object`. - * @param player the player. - * @param type the type. - * @param data the data. - */ - init { - this.data = data as Array - } } \ No newline at end of file diff --git a/Server/src/main/core/game/ge/GrandExchange.kt b/Server/src/main/core/game/ge/GrandExchange.kt index 5b763bf71..a7ead4d1c 100644 --- a/Server/src/main/core/game/ge/GrandExchange.kt +++ b/Server/src/main/core/game/ge/GrandExchange.kt @@ -6,14 +6,11 @@ import core.cache.def.impl.ItemDefinition import core.game.node.entity.player.Player import core.game.node.entity.player.info.PlayerDetails import core.game.system.command.Privilege -import core.game.system.config.ItemConfigParser import core.game.system.task.Pulse import core.game.world.GameWorld import core.game.world.repository.Repository import core.tools.Log import core.tools.SystemLogger -import core.tools.colorize -import org.rs09.consts.Sounds import java.lang.Integer.max import java.util.concurrent.LinkedBlockingDeque @@ -101,13 +98,6 @@ class GrandExchange : StartupListener, Commands { PriceIndex.allowItem(id) notify(player, "Allowed ${getItemName(id)} for GE trade.") } - - define("geprivacy", Privilege.STANDARD) {player, _ -> - val current = getAttribute(player, "ge-exclude", false) - val new = !current - notify(player, "Your name is now ${if (new) colorize("%RHIDDEN") else colorize("%RSHOWN")}.") - setAttribute(player, "/save:ge-exclude", new) - } } companion object { @@ -131,15 +121,11 @@ class GrandExchange : StartupListener, Commands { @JvmStatic fun getRecommendedPrice(itemID: Int, from_bot: Boolean = false): Int { - var base = max(PriceIndex.getValue(itemID), getItemDefPrice(itemID)) + var base = PriceIndex.getValue(itemID) if (from_bot) base = (max(BotPrices.getPrice(itemID), base) * 1.10).toInt() return base } - private fun getItemDefPrice(itemID: Int): Int { - return max(itemDefinition(itemID).getConfiguration(ItemConfigParser.GE_PRICE) ?: 0, itemDefinition(itemID).value) - } - @JvmStatic fun getOfferStats(itemID: Int, sale: Boolean) : String { @@ -235,8 +221,7 @@ class GrandExchange : StartupListener, Commands { //GrandExchangeRecords.getInstance(player).update(offer) if (offer.sell && !player.isArtificial) { - val username = if (getAttribute(player, "ge-exclude", false)) "?????" else player.username - Repository.sendNews(username + " just offered " + offer.amount + " " + getItemName(offer.itemID) + " on the GE.") + sendNews(player.username + " just offered " + offer.amount + " " + getItemName(offer.itemID) + " on the GE.") } if (ServerConstants.I_AM_A_CHEATER) { @@ -278,9 +263,6 @@ class GrandExchange : StartupListener, Commands { seller.completedAmount += amount buyer.completedAmount += amount - if(seller.amountLeft < 1 && seller.player != null) - playAudio(seller.player!!, Sounds.GE_COLLECT_COINS_4042) - seller.addWithdrawItem(995, amount * if(sellerBias) buyer.offeredValue else seller.offeredValue) buyer.addWithdrawItem(seller.itemID, amount) @@ -310,12 +292,13 @@ class GrandExchange : StartupListener, Commands { } */ - seller.update() - val sellerPlayer = Repository.uid_map[seller.playerUID] - sellerPlayer?.let { GrandExchangeRecords.getInstance(sellerPlayer).visualizeRecords() } - buyer.update() - val buyerPlayer = Repository.uid_map[buyer.playerUID] - buyerPlayer?.let { GrandExchangeRecords.getInstance(buyerPlayer).visualizeRecords() } + for (entity in arrayOf(buyer, seller)) { + entity.update() + val player = Repository.uid_map[entity.playerUID] ?: continue + val records = GrandExchangeRecords.getInstance(player) + records.visualizeRecords() + records.updateNotification = true + } } private fun canUpdatePriceIndex(seller: GrandExchangeOffer, buyer: GrandExchangeOffer): Boolean { diff --git a/Server/src/main/core/game/ge/GrandExchangeOffer.kt b/Server/src/main/core/game/ge/GrandExchangeOffer.kt index 919427567..eee8594dd 100644 --- a/Server/src/main/core/game/ge/GrandExchangeOffer.kt +++ b/Server/src/main/core/game/ge/GrandExchangeOffer.kt @@ -146,10 +146,7 @@ class GrandExchangeOffer() { visualize(player) stmt.close() - val username = if (getAttribute(player ?: return@run, "ge-exclude", false)) "?????" - else player?.username ?: "?????" - - Discord.postNewOffer(sell, itemID, offeredValue, amount, username) + Discord.postNewOffer(sell, itemID, offeredValue, amount, player?.username ?: return@run) } } } diff --git a/Server/src/main/core/game/ge/GrandExchangeRecords.kt b/Server/src/main/core/game/ge/GrandExchangeRecords.kt index 63aa20848..e970c01bb 100644 --- a/Server/src/main/core/game/ge/GrandExchangeRecords.kt +++ b/Server/src/main/core/game/ge/GrandExchangeRecords.kt @@ -22,6 +22,7 @@ import java.util.* class GrandExchangeRecords(private val player: Player? = null) : PersistPlayer, LoginListener { var history = arrayOfNulls(5) val offerRecords = arrayOfNulls(6) + var updateNotification = false override fun login(player: Player) { val instance = GrandExchangeRecords(player) diff --git a/Server/src/main/core/game/ge/GrandExchangeTimer.kt b/Server/src/main/core/game/ge/GrandExchangeTimer.kt new file mode 100644 index 000000000..2e68ca5f4 --- /dev/null +++ b/Server/src/main/core/game/ge/GrandExchangeTimer.kt @@ -0,0 +1,24 @@ +package core.game.ge + +import core.api.hasAwaitingGrandExchangeCollections +import core.api.playJingle +import core.api.sendMessage +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.RSTimer + +class GrandExchangeTimer : RSTimer(500, "GE periodic poll", isSoft = true, isAuto = true) { + override fun run(entity: Entity) : Boolean { + if (entity !is Player) return false + val player = entity + val records = GrandExchangeRecords.getInstance(player) + if (records.updateNotification) { + records.updateNotification = false + if (hasAwaitingGrandExchangeCollections(player)) { + sendMessage(player, "One or more of your Grand Exchange offers have been updated.") + playJingle(player, 284) + } + } + return true + } +} diff --git a/Server/src/main/core/game/global/action/ClimbActionHandler.java b/Server/src/main/core/game/global/action/ClimbActionHandler.java index f9042b724..1914c2440 100644 --- a/Server/src/main/core/game/global/action/ClimbActionHandler.java +++ b/Server/src/main/core/game/global/action/ClimbActionHandler.java @@ -77,9 +77,11 @@ public final class ClimbActionHandler { } switch (option) { case "climb-up": + case "walk-up": endLadder = getLadder(startLadder, false); break; case "climb-down": + case "walk-down": if (startLadder.getName().equals("Trapdoor")) { animation = CLIMB_DOWN; } diff --git a/Server/src/main/core/game/global/action/DoorActionHandler.java b/Server/src/main/core/game/global/action/DoorActionHandler.java index 98a886839..02234e136 100644 --- a/Server/src/main/core/game/global/action/DoorActionHandler.java +++ b/Server/src/main/core/game/global/action/DoorActionHandler.java @@ -1,5 +1,6 @@ package core.game.global.action; +import content.data.Quests; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; @@ -84,10 +85,6 @@ public final class DoorActionHandler { return; } DoorConfigLoader.Door d = DoorConfigLoader.Companion.forId(object.getId()); - if (d != null && !d.getQuestRequirement().equals("")) { - if (!hasRequirement(player, d.getQuestRequirement())) - return; - } if (d == null || d.isAutoWalk()) { handleAutowalkDoor(player, object); return; @@ -118,7 +115,9 @@ public final class DoorActionHandler { if (object.getCharge() == IN_USE_CHARGE) { return false; } - final Scenery second = (object.getId() == 3) ? null : getSecondDoor(object, entity); + // TODO: Maybe have this passed in as an optional parameter or overload handleAutowalkDoor? + boolean ignoreSecondDoor = (object.getId() == 3628 || object.getId() == 3629 || object.getId() == 3630 || object.getId() == 3631|| object.getId() == 3632); // Ignore second door for Maze Random + final Scenery second = (object.getId() == 3 || ignoreSecondDoor) ? null : getSecondDoor(object, entity); entity.lock(4); final Location loc = entity.getLocation(); if (entity instanceof Player) { diff --git a/Server/src/main/core/game/global/action/DropListener.kt b/Server/src/main/core/game/global/action/DropListener.kt index e183cbd58..254b582ff 100644 --- a/Server/src/main/core/game/global/action/DropListener.kt +++ b/Server/src/main/core/game/global/action/DropListener.kt @@ -12,6 +12,8 @@ import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.game.system.config.ItemConfigParser import content.global.skill.summoning.pet.Pets +import core.game.node.entity.player.info.LogType +import core.game.node.entity.player.info.PlayerMonitor import org.rs09.consts.Items import org.rs09.consts.Sounds @@ -33,7 +35,7 @@ class DropListener : InteractionListener { } private fun handleDropAction(player: Player, node: Node) : Boolean { val option = getUsedOption(player) - var item = node as? Item ?: return false + val item = node as? Item ?: return false if (option == "drop") { if (Pets.forId(item.id) != null) { player.familiarManager.summon(item, true, true) @@ -43,11 +45,21 @@ class DropListener : InteractionListener { sendMessage(player, "You cannot drop items on top of graves!") return false } - if (getAttribute(player, "equipLock:${node.id}", 0 ) > getWorldTicks()) + if (player.locks.equipmentLock != null) { return false + } - queueScript (player, strength = QueueStrength.SOFT) { - if (player.inventory.replace(null, item.slot) != item) return@queueScript stopExecuting(player) + closeAllInterfaces(player) + queueScript(player, strength = QueueStrength.SOFT) { //do this as a script to allow dropping multiple items in the same tick (authentic) + // It's possible for state to change between queueing the script and executing it at the end of the tick (https://forum.2009scape.org/viewtopic.php?f=8&t=1195-lost-bandos-chestplate-whilst-making-iron-titans&p=5292). So, sanity check: + val current = player.inventory.get(item.slot) + if (current == null || current !== item) { + return@queueScript stopExecuting(player) + } + if (player.inventory.replace(null, item.slot) !== item) { + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Potential exploit attempt when player ${player.name} tried to drop ${item.amount}x ${item.id}. The item has been lost and will need to be refunded to the player, but how did they get this to happen?") + return@queueScript stopExecuting(player) + } val droppedItem = item.dropItem if (droppedItem.id == Items.COINS_995) playAudio(player, DROP_COINS_SOUND) else playAudio(player, DROP_ITEM_SOUND) GroundItemManager.create(droppedItem, player.location, player) diff --git a/Server/src/main/core/game/global/action/EquipHandler.kt b/Server/src/main/core/game/global/action/EquipHandler.kt index bab80e779..29299e238 100644 --- a/Server/src/main/core/game/global/action/EquipHandler.kt +++ b/Server/src/main/core/game/global/action/EquipHandler.kt @@ -33,11 +33,19 @@ class EquipHandler : InteractionListener { fun handleEquip(player: Player, node: Node) { val item = node.asItem() + val itemEquipmentSlot = item.definition.getConfiguration(ItemConfigParser.EQUIP_SLOT, -1) - if (item == null || player.inventory[item.slot] != item || item.name.toLowerCase().contains("goblin mail")) { + val currentEquippedItem = player.equipment[itemEquipmentSlot] + if (item == null || currentEquippedItem == item || item.name.toLowerCase().contains("goblin mail")) { return } + if(currentEquippedItem != null){ + if(!InteractionListeners.run(currentEquippedItem.id, player, currentEquippedItem, false)){ + return + } + } + val equipStateListener = item.definition.getConfiguration>("equipment", null) if (equipStateListener != null) { val bool = equipStateListener.fireEvent("equip", player, item) @@ -45,7 +53,7 @@ class EquipHandler : InteractionListener { return } } - if (!InteractionListeners.run(node.id, player, node, true)) { + if (!InteractionListeners.run(node.id, player, item, true)) { return } @@ -70,7 +78,6 @@ class EquipHandler : InteractionListener { playAudio(player, item.definition.getConfiguration(ItemConfigParser.EQUIP_AUDIO, 2244)) if (player.properties.autocastSpell != null) { - val itemEquipmentSlot = item.definition.getConfiguration(ItemConfigParser.EQUIP_SLOT, -1) if (itemEquipmentSlot == EquipmentContainer.SLOT_WEAPON) { player.properties.autocastSpell = null diff --git a/Server/src/main/core/game/global/action/SpecialLadders.java b/Server/src/main/core/game/global/action/SpecialLadders.java index 467ad84d4..532bd896a 100644 --- a/Server/src/main/core/game/global/action/SpecialLadders.java +++ b/Server/src/main/core/game/global/action/SpecialLadders.java @@ -3,6 +3,7 @@ package core.game.global.action; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; import core.game.world.map.Location; +import content.region.kandarin.feldip.quest.zogreflesheaters.ZogreFleshEatersListeners; import java.util.Arrays; import java.util.HashMap; @@ -18,18 +19,27 @@ public enum SpecialLadders implements LadderAchievementCheck { SWENSEN_UP(Location.create(2665, 10037, 0),Location.create(2649, 3661, 0)), FOG_ENTER(Location.create(3240,3575,0),Location.create(1675,5599,0)), FOG_LEAVE(Location.create(1673,5598,0),Location.create(3242, 3574, 0)), - INTRO_LEAVE(Location.create(2522, 4999, 0),Location.create(3230, 3240, 0)), + INTRO_ENTER(Location.create(3230,3241,0),Location.create(3290, 4936, 0)), + INTRO_LEAVE(Location.create(3290, 4935, 0),Location.create(3230, 3240, 0)), JATIZSO_MINE_UP(Location.create(2406,10188,0),Location.create(2397, 3811, 0)), JATIZSO_MINE_DOWN(Location.create(2397, 3812, 0), Location.create(2405, 10188, 0)), JATIZSO_SHOUT_TOWER_UP(Location.create(2373, 3800, 2),Location.create(2374, 3800, 0)), JATIZSO_SHOUT_TOWER_DOWN(Location.create(2373, 3800, 0),Location.create(2374, 3800, 2)), + // sendMessage(player, "You descend into the somewhat smoky depths of the well, to the accompaniment of") + // sendMessage(player, "eery wails.") https://youtu.be/x8abdpkJ6ZA + POLLNIVNEACH_SLAYER_DUNGEON_UP(Location.create(3358,2971,0), Location.create(3359,9354,0)), + // sendMessage(player, "You nimbly climb up the bucket rope, emerging into Pollnivneach's bustling square.") https://youtu.be/LVwbmCNjlzQ + POLLNIVNEACH_SLAYER_DUNGEON_DOWN(Location.create(3358,9352,0), Location.create(3358,2970,0)), ALKHARID_ZEKE_UP(Location.create(3284,3186,0), Location.create(3284,3190,1)), ALKHARID_ZEKE_DOWN(Location.create(3284,3190,1), Location.create(3284,3186,0)), ALKHARID_CRAFTING_UP(Location.create(3311,3187,0),Location.create(3314,3187,1)), ALKHARID_CRAFTING_DOWN(Location.create(3314,3187,1),Location.create(3310,3187,0)), ALKHARID_SOCRCERESS_UP(Location.create(3325,3142,0),Location.create(3325,3139,1)), ALKHARID_SOCRCERESS_DOWN(Location.create(3325,3139,1),Location.create(3325,3143,0)), + KHARIDIAN_DESERT_SUMMONING_UP(Location.create(3299,9318,0),Location.create(3303,2897,0)), + KHARIDIAN_DESERT_SUMMONING_DOWN(Location.create(3304,2897,0),Location.create(3299,9317,0)), + SHADOW_DUNGEON_UP(new Location(2629, 5072,0), new Location(2548, 3421,0)), CLOCKTOWER_HIDDEN_LADDER(Location.create(2572,9631,0),Location.create(2572,3230,0)), @@ -58,6 +68,9 @@ public enum SpecialLadders implements LadderAchievementCheck { CASTLEWARS_ZAMORAK_OUTERWALL_STAIRS_UP(Location.create(2382, 3130, 0), Location.create(2383, 3132, 0)), CASTLEWARS_ZAMOUTER_WALL_STAIRS_DOWN(Location.create(2382, 3132, 0), Location.create(2382, 3129, 0)), + WHITE_WOLF_MOUNTAIN_FAKE_LADDER_1_UP(Location.create(2837, 9927, 0), Location.create(2837, 3527, 0)), + WHITE_WOLF_MOUNTAIN_FAKE_LADDER_2_UP(Location.create(2823, 9930, 0), Location.create(2823, 3529, 0)), + PORT_SARIM_RAT_PITS_DOWN(new Location(3018,3232,0), new Location(2962,9650,0)) { @Override public void checkAchievement(Player player) { @@ -71,6 +84,13 @@ public enum SpecialLadders implements LadderAchievementCheck { PATERDOMUS_TEMPLE_STAIRCASE_SOUTH_DOWN(new Location(3415, 3486,1), new Location(3414, 3486,0)), PHASMATYS_BAR_DOWN(new Location(3681,3498,0), new Location(3682,9961,0)), PHASMATYS_BAR_UP(new Location(3682,9962,0), new Location(3681,3497,0)), + YANNILE_HOUSE_DOWN(new Location(2597, 3107,1), new Location(2597, 3107,0)), + YANNILE_HOUSE_UP(new Location(2597, 3107,0), new Location(2597, 3107,1)) { + @Override + public void checkAchievement(Player player) { + ZogreFleshEatersListeners.ladderMakesSithikTurnIntoOgre(player); + } + }, SEERS_VILLAGE_SPINNING_HOUSE_ROOFTOP_UP(new Location(2715,3472,1), new Location(2714,3472,3)) { @Override public void checkAchievement(Player player) { diff --git a/Server/src/main/core/game/interaction/InteractionListeners.kt b/Server/src/main/core/game/interaction/InteractionListeners.kt index c28113500..e3cab3e34 100644 --- a/Server/src/main/core/game/interaction/InteractionListeners.kt +++ b/Server/src/main/core/game/interaction/InteractionListeners.kt @@ -164,6 +164,7 @@ object InteractionListeners { @JvmStatic fun run(id: Int, player: Player, node: Node, isEquip: Boolean): Boolean{ + player.scripts.removeWeakScripts() player.dispatch(InteractionEvent(node, if(isEquip) "equip" else "unequip")) if(isEquip){ return equipListeners["equip:$id"]?.invoke(player,node) ?: true @@ -174,6 +175,7 @@ object InteractionListeners { @JvmStatic fun run(used: Node, with: Node, type: IntType, player: Player): Boolean{ + player.scripts.removeWeakScripts() val flag = when(type){ IntType.NPC, IntType.PLAYER -> DestinationFlag.ENTITY IntType.SCENERY -> DestinationFlag.OBJECT @@ -224,6 +226,8 @@ object InteractionListeners { @JvmStatic fun run(id: Int, type: IntType, option: String, player: Player, node: Node): Boolean{ + player.scripts.removeWeakScripts() + val flag = when(type){ IntType.PLAYER -> DestinationFlag.ENTITY IntType.GROUNDITEM -> DestinationFlag.ITEM diff --git a/Server/src/main/core/game/interaction/MovementPulse.java b/Server/src/main/core/game/interaction/MovementPulse.java index ca60a54cf..3eb7c43bd 100644 --- a/Server/src/main/core/game/interaction/MovementPulse.java +++ b/Server/src/main/core/game/interaction/MovementPulse.java @@ -11,6 +11,7 @@ import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.Point; +import core.game.world.map.RegionManager; import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; import core.net.packet.PacketRepository; @@ -312,7 +313,7 @@ public abstract class MovementPulse extends Pulse { if (interactLocation == null) interactLocation = loc; - if (destination instanceof Entity || interactLocation == null || (!mover.getWalkingQueue().hasPath() && interactLocation.getDistance(mover.getLocation()) > 0) || (usingTruncatedPath && destination.getLocation().getDistance(mover.getLocation()) < 14)) { + if (destination instanceof Entity || interactLocation == null || (mover.getWalkingQueue().getQueue().size() <= 1 && interactLocation.getDistance(mover.getLocation()) > 0) || (usingTruncatedPath && destination.getLocation().getDistance(mover.getLocation()) < 14)) { if (!checkAllowMovement()) return; if (destination instanceof Entity && previousLoc != null && previousLoc.equals(loc) && mover.getWalkingQueue().hasPath()) @@ -380,44 +381,49 @@ public abstract class MovementPulse extends Pulse { return canMove; } - private Location checkForEntityPathInterrupt(Location loc) { - Location ml = mover.getLocation(); - Location dl = destination.getLocation(); - // Lead the target if they're walking/running, unless they're already within interaction range - if(loc != null && destination instanceof Entity) { - WalkingQueue wq = ((Entity)destination).getWalkingQueue(); - if(wq.hasPath()) { - Point[] points = wq.getQueue().toArray(new Point[0]); - if(points.length > 0) { - Point p = points[0]; - Point predictiveIntersection = null; - for(int i=0; i 0) { + Point p = points[0]; + Point predictiveIntersection = null; + for (int i = 0; i < points.length; i++) { + Location closestBorder = getClosestBorderToPoint(points[i], loc.getZ()); - int moverDist = Math.max(Math.abs(ml.getX() - closestBorder.getX()), Math.abs(ml.getY() - closestBorder.getY())); - float movementRatio = moverDist / (float) ((i + 1) / (mover.getWalkingQueue().isRunning() ? 2 : 1)); - if (predictiveIntersection == null && movementRatio <= 1.0) { //try to predict an intersection point on the path if possible - predictiveIntersection = points[i]; - break; - } + if (!RegionManager.isTeleportPermitted(closestBorder)) { // A nasty hack to discard invalid intersection points + continue; + } + int moverDist = Math.max(Math.abs(ml.getX() - closestBorder.getX()), Math.abs(ml.getY() - closestBorder.getY())); + float movementRatio = moverDist / (float) ((i + 1) / (mover.getWalkingQueue().isRunning() ? 2 : 1)); + if (predictiveIntersection == null && movementRatio <= 1.0) { //try to predict an intersection point on the path if possible + predictiveIntersection = points[i]; + break; + } + // Otherwise, we target the farthest point along target's planned movement that's within 1 tick's running, + // this ensures the player will run to catch up to the target if able. + if (moverDist <= 2) { + p = points[i]; + } + } + if (predictiveIntersection != null) + p = predictiveIntersection; - // Otherwise, we target the farthest point along target's planned movement that's within 1 tick's running, - // this ensures the player will run to catch up to the target if able. - if(moverDist <= 2) { - p = points[i]; - } - } + Location endLoc = getClosestBorderToPoint(p, loc.getZ()); - if (predictiveIntersection != null) - p = predictiveIntersection; - - Location endLoc = getClosestBorderToPoint(p, loc.getZ()); - return endLoc; - } - } - } - return loc; - } + if (!RegionManager.isTeleportPermitted(endLoc)) { // Basically a prayer + return loc; + } + return endLoc; + } + } + } + return loc; + } private Location getClosestBorderToPoint (Point p, int plane) { Vector pathDiff = Vector.betweenLocs (destination.getLocation(), Location.create(p.getX(), p.getY(), plane)); diff --git a/Server/src/main/core/game/interaction/ScriptProcessor.kt b/Server/src/main/core/game/interaction/ScriptProcessor.kt index ec6da95a4..2c638f35f 100644 --- a/Server/src/main/core/game/interaction/ScriptProcessor.kt +++ b/Server/src/main/core/game/interaction/ScriptProcessor.kt @@ -1,6 +1,7 @@ package core.game.interaction import core.api.* +import core.game.bots.AIPlayer import core.game.node.Node import core.game.node.entity.Entity import core.game.node.entity.npc.NPC @@ -10,11 +11,10 @@ import core.game.node.scenery.Scenery import core.game.world.GameWorld import core.game.world.map.Location import core.game.world.map.path.Pathfinder -import core.game.bots.AIPlayer import core.tools.Log -import core.tools.SystemLogger +import java.io.PrintWriter +import java.io.StringWriter import java.lang.Integer.max -import java.io.* class ScriptProcessor(val entity: Entity) { private var apScript: Script<*>? = null @@ -91,17 +91,12 @@ class ScriptProcessor(val entity: Entity) { } fun processQueue() : Boolean { - var strongInQueue = false - var softInQueue = false var anyExecuted = false - strongInQueue = hasTypeInQueue(QueueStrength.STRONG) - softInQueue = hasTypeInQueue(QueueStrength.SOFT) + val strongInQueue = hasTypeInQueue(QueueStrength.STRONG) - if (softInQueue || strongInQueue) { + if (strongInQueue) { if (entity is Player) { - entity.interfaceManager.close() - entity.interfaceManager.closeChatbox() - entity.dialogueInterpreter.close() + closeAllInterfaces(entity) } } @@ -118,10 +113,8 @@ class ScriptProcessor(val entity: Entity) { continue if (script.nextExecution > GameWorld.ticks) continue - if ((script.strength == QueueStrength.STRONG || script.strength == QueueStrength.SOFT) && entity is Player) { - entity.interfaceManager.close() - entity.interfaceManager.closeChatbox() - entity.dialogueInterpreter.close() + if ((script.strength == QueueStrength.STRONG) && entity is Player) { + closeAllInterfaces(entity) } script.nextExecution = GameWorld.ticks + 1 val finished = executeScript(script) @@ -142,10 +135,8 @@ class ScriptProcessor(val entity: Entity) { } if (script.nextExecution > GameWorld.ticks) continue - if ((script.strength == QueueStrength.STRONG || script.strength == QueueStrength.SOFT)) { - entity.interfaceManager.close() - entity.interfaceManager.closeChatbox() - entity.dialogueInterpreter.close() + if ((script.strength == QueueStrength.STRONG)) { + closeAllInterfaces(entity) } script.nextExecution = GameWorld.ticks + 1 val finished = executeScript(script) @@ -284,9 +275,7 @@ class ScriptProcessor(val entity: Entity) { return } if (strength == QueueStrength.STRONG && entity is Player) { - entity.interfaceManager.close() - entity.interfaceManager.closeChatbox() - entity.dialogueInterpreter.close() + closeAllInterfaces(entity) } script.nextExecution = max(GameWorld.ticks + 1, script.nextExecution) queue.add(script) diff --git a/Server/src/main/core/game/interaction/UseWithHandler.java b/Server/src/main/core/game/interaction/UseWithHandler.java index c38a5e6bc..4c5a6f414 100644 --- a/Server/src/main/core/game/interaction/UseWithHandler.java +++ b/Server/src/main/core/game/interaction/UseWithHandler.java @@ -134,7 +134,9 @@ public abstract class UseWithHandler implements Plugin { event.getPlayer().getPulseManager().run(new MovementPulse(event.getPlayer(), event.getUsedWith()) { @Override public boolean pulse() { - event.getPlayer().debug("Unhandled use with interaction: item used: " + event.getUsed() + " with: " + event.getUsedWith()); + event.getPlayer().debug("Unhandled use with interaction:"); + event.getPlayer().debug("Used: " + event.getUsed()); + event.getPlayer().debug("With: " + event.getUsedWith()); event.getPlayer().getPacketDispatch().sendMessage("Nothing interesting happens."); return true; } diff --git a/Server/src/main/core/game/node/entity/Entity.java b/Server/src/main/core/game/node/entity/Entity.java index c1c930645..9699be83d 100644 --- a/Server/src/main/core/game/node/entity/Entity.java +++ b/Server/src/main/core/game/node/entity/Entity.java @@ -78,7 +78,7 @@ public abstract class Entity extends Node { /** * The pulse manager. */ - private final PulseManager pulseManager = new PulseManager(); + private final PulseManager pulseManager = new PulseManager(this); /** * The impact handler. @@ -489,7 +489,7 @@ public abstract class Entity extends Node { } /** - * Checks if an entity can continue it's attack. + * Checks if an entity can continue its attack. * @param target the target. * @param style the style. * @return {@code True} if so. @@ -660,7 +660,7 @@ public abstract class Entity extends Node { /** * Registers a new force chat update flag to the update masks. * @param string The string. - * @return {@code True} if succesful. + * @return {@code True} if successful. */ public boolean sendChat(String string) { return getUpdateMasks().register(EntityFlag.ForceChat, string); diff --git a/Server/src/main/core/game/node/entity/combat/CombatPulse.kt b/Server/src/main/core/game/node/entity/combat/CombatPulse.kt index 0473df0a0..7e0475e3d 100644 --- a/Server/src/main/core/game/node/entity/combat/CombatPulse.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatPulse.kt @@ -296,6 +296,7 @@ class CombatPulse( } setVictim(victim) entity.onAttack(victim as Entity?) + victim.scripts.removeWeakScripts() if (!isAttacking) entity.pulseManager.run(this) @@ -468,7 +469,7 @@ class CombatPulse( } init { - movement = object : MovementPulse(entity, null, DestinationFlag.ENTITY) { + movement = object : MovementPulse(entity, null) { override fun pulse(): Boolean { return false } diff --git a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt index b5633d588..748e258d5 100644 --- a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt @@ -31,10 +31,14 @@ import kotlin.math.floor * Handles a combat swing. * @author Emperor * @author Ceikry - Kotlin refactoring, general cleanup + * @author Player Name - converted `flags` to ArrayList */ abstract class CombatSwingHandler(var type: CombatStyle?) { - var flags: Array = emptyArray() - constructor(type: CombatStyle?, vararg flags: SwingHandlerFlag) : this(type) { this.flags = flags } + var flags: ArrayList = ArrayList(SwingHandlerFlag.values().size) + constructor(type: CombatStyle?, vararg flags: SwingHandlerFlag) : this(type) { + this.flags = arrayListOf(*flags) + } + /** * The mapping of the special attack handlers. */ @@ -173,26 +177,21 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { * @return `True` if the hit is accurate. */ fun isAccurateImpact(entity: Entity?, victim: Entity?, style: CombatStyle?, accuracyMod: Double, defenceMod: Double): Boolean { - var mod = 1.33 + var mod = 1.0 if (victim == null || style == null) { return false } if (victim is Player && entity is Familiar && victim.prayer[PrayerType.PROTECT_FROM_SUMMONING]) { mod = 0.0 } - val attack = calculateAccuracy(entity) * accuracyMod * mod * getSetMultiplier(entity, Skills.ATTACK) - val defence = calculateDefence(victim, entity) * defenceMod * getSetMultiplier(victim, Skills.DEFENCE) + val attack = calculateAccuracy(entity) * accuracyMod * mod + val defence = calculateDefence(victim, entity) * defenceMod val chance: Double = if (attack > defence) { - 1 - ((defence + 2) / ((2 * attack) + 1)) + 1 - ((defence + 2) / (2 * (attack + 1))) } else { - attack / ((2 * defence) + 1) + attack / (2 * (defence + 1)) } - val ratio = chance * 100 - val accuracy = floor(ratio) - val block = floor(101 - ratio) - val acc = Math.random() * accuracy - val def = Math.random() * block - return (acc > def).also { if(entity?.username?.toLowerCase() == "test10") log(this::class.java, Log.FINE, "Should hit: $it") } + return Math.random() < chance } /** @@ -669,5 +668,6 @@ enum class SwingHandlerFlag { IGNORE_STAT_BOOSTS_DAMAGE, IGNORE_STAT_BOOSTS_ACCURACY, IGNORE_PRAYER_BOOSTS_DAMAGE, - IGNORE_PRAYER_BOOSTS_ACCURACY + IGNORE_PRAYER_BOOSTS_ACCURACY, + IGNORE_STAT_REDUCTION } diff --git a/Server/src/main/core/game/node/entity/combat/DeathTask.java b/Server/src/main/core/game/node/entity/combat/DeathTask.java index 3ea456d7b..da308856d 100644 --- a/Server/src/main/core/game/node/entity/combat/DeathTask.java +++ b/Server/src/main/core/game/node/entity/combat/DeathTask.java @@ -82,8 +82,8 @@ public final class DeathTask extends NodeTask { e.getProperties().setTeleportLocation(spawn); e.unlock(); e.finalizeDeath(killer); - e.getImpactHandler().getImpactLog().clear();// check if this needs to be - // before finalize + e.getImpactHandler().getNpcImpactLog().clear();// check if this needs to be before finalize + e.getImpactHandler().getPlayerImpactLog().clear();// check if this needs to be before finalize e.getImpactHandler().setDisabledTicks(4); e.dispatch(new SelfDeathEvent(killer)); } diff --git a/Server/src/main/core/game/node/entity/combat/ImpactHandler.java b/Server/src/main/core/game/node/entity/combat/ImpactHandler.java index af9f3794c..6e49734ec 100644 --- a/Server/src/main/core/game/node/entity/combat/ImpactHandler.java +++ b/Server/src/main/core/game/node/entity/combat/ImpactHandler.java @@ -1,12 +1,11 @@ package core.game.node.entity.combat; -import core.ServerConstants; +import content.data.EnchantedJewellery; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.skill.Skills; import content.global.skill.summoning.familiar.Familiar; import content.global.skill.summoning.pet.Pet; import core.game.node.entity.Entity; -import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.prayer.PrayerType; import core.game.node.item.Item; @@ -14,12 +13,13 @@ import core.game.system.task.Pulse; import core.game.bots.AIPlayer; import core.game.world.GameWorld; import core.game.world.map.zone.ZoneType; +import core.game.world.repository.Repository; +import org.rs09.consts.Items; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Queue; -import java.util.stream.Collectors; /** * Class used for handling combat impacts. @@ -38,9 +38,14 @@ public final class ImpactHandler { private int disabledTicks; /** - * The impact log. + * The NPC impact log. */ - private final Map impactLog = new HashMap<>(); + private final Map npcImpactLog = new HashMap<>(); + + /** + * The player impact log. This is by player uid to cope with players relogging. + */ + private final Map playerImpactLog = new HashMap<>(); /** * Gets the current hitsplats to show. @@ -166,11 +171,14 @@ public final class ImpactHandler { return null; } if (hit > 0) { - Integer value = impactLog.get(source); - if (value == null) { - value = 0; + if (source instanceof Player) { + int uid = source.asPlayer().getDetails().getUid(); + Integer value = playerImpactLog.get(uid); + playerImpactLog.put(uid, value == null ? hit : hit + value); + } else { + Integer value = npcImpactLog.get(source); + npcImpactLog.put(source, value == null ? hit : hit + value); } - impactLog.put(source, hit + value); } if (style != null && style.getSwingHandler() != null && source instanceof Player) { Player player = source.asPlayer(); @@ -200,12 +208,13 @@ public final class ImpactHandler { impactQueue.add(impact); if (entity instanceof Player && !dead) { final Player p = entity.asPlayer(); - if (p.getZoneMonitor().getType() != ZoneType.SAFE.getId() && p.getSkullManager().getLevel() <= 30 && (p.getEquipment().contains(2570, 1))) { + if (p.getZoneMonitor().getType() != ZoneType.SAFE.getId() && (p.getEquipment().contains(Items.RING_OF_LIFE_2570, 1))) { int percentage = (int) (entity.getSkills().getStaticLevel(Skills.HITPOINTS) * 0.10); if (p.getSkills().getLifepoints() <= percentage) { - p.getEquipment().remove(new Item(2570)); - p.sendMessage("Your ring of life saves you and in the process is destroyed."); - p.teleport(ServerConstants.HOME_LOCATION); + Item rolItem = new Item(Items.RING_OF_LIFE_2570); + if (EnchantedJewellery.RING_OF_LIFE.attemptTeleport(p, rolItem, 0, true)) { + p.sendMessage("Your ring of life saves you and in the process is destroyed."); + } } } } @@ -246,35 +255,48 @@ public final class ImpactHandler { if (entity instanceof Player) { return killer; } + if (killer instanceof AIPlayer) return killer; + int damage = -1; - for (Entity e : impactLog.keySet()) { - if (e == this.entity) { - continue; + if (playerImpactLog.isEmpty()) { + for (Entity e : npcImpactLog.keySet()) { + if (e == this.entity) { + continue; + } + int amount = npcImpactLog.get(e); + if (amount > damage) { + damage = amount; + entity = e; + } } - int amount = impactLog.get(e); - if (amount > damage || (entity instanceof NPC && e instanceof Player)) { + return entity; + } + + int player = 0; //needs to be fake-initialized because java is dumb + for (int uid : playerImpactLog.keySet()) { + int amount = playerImpactLog.get(uid); + if (amount > damage) { damage = amount; - entity = e; + player = uid; } } - return entity; + return Repository.getPlayerByUid(player); } /** - * Gets the impact log. - * @return The impact log. + * Gets the npc impact log. + * @return The npc impact log. */ - public Map getImpactLog() { - return impactLog; + public Map getNpcImpactLog() { + return npcImpactLog; } /** - * Gets the impact log filtered for Player objects - * @return The impact log of each Player and their damage + * Gets the player impact log. + * @return The player impact log. */ - public Map getPlayerImpactLog() { - return impactLog.entrySet().stream().filter(entry -> entry.getKey() instanceof Player).collect( - Collectors.toMap(m -> m.getKey().asPlayer(), m -> m.getValue())); + public Map getPlayerImpactLog() { + return playerImpactLog; } /** @@ -388,4 +410,4 @@ public final class ImpactHandler { public static enum HitsplatType { MISS, NORMAL, POISON, DISEASE, NORMAL_1, VENOM; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt index c7d850781..82bf1d5d8 100644 --- a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt @@ -1,5 +1,6 @@ package core.game.node.entity.combat +import content.global.skill.skillcapeperks.SkillcapePerks import core.game.node.entity.Entity import core.game.node.entity.combat.equipment.ArmourSet import core.game.node.entity.combat.spell.SpellType @@ -61,7 +62,7 @@ open class MagicSwingHandler (vararg flags: SwingHandlerFlag) for (s in state.targets) { var hit = -1 s.spell = spell - if (isAccurateImpact(entity, s.victim, CombatStyle.MAGIC, 1.3, 1.0)) { + if (isAccurateImpact(entity, s.victim, CombatStyle.MAGIC)) { s.maximumHit = max hit = RandomFunction.random(max) } @@ -136,31 +137,31 @@ open class MagicSwingHandler (vararg flags: SwingHandlerFlag) } override fun calculateAccuracy(entity: Entity?): Int { - val baseLevel = entity!!.skills.getStaticLevel(Skills.MAGIC) - var spellRequirement = baseLevel - if (entity is Player) { - if (entity.getProperties().spell != null) { - spellRequirement = entity.getProperties().spell.level - } else if (entity.getProperties().autocastSpell != null) { - spellRequirement = entity.getProperties().autocastSpell.level + entity ?: return 0 + + val styleAttackBonus = entity.properties.bonuses[WeaponInterface.BONUS_MAGIC] + 64 + when (entity) { + is Player -> { + var effectiveMagicLevel = entity.skills.getLevel(Skills.MAGIC, true).toDouble() + if(!flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) + effectiveMagicLevel = floor(effectiveMagicLevel + (entity.prayer.getSkillBonus(Skills.MAGIC) * effectiveMagicLevel)) + effectiveMagicLevel += 8 + effectiveMagicLevel *= getSetMultiplier(entity, Skills.MAGIC) + effectiveMagicLevel = floor(effectiveMagicLevel) + + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) + effectiveMagicLevel *= styleAttackBonus + else effectiveMagicLevel *= 64 + + return effectiveMagicLevel.toInt() + } + is NPC -> { + val magicLevel = entity.skills.getLevel(Skills.MAGIC) + 9 + return magicLevel * styleAttackBonus } } - var spellBonus = 0.0 - if (baseLevel > spellRequirement) { - spellBonus = (baseLevel - spellRequirement) * .3 - } - val level = entity.skills.getLevel(Skills.MAGIC, true) - var prayer = 1.0 - if (entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) { - prayer += entity.prayer.getSkillBonus(Skills.MAGIC) - } - val additional = getSetMultiplier(entity, Skills.MAGIC) - val effective = floor(level * prayer * additional + spellBonus) - val bonus = - if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) - entity.properties.bonuses[WeaponInterface.BONUS_MAGIC] - else 0 - return floor((effective + 8) * (bonus + 64) / 10).toInt() + + return 0 } override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { @@ -183,14 +184,31 @@ open class MagicSwingHandler (vararg flags: SwingHandlerFlag) } override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { - val level = victim!!.skills.getLevel(Skills.DEFENCE, true) - var prayer = 1.0 - if (victim is Player) { - prayer += victim.prayer.getSkillBonus(Skills.MAGIC) + victim ?: return 0 + attacker ?: return 0 + + val styleDefenceBonus = victim.properties.bonuses[WeaponInterface.BONUS_MAGIC + 5] + 64 + when (victim) { + is Player -> { + var effectiveDefenceLevel = victim.skills.getLevel(Skills.DEFENCE).toDouble() + effectiveDefenceLevel = floor(effectiveDefenceLevel + (victim.prayer.getSkillBonus(Skills.DEFENCE) * effectiveDefenceLevel)) + if (victim.properties.attackStyle.style == WeaponInterface.STYLE_DEFENSIVE || victim.properties.attackStyle.style == WeaponInterface.STYLE_LONG_RANGE) effectiveDefenceLevel += 3 + else if (victim.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveDefenceLevel += 1 + effectiveDefenceLevel *= getSetMultiplier(victim, Skills.DEFENCE) + + var effectiveMagicLevel = victim.skills.getLevel(Skills.MAGIC).toDouble() + effectiveMagicLevel = floor(effectiveMagicLevel + (victim.prayer.getSkillBonus(Skills.MAGIC) * effectiveMagicLevel)) + + effectiveDefenceLevel = effectiveDefenceLevel * 0.3 + effectiveMagicLevel * 0.7 + 8 + return effectiveDefenceLevel.toInt() * styleDefenceBonus + } + is NPC -> { + val defLevel = victim.skills.getLevel(Skills.MAGIC) + 9 + return defLevel * styleDefenceBonus + } } - val effective = floor(level * prayer * 0.3) + victim.skills.getLevel(Skills.MAGIC, true) * 0.7 - val equipment = victim.properties.bonuses[WeaponInterface.BONUS_MAGIC + 5] - return floor((effective + 8) * (equipment + 64) / 10).toInt() + + return 0 } override fun getSetMultiplier(e: Entity?, skillId: Int): Double { diff --git a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt index d755c269d..a20d7f5d8 100644 --- a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt @@ -3,6 +3,7 @@ package core.game.node.entity.combat import content.global.skill.skillcapeperks.SkillcapePerks import content.global.skill.slayer.SlayerEquipmentFlags import content.global.skill.slayer.SlayerManager +import content.global.skill.slayer.Tasks import core.api.* import core.api.EquipmentSlot import core.game.container.impl.EquipmentContainer @@ -64,11 +65,14 @@ open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) if (entity is Player) { state.weapon = Weapon(entity.equipment[3]) } - if (entity!!.properties.armourSet === ArmourSet.VERAC && RandomFunction.random(100) < 21) { + if (entity!!.properties.armourSet == ArmourSet.VERAC && RandomFunction.roll(4)) { state.armourEffect = ArmourSet.VERAC } - if (state.armourEffect === ArmourSet.VERAC || isAccurateImpact(entity, victim, CombatStyle.MELEE)) { - val max = calculateHit(entity, victim, 1.0) + if (state.armourEffect == ArmourSet.VERAC || isAccurateImpact(entity, victim, CombatStyle.MELEE)) { + var max = calculateHit(entity, victim, 1.0) + if (victim != null) { + if (entity is NPC && state.armourEffect == ArmourSet.VERAC && victim.hasProtectionPrayer(CombatStyle.MELEE)) max = max * 2 / 3 + } state.maximumHit = max hit = RandomFunction.random(max + 1) } @@ -111,16 +115,23 @@ open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) val name = entity.equipment.getNew(3).name var damage = -1 if (name.contains("(p++") || name.contains("(s)") || name.contains("(kp)")) { - damage = 68 + damage = 30 } else if (name.contains("(p+)")) { - damage = 58 + damage = 25 } else if (name.contains("(p)")) { - damage = 48 + damage = 20 } if (damage > -1 && RandomFunction.random(10) < 4) { applyPoison (victim, entity, damage) } } + } else if (entity is NPC) { + val poisonous = entity.isPoisonous + val damage = entity.poisonSeverity() + + if (poisonous && damage > -1 && RandomFunction.random(10) < 4) { + applyPoison (victim, entity, damage) + } } super.adjustBattleState(entity, victim, state) } @@ -128,68 +139,86 @@ open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) override fun calculateAccuracy(entity: Entity?): Int { //formula taken from wiki: https://oldschool.runescape.wiki/w/Damage_per_second/Melee#Step_six:_Calculate_the_hit_chance Yes I know it's old school. It's the best resource we have for potentially authentic formulae. entity ?: return 0 - var effectiveAttackLevel = entity.skills.getLevel(Skills.ATTACK).toDouble() - if(entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) - effectiveAttackLevel = floor(effectiveAttackLevel + (entity.prayer.getSkillBonus(Skills.ATTACK) * effectiveAttackLevel)) - if(entity.properties.attackStyle.style == WeaponInterface.STYLE_ACCURATE) effectiveAttackLevel += 3 - else if(entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveAttackLevel += 1 - effectiveAttackLevel += 8 - if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.PRECISION_STRIKES, entity)){ //Attack skillcape perk - effectiveAttackLevel += 6 - } - effectiveAttackLevel *= getSetMultiplier(entity, Skills.ATTACK) - effectiveAttackLevel = floor(effectiveAttackLevel) - if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) - effectiveAttackLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) - else effectiveAttackLevel *= 64 + val styleAttackBonus = entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64 + when (entity) { + is Player -> { + var effectiveAttackLevel = entity.skills.getLevel(Skills.ATTACK).toDouble() + if(!flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) + effectiveAttackLevel = floor(effectiveAttackLevel + (entity.prayer.getSkillBonus(Skills.ATTACK) * effectiveAttackLevel)) + if(entity.properties.attackStyle.style == WeaponInterface.STYLE_ACCURATE) effectiveAttackLevel += 3 + else if(entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveAttackLevel += 1 + effectiveAttackLevel += 8 + if(SkillcapePerks.isActive(SkillcapePerks.PRECISION_STRIKES, entity)){ //Attack skillcape perk + effectiveAttackLevel += 6 + } + effectiveAttackLevel *= getSetMultiplier(entity, Skills.ATTACK) + effectiveAttackLevel = floor(effectiveAttackLevel) + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) + effectiveAttackLevel *= styleAttackBonus + else effectiveAttackLevel *= 64 - val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none" + val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none" - // attack bonus for specialized equipments (salve amulets, slayer equips) - if (entity is Player) { - val amuletId = getItemFromEquipment(entity, EquipmentSlot.NECK)?.id ?: 0 - if ((amuletId == Items.SALVE_AMULET_4081 || amuletId == Items.SALVE_AMULETE_10588) && checkUndead(victimName)) { - effectiveAttackLevel *= if (amuletId == Items.SALVE_AMULET_4081) 1.15 else 1.2 - } else if (getSlayerTask(entity)?.ids?.contains((entity.properties.combatPulse?.getVictim()?.id ?: 0)) == true) { - effectiveAttackLevel *= SlayerEquipmentFlags.getDamAccBonus(entity) //Slayer Helm/ Black Mask/ Slayer cape - if (getSlayerTask(entity)?.dragon == true && inEquipment(entity, Items.DRAGON_SLAYER_GLOVES_12862)) - effectiveAttackLevel *= 1.1 + // attack bonus for specialized equipments (salve amulets, slayer equips) + val amuletId = getItemFromEquipment(entity, EquipmentSlot.NECK)?.id ?: 0 + if ((amuletId == Items.SALVE_AMULET_4081 || amuletId == Items.SALVE_AMULETE_10588) && checkUndead(victimName)) { + effectiveAttackLevel *= if (amuletId == Items.SALVE_AMULET_4081) 1.15 else 1.2 + } else if (getSlayerTask(entity)?.let { task -> + val victimId = entity.properties.combatPulse?.getVictim()?.id ?: 0 + task.ids.contains(victimId) || (task == Tasks.KALPHITES && (victimId == 1158)) // Kalphite Queen phase 1 + } == true) { + effectiveAttackLevel *= SlayerEquipmentFlags.getDamAccBonus(entity) //Slayer Helm/ Black Mask/ Slayer cape + if (getSlayerTask(entity)?.dragon == true && inEquipment(entity, Items.DRAGON_SLAYER_GLOVES_12862)) + effectiveAttackLevel *= 1.1 + } + + return effectiveAttackLevel.toInt() + } + is NPC -> { + val attackLevel = entity.skills.getLevel(Skills.ATTACK) + 9 + return attackLevel * styleAttackBonus } } - return floor(effectiveAttackLevel).toInt() + return 0 + + } override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { - val level = entity!!.skills.getLevel(Skills.STRENGTH) - var bonus = entity.properties.bonuses[11] - var prayer = 1.0 - if (entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE)) { - prayer += entity.prayer.getSkillBonus(Skills.STRENGTH) - } - var cumulativeStr = floor(level * prayer) - if (entity.properties.attackStyle.style == WeaponInterface.STYLE_AGGRESSIVE) { - cumulativeStr += 3.0 - } else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) { - cumulativeStr += 1.0 + entity ?: return 0 + + var styleStrengthBonus = entity.properties.bonuses[11] + 64 + when (entity) { + is Player -> { + var effectiveStrengthLevel = entity.skills.getLevel(Skills.STRENGTH).toDouble() + if(!flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE)) + effectiveStrengthLevel = floor(effectiveStrengthLevel + (entity.prayer.getSkillBonus(Skills.STRENGTH) * effectiveStrengthLevel)) + if(entity.properties.attackStyle.style == WeaponInterface.STYLE_AGGRESSIVE) effectiveStrengthLevel += 3 + else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveStrengthLevel += 1 + effectiveStrengthLevel += 8 + effectiveStrengthLevel *= getSetMultiplier(entity, Skills.STRENGTH) + effectiveStrengthLevel = floor(effectiveStrengthLevel) + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE)) + effectiveStrengthLevel *= styleStrengthBonus + else effectiveStrengthLevel *= 64 + if (getSlayerTask(entity)?.let { task -> + val victimId = entity.properties.combatPulse?.getVictim()?.id ?: 0 + task.ids.contains(victimId) || (task == Tasks.KALPHITES && (victimId == 1158)) // Kalphite Queen phase 1 + } == true) { + effectiveStrengthLevel *= SlayerEquipmentFlags.getDamAccBonus(entity) //Slayer Helm/ Black Mask/ Slayer cape + } + + return (floor((0.5 + (effectiveStrengthLevel / 640.0))) * modifier).toInt() + } + is NPC -> { + val strengthLevel = entity.skills.getLevel(Skills.STRENGTH) + 9 + return (floor((0.5 + (strengthLevel * styleStrengthBonus / 640.0))) * modifier).toInt() + } } - //Strength skillcape perk - if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.FINE_ATTUNEMENT, entity) && getItemFromEquipment(entity, EquipmentSlot.WEAPON)?.definition?.getRequirement(Skills.STRENGTH) != 0) - bonus = ceil(bonus * 1.20).toInt() - - if (flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE)) - bonus = 0 - - cumulativeStr *= getSetMultiplier(entity, Skills.STRENGTH) - - if(entity is Player && getSlayerTask(entity)?.ids?.contains((entity.properties.combatPulse?.getVictim()?.id ?: 0)) == true) - cumulativeStr *= SlayerEquipmentFlags.getDamAccBonus(entity) //Slayer helm/black mask/skillcape - - /*val hit = (16 + cumulativeStr + bonus / 8 + cumulativeStr * bonus * 0.016865) * modifier - return (hit / 10).toInt() + 1*/ - return ((1.3 + (cumulativeStr / 10) + (bonus / 80) + ((cumulativeStr * bonus) / 640)) * modifier).toInt() + return 0 } override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { @@ -197,19 +226,19 @@ open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) victim ?: return 0 attacker ?: return 0 - when(victim){ + val styleDefenceBonus = victim.properties.bonuses[attacker.properties.attackStyle.bonusType + 5] + 64 + when (victim) { is Player -> { var effectiveDefenceLevel = victim.skills.getLevel(Skills.DEFENCE).toDouble() effectiveDefenceLevel = floor(effectiveDefenceLevel + (victim.prayer.getSkillBonus(Skills.DEFENCE) * effectiveDefenceLevel)) - if(victim.properties.attackStyle.style == WeaponInterface.STYLE_DEFENSIVE) effectiveDefenceLevel += 3 - else if(victim.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveDefenceLevel += 1 + if (victim.properties.attackStyle.style == WeaponInterface.STYLE_DEFENSIVE || victim.properties.attackStyle.style == WeaponInterface.STYLE_LONG_RANGE) effectiveDefenceLevel += 3 + else if (victim.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveDefenceLevel += 1 effectiveDefenceLevel += 8 - effectiveDefenceLevel = floor(effectiveDefenceLevel) - return floor(effectiveDefenceLevel * (victim.properties.bonuses[attacker.properties.attackStyle.bonusType + 5] + 64)).toInt() + effectiveDefenceLevel *= getSetMultiplier(victim, Skills.DEFENCE) + return effectiveDefenceLevel.toInt() * styleDefenceBonus } is NPC -> { - val defLevel = victim.skills.getLevel(Skills.DEFENCE) - val styleDefenceBonus = victim.properties.bonuses[attacker.properties.attackStyle.bonusType + 5] + 64 + val defLevel = victim.skills.getLevel(Skills.DEFENCE) + 9 return defLevel * styleDefenceBonus } } diff --git a/Server/src/main/core/game/node/entity/combat/MultiSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MultiSwingHandler.kt index 41ac8dca3..d283f3aff 100644 --- a/Server/src/main/core/game/node/entity/combat/MultiSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MultiSwingHandler.kt @@ -8,7 +8,7 @@ import core.tools.RandomFunction /** * Handles combat swings with switching combat styles. * @author Emperor - * @author Ceirky, Kotlin conversion + * @author Ceikry, Kotlin conversion */ open class MultiSwingHandler(meleeDistance: Boolean, vararg attacks: SwitchAttack) : CombatSwingHandler(CombatStyle.RANGE) { /** diff --git a/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt index 325dd3f78..335a32abc 100644 --- a/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt @@ -13,14 +13,14 @@ import core.game.node.entity.skill.Skills import core.game.node.item.GroundItem import core.game.node.item.GroundItemManager import core.game.node.item.Item +import core.game.system.config.ItemConfigParser import core.game.system.task.Pulse +import core.game.world.GameWorld import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.update.flag.context.Graphics -import core.tools.RandomFunction -import core.tools.SystemLogger -import core.game.world.GameWorld import core.tools.Log +import core.tools.RandomFunction import java.util.* import kotlin.math.ceil import kotlin.math.floor @@ -30,11 +30,10 @@ import kotlin.math.floor * @author Emperor * @author Ceikry, conversion to Kotlin + cleanup */ -open class RangeSwingHandler (vararg flags: SwingHandlerFlag) +open class RangeSwingHandler (vararg flags: SwingHandlerFlag) : CombatSwingHandler(CombatStyle.RANGE, *flags) { /** * Constructs a new `RangeSwingHandler` {@Code Object}. */ - : CombatSwingHandler(CombatStyle.RANGE, *flags) { override fun canSwing(entity: Entity, victim: Entity): InteractionType? { if (!isProjectileClipped(entity, victim, false)) { return InteractionType.NO_INTERACT @@ -75,15 +74,24 @@ open class RangeSwingHandler (vararg flags: SwingHandlerFlag) return -1 } var hit = 0 - if (isAccurateImpact(entity, victim, CombatStyle.RANGE)) { - val max = calculateHit(entity, victim, 1.0).also { if(entity?.name?.toLowerCase() == "test10") log(this::class.java, Log.FINE, "Damage: $it") } + val armourPierce = state.ammunition != null && state.ammunition.effect != null && state.ammunition.effect == BoltEffect.DIAMOND && state.ammunition.effect.canFire(state) + if (armourPierce || isAccurateImpact(entity, victim, CombatStyle.RANGE)) { + val max: Int + if (armourPierce) { + state.ammunition.effect.impact(state) + flags.add(SwingHandlerFlag.IGNORE_STAT_REDUCTION) + max = (calculateHit(entity, victim, 1.0) * 1.15).toInt() + flags.remove(SwingHandlerFlag.IGNORE_STAT_REDUCTION) + } else { + max = calculateHit(entity, victim, 1.0) + } state.maximumHit = max hit = RandomFunction.random(max + 1) } state.estimatedHit = hit if (state.weapon.type == WeaponType.DOUBLE_SHOT) { if (isAccurateImpact(entity, victim, CombatStyle.RANGE)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)) + hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1) } state.secondaryHit = hit } @@ -124,6 +132,13 @@ open class RangeSwingHandler (vararg flags: SwingHandlerFlag) if (state.ammunition != null && entity is Player) { val damage = state.ammunition.poisonDamage if (state.estimatedHit > 0 && damage > 8 && RandomFunction.random(10) < 4) { + applyPoison(victim, entity, damage) + } + } else if (entity is NPC) { + val poisonous = entity.isPoisonous + val damage = entity.poisonSeverity() + + if (poisonous && damage > -1 && RandomFunction.random(10) < 4) { applyPoison (victim, entity, damage) } } @@ -161,7 +176,7 @@ open class RangeSwingHandler (vararg flags: SwingHandlerFlag) } override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { - if (state!!.ammunition != null && state.ammunition.effect != null && state.ammunition.effect.canFire(state)) { + if (state!!.ammunition != null && state.ammunition.effect != null && state.ammunition.effect != BoltEffect.DIAMOND && state.ammunition.effect.canFire(state)) { state.ammunition.effect.impact(state) } val hit = state.estimatedHit @@ -183,56 +198,98 @@ open class RangeSwingHandler (vararg flags: SwingHandlerFlag) override fun calculateAccuracy(entity: Entity?): Int { entity ?: return 0 - var effectiveRangedLevel = entity.skills.getLevel(Skills.RANGE).toDouble() - if(entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) - effectiveRangedLevel = floor(effectiveRangedLevel + (entity.prayer.getSkillBonus(Skills.RANGE) * effectiveRangedLevel)) - if(entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) effectiveRangedLevel += 3 - effectiveRangedLevel += 8 - effectiveRangedLevel *= getSetMultiplier(entity, Skills.RANGE) - if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.ACCURATE_MARKSMAN,entity)) effectiveRangedLevel *= 1.1 - effectiveRangedLevel = floor(effectiveRangedLevel) - if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) - effectiveRangedLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) - else effectiveRangedLevel *= 64 + val styleAttackBonus = entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64 + when (entity) { + is Player -> { + var effectiveRangedLevel = entity.skills.getLevel(Skills.RANGE).toDouble() + if(!flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) + effectiveRangedLevel = floor(effectiveRangedLevel + (entity.prayer.getSkillBonus(Skills.RANGE) * effectiveRangedLevel)) + if(entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) effectiveRangedLevel += 3 + effectiveRangedLevel += 8 + effectiveRangedLevel *= getSetMultiplier(entity, Skills.RANGE) + if(SkillcapePerks.isActive(SkillcapePerks.ACCURATE_MARKSMAN,entity)) effectiveRangedLevel *= 1.1 - return floor(effectiveRangedLevel).toInt() + effectiveRangedLevel = floor(effectiveRangedLevel) + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) + effectiveRangedLevel *= styleAttackBonus + else effectiveRangedLevel *= 64 + + return effectiveRangedLevel.toInt() + } + is NPC -> { + val rangedLevel = entity.skills.getLevel(Skills.RANGE) + 9 + return rangedLevel * styleAttackBonus + } + } + + return 0 } override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { - val level = entity!!.skills.getLevel(Skills.RANGE) - val bonus = entity.properties.bonuses[14] - var prayer = 1.0 - if (entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE)) { - prayer += entity.prayer.getSkillBonus(Skills.RANGE) - } - var cumulativeStr = floor(level * prayer) - if (entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) { - cumulativeStr += 3.0 - } - cumulativeStr *= getSetMultiplier(entity, Skills.RANGE) + entity ?: return 0 - if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE)) - cumulativeStr *= (bonus + 64) - else cumulativeStr *= 64 + var styleStrengthBonus = entity.properties.bonuses[14] + 64 + when (entity) { + is Player -> { + if(entity.equipment[EquipmentContainer.SLOT_WEAPON] != null && RangeWeapon.get(entity.equipment[EquipmentContainer.SLOT_WEAPON].id).ammunitionSlot != EquipmentContainer.SLOT_ARROWS && entity.equipment[EquipmentContainer.SLOT_ARROWS] != null) + styleStrengthBonus -= entity.equipment[EquipmentContainer.SLOT_ARROWS].definition.getConfiguration(ItemConfigParser.BONUS)[14] + var effectiveStrengthLevel = entity.skills.getLevel(Skills.RANGE).toDouble() + if (flags.contains(SwingHandlerFlag.IGNORE_STAT_REDUCTION)) { + val staticLevel = entity.skills.getStaticLevel(Skills.RANGE).toDouble() + if (staticLevel > effectiveStrengthLevel) { + effectiveStrengthLevel = staticLevel + } + } + if (!flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE)) + effectiveStrengthLevel = floor(effectiveStrengthLevel + (entity.prayer.getSkillBonus(Skills.RANGE) * effectiveStrengthLevel)) + if(entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) effectiveStrengthLevel += 3 + effectiveStrengthLevel += 8 + effectiveStrengthLevel *= getSetMultiplier(entity, Skills.RANGE) + effectiveStrengthLevel = floor(effectiveStrengthLevel) + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE)) + effectiveStrengthLevel *= styleStrengthBonus + else effectiveStrengthLevel *= 64 - return floor((1.5 + (ceil(cumulativeStr) / 640.0)) * modifier).toInt() - //return ((14 + cumulativeStr + bonus / 8 + cumulativeStr * bonus * 0.016865) * modifier).toInt() / 10 + 1 + return (floor((0.5 + (effectiveStrengthLevel / 640.0))) * modifier).toInt() + } + is NPC -> { + val rangedLevel = entity.skills.getLevel(Skills.RANGE) + 9 + return (floor((0.5 + (rangedLevel * styleStrengthBonus / 640.0))) * modifier).toInt() + } + } + + return 0 } override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { victim ?: return 0 attacker ?: return 0 - val defLevel = victim.skills.getLevel(Skills.DEFENCE) val styleDefenceBonus = victim.properties.bonuses[attacker.properties.attackStyle.bonusType + 5] + 64 - return defLevel * styleDefenceBonus + when (victim) { + is Player -> { + var effectiveDefLevel = victim.skills.getLevel(Skills.DEFENCE).toDouble() + effectiveDefLevel = floor(effectiveDefLevel + (victim.prayer.getSkillBonus(Skills.DEFENCE) * effectiveDefLevel)) + if (victim.properties.attackStyle.style == WeaponInterface.STYLE_DEFENSIVE || victim.properties.attackStyle.style == WeaponInterface.STYLE_LONG_RANGE) effectiveDefLevel += 3 + else if (victim.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveDefLevel += 1 + effectiveDefLevel += 8 + effectiveDefLevel *= getSetMultiplier(victim, Skills.DEFENCE) + return effectiveDefLevel.toInt() * styleDefenceBonus + } + is NPC -> { + val defLevel = victim.skills.getLevel(Skills.DEFENCE) + 9 + return defLevel * styleDefenceBonus + } + } + + return 0 } override fun getSetMultiplier(e: Entity?, skillId: Int): Double { if(skillId == Skills.RANGE) { if(e is Player && e.isWearingVoid(CombatStyle.RANGE)) { - return 1.1 + return 1.2 } } return 1.0 diff --git a/Server/src/main/core/game/node/entity/combat/equipment/Ammunition.java b/Server/src/main/core/game/node/entity/combat/equipment/Ammunition.java index 33461f024..a7b895166 100644 --- a/Server/src/main/core/game/node/entity/combat/equipment/Ammunition.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/Ammunition.java @@ -74,7 +74,7 @@ public final class Ammunition { * Loads all the {@code Ammunition} info to the mapping. * @return {@code True}. */ - public static final boolean initialize() { + public static boolean initialize() { Document doc; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); @@ -141,7 +141,7 @@ public final class Ammunition { * @param id The ammo id. * @return The ammunition object. */ - public static final Ammunition get(int id) { + public static Ammunition get(int id) { return AMMUNITION.get(id); } @@ -189,7 +189,7 @@ public final class Ammunition { } /** - * Sets the baeffect. + * Sets the effect. * @param effect the effect to set. */ public void setEffect(BoltEffect effect) { diff --git a/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java b/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java index 16097c032..f311b95e6 100644 --- a/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java @@ -21,7 +21,7 @@ public enum ArmourSet { AHRIM(new Graphics(401, 96), new int[][] { { 4708, 4856, 4857, 4858, 4859 }, { 4710, 4862, 4863, 4864, 4865 }, { 4712, 4868, 4869, 4870, 4871 }, { 4714, 4874, 4875, 4876, 4877 } }) { @Override public boolean effect(Entity e, Entity victim, BattleState state) { - if (RandomFunction.random(100) < 20) { + if (RandomFunction.random(100) < 25 && state.getEstimatedHit() > -1) { victim.getSkills().updateLevel(Skills.STRENGTH, -5, 0); return true; } @@ -78,8 +78,8 @@ public enum ArmourSet { KARIL(new Graphics(400, 96), new int[][] { { 4732, 4928, 4929, 4930, 4931 }, { 4734, 4934, 4935, 4936, 4937 }, { 4736, 4940, 4941, 4942, 4943 }, { 4738, 4946, 4947, 4948, 4949 } }) { @Override public boolean effect(Entity e, Entity victim, BattleState state) { - if (state.getEstimatedHit() > 9 && RandomFunction.random(100) < 20) { - victim.getSkills().updateLevel(Skills.AGILITY, -(state.getEstimatedHit() / 10), 0); + if (state.getEstimatedHit() > 0 && RandomFunction.random(100) < 25) { + victim.getSkills().updateLevel(Skills.AGILITY, -(victim.getSkills().getDynamicLevels()[Skills.AGILITY] / 5), 0); return true; } return false; @@ -100,7 +100,7 @@ public enum ArmourSet { TORAG(new Graphics(399, 96), new int[][] { { 4745, 4952, 4953, 4954, 4955 }, { 4747, 4958, 4959, 4960, 4961 }, { 4749, 4964, 4965, 4966, 4967 }, { 4751, 4970, 4971, 4972, 4973 } }) { @Override public boolean effect(Entity e, Entity victim, BattleState state) { - if (state.getEstimatedHit() > 0 && RandomFunction.random(100) < 20) { + if (state.getEstimatedHit() > 0 && RandomFunction.random(100) < 25) { if (victim instanceof Player) { ((Player) victim).getSettings().updateRunEnergy(20); } diff --git a/Server/src/main/core/game/node/entity/combat/equipment/BoltEffect.java b/Server/src/main/core/game/node/entity/combat/equipment/BoltEffect.java index eabede5ff..7fb24f1a5 100644 --- a/Server/src/main/core/game/node/entity/combat/equipment/BoltEffect.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/BoltEffect.java @@ -21,7 +21,6 @@ import static core.api.ContentAPIKt.*; */ public enum BoltEffect { OPAL(9236, Graphics.create(749), new Audio(2918)) { - @Override public void impact(BattleState state) { state.setEstimatedHit(state.getEstimatedHit() + RandomFunction.random(3, 20)); @@ -33,7 +32,6 @@ public enum BoltEffect { }, JADE(9237, new Graphics(755), new Audio(2916)) { - @Override public void impact(BattleState state) { if (state.getVictim() instanceof Player) { @@ -59,7 +57,6 @@ public enum BoltEffect { }, PEARL(9238, Graphics.create(750), new Audio(2920)) { - @Override public void impact(BattleState state) { state.setEstimatedHit(state.getEstimatedHit() + RandomFunction.random(3, 20)); @@ -108,12 +105,11 @@ public enum BoltEffect { EMERALD(9241, new Graphics(752), new Audio(2919)) { @Override public void impact(BattleState state) { - applyPoison(state.getVictim(), state.getAttacker(), 40); + applyPoison(state.getVictim(), state.getAttacker(), 40); super.impact(state); } }, RUBY(9242, new Graphics(754), new Audio(2911, 1)) { // in this case, volume is the number of times to play the sound... - @Override public void impact(BattleState state) { // hit target for 20% of their HP, hit self for 10% of HP int victimPoints = (int) (state.getVictim().getSkills().getLifepoints() * 0.20); @@ -135,15 +131,8 @@ public enum BoltEffect { return super.canFire(state) && state.getAttacker().getSkills().getLifepoints() - playerPoints >= 1; } }, - DIAMOND(9243, new Graphics(758), new Audio(2913)) { - @Override - public void impact(BattleState state) { - state.setEstimatedHit(state.getEstimatedHit() + RandomFunction.random(5, 14)); // unauthentic, needs fixing - super.impact(state); - } - }, + DIAMOND(9243, new Graphics(758), new Audio(2913)) { /* handled in RangeSwingHandler.kt::swing(entity: Entity?, victim: Entity?, state: BattleState?) */ }, DRAGON(9244, new Graphics(756), new Audio(2915)) { - @Override public void impact(BattleState state) { state.setEstimatedHit(state.getEstimatedHit() + RandomFunction.random(17, 29)); @@ -165,10 +154,8 @@ public enum BoltEffect { } return super.canFire(state); } - }, ONYX(9245, new Graphics(753), new Audio(2917)) { - @Override public void impact(BattleState state) { int newDamage = (int) (state.getEstimatedHit() * 0.25); @@ -270,5 +257,4 @@ public enum BoltEffect { public int getItemId() { return itemId; } - } diff --git a/Server/src/main/core/game/node/entity/combat/graves/Grave.kt b/Server/src/main/core/game/node/entity/combat/graves/Grave.kt index efd2f9b17..e330489a9 100644 --- a/Server/src/main/core/game/node/entity/combat/graves/Grave.kt +++ b/Server/src/main/core/game/node/entity/combat/graves/Grave.kt @@ -1,5 +1,6 @@ package core.game.node.entity.combat.graves +import content.global.handlers.item.equipment.BarrowsEquipment import core.api.clearHintIcon import core.api.registerHintIcon import core.api.sendMessage @@ -49,7 +50,7 @@ class Grave : AbstractNPC { this.ownerUid = player.details.uid this.ownerUsername = player.username - this.location = location + this.location = player.getAttribute("/save:original-loc",location) this.isRespawn = false this.isWalks = false this.isNeverWalks = true @@ -65,7 +66,11 @@ class Grave : AbstractNPC { continue } - val finalItem = GraveController.checkTransform(item) + val finalItem = if (BarrowsEquipment.isBarrowsItem(item.id) && !BarrowsEquipment.isFullyRepaired(item.id)) { + BarrowsEquipment.graveDeathDurabilityReduction(item) ?: item + } else { + GraveController.checkTransform(item) + } val gi = GroundItemManager.create(finalItem, this.location, player) gi.isRemainPrivate = true diff --git a/Server/src/main/core/game/node/entity/combat/graves/GraveController.kt b/Server/src/main/core/game/node/entity/combat/graves/GraveController.kt index a57a76562..00e0e0b1b 100644 --- a/Server/src/main/core/game/node/entity/combat/graves/GraveController.kt +++ b/Server/src/main/core/game/node/entity/combat/graves/GraveController.kt @@ -17,6 +17,7 @@ import org.rs09.consts.Items import core.ServerStore import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.interaction.QueueStrength import core.game.system.command.Privilege import core.game.world.GameWorld import core.game.world.map.zone.impl.WildernessZone @@ -36,18 +37,16 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command } override fun defineCommands() { - define("forcegravedeath", Privilege.ADMIN, "", "Forces a death that should produce a grave.") {player, _ -> + define("forcegravedeath", Privilege.ADMIN, "", "Forces a death that should produce a grave.") { player, _ -> player.details.rights = Rights.REGULAR_PLAYER setAttribute(player, "tutorial:complete", true) player.impactHandler.manualHit(player, player.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL) - notify(player, "Grave created at ${player.location}") - GameWorld.Pulser.submit(object : Pulse(15) { - override fun pulse(): Boolean { - player.details.rights = Rights.ADMINISTRATOR - sendMessage(player, "Rights restored") - return true - } - }) + notify(player, "Grave created at ${player.getAttribute("/save:original-loc", player.location)}") + queueScript(player, 15, QueueStrength.SOFT) { stage: Int -> + player.details.rights = Rights.ADMINISTRATOR + sendMessage(player, "Rights restored") + return@queueScript stopExecuting(player) + } } } @@ -106,6 +105,12 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command return true } + val gOwner = Repository.uid_map[g.ownerUid] + if (gOwner != null && gOwner.ironmanManager.isIronman) { + sendMessage(player, "This grave belongs to an Ironman.") + return true + } + if (getStatLevel(player, Skills.PRAYER) < 70) { sendMessage(player, "You need a Prayer level of 70 to bless a grave.") return true @@ -125,7 +130,6 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command playAudio(player, Sounds.PRAYER_RECHARGE_2674) animate(player, 645) - val gOwner = Repository.uid_map[g.ownerUid] if (gOwner != null) { sendMessage(gOwner, colorize("%RYour grave has been blessed.")) } @@ -290,4 +294,4 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command } } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/combat/graves/GravePurchaseInterface.kt b/Server/src/main/core/game/node/entity/combat/graves/GravePurchaseInterface.kt index fb2f2927e..86a589f17 100644 --- a/Server/src/main/core/game/node/entity/combat/graves/GravePurchaseInterface.kt +++ b/Server/src/main/core/game/node/entity/combat/graves/GravePurchaseInterface.kt @@ -43,7 +43,7 @@ class GravePurchaseInterface : InterfaceListener { val cost = selectedType.cost val requirement = selectedType.requiredQuest - if (requirement.isNotEmpty() && !isQuestComplete(player, requirement)) { + if (requirement != null && !isQuestComplete(player, requirement)) { sendDialogue(player, "That gravestone requires completion of $requirement.") return@on true } diff --git a/Server/src/main/core/game/node/entity/combat/graves/GraveType.kt b/Server/src/main/core/game/node/entity/combat/graves/GraveType.kt index 7d4f7bc5b..1a9105873 100644 --- a/Server/src/main/core/game/node/entity/combat/graves/GraveType.kt +++ b/Server/src/main/core/game/node/entity/combat/graves/GraveType.kt @@ -1,8 +1,9 @@ package core.game.node.entity.combat.graves import org.rs09.consts.NPCs +import content.data.Quests -enum class GraveType(val npcId: Int, val cost: Int, val durationMinutes: Int, val isMembers: Boolean, val requiredQuest: String = "", val text: String) { +enum class GraveType(val npcId: Int, val cost: Int, val durationMinutes: Int, val isMembers: Boolean, val requiredQuest: Quests? = null, val text: String) { MEM_PLAQUE(NPCs.GRAVE_MARKER_6565, 0, 2, false, text = "In memory of @name,
who died here."), FLAG(NPCs.GRAVE_MARKER_6568, 50, 2, false, text = MEM_PLAQUE.text), SMALL_GS(NPCs.GRAVESTONE_6571, 500, 2, false, text = "In loving memory of our dear friend @name,
who died in this place @mins ago."), @@ -12,9 +13,9 @@ enum class GraveType(val npcId: Int, val cost: Int, val durationMinutes: Int, va SARA_SYMBOL(NPCs.SARADOMIN_SYMBOL_6583, 50000, 4, true, text = "@name,
an enlightened servant of Saradomin,
perished in this place."), ZAM_SYMBOL(NPCs.ZAMORAK_SYMBOL_6586, 50000, 4, true, text = "@name,
a most bloodthirsty follower of Zamorak,
perished in this place."), GUTH_SYMBOL(NPCs.GUTHIX_SYMBOL_6589, 50000, 4, true, text = "@name,
who walked with the Balance of Guthix,
perished in this place."), - BAND_SYMBOL(NPCs.BANDOS_SYMBOL_6592, 50000, 4, true, requiredQuest = "Land of the Goblins", text = "@name,
a vicious warrior dedicated to Bandos,
perished in this place. "), - ARMA_SYMBOL(NPCs.ARMADYL_SYMBOL_6595, 50000, 4, true, requiredQuest = "Temple of Ikov", text = "@name,
a follower of the Law of Armadyl,
perished in this place."), - ZARO_SYMBOL(NPCs.MEMORIAL_STONE_6598, 50000, 4, true, requiredQuest = "Desert Treasure", text = "@name,
servant of the Unknown Power,
perished in this place."), + BAND_SYMBOL(NPCs.BANDOS_SYMBOL_6592, 50000, 4, true, requiredQuest = Quests.LAND_OF_THE_GOBLINS, text = "@name,
a vicious warrior dedicated to Bandos,
perished in this place. "), + ARMA_SYMBOL(NPCs.ARMADYL_SYMBOL_6595, 50000, 4, true, requiredQuest = Quests.TEMPLE_OF_IKOV, text = "@name,
a follower of the Law of Armadyl,
perished in this place."), + ZARO_SYMBOL(NPCs.MEMORIAL_STONE_6598, 50000, 4, true, requiredQuest = Quests.DESERT_TREASURE, text = "@name,
servant of the Unknown Power,
perished in this place."), ANGEL_DEATH(NPCs.MEMORIAL_STONE_6601, 500000, 5, true, text = "Ye frail mortals who gaze upon this sight,
forget not the fate of @name, once mighty, now
surrendered to the inescapable grasp of destiny.
Requiescat in pace."); companion object { diff --git a/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java b/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java index 3601d6849..537262cf9 100644 --- a/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java +++ b/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java @@ -1,5 +1,6 @@ package core.game.node.entity.combat.spell; +import content.global.skill.summoning.familiar.Familiar; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.InteractionType; @@ -13,6 +14,7 @@ import core.game.node.entity.player.link.SpellBookManager; import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.world.map.RegionManager; +import core.game.world.map.zone.impl.WildernessZone; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import org.rs09.consts.Sounds; @@ -106,27 +108,42 @@ public abstract class CombatSpell extends MagicSpell { } - /** - * Gets a list of possible targets for a multihitting spell. - * @param entity The caster of the spell. - * @param target The victim. - * @param max The max amount of victims. - * @return The list of targets. - */ - public List getMultihitTargets(Entity entity, Entity target, int max) { - List list = new ArrayList<>(20); - list.add(target); - boolean npc = target instanceof NPC; - for (Entity e : npc ? RegionManager.getSurroundingNPCs(target) : RegionManager.getSurroundingPlayers(target)) { - if (e != target && e != entity && CombatStyle.MAGIC.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) { - list.add(e); + /** + * Gets a list of valid targets for a multihitting spell. + * @param entity The caster of the spell. + * @param target The primary victim. + * @param max The maximum number of extra victims that may be hit. + * @return The list of targets (the primary target is always at index 0). + */ + public List getMultihitTargets(Entity entity, Entity target, int max) { + List victims = new ArrayList<>(20); + victims.add(target); + + List surrounding = new ArrayList<>(); + surrounding.addAll(RegionManager.getSurroundingPlayers(target)); + surrounding.addAll(RegionManager.getSurroundingNPCs(target)); + + for (Entity e : surrounding) { + if (e == target || e == entity) { + continue; + } + if (CombatStyle.MAGIC.getSwingHandler().canSwing(entity, e) == InteractionType.NO_INTERACT) { + continue; + } + if (e instanceof Familiar) { + Player owner = ((Familiar) e).getOwner(); + if (owner != entity && WildernessZone.getInstance().continueAttack(entity, owner, CombatStyle.MAGIC, true)) { + victims.add(e); + } + } else { + victims.add(e); } if (--max < 1) { break; } } - return list; - } + return victims; + } /** * Visualizes the impact. @@ -235,4 +252,4 @@ public abstract class CombatSpell extends MagicSpell { return SPLASH_GRAPHIC; } -} +} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/combat/spell/MagicStaff.java b/Server/src/main/core/game/node/entity/combat/spell/MagicStaff.java index 103a63264..6664e721b 100644 --- a/Server/src/main/core/game/node/entity/combat/spell/MagicStaff.java +++ b/Server/src/main/core/game/node/entity/combat/spell/MagicStaff.java @@ -27,7 +27,7 @@ public enum MagicStaff { /** * Represents the earth rune staves. */ - EARTH_RUNE(557, 3053, 3055, 3056, 1385, 1399, 1407, 557, 6563, 6562); + EARTH_RUNE(557, 3053, 3054, 3055, 3056, 1385, 1399, 1407, 557, 6563, 6562); /** * The magic staves mapping. diff --git a/Server/src/main/core/game/node/entity/impl/Animator.java b/Server/src/main/core/game/node/entity/impl/Animator.java index 3afafb41b..430c4d551 100644 --- a/Server/src/main/core/game/node/entity/impl/Animator.java +++ b/Server/src/main/core/game/node/entity/impl/Animator.java @@ -2,7 +2,6 @@ package core.game.node.entity.impl; import core.game.interaction.Clocks; import core.game.node.entity.Entity; -import core.game.node.entity.npc.NPC; import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; diff --git a/Server/src/main/core/game/node/entity/impl/PulseManager.java b/Server/src/main/core/game/node/entity/impl/PulseManager.java index 74a78e7cc..6f93e1250 100644 --- a/Server/src/main/core/game/node/entity/impl/PulseManager.java +++ b/Server/src/main/core/game/node/entity/impl/PulseManager.java @@ -17,6 +17,12 @@ import java.util.HashMap; */ public final class PulseManager { + private final Entity entity; + + public PulseManager(Entity entity) { + this.entity = entity; + } + /** * The movement pulse. */ @@ -61,6 +67,9 @@ public final class PulseManager { } public void clear() { + entity.scripts.removeWeakScripts(); + entity.scripts.removeNormalScripts(); + currentPulses.forEach((type, pulse) -> { if (type != PulseType.STRONG && pulse != null) pulse.stop(); }); @@ -70,6 +79,9 @@ public final class PulseManager { * Clears the pulses. */ public boolean clear(PulseType pulseType) { + entity.scripts.removeWeakScripts(); + entity.scripts.removeNormalScripts(); + Pulse pulse = currentPulses.get(pulseType); if (pulse != null) { diff --git a/Server/src/main/core/game/node/entity/npc/Metamorphosis.java b/Server/src/main/core/game/node/entity/npc/Metamorphosis.java deleted file mode 100644 index 9b3175aed..000000000 --- a/Server/src/main/core/game/node/entity/npc/Metamorphosis.java +++ /dev/null @@ -1,94 +0,0 @@ -package core.game.node.entity.npc; - -import core.cache.def.impl.NPCDefinition; -import core.game.dialogue.DialoguePlugin; -import content.global.skill.summoning.familiar.Familiar; -import content.global.skill.summoning.pet.Pets; -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 core.tools.RandomFunction; - -/** - * A superclass plugin for any pets that have a metamorphosis option. - * @author Empathy - * - */ -public abstract class Metamorphosis extends OptionHandler { - - /** - * The ids of the possible npcs to metamorph into. - */ - protected int[] ids; - - - /** - * Constructs a new {@code Metamorphosis} {@code Object}. - * @param ids the id to transform. - */ - public Metamorphosis(int...ids) { - this.ids = ids; - } - - /** - * The dialogue plugin for the pet. - * @return the plugin. - */ - public abstract DialoguePlugin getDialoguePlugin(); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : getIds()) { - NPCDefinition.forId(id).getHandlers().put("option:metamorphosis", this); - } - if (getDialoguePlugin() != null) { - ClassScanner.definePlugin(getDialoguePlugin()); - } - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Familiar familiar = (Familiar) node; - switch (option) { - case "metamorphosis": - if (player.getFamiliarManager().isOwner(familiar)) { - int newNpc = player.getFamiliarManager().getFamiliar().getId(); - while (newNpc == player.getFamiliarManager().getFamiliar().getId()) { - newNpc = getRandomNpcId(); - } - for (Pets p : Pets.values()) { - if (p.getBabyNpcId() == newNpc) { - player.getFamiliarManager().morphPet(new Item(p.getBabyItemId()), false, player.getFamiliarManager().getFamiliar().getLocation()); - break; - } - } - player.getPacketDispatch().sendMessage("You transform your " + player.getFamiliarManager().getFamiliar().getName() + "!"); - } else { - player.getPacketDispatch().sendMessage("This is not your familiar."); - } - break; - } - return true; - } - - /** - * Gets a random npc id. - * @return - */ - public int getRandomNpcId() { - int i = RandomFunction.getRandom(getIds().length - 1); - return getIds()[i]; - } - - /** - * Gets the npc ids. - * @return the id. - */ - public int[] getIds() { - return ids; - } -} diff --git a/Server/src/main/core/game/node/entity/npc/NPC.java b/Server/src/main/core/game/node/entity/npc/NPC.java index 31dcd8ca3..2ce5481cc 100644 --- a/Server/src/main/core/game/node/entity/npc/NPC.java +++ b/Server/src/main/core/game/node/entity/npc/NPC.java @@ -8,7 +8,6 @@ import core.game.interaction.MovementPulse; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.spell.CombatSpell; -import core.game.node.entity.combat.CombatPulse; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.spell.DefaultCombatSpell; import core.game.node.entity.combat.equipment.WeaponInterface; @@ -29,7 +28,7 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.*; import core.tools.RandomFunction; -import core.api.utils.GlobalKillCounter; +import core.api.utils.PlayerStatsCounter; import core.api.utils.Vector; import core.game.shops.Shops; import core.game.node.entity.combat.CombatSwingHandler; @@ -477,7 +476,6 @@ public class NPC extends Entity { public boolean pulse() { getProperties().getCombatPulse().stop(); getLocks().unlockMovement(); - fullRestore(); getImpactHandler().setDisabledTicks(0); removeAttribute("return-to-spawn"); removeAttribute("return-to-spawn-pulse"); @@ -523,6 +521,9 @@ public class NPC extends Entity { return false; } + public int getNextWalk() { + return nextWalk; + } /** * Sets the next walk. */ @@ -561,6 +562,14 @@ public class NPC extends Entity { return definition.getConfiguration(NPCConfigParser.POISON_IMMUNE, false); } + public boolean isPoisonous() { + return definition.getConfiguration(NPCConfigParser.POISONOUS, false); + } + + public int poisonSeverity() { + return definition.getConfiguration(NPCConfigParser.POISON_AMOUNT, 0); + } + @Override public void finalizeDeath(Entity killer) { super.finalizeDeath(killer); @@ -570,12 +579,12 @@ public class NPC extends Entity { Player p = !(killer instanceof Player) ? null : (Player) killer; if (p != null) { p.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_ENEMIES_KILLED); - GlobalKillCounter.incrementKills(p, originalId); + PlayerStatsCounter.incrementKills(p, originalId); } handleDrops(p, killer); if (!isRespawn()) clear(); - isRespawning = true; + isRespawning = true; behavior.onDeathFinished(this, killer); killer.dispatch(new NPCKillEvent(this)); setRespawnTick(GameWorld.getTicks() + definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 17)); diff --git a/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java b/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java index 0e525cf95..50f93be3f 100644 --- a/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java +++ b/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java @@ -72,9 +72,9 @@ public class AggressiveBehavior { if (entity instanceof NPC && target instanceof Player) { NPC npc = (NPC) entity; if (npc.getAggressiveHandler() != null && npc.getAggressiveHandler().isAllowTolerance() && !WildernessZone.isInZone(npc)) { - if (RegionManager.forId(regionId).isTolerated(target.asPlayer())) { - return false; - } + if (RegionManager.forId(regionId).isTolerated(target.asPlayer())) { + return false; + } } } int level = target.getProperties().getCurrentCombatLevel(); @@ -84,9 +84,9 @@ public class AggressiveBehavior { return true; } - public boolean ignoreCombatLevelDifference() { - return false; - } + public boolean ignoreCombatLevelDifference() { + return false; + } /** * Gets the priority flag. @@ -132,9 +132,9 @@ public class AggressiveBehavior { */ public Entity getLogicalTarget(Entity entity, List possibleTargets) { Entity target = null; - int comparingFlag = Integer.MAX_VALUE; + double comparingFlag = Double.MAX_VALUE; for (Entity e : possibleTargets) { - int flag = getPriorityFlag(e); + double flag = (double)getPriorityFlag(e) + Math.random(); if (flag <= comparingFlag) { comparingFlag = flag; target = e; diff --git a/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java index 31397a4c4..baacc117b 100644 --- a/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java +++ b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java @@ -147,7 +147,7 @@ public final class NPCDropTables { List players = RegionManager.getLocalPlayers(npc, 16); List looters = new ArrayList<>(20); for (Player p : players) { - if (p != null && p.getCommunication().getClan() != null && p.getCommunication().getClan() == player.getCommunication().getClan() && p.getCommunication().isLootShare() && p.getCommunication().getLootRequirement().ordinal() >= p.getCommunication().getClan().getLootRequirement().ordinal() && npc.getImpactHandler().getImpactLog().containsKey(p)) { + if (p != null && p.getCommunication().getClan() != null && p.getCommunication().getClan() == player.getCommunication().getClan() && p.getCommunication().isLootShare() && p.getCommunication().getLootRequirement().ordinal() >= p.getCommunication().getClan().getLootRequirement().ordinal()) { looters.add(p); } } diff --git a/Server/src/main/core/game/node/entity/player/Player.java b/Server/src/main/core/game/node/entity/player/Player.java index 330f2fb0e..dfe357ff0 100644 --- a/Server/src/main/core/game/node/entity/player/Player.java +++ b/Server/src/main/core/game/node/entity/player/Player.java @@ -1,11 +1,11 @@ package core.game.node.entity.player; +import content.global.handlers.item.equipment.BarrowsEquipment; import content.global.handlers.item.equipment.special.SalamanderSwingHandler; import content.global.skill.runecrafting.PouchManager; -import core.api.EquipmentSlot; +import core.api.ContentAPIKt; import core.game.component.Component; import core.game.container.Container; -import core.game.container.ContainerType; import core.game.container.impl.BankContainer; import core.game.container.impl.EquipmentContainer; import core.game.container.impl.InventoryListener; @@ -44,6 +44,7 @@ import core.game.system.task.Pulse; import core.game.world.map.*; import core.game.world.map.build.DynamicRegion; import core.game.world.map.path.Pathfinder; +import core.game.world.map.zone.ZoneRestriction; import core.game.world.map.zone.ZoneType; import core.game.world.update.flag.PlayerFlags; import core.game.world.update.flag.*; @@ -69,9 +70,6 @@ import core.game.node.entity.combat.CombatSwingHandler; import content.global.handlers.item.equipment.EquipmentDegrader; import core.game.node.entity.combat.graves.Grave; import core.game.node.entity.combat.graves.GraveController; -import core.game.node.entity.player.info.login.PlayerSaver; -import core.game.node.entity.state.State; -import core.game.node.entity.state.StateRepository; import core.game.world.GameWorld; import core.game.world.repository.Repository; import core.game.world.update.MapChunkRenderer; @@ -88,9 +86,11 @@ import java.util.*; import java.util.concurrent.TimeUnit; import static core.api.ContentAPIKt.*; +import static core.api.utils.PermadeathKt.permadeath; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_BASE; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_DEATHS; import static core.tools.GlobalsKt.colorize; +import static org.rs09.consts.Items.BONES_526; /** * Represents a player entity. @@ -120,13 +120,14 @@ public class Player extends Entity { public VarpManager varpManager = new VarpManager(this); - public HashMap varpMap = new HashMap<>(); - public HashMap saveVarp = new HashMap<>(); + public HashMap varpMap = new HashMap<>(); - public HashMap states = new HashMap<>(); + public HashMap saveVarp = new HashMap<>(); public HashMap> logoutListeners = new HashMap<>(); + public Boolean isAfkLogout; + /** * The inventory. */ @@ -152,21 +153,6 @@ public class Player extends Entity { */ public boolean useSecondaryBank = false; - /** - * The Blast Furnace Coal Container. - */ - public final Container blastCoal = new Container(225, ContainerType.NEVER_STACK); - - /** - * The Blast Furnace Ore Container. - */ - public final Container blastOre = new Container(28, ContainerType.NEVER_STACK); - - /** - * The Blast Furnace Bars Container. - */ - public final Container blastBars = new Container(28, ContainerType.NEVER_STACK); - /** * The packet dispatcher. */ @@ -310,12 +296,19 @@ public class Player extends Entity { * The amount of targets that the player can shoot left for the archery minigame. */ private int archeryTargets = 0; - private int archeryTotal = 0; - public byte[] opCounts = new byte[255]; + /** + * The save file version. + */ + public int version = ServerConstants.CURRENT_SAVEFILE_VERSION; - public int invalidPacketCount = 0; + /** + * Packet administration. + * opCounts is used to enforce an authentic limit of 10 of each inbound packet per user per tick. + */ + public byte[] opCounts = new byte[255]; + public int invalidPacketCount = 0; /** * Constructs a new {@code Player} {@code Object}. @@ -331,10 +324,8 @@ public class Player extends Entity { @Override public void init() { - if(!artificial) - log(this.getClass(), Log.INFO, getUsername() + " initialising..."); if (!artificial) { - getProperties().setSpawnLocation(ServerConstants.HOME_LOCATION); + log(this.getClass(), Log.INFO, getUsername() + " initialising..."); getDetails().getSession().setObject(this); } super.init(); @@ -467,11 +458,23 @@ public class Player extends Entity { settings.setSpecialEnergy(100); } - //Decrements prayer points + // Decrement prayer points getPrayer().tick(); - //update wealth tracking + // Update wealth tracking checkForWealthUpdate(false); + + // Check if the player is on the map, runs only every 6 seconds for performance reasons. + // This is only a sanity check to detect improper usage of the 'original-loc' attribute, hence only do this work if the attribute is set. + // Does not run if the player is currently in the middle of a kidnap sequence (to avoid false positives while the teleport is taking place). + if (GameWorld.getTicks() % 10 == 0 && ContentAPIKt.getAttribute(this, "/save:original-loc", null) != null && !ContentAPIKt.getAttribute(this, "kidnapped-by-random", false)) { + int rid = location.getRegionId(); + Region r = RegionManager.forId(rid); + if (!(r instanceof DynamicRegion) && !getZoneMonitor().isRestricted(ZoneRestriction.OFF_MAP)) { + log(this.getClass(), Log.ERR, "Player " + getUsername() + " has the original-loc attribute set but isn't actually off-map! This indicates a bug in the code that set that attribute. The original-loc is " + getAttribute("/save:original-loc") + ", the current region is " + rid + ". Good luck debugging!"); + ContentAPIKt.removeAttribute(this, "original-loc"); + } + } } private void checkForWealthUpdate(boolean force) { @@ -494,21 +497,21 @@ public class Player extends Entity { if (i == null) break; totalWealth += (long) i.getDefinition().getValue() * i.getAmount(); } - GrandExchangeRecords ge = GrandExchangeRecords.getInstance(this); - for (int i=0; i<6; i++) { - GrandExchangeOffer offer = ge.getOffer(i); - if (offer != null) { - totalWealth += offer.cacheValue(); - } - } + GrandExchangeRecords ge = GrandExchangeRecords.getInstance(this); + for (int i=0; i<6; i++) { + GrandExchangeOffer offer = ge.getOffer(i); + if (offer != null) { + totalWealth += offer.cacheValue(); + } + } - // This can lead to a false positive of up to 3 * 187.5k, but only for 3 ticks while a cannon is being constructed - if (this.getAttribute("dmc", null) != null) { - totalWealth += ItemDefinition.forId(Items.CANNON_BASE_6).getValue(); - totalWealth += ItemDefinition.forId(Items.CANNON_STAND_8).getValue(); - totalWealth += ItemDefinition.forId(Items.CANNON_BARRELS_10).getValue(); - totalWealth += ItemDefinition.forId(Items.CANNON_FURNACE_12).getValue(); - } + // This can lead to a false positive of up to 3 * 187.5k, but only for 3 ticks while a cannon is being constructed + if (this.getAttribute("dmc", null) != null) { + totalWealth += ItemDefinition.forId(Items.CANNON_BASE_6).getValue(); + totalWealth += ItemDefinition.forId(Items.CANNON_STAND_8).getValue(); + totalWealth += ItemDefinition.forId(Items.CANNON_BARRELS_10).getValue(); + totalWealth += ItemDefinition.forId(Items.CANNON_FURNACE_12).getValue(); + } long diff = previousWealth == -1 ? 0L : totalWealth - previousWealth; setAttribute("/save:last-wealth", totalWealth); @@ -551,15 +554,15 @@ public class Player extends Entity { return this.getIndex() | 0x8000; } - @Override - public void onAttack (Entity e) { - if (e instanceof Player) { - Player p = (Player) e; - if (skullManager.isWildernessDisabled()) { - return; - } - } - } + @Override + public void onAttack (Entity e) { + if (e instanceof Player) { + Player p = (Player) e; + if (skullManager.isWildernessDisabled()) { + return; + } + } + } @Override public CombatSwingHandler getSwingHandler(boolean swing) { @@ -588,7 +591,7 @@ public class Player extends Entity { @Override public void commenceDeath(Entity killer) { - if (!isPlaying()) return; + if (!isPlaying()) return; super.commenceDeath(killer); if (prayer.get(PrayerType.RETRIBUTION)) { prayer.startRetribution(killer); @@ -597,7 +600,7 @@ public class Player extends Entity { @Override public void finalizeDeath(Entity killer) { - if (!isPlaying()) return; //if the player has already been full cleared, it has already disconnected. This code is probably getting called because something is maintaining a stale reference. + if (!isPlaying()) return; //if the player has already been full cleared, it has already disconnected. This code is probably getting called because something is maintaining a stale reference. GlobalStats.incrementDeathCount(); settings.setSpecialEnergy(100); settings.updateRunEnergy(settings.getRunEnergy() - 100); @@ -611,10 +614,8 @@ public class Player extends Entity { if (this.isArtificial() && killer instanceof NPC) { return; } - if (killer instanceof Player && getWorldTicks() - killer.getAttribute("/save:last-murder-news", 0) >= 500) { - Item wep = getItemFromEquipment((Player) killer, EquipmentSlot.WEAPON); - sendNews(killer.getUsername() + " has murdered " + getUsername() + " with " + (wep == null ? "their fists." : (StringUtils.isPlusN(wep.getName()) ? "an " : "a ") + wep.getName())); - killer.setAttribute("/save:last-murder-news", getWorldTicks()); + if (killer instanceof Player && !Objects.equals(killer.getName(), getName())) { // the latter happens if you died via typeless damage from an external cause, e.g. bugs in a dark cave without a light source + ContentAPIKt.sendMessage((Player) killer, "You have defeated " + getUsername() + "."); } getPacketDispatch().sendMessage("Oh dear, you are dead!"); incrementAttribute("/save:"+STATS_BASE+":"+STATS_DEATHS); @@ -622,32 +623,15 @@ public class Player extends Entity { packetDispatch.sendTempMusic(90); if (!getZoneMonitor().handleDeath(killer) && (!getProperties().isSafeZone() && getZoneMonitor().getType() != ZoneType.SAFE.getId()) && getDetails().getRights() != Rights.ADMINISTRATOR) { //If player was a Hardcore Ironman, announce that they died - if (this.getIronmanManager().getMode().equals(IronmanMode.HARDCORE)){ //if this was checkRestriction, ultimate irons would be moved to HARDCORE_DEAD as well + if (this.getIronmanManager().getMode().equals(IronmanMode.HARDCORE)) { //if this was checkRestriction, ultimate irons would be moved to HARDCORE_DEAD as well String gender = this.isMale() ? "man " : "woman "; if (getAttributes().containsKey("permadeath")) { Repository.sendNews("Permadeath Hardcore Iron" + gender + " " + this.getUsername() + " has fallen. Total Level: " + this.getSkills().getTotalLevel()); // Not enough room for XP - equipment.clear(); - inventory.clear(); - bank.clear(); - skills = new Skills(this); - clearAttributes(); - setAttribute("/save:permadeath", true); - savedData = new SavedData(this); - questRepository = new QuestRepository(this); - varpManager = new VarpManager(this); - varpMap.clear(); - saveVarp.clear(); - new PlayerSaver(this).save(); - clear(); + permadeath(this); return; - } else { - Repository.sendNews("Hardcore Iron " + gender + " " + this.getUsername() + " has fallen. Total Level: " + this.getSkills().getTotalLevel()); // Not enough room for XP - this.getIronmanManager().setMode(IronmanMode.STANDARD); - asPlayer().getSavedData().getActivityData().setHardcoreDeath(true); - this.sendMessage("You have fallen as a Hardcore Iron Man, your Hardcore status has been revoked."); } } - GroundItemManager.create(new Item(526), getLocation(), k); + GroundItemManager.create(new Item(BONES_526), this.getAttribute("/save:original-loc",location), k); final Container[] c = DeathTask.getContainers(this); for (Item i : getEquipment().toArray()) { @@ -669,11 +653,13 @@ public class Player extends Entity { if (item == null) continue; if (killer instanceof Player) itemsLost.append(getItemName(item.getId())).append("(").append(item.getAmount()).append("), "); + if (GraveController.shouldCrumble(item.getId())) continue; if (GraveController.shouldRelease(item.getId())) continue; - if (!item.getDefinition().isTradeable()) { + + if (!BarrowsEquipment.isBarrowsItem(item.getId()) && !item.getDefinition().isTradeable()) { if (killer instanceof Player) { int value = item.getDefinition().getAlchemyValue(true); if (getStatLevel(killer, Skills.MAGIC) < 55) value /= 2; @@ -681,7 +667,14 @@ public class Player extends Entity { continue; } else stayPrivate = true; } - item = GraveController.checkTransform(item); + if (BarrowsEquipment.isBarrowsItem(item.getId())) { + if (!BarrowsEquipment.isBroken(item.getId())) { + int brokenItemId = Objects.requireNonNull(BarrowsEquipment.getDefinition(item.getId())).getBrokenId(); + item = new Item(brokenItemId, item.getAmount()); + } + } else { + item = GraveController.checkTransform(item); + } GroundItem gi = GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this); gi.setRemainPrivate(stayPrivate); } @@ -701,6 +694,10 @@ public class Player extends Entity { skullManager.setSkulled(false); removeAttribute("combat-time"); getPrayer().reset(); + removeAttribute("original-loc"); //in case you died inside a random event + interfaceManager.openDefaultTabs(); //in case you died inside a random that had blanked them + setComponentVisibility(this, 548, 69, false); //reenable the logout button (SD) + setComponentVisibility(this, 746, 12, false); //reenable the logout button (HD) super.finalizeDeath(killer); appearance.sync(); if (!getSavedData().getGlobalData().isDeathScreenDisabled()) { @@ -754,18 +751,18 @@ public class Player extends Entity { if (entity instanceof NPC && !((NPC) entity).getDefinition().hasAction("attack") && !((NPC) entity).isIgnoreAttackRestrictions(this)) { return false; } - if (entity instanceof Player) { - Player p = (Player) entity; - if (p.getSkullManager().isWilderness() && skullManager.isWilderness()) { - if (!GameWorld.getSettings().getWild_pvp_enabled()) - return false; - if (p.getSkullManager().hasWildernessProtection()) - return false; - if (skullManager.hasWildernessProtection()) - return false; - return true; - } else return false; - } + if (entity instanceof Player) { + Player p = (Player) entity; + if (p.getSkullManager().isWilderness() && skullManager.isWilderness()) { + if (!GameWorld.getSettings().getWild_pvp_enabled()) + return false; + if (p.getSkullManager().hasWildernessProtection()) + return false; + if (skullManager.hasWildernessProtection()) + return false; + return true; + } else return false; + } return super.isAttackable(entity, style, message); } @@ -1353,7 +1350,6 @@ public class Player extends Entity { return "Player [name=" + name + ", getRights()=" + getRights() + "]"; } - public String getCustomState() { return customState; } @@ -1379,37 +1375,15 @@ public class Player extends Entity { this.archeryTotal = archeryTotal; } - public boolean hasActiveState(String key){ - State state = states.get(key); - if(state != null && state.getPulse() != null){ - return true; + public void updateAppearance() { + getUpdateMasks().register(EntityFlag.Appearance, this); + } + + public void incrementInvalidPacketCount() { + invalidPacketCount++; + if (invalidPacketCount >= 5) { + clear(); + log(this.getClass(), Log.ERR, "Disconnecting " + getName() + " for having a high rate of invalid packets. Potential packet bot misbehaving, or simply really bad connection."); } - return false; } - - public State registerState(String key){ - return StateRepository.forKey(key, this); - } - - public void clearState(String key){ - State state = states.get(key); - if(state == null) return; - Pulse pulse = state.getPulse(); - if(pulse != null) { - pulse.stop(); - } - states.remove(key); - } - - public void updateAppearance() { - getUpdateMasks().register(EntityFlag.Appearance, this); - } - - public void incrementInvalidPacketCount() { - invalidPacketCount++; - if (invalidPacketCount >= 5) { - clear(); - log(this.getClass(), Log.ERR, "Disconnecting " + getName() + " for having a high rate of invalid packets. Potential packet bot misbehaving, or simply really bad connection."); - } - } } diff --git a/Server/src/main/core/game/node/entity/player/info/Rights.java b/Server/src/main/core/game/node/entity/player/info/Rights.java index c50de0e95..95009c23a 100644 --- a/Server/src/main/core/game/node/entity/player/info/Rights.java +++ b/Server/src/main/core/game/node/entity/player/info/Rights.java @@ -25,11 +25,11 @@ public enum Rights { if (c != Rights.REGULAR_PLAYER && c != null) { return c.toInteger(); } - if (ServerConstants.IRONMAN_ICONS) { - if (player.getIronmanManager().isIronman()) { - return player.getIronmanManager().getMode().getIcon(); - } - } + if (ServerConstants.IRONMAN) { + if (player.getIronmanManager().isIronman()) { + return player.getIronmanManager().getMode().getIcon(); + } + } return 0; } diff --git a/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java b/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java index 4dc80dda2..fb50ee051 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java +++ b/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java @@ -1,6 +1,5 @@ package core.game.node.entity.player.info.login; -import core.game.component.CloseEvent; import core.game.component.Component; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.emote.Emotes; @@ -14,12 +13,10 @@ import core.ServerConstants; import core.game.interaction.InteractionListeners; import content.global.handlers.iface.RulesAndInfo; import core.tools.Log; -import core.tools.SystemLogger; import core.game.world.GameWorld; import core.game.world.repository.Repository; import core.game.world.update.UpdateSequence; import core.game.node.entity.player.link.SpellBookManager; -import core.game.node.item.GroundItemManager; import java.util.ArrayList; import java.util.Date; @@ -30,6 +27,7 @@ import java.util.stream.IntStream; import static core.api.ContentAPIKt.*; import static core.tools.GlobalsKt.colorize; +import content.data.Quests; /** @@ -49,22 +47,17 @@ public final class LoginConfiguration { */ private static final Component LOBBY_PANE = new Component(549); + /** + * The lobby interface component. + */ + private static final Component LOBBY_INTERFACE = new Component(378); + /** * The lobby message of the week models & constant to be set for auto selecting the models */ private static final int[] MESSAGE_MODEL = {15, 16, 17, 18, 19, 20, 21, 22, 23, 405, 447, 622, 623, 679, 715, 800}; private static int messModel; - /** - * The lobby interface close event. - */ - private static final Component LOBBY_INTERFACE = new Component(378).setCloseEvent(new CloseEvent() { - @Override - public boolean close(Player player, Component c) { - return player.getLocks().isLocked("login"); - } - }); - /** * Constructs a new {@Code LoginConfiguration} {@Code Object} */ @@ -103,25 +96,25 @@ public final class LoginConfiguration { } } Repository.getLobbyPlayers().add(player); - player.getPacketDispatch().sendString(getLastLogin(player), 378, 116); - player.getPacketDispatch().sendString("Welcome to " + GameWorld.getSettings().getName(), 378, 115); - player.getPacketDispatch().sendString(" ", 378, 37); - player.getPacketDispatch().sendString("Want to stay up to date with the latest news and updates? Join our
discord by using the link below!", 378, 38); - player.getPacketDispatch().sendString(" ", 378, 39); - player.getPacketDispatch().sendString("Discord Invite", 378, 14); - player.getPacketDispatch().sendString("Discord Invite", 378, 129); - player.getPacketDispatch().sendString("Credits", 378, 94); - player.getPacketDispatch().sendString(player.getDetails().getCredits() + "", 378, 96); - player.getPacketDispatch().sendString(" ", 378, 229); - player.getPacketDispatch().sendString("Want to contribute to " + ServerConstants.SERVER_NAME + "?
Visit the GitLab using the link below!", 378, 230); - player.getPacketDispatch().sendString(" ", 378, 231); - player.getPacketDispatch().sendString("Github", 378, 240); - player.getPacketDispatch().sendString(GameWorld.getSettings().getMessage_string(), messModel, getMessageChild(messModel)); - player.getPacketDispatch().sendString("You can gain more credits by voting, reporting bugs and various other methods of contribution.", 378, 93); + setInterfaceText(player, "Welcome to " + ServerConstants.SERVER_NAME, 378, 115); + setInterfaceText(player, getLastLogin(player), 378, 116); + setInterfaceText(player, "", 378, 37); + setInterfaceText(player, "Want to stay up to date with the latest news and updates? Join our discord by using the link on our website!", 378, 38); + setInterfaceText(player, "", 378, 39); + setInterfaceText(player, "Discord Invite", 378, 14); + setInterfaceText(player, "Discord Invite", 378, 129); + setInterfaceText(player, "You can gain more credits by reporting bugs and various other methods of contribution.", 378, 93); + setInterfaceText(player, player.getDetails().getCredits() + "", 378, 96); + setInterfaceText(player, "Credits", 378, 94); + setInterfaceText(player, "", 378, 229); + setInterfaceText(player, "Want to contribute to " + ServerConstants.SERVER_NAME + "? Visit the GitLab using the link on our website!", 378, 230); + setInterfaceText(player, "", 378, 231); + setInterfaceText(player, "Github", 378, 240); + setInterfaceText(player, GameWorld.getSettings().getMessage_string(), messModel, getMessageChild(messModel)); player.getInterfaceManager().openWindowsPane(LOBBY_PANE); player.getInterfaceManager().setOpened(LOBBY_INTERFACE); - PacketRepository.send(Interface.class, new InterfaceContext(player, 549, 2, 378, true)); - PacketRepository.send(Interface.class, new InterfaceContext(player, 549, 3, messModel, true));//UPDATE `configs` SET `value`=FLOOR(RAND()*(25-10)+10) WHERE key_="messageInterface" + PacketRepository.send(Interface.class, new InterfaceContext(player, LOBBY_PANE.getId(), 2, 378, true)); + PacketRepository.send(Interface.class, new InterfaceContext(player, LOBBY_PANE.getId(), 3, messModel, true));//UPDATE `configs` SET `value`=FLOOR(RAND()*(25-10)+10) WHERE key_="messageInterface" player.getDetails().setLastLogin(System.currentTimeMillis()); } @@ -140,9 +133,11 @@ public final class LoginConfiguration { player.updateAppearance(); player.getPlayerFlags().setUpdateSceneGraph(true); player.getPacketDispatch().sendInterfaceConfig(226, 1, true); + if(player.getGlobalData().getTestStage() == 3 && !player.getEmoteManager().isUnlocked(Emotes.SAFETY_FIRST)){ player.getEmoteManager().unlock(Emotes.SAFETY_FIRST); } + for (Item item : player.getEquipment().toArray()) { //Run equip hooks for all items equipped on login. //We should have already been doing this. @@ -150,37 +145,28 @@ public final class LoginConfiguration { if (item == null) continue; player.getEquipment().remove(item); if (!InteractionListeners.run(item.getId(), player, item, true) || !player.getEquipment().add(item, true, false)) { - if (player.getInventory().add(item)) - player.sendMessage (colorize("%RAs you can no longer wear " + item.getName() + ", it has been unequipped.")); - else if (player.getBankPrimary().add(item)) - player.sendMessage (colorize("%RAs you can no longer wear " + item.getName() + ", it has been sent to your bank.")); - else if (player.getBankSecondary().add(item)) - player.sendMessage (colorize("%RAs you can no longer wear " + item.getName() + ", it has been sent to your secondary bank.")); - else { - player.sendMessage (colorize("%RAs you can no longer wear " + item.getName() + ", and your inventory and both banks are full,")); - player.sendMessage (colorize("%RIt has been placed on the ground under your feet. Don't forget to grab it.")); - player.sendMessage ("(Also, consider cleaning out your banks maybe? I mean jesus.)"); - GroundItemManager.create (item, player); - } + player.sendMessage(colorize("%RAs you can no longer wear " + item.getName() + ", it has been unequipped.")); + addItemOrBank(player, item.getId(), item.getAmount()); } } + SpellBookManager.SpellBook currentSpellBook = SpellBookManager.SpellBook.forInterface(player.getSpellBookManager().getSpellBook()); - if (currentSpellBook == SpellBookManager.SpellBook.ANCIENT && !hasRequirement(player, "Desert Treasure")) { + if (currentSpellBook == SpellBookManager.SpellBook.ANCIENT && !hasRequirement(player, Quests.DESERT_TREASURE)) { player.sendMessage(colorize("%RAs you can no longer use Ancient Magic, you have been set back to Modern.")); player.getSpellBookManager().setSpellBook(SpellBookManager.SpellBook.MODERN); - } else if (currentSpellBook == SpellBookManager.SpellBook.LUNAR && !hasRequirement(player, "Lunar Diplomacy")) { + } else if (currentSpellBook == SpellBookManager.SpellBook.LUNAR && !hasRequirement(player, Quests.LUNAR_DIPLOMACY)) { player.sendMessage(colorize("%RAs you can no longer use Lunar Magic, you have been set back to Modern.")); player.getSpellBookManager().setSpellBook(SpellBookManager.SpellBook.MODERN); } player.getSpellBookManager().update(player); + // 1050 is checked client-side for making piety/chivalry disallowed sfx, likely due to the minigame requirement. // Set it here unconditionally until the minigame is implemented. - setVarbit(player, 3909, 8, false); + if (hasRequirement(player, Quests.KINGS_RANSOM, false)) { + setVarbit(player, 3909, 8, false); + } if(ServerConstants.RULES_AND_INFO_ENABLED) RulesAndInfo.openFor(player); - /*if (GameWorld.getSettings().isPvp()) { - player.getPacketDispatch().sendString("", 226, 1); - }*/ /*if (TutorialSession.getExtension(player).getStage() != 73) { TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), true); }*/ @@ -213,21 +199,14 @@ public final class LoginConfiguration { * @param player the player. Fullscreen mode Object id: */ public static final void welcome(final Player player) { - if (GameWorld.getSettings().isPvp()) { - player.getPacketDispatch().sendString("", 226, 0); - } if (player.isArtificial()) { - return; } - player.getPacketDispatch().sendMessage("Welcome to " + GameWorld.getSettings().getName() + "."); - //player.getPacketDispatch().sendMessage("You are currently playing in beta version 1.2"); + player.getPacketDispatch().sendMessage("Welcome to " + ServerConstants.SERVER_NAME + "."); if (player.getDetails().isMuted()) { player.getPacketDispatch().sendMessage("You are muted."); player.getPacketDispatch().sendMessage("To prevent further mutes please read the rules."); } -// ResourceAIPManager.get().load(player); -// ResourceAIPManager.get().save(player); } /** @@ -247,7 +226,8 @@ public final class LoginConfiguration { player.getPacketDispatch().sendRunEnergy(); player.getFamiliarManager().login(); player.getInterfaceManager().openDefaultTabs(); - player.getPacketDispatch().sendString("Friends List - World " + GameWorld.getSettings().getWorldId(), 550, 3); + setInterfaceText(player, "Friends List - " + ServerConstants.SERVER_NAME + " " + GameWorld.getSettings().getWorldId(), 550, 3); + setInterfaceText(player, "When you have finished playing " + ServerConstants.SERVER_NAME + ", always use the button below to logout safely.", 182, 0); player.getQuestRepository().syncronizeTab(player); player.getInterfaceManager().close(); player.getEmoteManager().refresh(); @@ -267,11 +247,11 @@ public final class LoginConfiguration { return 8; } else if (interfaceId == 16) { return 6; - } else if (interfaceId == 17 || interfaceId == 15 || interfaceId == 18 || interfaceId == 19 || interfaceId == 21 || interfaceId == 22 || interfaceId == 447 || interfaceId == 405) { - return 4; } else if (interfaceId == 20 || interfaceId == 623) { return 5; - } else if (interfaceId == 23 || interfaceId == 800) { + } else if (interfaceId == 15 || interfaceId == 18 || interfaceId == 19 || interfaceId == 21 || interfaceId == 22 || interfaceId == 447 || interfaceId == 405) { + return 4; + } else if (interfaceId == 17 || interfaceId == 23 || interfaceId == 800) { return 3; } else if (interfaceId == 715) { return 2; @@ -296,12 +276,8 @@ public final class LoginConfiguration { * @return the last login. */ public static String getLastLogin(Player player) { - String lastIp = player.getDetails().accountInfo.getLastUsedIp(); - if (lastIp.equals("")) { - lastIp = player.getDetails().getIpAddress(); - } - player.getDetails().accountInfo.setLastUsedIp(player.getDetails().getIpAddress()); - String string = "You last logged in @timeAgo from: " + lastIp; + player.getPacketDispatch().sendLastLoginInfo(); + String string = "You last logged in @timeAgo from:"; long time = player.getDetails().getLastLogin(); Date lastLogin = new Date(time); Date current = new Date(); diff --git a/Server/src/main/core/game/node/entity/player/info/login/LoginParser.kt b/Server/src/main/core/game/node/entity/player/info/login/LoginParser.kt index 85fdf972a..649db34a5 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/LoginParser.kt +++ b/Server/src/main/core/game/node/entity/player/info/login/LoginParser.kt @@ -1,11 +1,12 @@ package core.game.node.entity.player.info.login +import core.ServerConstants import core.api.* +import core.auth.AuthResponse import core.game.node.entity.player.Player import core.game.node.entity.player.info.PlayerDetails import core.game.system.SystemManager import core.game.system.task.Pulse -import core.auth.AuthResponse import core.game.world.GameWorld import core.game.world.GameWorld.loginListeners import core.game.world.repository.Repository @@ -27,8 +28,7 @@ class LoginParser(val details: PlayerDetails) { parser = PlayerParser.parse(player) ?: throw IllegalStateException("Failed parsing save for: " + player.username) //Parse core } - catch (e: Exception) - { + catch (e: Exception) { e.printStackTrace() Repository.removePlayer(player) flag(AuthResponse.ErrorLoadingProfile) @@ -37,9 +37,11 @@ class LoginParser(val details: PlayerDetails) { override fun pulse(): Boolean { try { if (details.session.isActive) { + player.properties.spawnLocation = getAttribute(player, "/save:spawnLocation", ServerConstants.HOME_LOCATION) loginListeners.forEach(Consumer { listener: LoginListener -> listener.login(player) }) //Run our login hooks parser.runContentHooks() //Run our saved-content-parsing hooks player.details.session.setObject(player) + player.getDetails().accountInfo.lastUsedIp = player.getDetails().getIpAddress() if (reconnect) { reconnect(player) } else { diff --git a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaveParser.kt b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaveParser.kt index e5984d7e2..028e8121d 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaveParser.kt +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaveParser.kt @@ -1,7 +1,5 @@ package core.game.node.entity.player.info.login -import content.global.skill.farming.CompostBins -import content.global.skill.farming.FarmingPatch import core.JSONUtils import core.api.PersistPlayer import core.game.node.entity.combat.spell.CombatSpell @@ -18,7 +16,6 @@ import core.ServerConstants import core.api.log import core.game.node.entity.combat.graves.GraveController import core.game.node.entity.combat.graves.GraveType -import core.tools.SystemLogger import core.game.world.GameWorld import core.tools.Log import java.io.File @@ -48,14 +45,12 @@ class PlayerSaveParser(val player: Player) { reader ?: log(this::class.java, Log.WARN, "Couldn't find save file for ${player.name}, or save is corrupted.").also { read = false } if (read) { saveFile = parser.parse(reader) as JSONObject - } - - if (read) { parseData() } } fun parseData() { + parseVersion() parseCore() parseAttributes() parseSkills() @@ -64,7 +59,6 @@ class PlayerSaveParser(val player: Player) { parseAppearance() parseGrave() parseVarps() - parseStates() parseSpellbook() parseSavedData() parseAutocastSpell() @@ -80,7 +74,6 @@ class PlayerSaveParser(val player: Player) { parseStatistics() parseAchievements() parsePouches() - parsePouches() } fun runContentHooks() @@ -180,22 +173,6 @@ class PlayerSaveParser(val player: Player) { player.bankPinManager.parse(bpData) } - fun parseStates() { - player.states.clear() - if (saveFile!!.containsKey("states")) { - val states: JSONArray = saveFile!!["states"] as JSONArray - for (state in states) { - val s = state as JSONObject - val stateId = s["stateKey"].toString() - if(player.states[stateId] != null) continue - val stateClass = player.registerState(stateId) - stateClass?.parse(s) - stateClass?.init() - player.states.put(stateId,stateClass) - } - } - } - fun parseFamiliars() { val familiarData = saveFile!!["familiarManager"] as JSONObject player.familiarManager.parse(familiarData) @@ -330,9 +307,6 @@ class PlayerSaveParser(val player: Player) { player.bankPrimary.parse(bank) player.bankSecondary.parse(bankSecondary) player.equipment.parse(equipment) - bBars?.let{player.blastBars.parse(it)} - bOre?.let{player.blastOre.parse(bOre)} - bCoal?.let{player.blastCoal.parse(bCoal)} player.location = JSONUtils.parseLocation(location) if (varpData != null) { @@ -355,13 +329,10 @@ class PlayerSaveParser(val player: Player) { val skillData = saveFile!!["skills"] as JSONArray player.skills.parse(skillData) player.skills.experienceGained = saveFile!!["totalEXP"].toString().toDouble() - player.skills.experienceMutiplier = saveFile!!["exp_multiplier"].toString().toDouble() - if (GameWorld.settings?.default_xp_rate != 5.0) { - player.skills.experienceMutiplier = GameWorld.settings?.default_xp_rate!! - } + player.skills.experienceMultiplier = saveFile!!["exp_multiplier"].toString().toDouble() val divisor: Double - if(player.skills.experienceMutiplier >= 10 && !player.attributes.containsKey("permadeath")){ //exclude permadeath HCIMs from XP squish - divisor = player.skills.experienceMutiplier / 5.0 + if(player.skills.experienceMultiplier >= 10 && !player.attributes.containsKey("permadeath")){ //exclude permadeath HCIMs from XP squish + divisor = player.skills.experienceMultiplier / 5.0 player.skills.correct(divisor) } if (saveFile!!.containsKey("milestone")) { @@ -377,5 +348,11 @@ class PlayerSaveParser(val player: Player) { player.settings.parse(settingsData) } - + fun parseVersion() { + saveFile ?: return + player.version = 0 + if (saveFile!!.containsKey("version")) { + player.version = saveFile!!["version"].toString().toInt() + } + } } diff --git a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt index d9d8a0c4f..470818396 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt @@ -19,7 +19,6 @@ import org.json.simple.JSONObject import java.io.File import java.io.FileWriter import java.io.IOException -import java.lang.Math.ceil import javax.script.ScriptEngineManager import java.util.* @@ -36,6 +35,7 @@ class PlayerSaver (val player: Player){ } fun populate(): JSONObject { val saveFile = JSONObject() + saveVersion(saveFile) saveCoreData(saveFile) saveSkills(saveFile) saveSettings(saveFile) @@ -47,7 +47,6 @@ class PlayerSaver (val player: Player){ savePlayerMonitor(saveFile) saveMusicPlayer(saveFile) saveFamiliarManager(saveFile) - saveStateManager(saveFile) saveBankPinData(saveFile) saveHouseData(saveFile) saveAchievementData(saveFile) @@ -96,6 +95,10 @@ class PlayerSaver (val player: Player){ player.pouchManager.save(root) } + fun saveVersion(root: JSONObject){ + root.put("version", player.version) + } + fun saveAttributes(root: JSONObject){ if(player.gameAttributes.savedAttributes.isNotEmpty()){ val attrs = JSONArray() @@ -258,32 +261,23 @@ class PlayerSaver (val player: Player){ root.put("bankPinManager",bankPinManager) } - fun saveStateManager(root: JSONObject){ - val states = JSONArray() - player.states.forEach{key,clazz -> - if(clazz != null && clazz.pulse != null) { - val stateObj = JSONObject() - stateObj.put("stateKey", key) - clazz.save(stateObj) - states.add(stateObj) - } - } - root.put("states",states) - } - fun saveFamiliarManager(root: JSONObject){ val familiarManager = JSONObject() - val petDetails = JSONArray() + val petDetails = JSONObject() player.familiarManager.petDetails.map { - val detail = JSONObject() - detail.put("petId",it.key.toString()) - detail.put("hunger",it.value.hunger.toString()) - detail.put("growth",it.value.growth.toString()) - petDetails.add(detail) + val petId = it.key + val petData = JSONArray() + for (v in it.value) { + val pet = JSONObject() + pet.put("hunger",v.hunger.toString()) + pet.put("growth",v.growth.toString()) + petData.add(pet) + } + petDetails.put(petId.toString(), petData) } familiarManager.put("petDetails",petDetails) if(player.familiarManager.hasPet()){ - familiarManager.put("currentPet",(player.familiarManager.familiar as Pet).getItemIdHash().toString()) + familiarManager.put("currentPet",(player.familiarManager.familiar as Pet).getItemId().toString()) } else if (player.familiarManager.hasFamiliar()){ val familiar = JSONObject() familiar.put("originalId",player.familiarManager.familiar.originalId.toString()) @@ -328,7 +322,6 @@ class PlayerSaver (val player: Player){ fun saveGlobalData(root: JSONObject){ val globalData = JSONObject() - globalData.put("tutorialStage",player.savedData.globalData.tutorialStage.toString()) globalData.put("homeTeleportDelay",player.savedData.globalData.homeTeleportDelay.toString()) globalData.put("lumbridgeRope",player.savedData.globalData.hasTiedLumbridgeRope()) globalData.put("apprentice",player.savedData.globalData.hasSpokenToApprentice()) @@ -557,19 +550,19 @@ class PlayerSaver (val player: Player){ val skill = JSONObject() skill.put("id",i.toString()) skill.put("static",player.skills.staticLevels[i].toString()) - if(i == Skills.HITPOINTS){ - skill.put("dynamic",player.skills.lifepoints.toString()) - } else if (i == Skills.PRAYER){ - skill.put("dynamic",ceil(player.skills.prayerPoints).toInt().toString()) - } else { - skill.put("dynamic",player.skills.dynamicLevels[i].toString()) + skill.put("dynamic",player.skills.dynamicLevels[i].toString()) + if (i == Skills.HITPOINTS) { + skill.put("lifepoints",player.skills.lifepoints.toString()) + } + if (i == Skills.PRAYER) { + skill.put("prayerPoints",player.skills.prayerPoints.toString()) } skill.put("experience",player.skills.getExperience(i).toString()) skills.add(skill) } root.put("skills",skills) root.put("totalEXP",player.skills.experienceGained.toString()) - root.put("exp_multiplier",player.skills.experienceMutiplier.toString()) + root.put("exp_multiplier",player.skills.experienceMultiplier.toString()) if(player.skills.combatMilestone > 0 || player.skills.skillMilestone > 0){ val milestone = JSONObject() milestone.put("combatMilestone",player.skills.combatMilestone.toString()) @@ -604,15 +597,6 @@ class PlayerSaver (val player: Player){ val bankSecondary = saveContainer(player.bankSecondary) coreData.put("bankSecondary",bankSecondary) - val bBars = saveContainer(player.blastBars) - coreData.put("blastBars",bBars) - - val bOre = saveContainer(player.blastOre) - coreData.put("blastOre",bOre) - - val bCoal = saveContainer(player.blastCoal) - coreData.put("blastCoal",bCoal) - val bankTabs = JSONArray() for(i in player.bankPrimary.tabStartSlot.indices){ val tab = JSONObject() diff --git a/Server/src/main/core/game/node/entity/player/info/login/SaveVersionHooks.kt b/Server/src/main/core/game/node/entity/player/info/login/SaveVersionHooks.kt new file mode 100644 index 000000000..f1d5e4819 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/info/login/SaveVersionHooks.kt @@ -0,0 +1,104 @@ +package core.game.node.entity.player.info.login + +import content.data.Quests +import content.global.skill.summoning.pet.Pets +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCity +import core.ServerConstants +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.tools.Log +import org.rs09.consts.Items +import org.rs09.consts.Vars + +/** + * Runs one-time save-version-related hooks. + * @author Player Name + */ + +class SaveVersionHooks : LoginListener { + override fun login(player: Player) { + if (player.version < ServerConstants.CURRENT_SAVEFILE_VERSION) { + log(this::class.java, Log.FINE, "Upgrading ${player.name} from ${player.version} to ${ServerConstants.CURRENT_SAVEFILE_VERSION}") + + if (player.version < 1) { // GL !1811 + // Give out crafting hoods if the player bought any crafting capes when the hoods were not obtainable + var hasHoods = 0 + var hasCapes = 0 + val searchSpace = arrayOf(player.inventory, player.bankPrimary, player.bankSecondary) + for (container in searchSpace) { + for (hood in container.getAll(Item(Items.CRAFTING_HOOD_9782))) { + hasHoods += hood.amount + } + for (id in arrayOf(Items.CRAFTING_CAPE_9780, Items.CRAFTING_CAPET_9781)) { + for (cape in container.getAll(Item(id))) { + hasCapes += cape.amount + } + } + } + val need = hasCapes - hasHoods + if (need > 0) { + sendMessage(player, "You are being given $need crafting hood(s), because we think you bought $need crafting cape(s) when the hoods were still unobtainable.") + addItemOrBank(player, Items.CRAFTING_HOOD_9782, need) + } + + // Unlock Surok's Theme if eligible + if (getQuestStage(player, Quests.WHAT_LIES_BELOW) > 70) { + player.musicPlayer.unlock(250, false) + } + + // Migrate random-event saved location attributes to the uniform naming scheme + for (old in arrayOf("/save:drilldemon:original-loc","/save:evilbob:prevlocation","/save:freakyf:location","supexam:loc")) { + val oldloc = player.getAttribute(old, player.location) + if (oldloc != player.location) { + player.setAttribute("/save:original-loc", oldloc) + } + player.removeAttribute(old) + } + + // Set the missing tutorial island varp if eligible + if (getAttribute(player, "/save:tutorial:complete", false)) { + setVarp(player, 281, 1000, true) + } + } + + if (player.version < 2) { //GL !1799 + // Most of the migration for this MR happens in FamiliarManager.java, but we fix up any pet items here + val pets = Pets.values() + for (pet in pets) { + for (id in arrayOf(pet.babyItemId, pet.grownItemId, pet.overgrownItemId)) { + replaceAllItems(player, id, id) + // The trick here is that replaceAllItems ignores the item charge value, and will hence cause it to be lost, making all pets authentically stack again + } + } + } + + if (player.version < 3) { + // Damage control on Plague city. There are a few varbits that should have been set for spawning + when (getQuestStage(player, Quests.PLAGUE_CITY)){ + in 6..98 -> setVarbit(player, Vars.VARBIT_QUEST_PLAGUE_CITY_EDMOND_TUNNELS, 1) // Edmond is in the tunnel + in 99..100 ->setVarbit(player, Vars.VARBIT_QUEST_PLAGUE_CITY_RESCUE_ELENA, 1) // Elena is free + } + } + + if (player.version < 4) { //GL !2065 + replaceAllItems(player, Items.BLURBERRY_SPECIAL_9520, Items.BLURBERRY_SPECIAL_2064) + replaceAllItems(player, Items.BLURBERRY_SPECIAL_9521, Items.BLURBERRY_SPECIAL_2065) + replaceAllItems(player, Items.LAMP_6796, Items.LAMP_2528) + replaceAllItems(player, Items.RUNE_SHIELDH1_10667, Items.RUNE_SHIELDH1_7336) + replaceAllItems(player, Items.RUNE_SHIELDH2_10670, Items.RUNE_SHIELDH2_7342) + replaceAllItems(player, Items.RUNE_SHIELDH3_10673, Items.RUNE_SHIELDH3_7348) + replaceAllItems(player, Items.RUNE_SHIELDH4_10676, Items.RUNE_SHIELDH4_7354) + replaceAllItems(player, Items.RUNE_SHIELDH5_10679, Items.RUNE_SHIELDH5_7360) + replaceAllItems(player, Items.ADAMANT_SHIELDH1_10666, Items.ADAMANT_SHIELDH1_7334) + replaceAllItems(player, Items.ADAMANT_SHIELDH2_10669, Items.ADAMANT_SHIELDH2_7340) + replaceAllItems(player, Items.ADAMANT_SHIELDH3_10672, Items.ADAMANT_SHIELDH3_7346) + replaceAllItems(player, Items.ADAMANT_SHIELDH4_10675, Items.ADAMANT_SHIELDH4_7352) + replaceAllItems(player, Items.ADAMANT_SHIELDH5_10678, Items.ADAMANT_SHIELDH5_7358) + } + + // Finish up + player.version = ServerConstants.CURRENT_SAVEFILE_VERSION + } + } +} diff --git a/Server/src/main/core/game/node/entity/player/link/BankPinManager.java b/Server/src/main/core/game/node/entity/player/link/BankPinManager.java index b4dc61d26..321b5f8bf 100644 --- a/Server/src/main/core/game/node/entity/player/link/BankPinManager.java +++ b/Server/src/main/core/game/node/entity/player/link/BankPinManager.java @@ -470,17 +470,25 @@ public class BankPinManager { } /** - * Cancels the pin. + * Cancels the pin and shows the correct interface. */ public void cancelPin(String... messages) { + doCancelPin(); + playAudio(player, Sounds.PIN_CANCEL_1042); + openSettings(messages); + } + + /** + * Actually cancels the pin. + */ + public void doCancelPin() { status = PinStatus.NO_PIN; pendingDelay = -1; pin = null; unlocked = false; - playAudio(player, Sounds.PIN_CANCEL_1042); - openSettings(messages); } + /** * Checks if the pin is an easy guess. * @return {@code True} if so. diff --git a/Server/src/main/core/game/node/entity/player/link/GlobalData.java b/Server/src/main/core/game/node/entity/player/link/GlobalData.java index 4286f1939..62873b245 100644 --- a/Server/src/main/core/game/node/entity/player/link/GlobalData.java +++ b/Server/src/main/core/game/node/entity/player/link/GlobalData.java @@ -10,12 +10,6 @@ import org.json.simple.JSONObject; * @author 'Vexia */ public final class GlobalData { - - /** - * Represents the tutorial stage. - */ - private int tutorialStage; - /** * Represents the home teleport delay. */ @@ -298,7 +292,6 @@ public final class GlobalData { private boolean macroDisabled = false; public void parse(JSONObject data){ - tutorialStage = Integer.parseInt( data.get("tutorialStage").toString()); homeTeleportDelay = Long.parseLong(data.get("homeTeleportDelay").toString()); lumbridgeRope = (boolean) data.get("lumbridgeRope"); apprentice = (boolean) data.get("apprentice"); @@ -467,22 +460,6 @@ public final class GlobalData { playerTestStage = stage; } - /** - * Gets the tutorialStage. - * @return The tutorialStage. - */ - public int getTutorialStage() { - return tutorialStage; - } - - /** - * Sets the tutorialStage. - * @param tutorialStage The tutorialStage to set. - */ - public void setTutorialStage(int tutorialStage) { - this.tutorialStage = tutorialStage; - } - /** * Gets the homeTeleportDelay. * @return The homeTeleportDelay. @@ -821,8 +798,8 @@ public final class GlobalData { } /** - * Gets the read plagues. - * @return The plague + * Gets the read plaques. + * @return The plaques */ public boolean[] getReadPlaques() { return readPlaques; diff --git a/Server/src/main/core/game/node/entity/player/link/IronmanMode.java b/Server/src/main/core/game/node/entity/player/link/IronmanMode.java index 2573b3fe4..3aa5e2a7a 100644 --- a/Server/src/main/core/game/node/entity/player/link/IronmanMode.java +++ b/Server/src/main/core/game/node/entity/player/link/IronmanMode.java @@ -5,7 +5,7 @@ package core.game.node.entity.player.link; * @author Vexia */ public enum IronmanMode { - // HARDCORE_DEAD has to be before Ultimate so that it does not adopt it's restrictions (on the basis of >= in IronmanManager.java?) + // HARDCORE_DEAD has to be before Ultimate so that it does not adopt its restrictions (on the basis of >= in IronmanManager.java) NONE(-1), STANDARD(5), HARDCORE(6), ULTIMATE(7); /** diff --git a/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java b/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java index 08adc715b..de6bb8233 100644 --- a/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java +++ b/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java @@ -3,7 +3,6 @@ package core.game.node.entity.player.link; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.tools.Log; -import core.tools.SystemLogger; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; @@ -202,7 +201,12 @@ public final class PacketDispatch { * @param childId The child id. */ public void sendPlayerOnInterface(int interfaceId, int childId) { - PacketRepository.send(DisplayModel.class, new DisplayModelContext(player, interfaceId, childId)); + // fixme right now for iface 68-71 the player is massive + // The zoom for the other windows is 2150 + // for these 4 individuals it should be 796 but dmc.setZoom doesn't work + DisplayModelContext dmc = new DisplayModelContext(player, interfaceId, childId); + dmc.setZoom(796); // this appears to do nothing + PacketRepository.send(DisplayModel.class, dmc); } /** @@ -219,6 +223,10 @@ public final class PacketDispatch { PacketRepository.send(DisplayModel.class, new DisplayModelContext(player, ModelType.MODEL, modelID,zoom,interfaceId,childId,new Object())); } + public void sendRepositionOnInterface(int interfaceId, int childId, int positionX, int positionY){ + PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, interfaceId, childId, positionX, positionY)); + } + public void sendAngleOnInterface(int interfaceId, int childId, int zoom, int pitch, int yaw){ PacketRepository.send(InterfaceSetAngle.class, new DefaultContext(player, pitch, zoom, yaw, interfaceId, childId)); } @@ -422,8 +430,11 @@ public final class PacketDispatch { PacketRepository.send(CSConfigPacket.class, new CSConfigContext(player, id, value, type, params)); } - public void resetInterface(int id) - { + public void resetInterface(int id) { PacketRepository.send(ResetInterface.class, new InterfaceContext(player, 0, 0, id, false)); } + + public void sendLastLoginInfo() { + PacketRepository.send(LastLoginInfo.class, new PlayerContext(player)); + } } diff --git a/Server/src/main/core/game/node/entity/player/link/QuestData.java b/Server/src/main/core/game/node/entity/player/link/QuestData.java index aa58c395f..110c89e01 100644 --- a/Server/src/main/core/game/node/entity/player/link/QuestData.java +++ b/Server/src/main/core/game/node/entity/player/link/QuestData.java @@ -5,7 +5,6 @@ import core.game.node.item.Item; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import java.nio.ByteBuffer; import java.util.Arrays; /** @@ -97,19 +96,6 @@ public final class QuestData { witchsExperimentStage = Integer.parseInt( data.get("witchsExperimentStage").toString()); } - /** - * Saves the desert treasure node. - * @param buffer The buffer. - */ - private final void saveDesertTreasureNode(ByteBuffer buffer) { - buffer.put((byte) 8); - for (int i = 0; i < desertTreasure.length; i++) { - Item item = desertTreasure[i]; - buffer.putShort((short) item.getId()); - buffer.put((byte) item.getAmount()); - } - } - /** * Gets the draynorLever. * @return The draynorLever. diff --git a/Server/src/main/core/game/node/entity/player/link/SavedData.java b/Server/src/main/core/game/node/entity/player/link/SavedData.java index fc62f3a72..a0e005592 100644 --- a/Server/src/main/core/game/node/entity/player/link/SavedData.java +++ b/Server/src/main/core/game/node/entity/player/link/SavedData.java @@ -2,8 +2,6 @@ package core.game.node.entity.player.link; import core.game.node.entity.player.Player; -import java.nio.ByteBuffer; - /** * Represents a managing class of saved data related to ingame interactions, * such as questing data, npc talking data, etc. @@ -39,45 +37,6 @@ public class SavedData { this.player = player; } - /** - * Method used to save an activity var that isn't valued at default. - * @param buffer the buffer. - * @param var the variable to save. - */ - public static final void save(final ByteBuffer buffer, final Object var, final int index) { - if (var instanceof Integer ? (int) var != 0 : var instanceof Double ? (double) var != 0.0 : var instanceof Byte ? (byte) var != 0 : var instanceof Short ? (short) var != 0 : var instanceof Long ? (long) var != 0L : var instanceof Boolean ? (boolean) var != false : var != null) { - buffer.put((byte) index); - if (var instanceof Integer) { - buffer.putInt((int) var); - } else if (var instanceof Byte) { - buffer.put((byte) var); - } else if (var instanceof Short) { - buffer.putShort((short) var); - } else if (var instanceof Long) { - buffer.putLong((long) var); - } else if (var instanceof Boolean) { - buffer.put((byte) 1); - } else if (var instanceof Double) { - buffer.putDouble((double) var); - } else if (var instanceof double[]) { - double[] doubleArray = ((double[]) var); - for (int i = 0; i < doubleArray.length; i++) { - buffer.putDouble(doubleArray[i]); - } - } else if (var instanceof boolean[]) { - boolean[] booleanArray = ((boolean[]) var); - for (int i = 0; i < booleanArray.length; i++) { - buffer.put((byte) (booleanArray[i] ? 1 : 0)); - } - } else if (var instanceof int[]) { - int[] intArray = ((int[]) var); - for (int i = 0; i < intArray.length; i++) { - buffer.putInt(intArray[i]); - } - } - } - } - /** * Gets the boolean value. * @param value the value. @@ -87,15 +46,6 @@ public class SavedData { return value == 1; } - /** - * Gets the boolean value. - * @param buffer the buffer. - * @return the value. - */ - public static boolean getBoolean(ByteBuffer buffer) { - return getBoolean(buffer.get()); - } - /** * Gets the activityData. * @return The activityData. diff --git a/Server/src/main/core/game/node/entity/player/link/Settings.java b/Server/src/main/core/game/node/entity/player/link/Settings.java index 640e44723..920a65629 100644 --- a/Server/src/main/core/game/node/entity/player/link/Settings.java +++ b/Server/src/main/core/game/node/entity/player/link/Settings.java @@ -4,12 +4,8 @@ import core.game.system.config.ItemConfigParser; import org.json.simple.JSONObject; import core.game.node.entity.player.Player; import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; import core.net.packet.IoBuffer; -import java.nio.ByteBuffer; - import static core.api.ContentAPIKt.*; @@ -199,61 +195,9 @@ public final class Settings { } /** - * Writes the settings on the byte buffer. - * @param buffer The byte buffer. + * Parses the settings from the save file. + * @param settingsData The JSON object. */ - public void save(ByteBuffer buffer) { - buffer.put((byte) 1).put((byte) brightness).put((byte) musicVolume).put((byte) soundEffectVolume).put((byte) areaSoundVolume).put((byte) (singleMouseButton ? 1 : 0)).put((byte) (disableChatEffects ? 1 : 0)).put((byte) (splitPrivateChat ? 1 : 0)).put((byte) (acceptAid ? 1 : 0)).put((byte) (runToggled ? 1 : 0)).put((byte) publicChatSetting).put((byte) privateChatSetting).put((byte) clanChatSetting).put((byte) tradeSetting).put((byte) assistSetting).put(((byte) runEnergy)); - if (!player.getProperties().isRetaliating()) { - buffer.put((byte) 2); - } - if (specialEnergy != 100) { - buffer.put((byte) 3).put((byte) specialEnergy); - } - if (attackStyleIndex != 0) { - buffer.put((byte) 4).put((byte) attackStyleIndex); - } - buffer.put((byte) 0); - } - - /** - * Parses the settings from the byte buffer. - * @param buffer The byte buffer. - */ - public void parse(ByteBuffer buffer) { - int opcode; - while ((opcode = buffer.get() & 0xFF) != 0) { - switch (opcode) { - case 1: - brightness = buffer.get(); - musicVolume = buffer.get(); - soundEffectVolume = buffer.get(); - areaSoundVolume = buffer.get(); - singleMouseButton = buffer.get() == 1; - disableChatEffects = buffer.get() == 1; - splitPrivateChat = buffer.get() == 1; - acceptAid = buffer.get() == 1; - runToggled = buffer.get() == 1; - publicChatSetting = buffer.get(); - privateChatSetting = buffer.get(); - clanChatSetting = buffer.get(); - tradeSetting = buffer.get(); - assistSetting = buffer.get(); - runEnergy = buffer.get(); - break; - case 2: - player.getProperties().setRetaliating(false); - break; - case 3: - specialEnergy = buffer.get() & 0xFF; - break; - case 4: - attackStyleIndex = buffer.get(); - break; - } - } - } - public void parse(JSONObject settingsData){ brightness = Integer.parseInt( settingsData.get("brightness").toString()); musicVolume = Integer.parseInt( settingsData.get("musicVolume").toString()); diff --git a/Server/src/main/core/game/node/entity/player/link/SkullManager.java b/Server/src/main/core/game/node/entity/player/link/SkullManager.java index fd50bdeca..7b3f97c33 100644 --- a/Server/src/main/core/game/node/entity/player/link/SkullManager.java +++ b/Server/src/main/core/game/node/entity/player/link/SkullManager.java @@ -236,7 +236,7 @@ public final class SkullManager { } public void setDeepWilderness (boolean deepWildy) { - if(deepWildy) { + if(ServerConstants.ENHANCED_DEEP_WILDERNESS && deepWildy) { updateDWSkullIcon(); } else { removeDWSkullIcon(); diff --git a/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java b/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java index f5dbc2de4..7e0e456c4 100644 --- a/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java +++ b/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java @@ -4,9 +4,11 @@ import core.game.component.Component; import core.game.node.entity.combat.spell.MagicSpell; import core.game.node.entity.player.Player; - import java.util.HashMap; import java.util.Map; +import java.util.Objects; + +import static core.api.ContentAPIKt.setVarbit; /** * Represents a managing class of a players spell book. @@ -15,7 +17,7 @@ import java.util.Map; public final class SpellBookManager { /** - * Represents the current interface if of the spellbook. + * Represents the current interface if of the spell book. */ private int spellBook = SpellBook.MODERN.getInterfaceId(); @@ -23,25 +25,26 @@ public final class SpellBookManager { * Constructs a new {@code SpellBookManager} {@code Object}. */ public SpellBookManager() { - /** + /* * empty. */ } /** * Sets the spell book. - * @param book + * @param book the spell book. */ public void setSpellBook(SpellBook book) { this.spellBook = book.getInterfaceId(); } /** - * Updates the - * @param player + * Updates the spell book. + * @param player the player. */ public void update(Player player) { - player.getInterfaceManager().openTab(new Component(SpellBook.forInterface(this.spellBook).getInterfaceId())); + player.getInterfaceManager().openTab(new Component(spellBook)); + setVarbit(player, 357, Objects.requireNonNull(SpellBook.forInterface(spellBook)).ordinal()); } /** @@ -53,7 +56,7 @@ public final class SpellBookManager { } /** - * Represents a characters spellbook. + * Represents a characters spell book. * @author 'Vexia * @author Emperor */ diff --git a/Server/src/main/core/game/node/entity/player/link/TeleportManager.java b/Server/src/main/core/game/node/entity/player/link/TeleportManager.java index 140ecf405..3da78d55b 100644 --- a/Server/src/main/core/game/node/entity/player/link/TeleportManager.java +++ b/Server/src/main/core/game/node/entity/player/link/TeleportManager.java @@ -26,16 +26,6 @@ public class TeleportManager { */ public static final int WILDY_TELEPORT = 1 << 16 | 8; - /** - * The animations used in the home teleport. - */ - private final static int[] HOME_ANIMATIONS = {1722, 1723, 1724, 1725, 2798, 2799, 2800, 3195, 4643, 4645, 4646, 4847, 4848, 4849, 4850, 4851, 4852, 65535}; - - /** - * The graphics used in the home teleport. - */ - private final static int[] HOME_GRAPHICS = {775, 800, 801, 802, 803, 804, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 65535}; - /** * The entity being handled. */ @@ -62,7 +52,6 @@ public class TeleportManager { */ public TeleportManager(Entity entity) { this.entity = entity; - lastTeleport = TeleportType.HOME.getPulse(entity, ServerConstants.HOME_LOCATION); } /** @@ -116,16 +105,11 @@ public class TeleportManager { } this.teleportType = teleportType; entity.getWalkingQueue().reset(); - lastTeleport = currentTeleport; currentTeleport = type.getPulse(entity, location); entity.getPulseManager().clear(); - if (type == TeleportType.HOME) { - entity.getPulseManager().run(type.getPulse(entity, location)); - } else { - entity.lock(12); - entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12); - GameWorld.getPulser().submit(currentTeleport); - } + entity.lock(12); + entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12); + GameWorld.getPulser().submit(currentTeleport); if (entity instanceof Player) { ((Player) entity).getInterfaceManager().close(); } @@ -135,7 +119,7 @@ public class TeleportManager { /** * Fires a random event. * @param entity The entity teleporting. - * @param location The destination lcoation. + * @param location The destination location. */ public static void fireRandom(Entity entity, Location location) { if (entity instanceof Player && entity.getTeleporter().getTeleportType() == 0) { @@ -143,22 +127,6 @@ public class TeleportManager { } } - /** - * Get the home teleport audio based on tick count. - * @param count - */ - private static int getAudio(int count){ - switch(count){ - case 0: - return 193; - case 4: - return 194; - case 11: - return 195; - } - return -1; - } - /** * Gets the entity. * @return the Entity @@ -167,14 +135,6 @@ public class TeleportManager { return entity; } - /** - * Gets the last teleport pulse. - * @return the Pulse - */ - public final Pulse getLastTeleport() { - return lastTeleport; - } - /** * Gets the current teleport pulse. * @return the Pulse @@ -183,7 +143,6 @@ public class TeleportManager { return currentTeleport; } - /** * Represents a NodeType for Teleporter * @author SonicForce41 @@ -330,57 +289,6 @@ public class TeleportManager { }; } }, - HOME(new TeleportSettings(4847, 4857, 800, 804)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new TeleportPulse(entity) { - int count; - Player player; - - @Override - public boolean pulse() { - switch (count) { - case 18: - player.getProperties().setTeleportLocation(location); - return true; - default: - playGlobalAudio(entity.getLocation(), getAudio(count)); - player.getPacketDispatch().sendGraphic(HOME_GRAPHICS[count]); - player.getPacketDispatch().sendAnimation(HOME_ANIMATIONS[count]); - break; - } - count++; - return false; - } - @Override - public void start() { - player = ((Player) entity); - /*if (player.getSavedData().getGlobalData().getHomeTeleportDelay() > System.currentTimeMillis() && !player.isDonator()) { - long milliseconds = player.getSavedData().getGlobalData().getHomeTeleportDelay() - System.currentTimeMillis(); - int minutes = (int) Math.round(milliseconds / 120000); - if (minutes > 15) { - player.getSavedData().getGlobalData().setHomeTeleportDelay(System.currentTimeMillis() + 1200000); - milliseconds = player.getSavedData().getGlobalData().getHomeTeleportDelay() - System.currentTimeMillis(); - minutes = (int) Math.round(milliseconds / 120000); - } - if (minutes != 0) { - player.getPacketDispatch().sendMessage("You need to wait another " + minutes + " " + (minutes == 1 ? "minute" : "minutes") + " to cast this spell."); - stop(); - return; - } - }*/ - super.start(); - } - - @Override - public void stop() { - super.stop(); - entity.getAnimator().forceAnimation(new Animation(-1)); - player.graphics(new Graphics(-1)); - } - }; - } - }, OBELISK(new TeleportSettings(8939, 8941, 661, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { diff --git a/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiary.java b/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiary.java index 1643dac95..6027731bf 100644 --- a/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiary.java +++ b/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiary.java @@ -114,13 +114,12 @@ public class AchievementDiary { } child++; } - // sendString(player, builder.toString(), 11); - //Changes the size of the scroll bar - //player.getPacketDispatch().sendRunScript(1207, "i", new Object[] { 330 }); - //sendString(player, builder.toString(), 11); if (!player.getInterfaceManager().isOpened()) { player.getInterfaceManager().open(new Component(DIARY_COMPONENT)); } + // Changes the size of the scroll bar (see 1207.cs2 for more) + // (args1: 1 is to start from top of scroll) (args0: child-12 lines to display) + player.getPacketDispatch().sendRunScript(1207, "ii", 1, child - 10); } /** @@ -207,7 +206,7 @@ public class AchievementDiary { } if(complete){ completedLevels.add(level); - } else if(completedLevels.contains(level)) completedLevels.remove(level); + } else if(completedLevels.contains(level)) completedLevels.remove((Object) level); } } @@ -489,7 +488,7 @@ public class AchievementDiary { /** * Determines if a replacement reward can be given for the particular diary and level. - * Checks to make sure the player has completed the level and claimed the rewards, and has not completed + * Checks to make sure the player has completed the level and claimed the rewards, and has not claimed * the next diary level. * @param player the player to check * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. @@ -497,11 +496,11 @@ public class AchievementDiary { * @return whether or not a replacement can be granted. */ public static boolean canReplaceReward(Player player, DiaryType type, int level) { - Item reward = type.getRewards(level)[0]; - return hasCompletedLevel(player, type, level) - && !hasCompletedLevel(player, type, level + 1) + Item reward = type.getRewards(level)[0]; + boolean claimed = hasCompletedLevel(player, type, level) && hasClaimedLevelRewards(player, type, level) && !player.hasItem(reward); + return level == 2 ? claimed : claimed && !hasClaimedLevelRewards(player, type, level+1); } /** @@ -543,14 +542,18 @@ public class AchievementDiary { /** * Checks if a player can claim the rewards for the given level of the given diary - * Checks to make sure the player hasn't completed the next level. + * Checks to make sure the player hasn't claimed the next level. * @param player the player to check * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. * @param level the level to check * @return whether or not the player can claim the rewards */ public static boolean canClaimLevelRewards(Player player, DiaryType type, int level) { - return !hasCompletedLevel(player, type, level + 1) && hasCompletedLevel(player, type, level) && !hasClaimedLevelRewards(player, type, level); + if (level == 2) + // Cannot be a higher level to claim + return hasCompletedLevel(player, type, level) && !hasClaimedLevelRewards(player, type, level); + else + return !hasClaimedLevelRewards(player, type, level + 1) && hasCompletedLevel(player, type, level) && !hasClaimedLevelRewards(player, type, level); } /** diff --git a/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java b/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java index a91f4b5bc..8a8e51827 100644 --- a/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java +++ b/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java @@ -34,14 +34,14 @@ public enum DiaryType { "Use Vigroy and Hajedy's cart service", "Earn 100% favour in the village of Tai Bwo Wannai Cleanup", // todo tai bwo wannai cleanup "Cook a spider on stick", // todo tai bwo wannai cleanup - "Charter the Lady of the Waves from Cairn Isle to Port Khazard", // todo verify + "Charter the Lady of the Waves from Cairn Isle to Port Khazard", "Cut a log from a teak tree", "Cut a log from a mahogany tree", "Catch a karambwan", // todo need Tai Bwo Wannai Trio "Exchange gems, a gout tuber, trading sticks for a machete", // todo "Use the gnome glider to travel to Karamja", - "Grow a healthy fruit tree in the patch near Brimhaven", // todo verify - "Trap a Horned Graahk", // todo need to implement pitfall trapping + "Grow a healthy fruit tree in the patch near Brimhaven", + "Trap a Horned Graahk", "Chop the vines to gain deeper access to Brimhaven Dungeon", "Cross the lava using the stepping stones within Brimhaven

Dungeon", "Climb the stairs within Brimhaven Dungeon", diff --git a/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java b/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java index e859a598d..2bda2ca57 100644 --- a/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java +++ b/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java @@ -1,5 +1,6 @@ package core.game.node.entity.player.link.emote; +import content.data.Quests; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.player.info.Rights; import core.game.world.map.Direction; @@ -14,6 +15,7 @@ import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import static core.api.ContentAPIKt.playJingle; /** * Represents an emote. * @@ -50,10 +52,10 @@ public enum Emotes { SHRUG(8, Animation.create(2113)), CHEER(9, Animation.create(862)), BECKON(10, Animation.create(864)), - //Switch laugh id 11, to 12, and switch Jump id 12 to 11. Id's were mismatched. - //This caused the LAUGH to proc JUMP_FOR_JOY and JUMP_FOR_JOY to proc LAUGH! :) - JUMP_FOR_JOY(11, Animation.create(2109)), - LAUGH(12, Animation.create(861)), + //Switch laugh id 11, to 12, and switch Jump id 12 to 11. Id's were mismatched. + //This caused the LAUGH to proc JUMP_FOR_JOY and JUMP_FOR_JOY to proc LAUGH! :) + JUMP_FOR_JOY(11, Animation.create(2109)), + LAUGH(12, Animation.create(861)), YAWN(13, Animation.create(2111)) { @Override public void play(Player player) { @@ -105,7 +107,7 @@ public enum Emotes { public void play(Player player) { if(player.getLocation().getRegionId() == 13206 && !player.getAttribute("mistag-greeted", false)) { RegionManager.getLocalNpcs(player).forEach(npc -> { - if (npc.getId() == 2084 && npc.getLocation().withinDistance(player.getLocation(), 3) && player.getQuestRepository().getQuest("Lost Tribe").getStage(player) == 45) { + if (npc.getId() == 2084 && npc.getLocation().withinDistance(player.getLocation(), 3) && player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 45) { player.getDialogueInterpreter().open(2084,npc,"greeting"); player.setAttribute("/save:mistag-greeted",true); } @@ -166,7 +168,12 @@ public enum Emotes { } }, SNOWMAN_DANCE(40, Animation.create(7531), "This emote can be unlocked by playing a Christmas holiday event."), - AIR_GUITAR(41, Animation.create(2414), Graphics.create(1537), "This emote can be accessed by unlocking 200 pieces of music."), + AIR_GUITAR(41, Animation.create(2414), Graphics.create(1537), "This emote can be accessed by unlocking 200 pieces of music.") { + public void play(Player player) { + playJingle(player, 302); + super.play(player); + } + }, SAFETY_FIRST(42, Animation.create(8770), Graphics.create(1553), "You can't use this emote yet. Visit the Stronghold of Player safety to
unlock it."), EXPLORE(43, Animation.create(9990), Graphics.create(1734), "You can't use this emote yet. You must complete all the Lumbridge
and Draynor beginner tasks to unlock it."), TRICK(44, Animation.create(10530), Graphics.create(1863), "This emote can be unlocked by playing a Halloween holiday event."), @@ -174,7 +181,7 @@ public enum Emotes { GIVE_THANKS(46, "This emote can be unlocked by playing a Thanksgiving holiday event.") { @Override public void play(final Player player) { - GameWorld.getPulser().submit(new Pulse(1, player) { + GameWorld.getPulser().submit(new Pulse(1, player) { int counter; @Override @@ -291,7 +298,7 @@ public enum Emotes { } /** - * Handles the reward button for an emote. + * Handles the button for an emote. * * @param player the player. * @param buttonId the button id. @@ -415,4 +422,4 @@ public enum Emotes { return lockedMessage; } -} +} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java b/Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java index b9874f71b..163cdadc5 100644 --- a/Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java +++ b/Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java @@ -10,9 +10,7 @@ import core.net.packet.context.StringContext; import core.net.packet.out.MusicPacket; import core.net.packet.out.StringPacket; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; +import java.util.*; import static core.api.ContentAPIKt.*; @@ -36,7 +34,7 @@ public final class MusicPlayer { /** * The configuration ids. */ - private static final int[] CONFIG_IDS = { 20, 21, 22, 23, 24, 25, 298, 311, 346, 414, 464, 598, 662, 721, 906, 1009, 1104, 1136, 180, 1202, 1381, 1394, }; + private static final int[] CONFIG_IDS = { 20, 21, 22, 23, 24, 25, 298, 311, 346, 414, 464, 598, 662, 721, 906, 1009, 1104, 1136, 1180, 1202}; /** * The player. @@ -95,6 +93,13 @@ public final class MusicPlayer { } } + /** + * Clears the unlocked songs. This should only be used in the permadeath code. + */ + public void clearUnlocked() { + this.unlocked.clear(); + } + /** * Checks if the player has enough songs unlocked for the Air guitar emote. * @return {@code True} if so. @@ -132,12 +137,11 @@ public final class MusicPlayer { int[] values = new int[CONFIG_IDS.length]; for (MusicEntry entry : unlocked.values()) { int listIndex = entry.getIndex(); - int index = (listIndex + 1) / 32; + int index = listIndex / 32; if (index >= CONFIG_IDS.length) { - continue; } - values[index] |= 1 << (listIndex - (index * 32)); + values[index] |= 1 << (listIndex & 31); } for (int i = 0; i < CONFIG_IDS.length; i++) { setVarp(player, CONFIG_IDS[i], values[i]); @@ -193,10 +197,9 @@ public final class MusicPlayer { public void unlock(int id, boolean play) { MusicEntry entry = MusicEntry.forId(id); if (entry == null) { - return; } - if (!unlocked.containsKey(entry.getIndex())) { + if (!entry.getName().equals(" ") && !unlocked.containsKey(entry.getIndex())) { unlocked.put(entry.getIndex(), entry); player.getPacketDispatch().sendMessage("You have unlocked a new music track: " + entry.getName() + "."); refreshList(); diff --git a/Server/src/main/core/game/node/entity/player/link/prayer/PrayerType.java b/Server/src/main/core/game/node/entity/player/link/prayer/PrayerType.java index e8fba525e..41f7dfbf2 100644 --- a/Server/src/main/core/game/node/entity/player/link/prayer/PrayerType.java +++ b/Server/src/main/core/game/node/entity/player/link/prayer/PrayerType.java @@ -1,5 +1,6 @@ package core.game.node.entity.player.link.prayer; +import content.data.Quests; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; @@ -221,11 +222,20 @@ public enum PrayerType { * @return True if it is permitted. */ public boolean permitted(final Player player) { - if (player.getSkills().getStaticLevel(Skills.PRAYER) < getLevel() || player.getSkills().getStaticLevel(Skills.DEFENCE) < defenceReq) { - playAudio(player, Sounds.PRAYER_OFF_2673); - player.getDialogueInterpreter().sendDialogue("You need a Prayer level of " + getLevel() + (player.getSkills().getStaticLevel(Skills.DEFENCE) < defenceReq ? (" and a Defence level of " + defenceReq) : "") + " to use " + StringUtils.formatDisplayName(name().toLowerCase().replace("_", " ")) + "."); + if (!hasLevelStat(player, Skills.PRAYER, level) || !hasLevelStat(player, Skills.DEFENCE, defenceReq)) { + sendDialogue(player, "You need a " + (!hasLevelStat(player, Skills.PRAYER, level) ? "Prayer level of " + level + (!hasLevelStat(player, Skills.DEFENCE, defenceReq) ? " and a " : "") : "") + (!hasLevelStat(player, Skills.DEFENCE, defenceReq) ? "Defence level of " + defenceReq : "") + " to use " + StringUtils.formatDisplayName(name().toLowerCase().replace("_", " ")) + "."); return false; } + + if (defenceReq == 65 || defenceReq == 70) { + if (!hasRequirement(player, Quests.KINGS_RANSOM)) { + return false; + } else { + if (getVarbit(player, 3909) != 8) { + setVarbit(player, 3909, 8, false); + } + } + } return true; } @@ -241,17 +251,12 @@ public enum PrayerType { player.getPrayer().getActive().add(this); iconify(player, getIcon(player, this)); playAudio(player, sound.id); - - if (this == PrayerType.PIETY - && new ZoneBorders(2732, 3467, 2739, 3471, 0).insideBorder(player)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 3); - } - player.dispatch (new PrayerActivatedEvent(this)); + player.dispatch (new PrayerActivatedEvent(this)); } else { player.getPrayer().getActive().remove(this); playAudio(player, Sounds.CANCEL_PRAYER_2663); findNextIcon(player); - player.dispatch (new PrayerDeactivatedEvent(this)); + player.dispatch (new PrayerDeactivatedEvent(this)); } return true; } diff --git a/Server/src/main/core/game/node/entity/player/link/quest/Quest.java b/Server/src/main/core/game/node/entity/player/link/quest/Quest.java index 5819ee669..364d0b95e 100644 --- a/Server/src/main/core/game/node/entity/player/link/quest/Quest.java +++ b/Server/src/main/core/game/node/entity/player/link/quest/Quest.java @@ -1,5 +1,6 @@ package core.game.node.entity.player.link.quest; +import content.data.Quests; import core.game.component.Component; import core.game.node.entity.player.Player; import core.plugin.Plugin; @@ -50,9 +51,9 @@ public abstract class Quest implements Plugin { public static final int REWARD_COMPONENT = 277; /** - * The name of the quest. + * The quest as a Quests item (currently only used for the quest's name). */ - private final String name; + private final Quests quest; /** * The index id of the quest. @@ -75,15 +76,30 @@ public abstract class Quest implements Plugin { private final int[] configs; /** - * Constructs a new {@Code Quest} {@Code Object} - * @param name The name. - * @param index The index. - * @param buttonId The button Id. - * @param questPoints The rewarded quest points. - * @param configs The configs. + * Constructs a new {@link Quest} + * @param quest of the quest. Prereqs reference this + * @param index of the quest, usually buttonId + 1 + * @param buttonId of the quest on the quest list in game + * @param questPoints rewarded after completing quest + * @param configs of Varp/Varbit and values to set the quest color to red/yellow/green. e.g. {234, 0, 1, 10} + *

+ * Configs are made of either 4/5 numbers:
+ * 4 numbers: {1: VARP to set, 2: red quest name, 3: yellow quest name, 4: green quest name}
+ * 5 numbers: {1: VARP(Ignored), 2: VARPBIT to set, 3: red quest name, 4: yellow quest name, 5: green quest name}
+ *
+ * VARP/VARPBIT is set to values before/during/after quest at stage 0/1-99/100. + * Get these values from the VARPTOOL.
+ *
+ * If you see VARP (e.g. ./get_varp.sh 120):
+ * if (VARP[26] == 80 || VARP[26] == 90) return 2; if (VARP[26] == 0) return 0; return 1; }; if (arg0 == 89)
+ * Use 4 numbers: {26, 0, 1, 80} -> {VARP, return 0, return 1, return 2}
+ *
+ * If you see VARPBIT (e.g. ./get_varp.sh 119):
+ * if (VARPBIT[451] > 1) return 2; if (VARPBIT[451] == 0) return 0; return 1; }; if (arg0 == 88)
+ * Use 5 numbers: {0, 451, 0, 1, 2} -> {Ignore, VARPBIT, return 0, return 1, return 2}
*/ - public Quest(String name, int index, int buttonId, int questPoints, int...configs) { - this.name = name; + public Quest(Quests quest, int index, int buttonId, int questPoints, int...configs) { + this.quest = quest; this.index = index; this.buttonId = buttonId; this.questPoints = questPoints; @@ -116,7 +132,7 @@ public abstract class Quest implements Plugin { for (int i = 0; i < 311; i++) { player.getPacketDispatch().sendString("" , JOURNAL_COMPONENT, i); } - player.getPacketDispatch().sendString("" + getName() + "", JOURNAL_COMPONENT, 2); + player.getPacketDispatch().sendString("" + getQuest() + "", JOURNAL_COMPONENT, 2); } @@ -125,8 +141,8 @@ public abstract class Quest implements Plugin { * @param player The player. */ public void finish(Player player) { - if(player.getQuestRepository().isComplete(name)) { - throw new IllegalStateException("Tried to complete quest " + name + " twice, which is not allowed!"); + if(player.getQuestRepository().isComplete(quest)) { + throw new IllegalStateException("Tried to complete quest " + quest + " twice, which is not allowed!"); } for (int i = 0; i < 18; i++) { if (i == 9 || i == 3 || i == 6) { @@ -137,9 +153,12 @@ public abstract class Quest implements Plugin { player.getQuestRepository().setStage(this, 100); player.getQuestRepository().incrementPoints(getQuestPoints()); player.getQuestRepository().syncronizeTab(player); - player.getInterfaceManager().open(new Component(277)); + player.getInterfaceManager().open(new Component(277).setCloseEvent((p, c) -> { + this.questCloseEvent(p, c); + return true; + })); player.getPacketDispatch().sendString("" + player.getQuestRepository().getPoints() + "", 277, 7); - player.getPacketDispatch().sendString("You have completed the " + getName() + " Quest!", 277, 4); + player.getPacketDispatch().sendString("You have completed the " + getQuest() + " Quest!", 277, 4); player.getPacketDispatch().sendMessage("Congratulations! Quest complete!"); int questJingles[] = {152, 153, 154}; playJingle(player, questJingles[new Random().nextInt(3)]); @@ -151,6 +170,12 @@ public abstract class Quest implements Plugin { */ public void reset(Player player) {} + /** + * Function callback when closing the quest. + * Override this to follow up on dialogue after the component closes. + */ + public void questCloseEvent(Player player, Component component) {} + /** * Draws a line on the journal component. * @param player The player. @@ -190,6 +215,20 @@ public abstract class Quest implements Plugin { player.getPacketDispatch().sendString(crossed ? "" + send + "" : send, JOURNAL_COMPONENT, line); } + /** + * Limits the quest log scroll to the number of lines minus 9. + * Assumes that you start at line = 11 or line = 12. + * Call this function at the end of the drawJournal function like: limitScroll(player, line); + * @param player The player. + * @param line The number of lines to scroll. Due to sendRunScript, it handles less than 12 lines pretty well. + * @param startFromTop Whether to open the log at the top, defaults to opening the log at the very bottom. + */ + public void limitScrolling(Player player, int line, boolean startFromTop) { + // sendRunScript reverses the objects you pass in + // (args1: 0 is to start from bottom of scroll) (args0: child-12 lines to display) + player.getPacketDispatch().sendRunScript(1207, "ii", startFromTop ? 1 : 0, line - 9); // -9 to give text some padding instead of line - 11 or 12 + } + /** * Draws text on the quest reward component. * @param player The player. @@ -217,8 +256,13 @@ public abstract class Quest implements Plugin { */ public int[] getConfig(Player player, int stage) { if (configs.length < 4) { - throw new IndexOutOfBoundsException("Quest -> " + name + " configs array length was not valid. config length = " + configs.length + "!"); + throw new IndexOutOfBoundsException("Quest -> " + quest + " configs array length was not valid. config length = " + configs.length + "!"); } + if (configs.length >= 5) { + // {questVarpId, questVarbitId, valueToSet} + return new int[] {configs[0], configs[1], stage == 0 ? configs[2] : stage >= 100 ? configs[4] : configs[3]}; + } + // {questVarpId, valueToSet} return new int[] {configs[0], stage == 0 ? configs[1] : stage >= 100 ? configs[3] : configs[2]}; } @@ -265,8 +309,8 @@ public abstract class Quest implements Plugin { * Gets the name. * @return the name. */ - public String getName() { - return name; + public Quests getQuest() { + return quest; } /** @@ -303,7 +347,7 @@ public abstract class Quest implements Plugin { @Override public String toString() { - return "Quest [name=" + name + ", index=" + index + ", buttonId=" + buttonId + ", questPoints=" + questPoints + ", configs=" + Arrays.toString(configs) + "]"; + return "Quest [name=" + quest + ", index=" + index + ", buttonId=" + buttonId + ", questPoints=" + questPoints + ", configs=" + Arrays.toString(configs) + "]"; } } diff --git a/Server/src/main/core/game/node/entity/player/link/quest/QuestRepository.java b/Server/src/main/core/game/node/entity/player/link/quest/QuestRepository.java index ac7ac5cee..02ccf6b29 100644 --- a/Server/src/main/core/game/node/entity/player/link/quest/QuestRepository.java +++ b/Server/src/main/core/game/node/entity/player/link/quest/QuestRepository.java @@ -1,9 +1,9 @@ package core.game.node.entity.player.link.quest; +import content.data.Quests; import core.game.node.entity.player.Player; import core.tools.Log; -import core.tools.SystemLogger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -25,7 +25,7 @@ public final class QuestRepository { /** * The static mapping of instanced quests. */ - private static final Map QUESTS = new TreeMap<>(); + private static final Map QUESTS = new TreeMap<>(); /** * The mapping of quest indexes with related stages. @@ -74,8 +74,17 @@ public final class QuestRepository { int[] config = null; for(Quest quest : QUESTS.values()){ config = quest.getConfig(player,getStage(quest)); - - setVarp(player, config[0], config[1]); + + // {questVarpId, questVarbitId, valueToSet} + if (config.length == 3) { + // This is to set quests with VARPBIT, ignoring VARP value + setVarbit(player, config[1], config[2]); + } else { + // This is the original VARP quests + // {questVarpId, valueToSet} + setVarp(player, config[0], config[1]); + } + quest.updateVarps(player); } } @@ -91,7 +100,7 @@ public final class QuestRepository { if(oldStage < stage) { quests.put(quest.getIndex(), stage); } else { - log(this.getClass(), Log.WARN, String.format("Nonmonotonic QuestRepository.setStage call for player \"%s\", quest \"%s\", old stage %d, new stage %d", player.getName(), quest.getName(), oldStage, stage)); + log(this.getClass(), Log.WARN, String.format("Nonmonotonic QuestRepository.setStage call for player \"%s\", quest \"%s\", old stage %d, new stage %d", player.getName(), quest.getQuest(), oldStage, stage)); } } @@ -189,51 +198,49 @@ public final class QuestRepository { /** * Checks if the quest is complete. * - * @param name The name of the quest. + * @param quest The quest. * @return {@code True} if so. */ - public boolean isComplete(String name) { - Quest quest = getQuest(name); - if (quest == null) { - log(this.getClass(), Log.ERR, "Error can't check if quest is complete for " + name); + public boolean isComplete(Quests quest) { + Quest theQuest = getQuest(quest); + if (theQuest == null) { + log(this.getClass(), Log.ERR, "Error can't check if quest is complete for " + quest); return false; } - return quest.getStage(player) >= 100; + return theQuest.getStage(player) >= 100; } /** * Checks if the quest has at least started. * - * @param name The name of the quest. + * @param quest The quest by id. * @return {@code True} if so. */ - public boolean hasStarted(String name) { - Quest quest = getQuest(name); + public boolean hasStarted(Quests quest) { + Quest theQuest = getQuest(quest); if (quest == null) { - log(this.getClass(), Log.ERR, "Error can't check if quest is complete for " + name); + log(this.getClass(), Log.ERR, "Error can't check if quest is complete for " + quest); return false; } - return quest.getStage(player) > 0; + return theQuest.getStage(player) > 0; } /** - * Gets the stage of quest by name. - * - * @param name The name of the quest. + * Gets the stage of quest by id. + * @param quest The quest. * @return The stage. */ - public int getStage(String name) { - var quest = QUESTS.get(name); - if (quest == null) { + public int getStage(Quests quest) { + var theQuest = QUESTS.get(quest); + if (theQuest == null) { return 0; } - return getStage(quest); + return getStage(theQuest); } /** * Gets the stage of a quest. - * * @param quest The quest. * @return The stage. */ @@ -242,13 +249,12 @@ public final class QuestRepository { } /** - * Gets the quest by name. - * - * @param name The name. + * Gets the quest by id. + * @param quest The quest. * @return The quest. */ - public Quest getQuest(String name) { - return QUESTS.get(name); + public Quest getQuest(Quests quest) { + return QUESTS.get(quest); } /** @@ -275,7 +281,7 @@ public final class QuestRepository { * @param quest The quest. */ public static void register(Quest quest) { - QUESTS.put(quest.getName(), quest); + QUESTS.put(quest.getQuest(), quest); } /** @@ -283,7 +289,7 @@ public final class QuestRepository { * * @return the quests. */ - public static Map getQuests() { + public static Map getQuests() { return QUESTS; } diff --git a/Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java index c58521aab..22fab5af3 100644 --- a/Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java +++ b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java @@ -161,7 +161,8 @@ public final class TradeContainer extends Container { String targetMac = target.getDetails().getMacAddress(); String playerHost = player.getDetails().getCompName(); String targetHost = target.getDetails().getCompName(); - if (item.getId() == 11174 || item.getId() == 11173 || item.getId() == 759) { + // Ironman trading exceptions for Shield of Arrav and Heroes Quest + if (item.getId() == 11174 || item.getId() == 11173 || item.getId() == 759 || item.getId() == 1586 || item.getId() == 1577) { return true; } if (player.getIronmanManager().isIronman() || target != null && target.getIronmanManager().isIronman()) { diff --git a/Server/src/main/core/game/node/entity/skill/Skills.java b/Server/src/main/core/game/node/entity/skill/Skills.java index 88d56aac9..c545ed949 100644 --- a/Server/src/main/core/game/node/entity/skill/Skills.java +++ b/Server/src/main/core/game/node/entity/skill/Skills.java @@ -1,6 +1,7 @@ package core.game.node.entity.skill; import content.global.skill.skillcapeperks.SkillcapePerks; +import core.ServerConstants; import core.game.event.DynamicSkillLevelChangeEvent; import core.game.event.XPGainEvent; import content.global.handlers.item.equipment.brawling_gloves.BrawlingGloves; @@ -24,7 +25,6 @@ import core.plugin.CorePluginTypes.XPGainPlugins; import org.rs09.consts.Items; import org.rs09.consts.Sounds; -import java.nio.ByteBuffer; import java.util.ArrayList; import static core.api.ContentAPIKt.getWorldTicks; @@ -41,7 +41,7 @@ public final class Skills { /** * Represents the constant modifier of experience. */ - public double experienceMutiplier = 5.0; + public double experienceMultiplier = 1.0; /** * The maximum experience multiplier. @@ -289,7 +289,7 @@ public final class Skills { private double getExperienceMod(int slot, double experience, boolean playerMod, boolean multiplyer) { //Keywords for people ctrl + Fing the project //xprate xp rate xp multiplier skilling rate - return experienceMutiplier; + return experienceMultiplier; /*if (!(entity instanceof Player)) { return 1.0; } @@ -363,46 +363,38 @@ public final class Skills { int staticLevel = getStaticLevel(i); setLevel(i, staticLevel); } - if (entity instanceof Player) { - playAudio(entity.asPlayer(), Sounds.PRAYER_RECHARGE_2674); - } rechargePrayerPoints(); } - /** - * Parses the skill data from the buffer. - * @param buffer The byte buffer. - */ - public void parse(ByteBuffer buffer) { - for (int i = 0; i < 24; i++) { - experience[i] = ((double) buffer.getInt() / 10D); - dynamicLevels[i] = buffer.get() & 0xFF; - if (i == HITPOINTS) { - lifepoints = dynamicLevels[i]; - } else if (i == PRAYER) { - prayerPoints = dynamicLevels[i]; - } - staticLevels[i] = buffer.get() & 0xFF; - } - experienceGained = buffer.getInt(); - } - public void parse(JSONArray skillData){ for(int i = 0; i < skillData.size(); i++){ JSONObject skill = (JSONObject) skillData.get(i); int id = Integer.parseInt( skill.get("id").toString()); - dynamicLevels[id] = Integer.parseInt( skill.get("dynamic").toString()); - if (id == HITPOINTS) { - lifepoints = dynamicLevels[i]; - } else if (id == PRAYER) { - prayerPoints = dynamicLevels[i]; - } - staticLevels[id] = Integer.parseInt( skill.get("static").toString()); + dynamicLevels[id] = Integer.parseInt(skill.get("dynamic").toString()); + staticLevels[id] = Integer.parseInt(skill.get("static").toString()); experience[id] = Double.parseDouble(skill.get("experience").toString()); + int version = entity instanceof Player ? entity.asPlayer().version : ServerConstants.CURRENT_SAVEFILE_VERSION; + if (i == HITPOINTS) { + if (version < 3 && !skill.containsKey("lifepoints")) { //!1881 + lifepoints = dynamicLevels[id]; + dynamicLevels[id] = staticLevels[id]; + } else { + lifepoints = Integer.parseInt(skill.get("lifepoints").toString()); + } + } + if (i == PRAYER) { + if (version < 3 && !skill.containsKey("prayerPoints")) { //!1881 + prayerPoints = dynamicLevels[id]; + dynamicLevels[id] = staticLevels[id]; + } else { + prayerPoints = Double.parseDouble(skill.get("prayerPoints").toString()); + } + } } } public void correct(double divisor){ + // XP squish for legacy x20 accounts for(int i = 0; i < staticLevels.length; i++){ experience[i] /= divisor; staticLevels[i] = getStaticLevelByExperience(i); @@ -414,40 +406,10 @@ public final class Skills { setLifepoints(staticLevels[i]); } } - experienceMutiplier = 5.0; + experienceMultiplier = 5.0; updateCombatLevel(); } - public void parseExpRate(ByteBuffer buffer) { - experienceMutiplier = buffer.getDouble(); - if(GameWorld.getSettings().getDefault_xp_rate() != experienceMutiplier){ - experienceMutiplier = GameWorld.getSettings().getDefault_xp_rate(); - } - } - - /** - * Saves the skill data on the buffer. - * @param buffer The byte buffer. - */ - public void save(ByteBuffer buffer) { - for (int i = 0; i < 24; i++) { - buffer.putInt((int) (experience[i] * 10)); - if (i == HITPOINTS) { - buffer.put((byte) lifepoints); - } else if (i == PRAYER) { - buffer.put((byte) Math.ceil(prayerPoints)); - } else { - buffer.put((byte) dynamicLevels[i]); - } - buffer.put((byte) staticLevels[i]); - } - buffer.putInt((int) experienceGained); - } - - public void saveExpRate(ByteBuffer buffer) { - buffer.putDouble(experienceMutiplier); - } - /** * Refreshes all the skill levels. */ diff --git a/Server/src/main/core/game/node/entity/state/PlayerState.kt b/Server/src/main/core/game/node/entity/state/PlayerState.kt deleted file mode 100644 index bfc15b2c4..000000000 --- a/Server/src/main/core/game/node/entity/state/PlayerState.kt +++ /dev/null @@ -1,3 +0,0 @@ -package core.game.node.entity.state - -annotation class PlayerState(val key: String) diff --git a/Server/src/main/core/game/node/entity/state/State.kt b/Server/src/main/core/game/node/entity/state/State.kt deleted file mode 100644 index 20dcd9385..000000000 --- a/Server/src/main/core/game/node/entity/state/State.kt +++ /dev/null @@ -1,42 +0,0 @@ -package core.game.node.entity.state - -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import org.json.simple.JSONObject -import core.game.world.GameWorld.Pulser - -/** - * A class representing a state that the player or some associated thing can be in. - * @param player The player the state is for - * @author Ceikry - */ -abstract class State(val player: Player? = null) { - var pulse: Pulse? = null - - /** - * Saves any additional data the state might need to the player's save. - */ - abstract fun save(root: JSONObject) - - /** - * Parses any additional saved data the state might have. - */ - abstract fun parse(_data: JSONObject) - - /** - * Returns a new instance of the class constructed for the player. - */ - abstract fun newInstance(player: Player? = null) : State - - /** - * Method used to define the pulse the state uses. - * Called during the init method of the state, which is done during save parsing and done - * manually when first creating a state. - */ - abstract fun createPulse() - fun init() { - createPulse() - pulse ?: return - Pulser.submit(pulse!!) - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/state/StatePulse.java b/Server/src/main/core/game/node/entity/state/StatePulse.java deleted file mode 100644 index c4f006be0..000000000 --- a/Server/src/main/core/game/node/entity/state/StatePulse.java +++ /dev/null @@ -1,89 +0,0 @@ -package core.game.node.entity.state; - -import core.game.node.entity.Entity; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; - -import java.nio.ByteBuffer; - -/** - * Represents a state pulse. - * @author Emperor - */ -public abstract class StatePulse extends Pulse { - - /** - * The entity. - */ - protected final Entity entity; - - /** - * Constructs a new {@code StatePulse} {@code Object}. - * @param entity The entity. - * @param ticks The amount of ticks. - */ - public StatePulse(Entity entity, int ticks) { - super(ticks, entity); - super.stop(); - this.entity = entity; - } - - /** - * Checks if data has to be saved. - * @return {@code True} if so. - */ - public abstract boolean isSaveRequired(); - - /** - * Saves the state data. - * @param buffer The buffer. - */ - public abstract void save(ByteBuffer buffer); - - /** - * Parses the state data. - * @param entity The entity. - * @param buffer The buffer. - * @return The state pulse created. - */ - public abstract StatePulse parse(Entity entity, ByteBuffer buffer); - - /** - * Creates a new instance of this state pulse. - * @param entity The entity. - * @param args The arguments. - * @return The state pulse. - */ - public abstract StatePulse create(Entity entity, Object... args); - - /** - * Checks if this pulse can be ran for the given entity. - * @param entity The entity. - * @return {@code True} if so. - */ - public boolean canRun(Entity entity) { - return true; - } - - /** - * Called when the pulse gets manually removed. - */ - public void remove() { - /* - * empty. - */ - } - - /** - * Runs the pulse. - */ - public void run() { - if (isRunning()) { - return; - } - restart(); - start(); - GameWorld.getPulser().submit(this); - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/state/StateRepository.kt b/Server/src/main/core/game/node/entity/state/StateRepository.kt deleted file mode 100644 index 8e437e6f4..000000000 --- a/Server/src/main/core/game/node/entity/state/StateRepository.kt +++ /dev/null @@ -1,41 +0,0 @@ -package core.game.node.entity.state - -import core.api.StartupListener -import core.game.node.entity.player.Player -import io.github.classgraph.ClassGraph - -class StateRepository : StartupListener{ - override fun startup() { - loadStateClasses() - } - - companion object { - val states = HashMap() - - fun loadStateClasses() - { - val result = ClassGraph().enableClassInfo().enableAnnotationInfo().acceptPackages("content").scan() - result.getClassesWithAnnotation("core.game.node.entity.state.PlayerState").forEach{ - val key = it.getAnnotationInfo("core.game.node.entity.state.PlayerState").parameterValues[0].value as String - val clazz = it.loadClass().newInstance() - if(clazz is State) { - states.put(key, clazz) - } - } - } - - @JvmStatic - fun forKey(key: String, player: Player): State?{ - val state = states[key] - if(player.hasActiveState(key)){ - return states[key] - } - if(state != null){ - val clazz = state.newInstance(player) - player.states[key] = clazz - return clazz - } - return null - } - } -} diff --git a/Server/src/main/core/game/node/entity/state/impl/FireResistantPulse.java b/Server/src/main/core/game/node/entity/state/impl/FireResistantPulse.java deleted file mode 100644 index b00e4cbbd..000000000 --- a/Server/src/main/core/game/node/entity/state/impl/FireResistantPulse.java +++ /dev/null @@ -1,77 +0,0 @@ -package core.game.node.entity.state.impl; - -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.node.entity.state.StatePulse; -import core.game.world.GameWorld; - -import java.nio.ByteBuffer; - -/** - * The pulse used for fire resistant. - * @author Vexia - */ -public class FireResistantPulse extends StatePulse { - - /** - * The time to finish. - */ - private static int END_TIME = GameWorld.getSettings().isDevMode() ? 30 : 600; - - /** - * The current tick. - */ - private int currentTick; - - /** - * If the potion is an extended antifire. - */ - private boolean extended; - - /** - * Constructs a new {@Code FireResistantPulse} {@Code Object} - * @param entity the entity. - * @param ticks the ticks. - */ - public FireResistantPulse(Entity entity, int ticks, int currentTick, boolean extended) { - super(entity, ticks); - this.extended = extended; - this.currentTick = currentTick; - } - - @Override - public boolean isSaveRequired() { - return true; - } - - @Override - public void save(ByteBuffer buffer) { - buffer.putInt(currentTick); - } - - @Override - public StatePulse parse(Entity entity, ByteBuffer buffer) { - return new FireResistantPulse(entity, 1, buffer.getInt(), extended); - } - - @Override - public StatePulse create(Entity entity, Object... args) { - return new FireResistantPulse(entity, 1, 0, (boolean) args[0]); - } - - @Override - public boolean pulse() { - if(extended && currentTick == 0 && END_TIME < 1200){ - END_TIME += 600; - } - if (entity instanceof Player) { - if (currentTick == (END_TIME - 25)) { - entity.asPlayer().getPacketDispatch().sendMessage("Your resistance to dragonfire is about to run out."); - } else if (currentTick == (END_TIME - 1)) { - entity.asPlayer().getPacketDispatch().sendMessage("Your resistance to dragonfire has run out."); - } - } - return ++currentTick >= END_TIME; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/node/item/GroundItem.java b/Server/src/main/core/game/node/item/GroundItem.java index af9054ee8..ba5c4e272 100644 --- a/Server/src/main/core/game/node/item/GroundItem.java +++ b/Server/src/main/core/game/node/item/GroundItem.java @@ -37,6 +37,8 @@ public class GroundItem extends Item { */ private boolean removed; + public boolean forceVisible; + /** * Constructs a new {@code GroundItem} {@code Object}. * @param item The item @@ -99,7 +101,7 @@ public class GroundItem extends Item { * @return {@code True} if so. */ public boolean isPrivate() { - return remainPrivate || (decayTime - GameWorld.getTicks() > 100); + return !forceVisible && (remainPrivate || (decayTime - GameWorld.getTicks() > 100)); } @Override @@ -200,9 +202,13 @@ public class GroundItem extends Item { this.removed = removed; } + /** + * Gets the dropper uid. + */ public int getDropperUid() { return dropperUid; } + @Override public String toString() { return "GroundItem [dropper=" + (dropper != null ? dropper.getUsername() : dropper) + ", ticks=" + ticks + ", decayTime=" + decayTime + ", remainPrivate=" + remainPrivate + ", removed=" + removed + "]"; diff --git a/Server/src/main/core/game/node/item/Item.java b/Server/src/main/core/game/node/item/Item.java index f84c7d648..086784cf6 100644 --- a/Server/src/main/core/game/node/item/Item.java +++ b/Server/src/main/core/game/node/item/Item.java @@ -196,14 +196,6 @@ public class Item extends Node{ return idHash; } - /** - * Sets the id hash. - * @param hash the hash to set - */ - public void setIdHash(int hash) { - this.idHash = hash; - } - /** * Checks if the item has a wrapper plugin. * @return {@code True} if so. diff --git a/Server/src/main/core/game/requirement/Requirement.kt b/Server/src/main/core/game/requirement/Requirement.kt index 0cbd1c508..d43b68dac 100644 --- a/Server/src/main/core/game/requirement/Requirement.kt +++ b/Server/src/main/core/game/requirement/Requirement.kt @@ -7,6 +7,7 @@ import core.game.node.entity.skill.Skills import kotlin.math.min import java.util.ArrayList +import content.data.Quests interface Requirement { abstract fun evaluate (player: Player) : Pair> @@ -22,7 +23,7 @@ open class SkillReq (val skillId: Int, val level: Int, val soft: Boolean = false open class QuestReq (val questReq: QuestRequirements, val stageRequired: Int = 100) : Requirement { override fun evaluate (player: Player) : Pair> { - val quest = QuestRepository.getQuests()[questReq.questName] + val quest = QuestRepository.getQuests()[questReq.quest] val unmetRequirements = ArrayList() var isMet = true if (quest == null) { @@ -57,143 +58,144 @@ open class QPCumulative (val amount: Int) : Requirement { } } -enum class QuestRequirements (val questName: String, vararg val requirements: Requirement) { - COOK_ASSIST ("Cook's Assistant"), - DEMON_SLAYER ("Demon Slayer"), - DORIC_QUEST ("Doric's Quest"), - DRAGON_SLAYER ("Dragon Slayer", QPReq(32)), - ERNEST ("Ernest the Chicken"), - GOBLIN_DIP ("Goblin Diplomacy"), - IMP_CATCHER ("Imp Catcher"), - KNIGHT_SWORD ("The Knight's Sword", SkillReq(Skills.MINING, 10, true)), - PIRATE_T ("Pirate's Treasure"), - ALI_RESCUE ("Prince Ali Rescue"), - RESTLESS_GHOST ("The Restless Ghost"), - ROMEO ("Romeo & Juliet"), - RUNE_MYST ("Rune Mysteries"), - SHEEP ("Sheep Shearer"), - ARRAV ("Shield of Arrav"), - VAMPIRE ("Vampire Slayer"), - DORIC ("Doric's Quest"), - RUNE_MYSTERIES("Rune Mysteries"), - BLACK_KNIGHT("Black Knights' Fortress", QPReq(12)), - WITCH_POTION ("Witch's Potion"), - DRUIDIC_RITUAL ("Druidic Ritual"), - LOST_CITY ("Lost City", SkillReq(Skills.CRAFTING, 31, true), SkillReq(Skills.WOODCUTTING, 36, true)), - WITCH_HOUSE ("Witch's House"), - MERLIN ("Merlin's Crystal"), - HERO ("Heroes' Quest", QPReq(55), SkillReq(Skills.COOKING, 53, true), SkillReq(Skills.FISHING, 53, true), SkillReq(Skills.HERBLORE, 25, true), SkillReq(Skills.MINING, 50, true), QuestReq(ARRAV), QuestReq(LOST_CITY), QuestReq(MERLIN), QuestReq(DRAGON_SLAYER)), - SCORP_CATCHER ("Scorpion Catcher", SkillReq(Skills.PRAYER, 31)), - FAMILY_CREST ("Family Crest", SkillReq(Skills.MINING, 40, true), SkillReq(Skills.SMITHING, 40, true), SkillReq(Skills.MAGIC, 59, true), SkillReq(Skills.CRAFTING, 40, true)), - FISHING_CONTEST ("Fishing Contest", SkillReq(Skills.FISHING, 10)), - TOTEM ("Tribal Totem", SkillReq(Skills.THIEVING, 21)), - MONK ("Monk's Friend"), - IKOV ("Temple of Ikov", SkillReq(Skills.THIEVING, 42, true), SkillReq(Skills.RANGE, 40)), - CLOCK_TOWER ("Clock Tower"), - GRAIL ("Holy Grail", QuestReq (MERLIN), SkillReq (Skills.ATTACK, 20)), - GNOME_VILLAGE ("Tree Gnome Village"), - FIGHT_ARENA ("Fight Arena"), - HAZEEL ("Hazeel Cult"), - SHEEP_HERDER ("Sheep Herder"), - PLAGUE_CITY ("Plague City"), - SEA_SLUG ("Sea Slug", SkillReq(Skills.FIREMAKING, 30, true)), - WATERFALL ("Waterfall Quest"), - POTION ("Jungle Potion", SkillReq(Skills.HERBLORE, 3, true), QuestReq(DRUIDIC_RITUAL)), - GRAND_TREE ("The Grand Tree", SkillReq(Skills.AGILITY, 25, true)), - BIOHAZARD ("Biohazard", QuestReq(PLAGUE_CITY)), - UNDERGROUND_PASS ("Underground Pass", SkillReq (Skills.RANGE, 25), QuestReq(BIOHAZARD), QuestReq(PLAGUE_CITY)), - OBSERVATORY ("Observatory Quest"), - TOURIST ("The Tourist Trap", SkillReq (Skills.FLETCHING, 10, true), SkillReq(Skills.SMITHING, 20, true)), - WATCHTOWER ("Watchtower", SkillReq (Skills.MAGIC, 14, true), SkillReq(Skills.THIEVING, 15, true), SkillReq (Skills.AGILITY, 25, true), SkillReq (Skills.HERBLORE, 14, true), SkillReq(Skills.MINING, 40, true)), - DWARF_CANNON ("Dwarf Cannon"), - MURDER_MYS ("Murder Mystery"), - DIG_SITE ("Dig Site", SkillReq(Skills.AGILITY, 10, true), SkillReq (Skills.HERBLORE, 10, true), SkillReq (Skills.THIEVING, 25, true)), - GERTRUDE ("Gertrude's Cat"), - SHILO ("Shilo Village", QuestReq(POTION), SkillReq(Skills.CRAFTING, 20, true), SkillReq(Skills.AGILITY, 32, true)), - LEGEND ("Legend's Quest", QPReq(107), SkillReq(Skills.AGILITY, 50, true), SkillReq(Skills.CRAFTING, 50, true), SkillReq(Skills.HERBLORE, 45, true), SkillReq(Skills.MAGIC, 56, true), SkillReq(Skills.MINING, 52, true), SkillReq(Skills.PRAYER, 42, true), SkillReq(Skills.SMITHING, 50, true), SkillReq(Skills.STRENGTH, 50, true), SkillReq(Skills.THIEVING, 50, true), SkillReq(Skills.WOODCUTTING, 50, true), QuestReq(FAMILY_CREST), QuestReq(HERO), QuestReq(SHILO), QuestReq(UNDERGROUND_PASS), QuestReq(WATERFALL)), - DEATH_PLATEAU ("Death Plateau"), - TROLL_STRONGHOLD ("Troll Stronghold", QuestReq(DEATH_PLATEAU), SkillReq(Skills.AGILITY, 15, true)), - EADGAR ("Eadgar's Ruse", QuestReq (DRUIDIC_RITUAL), QuestReq (TROLL_STRONGHOLD), SkillReq(Skills.HERBLORE, 31, true)), - CHOMPY ("Big Chompy Bird Hunting", SkillReq (Skills.FLETCHING, 5, true), SkillReq (Skills.COOKING, 30, true), SkillReq(Skills.RANGE, 30, false)), - ELEMENTAL_W1 ("Elemental Workshop I", SkillReq(Skills.MINING, 20, true), SkillReq(Skills.SMITHING, 20, true), SkillReq(Skills.CRAFTING, 20, true)), - PRIEST ("Priest in Peril"), - NATURE_SPIRIT ("Nature Spirit", QuestReq(PRIEST), QuestReq(RESTLESS_GHOST)), - REGICIDE ("Regicide", QuestReq (UNDERGROUND_PASS), SkillReq (Skills.CRAFTING, 10), SkillReq(Skills.AGILITY, 56, true)), - TAI_BWO ("Tai Bwo Wannai Trio", SkillReq (Skills.AGILITY, 15, true), SkillReq(Skills.COOKING, 30), SkillReq(Skills.FISHING, 65, true), QuestReq(POTION)), - SHADES ("Shades of Mort'ton", QuestReq(PRIEST), SkillReq(Skills.CRAFTING, 20, true), SkillReq(Skills.HERBLORE, 15, true), SkillReq(Skills.FIREMAKING, 5, true)), - FREM_TRIALS ("Fremennik Trials", SkillReq(Skills.FLETCHING, 25, true), SkillReq(Skills.WOODCUTTING, 40, true), SkillReq(Skills.CRAFTING, 40, true)), - HORROR_DEEP ("Horror from the Deep", SkillReq(Skills.AGILITY, 35, true)), - THRONE ("Throne of Miscellania", QuestReq(HERO), QuestReq(FREM_TRIALS)), - MONKEY ("Monkey Madness", QuestReq(GRAND_TREE), QuestReq(GNOME_VILLAGE)), - MINE ("Haunted Mine", QuestReq(PRIEST), SkillReq(Skills.CRAFTING, 35, true)), - TROLL_ROMANCE ("Troll Romance", QuestReq(TROLL_STRONGHOLD), SkillReq(Skills.AGILITY, 28, true)), - SEARCH_MYREQUE ("In Search of the Myreque", QuestReq(NATURE_SPIRIT), SkillReq(Skills.AGILITY, 25, true)), - FENKENSTRAIN ("Creature of Fenkenstrain", QuestReq(PRIEST), QuestReq(RESTLESS_GHOST), SkillReq(Skills.THIEVING, 25, true), SkillReq(Skills.CRAFTING, 20, true)), - ROVING_ELVES ("Roving Elves", QuestReq(REGICIDE), QuestReq(WATERFALL), SkillReq(Skills.AGILITY, 56, true)), - GHOSTS_AHOY ("Ghosts Ahoy", QuestReq(PRIEST), QuestReq(RESTLESS_GHOST), SkillReq(Skills.AGILITY, 25, true), SkillReq(Skills.COOKING, 20, true)), - FAVOR ("One Small Favor", QuestReq(RUNE_MYSTERIES), QuestReq(SHILO), SkillReq(Skills.AGILITY, 36, true), SkillReq(Skills.CRAFTING, 25, true), SkillReq(Skills.HERBLORE, 18, true), SkillReq(Skills.SMITHING, 30, true)), - MOUNTAIN_DAUGHTER ("Mountain Daughter", SkillReq(Skills.AGILITY, 20, true)), - BETWEEN_ROCK ("Between a Rock...", QuestReq(DWARF_CANNON), QuestReq(FISHING_CONTEST), SkillReq(Skills.DEFENCE, 30, true), SkillReq(Skills.MINING, 40, true), SkillReq(Skills.SMITHING, 50, true)), - FEUD ("The Feud", SkillReq(Skills.THIEVING, 30)), - GOLEM ("The Golem", SkillReq(Skills.CRAFTING, 20, true), SkillReq(Skills.THIEVING, 25, true)), - DESERT ("Desert Treasure", QuestReq (DIG_SITE), QuestReq (IKOV), QuestReq(TOURIST), QuestReq(TROLL_STRONGHOLD), QuestReq(PRIEST), QuestReq(WATERFALL), SkillReq(Skills.THIEVING, 53), SkillReq(Skills.MAGIC, 50), SkillReq(Skills.FIREMAKING, 50, true), SkillReq(Skills.SLAYER, 10)), - ICTHLARIN ("Icthlarin's Little Helper", QuestReq (GERTRUDE)), - TEARS_OF_GUTHIX ("Tears of Guthix", QPReq(43), SkillReq(Skills.FIREMAKING, 49, true), SkillReq(Skills.CRAFTING, 20, true), SkillReq(Skills.MINING, 20, true)), - LOST_TRIBE ("Lost Tribe", QuestReq(GOBLIN_DIP), QuestReq(RUNE_MYSTERIES), SkillReq(Skills.AGILITY, 13, true), SkillReq(Skills.THIEVING, 13, true), SkillReq(Skills.MINING, 17, true)), - GIANT_DWARF ("The Giant Dwarf", SkillReq(Skills.CRAFTING, 12, true), SkillReq(Skills.FIREMAKING, 16, true), SkillReq(Skills.MAGIC, 33, true), SkillReq(Skills.THIEVING, 14, true)), - RECRUITMENT_DRIVE ("Recruitment Drive", QuestReq (BLACK_KNIGHT), QuestReq(DRUIDIC_RITUAL)), - MEP_1 ("Mourning's End Part I", SkillReq(Skills.RANGE, 60), SkillReq(Skills.THIEVING, 50), QuestReq(ROVING_ELVES), QuestReq(CHOMPY), QuestReq(SHEEP_HERDER)), - FORGETTABLE ("Forgettable Tale of a Drunken Dwarf", SkillReq (Skills.COOKING, 22, true), SkillReq(Skills.FARMING, 17, true), QuestReq(GIANT_DWARF), QuestReq(FISHING_CONTEST)), - GARDEN ("Garden of Tranquility", QuestReq(FENKENSTRAIN), SkillReq(Skills.FARMING, 25)), - TWO_CATS ("A Tale of Two Cats", QuestReq(ICTHLARIN)), - WANTED ("Wanted!", QPReq(32), QuestReq(RECRUITMENT_DRIVE), QuestReq(LOST_TRIBE), QuestReq(PRIEST)), - MEP_2 ("Mourning's End Part II", QuestReq(MEP_1)), - ZOGRE ("Zogre Flesh Eaters", QuestReq(CHOMPY), QuestReq(POTION), SkillReq(Skills.SMITHING, 4, true), SkillReq(Skills.HERBLORE, 8, true), SkillReq(Skills.RANGE, 30)), - RUM_DEAL ("Rum Deal", QuestReq(ZOGRE), QuestReq(PRIEST), SkillReq(Skills.CRAFTING, 42, true), SkillReq(Skills.FISHING, 50, true), SkillReq(Skills.FARMING, 40, true), SkillReq(Skills.PRAYER, 47, true), SkillReq(Skills.SLAYER, 42)), - SHADOW ("Shadow of the Storm", SkillReq(Skills.CRAFTING, 30, true), QuestReq(GOLEM), QuestReq(DEMON_SLAYER)), - HISTORY ("Making History", QuestReq(PRIEST), QuestReq(RESTLESS_GHOST)), - RATCATCHERS ("Ratcatchers", QuestReq(ICTHLARIN), QuestReq(GIANT_DWARF)), - SPIRITS_ELID ("Spirits of the Elid", SkillReq(Skills.MAGIC, 33, true), SkillReq(Skills.RANGE, 37, true), SkillReq(Skills.MINING, 37, true), SkillReq(Skills.THIEVING, 37, true)), - DEVIOUS ("Devious Minds", SkillReq(Skills.SMITHING, 65, true), SkillReq(Skills.RUNECRAFTING, 50, true), SkillReq(Skills.FLETCHING, 50, true), QuestReq(WANTED), QuestReq(TROLL_STRONGHOLD), QuestReq(DORIC)), - SAND ("The Hand in the Sand", SkillReq(Skills.THIEVING, 17, true), SkillReq(Skills.CRAFTING, 49, true)), - ENAKHRA ("Enakhra's Lament", SkillReq(Skills.CRAFTING, 50, true), SkillReq(Skills.FIREMAKING, 45, true), SkillReq(Skills.PRAYER, 43, true), SkillReq(Skills.MAGIC, 39, true)), - CABIN_FEVER ("Cabin Fever", QuestReq(PIRATE_T), QuestReq(RUM_DEAL), SkillReq(Skills.AGILITY, 42), SkillReq(Skills.CRAFTING, 45), SkillReq(Skills.SMITHING, 50), SkillReq(Skills.RANGE, 40)), - FAIRYTALE_1 ("Fairytale I - Growing Pains", QuestReq(LOST_CITY), QuestReq(NATURE_SPIRIT)), - RFD ("Recipe for Disaster", QPReq(175), QuestReq(COOK_ASSIST), SkillReq(Skills.COOKING, 70, true), SkillReq(Skills.AGILITY, 48, true), SkillReq(Skills.MINING, 50, true), SkillReq(Skills.FISHING, 53, true), SkillReq(Skills.THIEVING, 53, true), SkillReq(Skills.HERBLORE, 25, true), SkillReq(Skills.MAGIC, 59, true), SkillReq(Skills.SMITHING, 40, true), SkillReq(Skills.FIREMAKING, 50, true), SkillReq(Skills.RANGE, 40), SkillReq(Skills.CRAFTING, 40, true), SkillReq(Skills.FLETCHING, 10, true), SkillReq(Skills.WOODCUTTING, 36, true), QuestReq(FISHING_CONTEST), QuestReq(GOBLIN_DIP), QuestReq(CHOMPY), QuestReq(MURDER_MYS), QuestReq(NATURE_SPIRIT), QuestReq(WITCH_HOUSE), QuestReq(GERTRUDE), QuestReq(SHADOW), QuestReq(LEGEND), QuestReq(MONKEY), QuestReq(DESERT), QuestReq(HORROR_DEEP)), - AID_MYREQUE ("In Aid of the Myreque", QuestReq(SEARCH_MYREQUE), SkillReq(Skills.AGILITY, 25, true), SkillReq(Skills.CRAFTING, 25), SkillReq(Skills.MINING, 15), SkillReq(Skills.MAGIC, 7, true)), - SOUL_BANE ("A Soul's Bane"), - BONE_MAN_1 ("Rag and Bone Man I"), - SWAN ("Swan Song", QPReq(100), SkillReq(Skills.MAGIC, 66, true), SkillReq(Skills.COOKING, 62, true), SkillReq(Skills.FISHING, 62, true), SkillReq(Skills.SMITHING, 45, true), SkillReq(Skills.FIREMAKING, 42, true), SkillReq(Skills.CRAFTING, 40, true), QuestReq(FAVOR), QuestReq(GARDEN)), - ROYAL_TROUBLE ("Royal Trouble", SkillReq(Skills.AGILITY, 40, true), SkillReq(Skills.SLAYER, 40, true), QuestReq(THRONE)), - DEATH_DORGESHUUN ("Death to the Dorgeshuun", QuestReq(LOST_TRIBE), SkillReq(Skills.AGILITY, 23, true), SkillReq(Skills.THIEVING, 23, true)), - FAIRYTALE_2 ("Fairytale II - Cure a Queen", QuestReq(FAIRYTALE_1), SkillReq(Skills.THIEVING, 40), SkillReq(Skills.FARMING, 49, true), SkillReq(Skills.HERBLORE, 57, true)), - LUNAR_DIPLOMACY ("Lunar Diplomacy", QuestReq(FREM_TRIALS), QuestReq(LOST_CITY), QuestReq(RUNE_MYSTERIES), QuestReq(SHILO), SkillReq(Skills.HERBLORE, 5), SkillReq(Skills.CRAFTING, 61), SkillReq(Skills.DEFENCE, 40), SkillReq(Skills.FIREMAKING, 49), SkillReq(Skills.MAGIC, 65), SkillReq(Skills.MINING, 60), SkillReq(Skills.WOODCUTTING, 55)), - GLOUPHRIE ("The Eyes of Glouphrie", QuestReq(GRAND_TREE), SkillReq(Skills.CONSTRUCTION, 5), SkillReq(Skills.MAGIC, 46)), - HALLOWVALE ("Darkness of Hallowvale", QuestReq(AID_MYREQUE), SkillReq(Skills.CONSTRUCTION, 5, true), SkillReq(Skills.MINING, 20), SkillReq(Skills.THIEVING, 22), SkillReq(Skills.AGILITY, 26, true), SkillReq(Skills.CRAFTING, 32), SkillReq(Skills.MAGIC, 33, true), SkillReq(Skills.STRENGTH, 40)), - SLUG_MENACE ("The Slug Menace", QuestReq(WANTED), QuestReq(SEA_SLUG), SkillReq(Skills.CRAFTING, 30), SkillReq(Skills.RUNECRAFTING, 30), SkillReq(Skills.SLAYER, 30), SkillReq(Skills.THIEVING, 30)), - ELEMENTAL_W2 ("Elemental Workshop II", QuestReq(ELEMENTAL_W1), SkillReq(Skills.MAGIC, 20, true), SkillReq(Skills.SMITHING, 30, true)), - ARM_ADVENTURE ("My Arm's Big Adventure", SkillReq(Skills.FARMING, 29, true), SkillReq(Skills.WOODCUTTING, 10), QuestReq(EADGAR), QuestReq(FEUD), QuestReq(POTION)), - ENL_JOURNEY ("Enlightened Journey", QPReq(20), SkillReq(Skills.FIREMAKING, 20, true), SkillReq(Skills.FARMING, 30, true), SkillReq(Skills.CRAFTING, 36, true)), - EAGLE ("Eagles' Peak", SkillReq(Skills.HUNTER, 27, true)), - ANMA ("Animal Magnetism", QuestReq(RESTLESS_GHOST), QuestReq(ERNEST), QuestReq(PRIEST), SkillReq(Skills.SLAYER, 18), SkillReq(Skills.CRAFTING, 19), SkillReq(Skills.RANGE, 30), SkillReq(Skills.WOODCUTTING, 35)), - CONTACT ("Contact!", QuestReq(ALI_RESCUE), QuestReq(ICTHLARIN)), - COLD_WAR ("Cold War", SkillReq(Skills.HUNTER, 10), SkillReq(Skills.AGILITY, 30, true), SkillReq(Skills.CRAFTING, 30), SkillReq(Skills.CONSTRUCTION, 34), SkillReq(Skills.THIEVING, 15)), - FREM_ISLES ("The Fremennik Isles", QuestReq(FREM_TRIALS), SkillReq(Skills.CONSTRUCTION, 20, true)), - BRAIN_ROBBERY ("The Great Brain Robbery", SkillReq(Skills.CRAFTING, 16), SkillReq(Skills.CONSTRUCTION, 30), SkillReq(Skills.PRAYER, 50), QuestReq(FENKENSTRAIN), QuestReq(CABIN_FEVER), QuestReq(RFD)), - WHAT_LIES_BELOW ("What Lies Below", QuestReq(RUNE_MYSTERIES), SkillReq(Skills.RUNECRAFTING, 35)), - OLAF ("Olaf's Quest", QuestReq(FREM_TRIALS), SkillReq(Skills.FIREMAKING, 40, true), SkillReq(Skills.WOODCUTTING, 50, true)), - ANOTHER_SLICE ("Another Slice of H.A.M", SkillReq(Skills.ATTACK, 15), SkillReq(Skills.PRAYER, 25), QuestReq(DEATH_DORGESHUUN), QuestReq(GIANT_DWARF), QuestReq(DIG_SITE)), - DREAM_MENTOR ("Dream Mentor", QuestReq(LUNAR_DIPLOMACY), QuestReq(EADGAR)), - GRIM_TALES ("Grim Tales", QuestReq(WITCH_HOUSE), SkillReq(Skills.FARMING, 45, true), SkillReq(Skills.HERBLORE, 52, true), SkillReq(Skills.THIEVING, 58, true), SkillReq(Skills.AGILITY, 59, true), SkillReq(Skills.WOODCUTTING, 71, true)), - KINGS_RANSOM ("King's Ransom", SkillReq(Skills.MAGIC, 45), SkillReq(Skills.MINING, 45, true), SkillReq(Skills.DEFENCE, 65), QuestReq(BLACK_KNIGHT), QuestReq(GRAIL), QuestReq(MURDER_MYS), QuestReq(FAVOR)), - TOWER_OF_LIFE ("Tower of Life", SkillReq(Skills.CONSTRUCTION, 10)), - BONE_MAN_2 ("Rag and Bone Man II", SkillReq(Skills.SLAYER, 40, true), SkillReq(Skills.DEFENCE, 20), QuestReq(BONE_MAN_1), QuestReq(FREM_TRIALS), QuestReq(FENKENSTRAIN), QuestReq(ZOGRE), QuestReq(WATERFALL)), - LAND_GOBLINS ("Land of the Goblins", QuestReq(ANOTHER_SLICE), QuestReq(FISHING_CONTEST), SkillReq(Skills.AGILITY, 38), SkillReq(Skills.FISHING, 40), SkillReq(Skills.THIEVING, 45), SkillReq(Skills.HERBLORE, 48)), - PATH_GLOUPHRIE ("The Path of Glouphrie", QuestReq(GLOUPHRIE), QuestReq(GNOME_VILLAGE), QuestReq(WATERFALL), SkillReq(Skills.AGILITY, 45), SkillReq(Skills.RANGE, 47), SkillReq(Skills.SLAYER, 56), SkillReq(Skills.STRENGTH, 60), SkillReq(Skills.THIEVING, 56)), - DEFENDER_VARROCK ("Defender of Varrock", QuestReq(ARRAV), QuestReq(KNIGHT_SWORD), QuestReq(DEMON_SLAYER), QuestReq(IKOV), QuestReq(FAMILY_CREST), QuestReq(WHAT_LIES_BELOW), QuestReq(GARDEN), SkillReq(Skills.AGILITY, 51), SkillReq(Skills.HUNTER, 51), SkillReq(Skills.MINING, 59), SkillReq(Skills.SMITHING, 54)), - SPIRIT_OF_SUMMER ("Spirit of Summer", QuestReq(RESTLESS_GHOST), SkillReq(Skills.CONSTRUCTION, 40), SkillReq(Skills.FARMING, 26), SkillReq(Skills.PRAYER, 35), SkillReq(Skills.SUMMONING, 19)), - SUMMERS_END ("Summer's End", QuestReq(SPIRIT_OF_SUMMER), SkillReq(Skills.FIREMAKING, 47), SkillReq(Skills.HUNTER, 35), SkillReq(Skills.MINING, 45), SkillReq(Skills.PRAYER, 55), SkillReq(Skills.SUMMONING, 23), SkillReq(Skills.WOODCUTTING, 37)), - SEERGAZE ("Legacy of Seergaze", QuestReq(HALLOWVALE), SkillReq(Skills.AGILITY, 29), SkillReq(Skills.CONSTRUCTION, 20), SkillReq(Skills.CRAFTING, 47), SkillReq(Skills.FIREMAKING, 40), SkillReq(Skills.MAGIC, 49), SkillReq(Skills.MINING, 35), SkillReq(Skills.SLAYER, 31)), - SMOKING_KILLS ("Smoking Kills", QuestReq(RESTLESS_GHOST), QuestReq(ICTHLARIN), SkillReq(Skills.CRAFTING, 25), SkillReq(Skills.SLAYER, 35)), - WHILE_GUTHIX_SLEEPS ("While Guthix Sleeps", SkillReq(Skills.SUMMONING, 23), SkillReq(Skills.HUNTER, 55), SkillReq(Skills.THIEVING, 60), SkillReq(Skills.DEFENCE, 65), SkillReq(Skills.FARMING, 65), SkillReq(Skills.HERBLORE, 65), SkillReq(Skills.MAGIC, 75), QuestReq(DEFENDER_VARROCK), QuestReq(DREAM_MENTOR), QuestReq(SAND), QuestReq(KINGS_RANSOM), QuestReq(LEGEND), QuestReq(MEP_2), QuestReq(PATH_GLOUPHRIE), QuestReq(RFD), QuestReq(SUMMERS_END), QuestReq(SWAN), QuestReq(TEARS_OF_GUTHIX), QuestReq(ZOGRE)) +enum class QuestRequirements(val quest: Quests, vararg val requirements: Requirement) { + COOK_ASSIST (Quests.COOKS_ASSISTANT), + DEMON_SLAYER (Quests.DEMON_SLAYER), + DORIC_QUEST (Quests.DORICS_QUEST), + DRAGON_SLAYER (Quests.DRAGON_SLAYER, QPReq(32)), + ERNEST (Quests.ERNEST_THE_CHICKEN), + GOBLIN_DIP (Quests.GOBLIN_DIPLOMACY), + IMP_CATCHER (Quests.IMP_CATCHER), + KNIGHT_SWORD (Quests.THE_KNIGHTS_SWORD, SkillReq(Skills.MINING, 10, true)), + PIRATE_T (Quests.PIRATES_TREASURE), + ALI_RESCUE (Quests.PRINCE_ALI_RESCUE), + RESTLESS_GHOST (Quests.THE_RESTLESS_GHOST), + ROMEO (Quests.ROMEO_JULIET), + RUNE_MYST (Quests.RUNE_MYSTERIES), + SHEEP (Quests.SHEEP_SHEARER), + ARRAV (Quests.SHIELD_OF_ARRAV), + VAMPIRE (Quests.VAMPIRE_SLAYER), + DORIC (Quests.DORICS_QUEST), + RUNE_MYSTERIES(Quests.RUNE_MYSTERIES), + BLACK_KNIGHT(Quests.BLACK_KNIGHTS_FORTRESS, QPReq(12)), + WITCH_POTION (Quests.WITCHS_POTION), + DRUIDIC_RITUAL (Quests.DRUIDIC_RITUAL), + LOST_CITY (Quests.LOST_CITY, SkillReq(Skills.CRAFTING, 31, true), SkillReq(Skills.WOODCUTTING, 36, true)), + WITCH_HOUSE (Quests.WITCHS_HOUSE), + MERLIN (Quests.MERLINS_CRYSTAL), + HERO (Quests.HEROES_QUEST, QPReq(55), SkillReq(Skills.COOKING, 53, true), SkillReq(Skills.FISHING, 53, true), SkillReq(Skills.HERBLORE, 25, true), SkillReq(Skills.MINING, 50, true), QuestReq(ARRAV), QuestReq(LOST_CITY), QuestReq(MERLIN), QuestReq(DRAGON_SLAYER)), + SCORP_CATCHER (Quests.SCORPION_CATCHER, SkillReq(Skills.PRAYER, 31)), + FAMILY_CREST (Quests.FAMILY_CREST, SkillReq(Skills.MINING, 40, true), SkillReq(Skills.SMITHING, 40, true), SkillReq(Skills.MAGIC, 59, true), SkillReq(Skills.CRAFTING, 40, true)), + FISHING_CONTEST (Quests.FISHING_CONTEST, SkillReq(Skills.FISHING, 10)), + TOTEM (Quests.TRIBAL_TOTEM, SkillReq(Skills.THIEVING, 21)), + MONK (Quests.MONKS_FRIEND), + IKOV (Quests.TEMPLE_OF_IKOV, SkillReq(Skills.THIEVING, 42, true), SkillReq(Skills.RANGE, 40)), + CLOCK_TOWER (Quests.CLOCK_TOWER), + GRAIL (Quests.HOLY_GRAIL, QuestReq (MERLIN), SkillReq (Skills.ATTACK, 20)), + GNOME_VILLAGE (Quests.TREE_GNOME_VILLAGE), + FIGHT_ARENA (Quests.FIGHT_ARENA), + HAZEEL (Quests.HAZEEL_CULT), + SHEEP_HERDER (Quests.SHEEP_HERDER), + PLAGUE_CITY (Quests.PLAGUE_CITY), + SEA_SLUG (Quests.SEA_SLUG, SkillReq(Skills.FIREMAKING, 30, true)), + WATERFALL (Quests.WATERFALL_QUEST), + POTION (Quests.JUNGLE_POTION, SkillReq(Skills.HERBLORE, 3, true), QuestReq(DRUIDIC_RITUAL)), + GRAND_TREE (Quests.THE_GRAND_TREE, SkillReq(Skills.AGILITY, 25, true)), + BIOHAZARD (Quests.BIOHAZARD, QuestReq(PLAGUE_CITY)), + UNDERGROUND_PASS (Quests.UNDERGROUND_PASS, SkillReq (Skills.RANGE, 25), QuestReq(BIOHAZARD), QuestReq(PLAGUE_CITY)), + OBSERVATORY (Quests.OBSERVATORY_QUEST), + TOURIST (Quests.THE_TOURIST_TRAP, SkillReq (Skills.FLETCHING, 10, true), SkillReq(Skills.SMITHING, 20, true)), + WATCHTOWER (Quests.WATCHTOWER, SkillReq (Skills.MAGIC, 14, true), SkillReq(Skills.THIEVING, 15, true), SkillReq (Skills.AGILITY, 25, true), SkillReq (Skills.HERBLORE, 14, true), SkillReq(Skills.MINING, 40, true)), + DWARF_CANNON (Quests.DWARF_CANNON), + MURDER_MYS (Quests.MURDER_MYSTERY), + DIG_SITE (Quests.THE_DIG_SITE, SkillReq(Skills.AGILITY, 10, true), SkillReq (Skills.HERBLORE, 10, true), SkillReq (Skills.THIEVING, 25, true)), + GERTRUDE (Quests.GERTRUDES_CAT), + SHILO (Quests.SHILO_VILLAGE, QuestReq(POTION), SkillReq(Skills.CRAFTING, 20, true), SkillReq(Skills.AGILITY, 32, true)), + LEGEND (Quests.LEGENDS_QUEST, QPReq(107), SkillReq(Skills.AGILITY, 50, true), SkillReq(Skills.CRAFTING, 50, true), SkillReq(Skills.HERBLORE, 45, true), SkillReq(Skills.MAGIC, 56, true), SkillReq(Skills.MINING, 52, true), SkillReq(Skills.PRAYER, 42, true), SkillReq(Skills.SMITHING, 50, true), SkillReq(Skills.STRENGTH, 50, true), SkillReq(Skills.THIEVING, 50, true), SkillReq(Skills.WOODCUTTING, 50, true), QuestReq(FAMILY_CREST), QuestReq(HERO), QuestReq(SHILO), QuestReq(UNDERGROUND_PASS), QuestReq(WATERFALL)), + DEATH_PLATEAU (Quests.DEATH_PLATEAU), + TROLL_STRONGHOLD (Quests.TROLL_STRONGHOLD, QuestReq(DEATH_PLATEAU), SkillReq(Skills.AGILITY, 15, true)), + EADGAR (Quests.EADGARS_RUSE, QuestReq (DRUIDIC_RITUAL), QuestReq (TROLL_STRONGHOLD), SkillReq(Skills.HERBLORE, 31, true)), + CHOMPY (Quests.BIG_CHOMPY_BIRD_HUNTING, SkillReq (Skills.FLETCHING, 5, true), SkillReq (Skills.COOKING, 30, true), SkillReq(Skills.RANGE, 30, false)), + ELEMENTAL_W1 (Quests.ELEMENTAL_WORKSHOP_I, SkillReq(Skills.MINING, 20, true), SkillReq(Skills.SMITHING, 20, true), SkillReq(Skills.CRAFTING, 20, true)), + PRIEST (Quests.PRIEST_IN_PERIL), + NATURE_SPIRIT (Quests.NATURE_SPIRIT, QuestReq(PRIEST), QuestReq(RESTLESS_GHOST)), + REGICIDE (Quests.REGICIDE, QuestReq (UNDERGROUND_PASS), SkillReq (Skills.CRAFTING, 10), SkillReq(Skills.AGILITY, 56, true)), + TAI_BWO (Quests.TAI_BWO_WANNAI_TRIO, SkillReq (Skills.AGILITY, 15, true), SkillReq(Skills.COOKING, 30), SkillReq(Skills.FISHING, 65, true), QuestReq(POTION)), + SHADES (Quests.SHADES_OF_MORTTON, QuestReq(PRIEST), SkillReq(Skills.CRAFTING, 20, true), SkillReq(Skills.HERBLORE, 15, true), SkillReq(Skills.FIREMAKING, 5, true)), + FREM_TRIALS (Quests.THE_FREMENNIK_TRIALS, SkillReq(Skills.FLETCHING, 25, true), SkillReq(Skills.WOODCUTTING, 40, true), SkillReq(Skills.CRAFTING, 40, true)), + HORROR_DEEP (Quests.HORROR_FROM_THE_DEEP, SkillReq(Skills.AGILITY, 35, true)), + THRONE (Quests.THRONE_OF_MISCELLANIA, QuestReq(HERO), QuestReq(FREM_TRIALS)), + MONKEY (Quests.MONKEY_MADNESS, QuestReq(GRAND_TREE), QuestReq(GNOME_VILLAGE)), + MINE (Quests.HAUNTED_MINE, QuestReq(PRIEST), SkillReq(Skills.CRAFTING, 35, true)), + TROLL_ROMANCE (Quests.TROLL_ROMANCE, QuestReq(TROLL_STRONGHOLD), SkillReq(Skills.AGILITY, 28, true)), + SEARCH_MYREQUE (Quests.IN_SEARCH_OF_THE_MYREQUE, QuestReq(NATURE_SPIRIT), SkillReq(Skills.AGILITY, 25, true)), + FENKENSTRAIN (Quests.CREATURE_OF_FENKENSTRAIN, QuestReq(PRIEST), QuestReq(RESTLESS_GHOST), SkillReq(Skills.THIEVING, 25, true), SkillReq(Skills.CRAFTING, 20, true)), + ROVING_ELVES (Quests.ROVING_ELVES, QuestReq(REGICIDE), QuestReq(WATERFALL), SkillReq(Skills.AGILITY, 56, true)), + GHOSTS_AHOY (Quests.GHOSTS_AHOY, QuestReq(PRIEST), QuestReq(RESTLESS_GHOST), SkillReq(Skills.AGILITY, 25, true), SkillReq(Skills.COOKING, 20, true)), + FAVOR (Quests.ONE_SMALL_FAVOUR, QuestReq(RUNE_MYSTERIES), QuestReq(SHILO), SkillReq(Skills.AGILITY, 36, true), SkillReq(Skills.CRAFTING, 25, true), SkillReq(Skills.HERBLORE, 18, true), SkillReq(Skills.SMITHING, 30, true)), + MOUNTAIN_DAUGHTER (Quests.MOUNTAIN_DAUGHTER, SkillReq(Skills.AGILITY, 20, true)), + BETWEEN_ROCK (Quests.BETWEEN_A_ROCK, QuestReq(DWARF_CANNON), QuestReq(FISHING_CONTEST), SkillReq(Skills.DEFENCE, 30, true), SkillReq(Skills.MINING, 40, true), SkillReq(Skills.SMITHING, 50, true)), + FEUD (Quests.THE_FEUD, SkillReq(Skills.THIEVING, 30)), + GOLEM (Quests.THE_GOLEM, SkillReq(Skills.CRAFTING, 20, true), SkillReq(Skills.THIEVING, 25, true)), + DESERT (Quests.DESERT_TREASURE, QuestReq (DIG_SITE), QuestReq (IKOV), QuestReq(TOURIST), QuestReq(TROLL_STRONGHOLD), QuestReq(PRIEST), QuestReq(WATERFALL), SkillReq(Skills.THIEVING, 53), SkillReq(Skills.MAGIC, 50), SkillReq(Skills.FIREMAKING, 50, true), SkillReq(Skills.SLAYER, 10)), + ICTHLARIN (Quests.ICTHLARINS_LITTLE_HELPER, QuestReq (GERTRUDE)), + TEARS_OF_GUTHIX (Quests.TEARS_OF_GUTHIX, QPReq(43), SkillReq(Skills.FIREMAKING, 49, true), SkillReq(Skills.CRAFTING, 20, true), SkillReq(Skills.MINING, 20, true)), + LOST_TRIBE (Quests.THE_LOST_TRIBE, QuestReq(GOBLIN_DIP), QuestReq(RUNE_MYSTERIES), SkillReq(Skills.AGILITY, 13, true), SkillReq(Skills.THIEVING, 13, true), SkillReq(Skills.MINING, 17, true)), + GIANT_DWARF (Quests.THE_GIANT_DWARF, SkillReq(Skills.CRAFTING, 12, true), SkillReq(Skills.FIREMAKING, 16, true), SkillReq(Skills.MAGIC, 33, true), SkillReq(Skills.THIEVING, 14, true)), + RECRUITMENT_DRIVE (Quests.RECRUITMENT_DRIVE, QuestReq (BLACK_KNIGHT), QuestReq(DRUIDIC_RITUAL)), + MEP_1 (Quests.MOURNINGS_END_PART_I, SkillReq(Skills.RANGE, 60), SkillReq(Skills.THIEVING, 50), QuestReq(ROVING_ELVES), QuestReq(CHOMPY), QuestReq(SHEEP_HERDER)), + FORGETTABLE (Quests.FORGETTABLE_TALE, SkillReq (Skills.COOKING, 22, true), SkillReq(Skills.FARMING, 17, true), QuestReq(GIANT_DWARF), QuestReq(FISHING_CONTEST)), + GARDEN (Quests.GARDEN_OF_TRANQUILITY, QuestReq(FENKENSTRAIN), SkillReq(Skills.FARMING, 25)), + TWO_CATS (Quests.A_TAIL_OF_TWO_CATS, QuestReq(ICTHLARIN)), + WANTED (Quests.WANTED, QPReq(32), QuestReq(RECRUITMENT_DRIVE), QuestReq(LOST_TRIBE), QuestReq(PRIEST)), + MEP_2 (Quests.MOURNINGS_END_PART_II, QuestReq(MEP_1)), + ZOGRE (Quests.ZOGRE_FLESH_EATERS, QuestReq(CHOMPY), QuestReq(POTION), SkillReq(Skills.SMITHING, 4, true), SkillReq(Skills.HERBLORE, 8, true), SkillReq(Skills.RANGE, 30)), + RUM_DEAL (Quests.RUM_DEAL, QuestReq(ZOGRE), QuestReq(PRIEST), SkillReq(Skills.CRAFTING, 42, true), SkillReq(Skills.FISHING, 50, true), SkillReq(Skills.FARMING, 40, true), SkillReq(Skills.PRAYER, 47, true), SkillReq(Skills.SLAYER, 42)), + SHADOW (Quests.SHADOW_OF_THE_STORM, SkillReq(Skills.CRAFTING, 30, true), QuestReq(GOLEM), QuestReq(DEMON_SLAYER)), + HISTORY (Quests.MAKING_HISTORY, QuestReq(PRIEST), QuestReq(RESTLESS_GHOST)), + RATCATCHERS (Quests.RATCATCHERS, QuestReq(ICTHLARIN), QuestReq(GIANT_DWARF)), + SPIRITS_ELID (Quests.SPIRITS_OF_THE_ELID, SkillReq(Skills.MAGIC, 33, true), SkillReq(Skills.RANGE, 37, true), SkillReq(Skills.MINING, 37, true), SkillReq(Skills.THIEVING, 37, true)), + DEVIOUS (Quests.DEVIOUS_MINDS, SkillReq(Skills.SMITHING, 65, true), SkillReq(Skills.RUNECRAFTING, 50, true), SkillReq(Skills.FLETCHING, 50, true), QuestReq(WANTED), QuestReq(TROLL_STRONGHOLD), QuestReq(DORIC)), + SAND (Quests.THE_HAND_IN_THE_SAND, SkillReq(Skills.THIEVING, 17, true), SkillReq(Skills.CRAFTING, 49, true)), + ENAKHRA (Quests.ENAKHRAS_LAMENT, SkillReq(Skills.CRAFTING, 50, true), SkillReq(Skills.FIREMAKING, 45, true), SkillReq(Skills.PRAYER, 43, true), SkillReq(Skills.MAGIC, 39, true)), + CABIN_FEVER (Quests.CABIN_FEVER, QuestReq(PIRATE_T), QuestReq(RUM_DEAL), SkillReq(Skills.AGILITY, 42), SkillReq(Skills.CRAFTING, 45), SkillReq(Skills.SMITHING, 50), SkillReq(Skills.RANGE, 40)), + FAIRYTALE_1 (Quests.FAIRYTALE_I_GROWING_PAINS, QuestReq(LOST_CITY), QuestReq(NATURE_SPIRIT)), + RFD (Quests.RECIPE_FOR_DISASTER, QPReq(175), QuestReq(COOK_ASSIST), SkillReq(Skills.COOKING, 70, true), SkillReq(Skills.AGILITY, 48, true), SkillReq(Skills.MINING, 50, true), SkillReq(Skills.FISHING, 53, true), SkillReq(Skills.THIEVING, 53, true), SkillReq(Skills.HERBLORE, 25, true), SkillReq(Skills.MAGIC, 59, true), SkillReq(Skills.SMITHING, 40, true), SkillReq(Skills.FIREMAKING, 50, true), SkillReq(Skills.RANGE, 40), SkillReq(Skills.CRAFTING, 40, true), SkillReq(Skills.FLETCHING, 10, true), SkillReq(Skills.WOODCUTTING, 36, true), QuestReq(FISHING_CONTEST), QuestReq(GOBLIN_DIP), QuestReq(CHOMPY), QuestReq(MURDER_MYS), QuestReq(NATURE_SPIRIT), QuestReq(WITCH_HOUSE), QuestReq(GERTRUDE), QuestReq(SHADOW), QuestReq(LEGEND), QuestReq(MONKEY), QuestReq(DESERT), QuestReq(HORROR_DEEP)), + AID_MYREQUE (Quests.IN_AID_OF_THE_MYREQUE, QuestReq(SEARCH_MYREQUE), SkillReq(Skills.AGILITY, 25, true), SkillReq(Skills.CRAFTING, 25), SkillReq(Skills.MINING, 15), SkillReq(Skills.MAGIC, 7, true)), + SOUL_BANE (Quests.A_SOULS_BANE), + BONE_MAN_1 (Quests.RAG_AND_BONE_MAN), + SWAN (Quests.SWAN_SONG, QPReq(100), SkillReq(Skills.MAGIC, 66, true), SkillReq(Skills.COOKING, 62, true), SkillReq(Skills.FISHING, 62, true), SkillReq(Skills.SMITHING, 45, true), SkillReq(Skills.FIREMAKING, 42, true), SkillReq(Skills.CRAFTING, 40, true), QuestReq(FAVOR), QuestReq(GARDEN)), + ROYAL_TROUBLE (Quests.ROYAL_TROUBLE, SkillReq(Skills.AGILITY, 40, true), SkillReq(Skills.SLAYER, 40, true), QuestReq(THRONE)), + DEATH_DORGESHUUN (Quests.DEATH_TO_THE_DORGESHUUN, QuestReq(LOST_TRIBE), SkillReq(Skills.AGILITY, 23, true), SkillReq(Skills.THIEVING, 23, true)), + FAIRYTALE_2 (Quests.FAIRYTALE_II_CURE_A_QUEEN, QuestReq(FAIRYTALE_1), SkillReq(Skills.THIEVING, 40), SkillReq(Skills.FARMING, 49, true), SkillReq(Skills.HERBLORE, 57, true)), + LUNAR_DIPLOMACY (Quests.LUNAR_DIPLOMACY, QuestReq(FREM_TRIALS), QuestReq(LOST_CITY), QuestReq(RUNE_MYSTERIES), QuestReq(SHILO), SkillReq(Skills.HERBLORE, 5), SkillReq(Skills.CRAFTING, 61), SkillReq(Skills.DEFENCE, 40), SkillReq(Skills.FIREMAKING, 49), SkillReq(Skills.MAGIC, 65), SkillReq(Skills.MINING, 60), SkillReq(Skills.WOODCUTTING, 55)), + GLOUPHRIE (Quests.THE_EYES_OF_GLOUPHRIE, QuestReq(GRAND_TREE), SkillReq(Skills.CONSTRUCTION, 5), SkillReq(Skills.MAGIC, 46)), + HALLOWVALE (Quests.DARKNESS_OF_HALLOWVALE, QuestReq(AID_MYREQUE), SkillReq(Skills.CONSTRUCTION, 5, true), SkillReq(Skills.MINING, 20), SkillReq(Skills.THIEVING, 22), SkillReq(Skills.AGILITY, 26, true), SkillReq(Skills.CRAFTING, 32), SkillReq(Skills.MAGIC, 33, true), SkillReq(Skills.STRENGTH, 40)), + SLUG_MENACE (Quests.THE_SLUG_MENACE, QuestReq(WANTED), QuestReq(SEA_SLUG), SkillReq(Skills.CRAFTING, 30), SkillReq(Skills.RUNECRAFTING, 30), SkillReq(Skills.SLAYER, 30), SkillReq(Skills.THIEVING, 30)), + ELEMENTAL_W2 (Quests.ELEMENTAL_WORKSHOP_II, QuestReq(ELEMENTAL_W1), SkillReq(Skills.MAGIC, 20, true), SkillReq(Skills.SMITHING, 30, true)), + ARM_ADVENTURE (Quests.MY_ARMS_BIG_ADVENTURE, SkillReq(Skills.FARMING, 29, true), SkillReq(Skills.WOODCUTTING, 10), QuestReq(EADGAR), QuestReq(FEUD), QuestReq(POTION)), + ENL_JOURNEY (Quests.ENLIGHTENED_JOURNEY, QPReq(20), SkillReq(Skills.FIREMAKING, 20, true), SkillReq(Skills.FARMING, 30, true), SkillReq(Skills.CRAFTING, 36, true)), + EAGLE (Quests.EAGLES_PEAK, SkillReq(Skills.HUNTER, 27, true)), + ANMA (Quests.ANIMAL_MAGNETISM, QuestReq(RESTLESS_GHOST), QuestReq(ERNEST), QuestReq(PRIEST), SkillReq(Skills.SLAYER, 18), SkillReq(Skills.CRAFTING, 19), SkillReq(Skills.RANGE, 30), SkillReq(Skills.WOODCUTTING, 35)), + CONTACT (Quests.CONTACT, QuestReq(ALI_RESCUE), QuestReq(ICTHLARIN)), + COLD_WAR (Quests.COLD_WAR, SkillReq(Skills.HUNTER, 10), SkillReq(Skills.AGILITY, 30, true), SkillReq(Skills.CRAFTING, 30), SkillReq(Skills.CONSTRUCTION, 34), SkillReq(Skills.THIEVING, 15)), + FREM_ISLES (Quests.THE_FREMENNIK_ISLES, QuestReq(FREM_TRIALS), SkillReq(Skills.CONSTRUCTION, 20, true)), + BRAIN_ROBBERY (Quests.THE_GREAT_BRAIN_ROBBERY, SkillReq(Skills.CRAFTING, 16), SkillReq(Skills.CONSTRUCTION, 30), SkillReq(Skills.PRAYER, 50), QuestReq(FENKENSTRAIN), QuestReq(CABIN_FEVER), QuestReq(RFD)), + WHAT_LIES_BELOW (Quests.WHAT_LIES_BELOW, QuestReq(RUNE_MYSTERIES), SkillReq(Skills.RUNECRAFTING, 35)), + OLAF (Quests.OLAFS_QUEST, QuestReq(FREM_TRIALS), SkillReq(Skills.FIREMAKING, 40, true), SkillReq(Skills.WOODCUTTING, 50, true)), + ANOTHER_SLICE (Quests.ANOTHER_SLICE_OF_HAM, SkillReq(Skills.ATTACK, 15), SkillReq(Skills.PRAYER, 25), QuestReq(DEATH_DORGESHUUN), QuestReq(GIANT_DWARF), QuestReq(DIG_SITE)), + DREAM_MENTOR (Quests.DREAM_MENTOR, QuestReq(LUNAR_DIPLOMACY), QuestReq(EADGAR)), + GRIM_TALES (Quests.GRIM_TALES, QuestReq(WITCH_HOUSE), SkillReq(Skills.FARMING, 45, true), SkillReq(Skills.HERBLORE, 52, true), SkillReq(Skills.THIEVING, 58, true), SkillReq(Skills.AGILITY, 59, true), SkillReq(Skills.WOODCUTTING, 71, true)), + KINGS_RANSOM (Quests.KINGS_RANSOM, SkillReq(Skills.MAGIC, 45), SkillReq(Skills.MINING, 45, true), SkillReq(Skills.DEFENCE, 65), QuestReq(BLACK_KNIGHT), QuestReq(GRAIL), QuestReq(MURDER_MYS), QuestReq(FAVOR)), + TOWER_OF_LIFE (Quests.TOWER_OF_LIFE, SkillReq(Skills.CONSTRUCTION, 10)), + BONE_MAN_2 (Quests.RAG_AND_BONE_MAN, SkillReq(Skills.SLAYER, 40, true), SkillReq(Skills.DEFENCE, 20), QuestReq(BONE_MAN_1), QuestReq(FREM_TRIALS), QuestReq(FENKENSTRAIN), QuestReq(ZOGRE), QuestReq(WATERFALL)), + LAND_GOBLINS (Quests.LAND_OF_THE_GOBLINS, QuestReq(ANOTHER_SLICE), QuestReq(FISHING_CONTEST), SkillReq(Skills.AGILITY, 38), SkillReq(Skills.FISHING, 40), SkillReq(Skills.THIEVING, 45), SkillReq(Skills.HERBLORE, 48)), + PATH_GLOUPHRIE (Quests.THE_PATH_OF_GLOUPHRIE, QuestReq(GLOUPHRIE), QuestReq(GNOME_VILLAGE), QuestReq(WATERFALL), SkillReq(Skills.AGILITY, 45), SkillReq(Skills.RANGE, 47), SkillReq(Skills.SLAYER, 56), SkillReq(Skills.STRENGTH, 60), SkillReq(Skills.THIEVING, 56)), + DEFENDER_VARROCK (Quests.DEFENDER_OF_VARROCK, QuestReq(ARRAV), QuestReq(KNIGHT_SWORD), QuestReq(DEMON_SLAYER), QuestReq(IKOV), QuestReq(FAMILY_CREST), QuestReq(WHAT_LIES_BELOW), QuestReq(GARDEN), SkillReq(Skills.AGILITY, 51), SkillReq(Skills.HUNTER, 51), SkillReq(Skills.MINING, 59), SkillReq(Skills.SMITHING, 54)), + SPIRIT_OF_SUMMER (Quests.SPIRIT_OF_SUMMER, QuestReq(RESTLESS_GHOST), SkillReq(Skills.CONSTRUCTION, 40), SkillReq(Skills.FARMING, 26), SkillReq(Skills.PRAYER, 35), SkillReq(Skills.SUMMONING, 19)), + SUMMERS_END (Quests.SUMMERS_END, QuestReq(SPIRIT_OF_SUMMER), SkillReq(Skills.FIREMAKING, 47), SkillReq(Skills.HUNTER, 35), SkillReq(Skills.MINING, 45), SkillReq(Skills.PRAYER, 55), SkillReq(Skills.SUMMONING, 23), SkillReq(Skills.WOODCUTTING, 37)), + SEERGAZE (Quests.LEGACY_OF_SEERGAZE, QuestReq(HALLOWVALE), SkillReq(Skills.AGILITY, 29), SkillReq(Skills.CONSTRUCTION, 20), SkillReq(Skills.CRAFTING, 47), SkillReq(Skills.FIREMAKING, 40), SkillReq(Skills.MAGIC, 49), SkillReq(Skills.MINING, 35), SkillReq(Skills.SLAYER, 31)), + SMOKING_KILLS (Quests.SMOKING_KILLS, QuestReq(RESTLESS_GHOST), QuestReq(ICTHLARIN), SkillReq(Skills.CRAFTING, 25), SkillReq(Skills.SLAYER, 35)), + WHILE_GUTHIX_SLEEPS (Quests.WHILE_GUTHIX_SLEEPS, SkillReq(Skills.SUMMONING, 23), SkillReq(Skills.HUNTER, 55), SkillReq(Skills.THIEVING, 60), SkillReq(Skills.DEFENCE, 65), SkillReq(Skills.FARMING, 65), SkillReq(Skills.HERBLORE, 65), SkillReq(Skills.MAGIC, 75), QuestReq(DEFENDER_VARROCK), QuestReq(DREAM_MENTOR), QuestReq(SAND), QuestReq(KINGS_RANSOM), QuestReq(LEGEND), QuestReq(MEP_2), QuestReq(PATH_GLOUPHRIE), QuestReq(RFD), QuestReq(SUMMERS_END), QuestReq(SWAN), QuestReq(TEARS_OF_GUTHIX), QuestReq(ZOGRE)), + ALL_FIRED_UP (Quests.ALL_FIRED_UP, QuestReq(PRIEST), SkillReq(Skills.FIREMAKING, 43)) } diff --git a/Server/src/main/core/game/shops/Shop.kt b/Server/src/main/core/game/shops/Shop.kt index d19a4c2ec..1d041cf8a 100644 --- a/Server/src/main/core/game/shops/Shop.kt +++ b/Server/src/main/core/game/shops/Shop.kt @@ -167,7 +167,12 @@ class Shop(val title: String, val stock: Array, val general: Boolean = Items.TOKKUL_6529 -> item.definition.getConfiguration(ItemConfigParser.TOKKUL_PRICE, 1) Items.ARCHERY_TICKET_1464 -> item.definition.getConfiguration(ItemConfigParser.ARCHERY_TICKET_PRICE, 1) Items.CASTLE_WARS_TICKET_4067 -> item.definition.getConfiguration(ItemConfigParser.CASTLE_WARS_TICKET_PRICE, 1) - else -> getGPCost(Item(item.id, 1), if (isMainStock) stock[item.slot].amount else playerStock[slot].amount, if (isMainStock) item.amount else playerStock[slot].amount) + else -> { + val fixedPrice = fixedPriceItems[item.id] + fixedPrice ?: getGPCost(Item(item.id, 1), + if (isMainStock) stock[item.slot].amount else playerStock[slot].amount, + if (isMainStock) item.amount else playerStock[slot].amount) + } } return Item(currency, price) @@ -210,13 +215,16 @@ class Shop(val title: String, val stock: Array, val general: Boolean = } } - val price = when(currency) - { - Items.TOKKUL_6529 -> (item.definition.getConfiguration(ItemConfigParser.TOKKUL_PRICE, 1) / 10.0).toInt() // selling items authentically return 10x less tokkul (floored/truncated) than the item's shop price - Items.ARCHERY_TICKET_1464 -> item.definition.getConfiguration(ItemConfigParser.ARCHERY_TICKET_PRICE, 1) - Items.CASTLE_WARS_TICKET_4067 -> item.definition.getConfiguration(ItemConfigParser.CASTLE_WARS_TICKET_PRICE, 1) - else -> getGPSell(Item(shopItemId, 1), stockAmt, currentAmt) - } + val price = when (currency) { + // selling items authentically return 10x less tokkul (floored/truncated) than the item's shop price + Items.TOKKUL_6529 -> (item.definition.getConfiguration(ItemConfigParser.TOKKUL_PRICE, 1) / 10.0).toInt() + Items.ARCHERY_TICKET_1464 -> item.definition.getConfiguration(ItemConfigParser.ARCHERY_TICKET_PRICE, 1) + Items.CASTLE_WARS_TICKET_4067 -> item.definition.getConfiguration(ItemConfigParser.CASTLE_WARS_TICKET_PRICE, 1) + else -> { + val fixedPrice = fixedPriceItems[shopItemId] + fixedPrice ?: getGPSell(Item(shopItemId, 1), stockAmt, currentAmt) + } + } if(!general && stockAmt == 0 && shopSlot == -1) { @@ -293,10 +301,17 @@ class Shop(val title: String, val stock: Array, val general: Boolean = if(cost.id == -1) sendMessage(player, "This shop cannot sell that item.").also { return TransactionStatus.Failure("Shop cannot sell this item") } if(currency == Items.COINS_995){ - var amt = item.amount - var inStockAmt = inStock.amount - while(amt-- > 1) - cost.amount += getGPCost(Item(item.id, 1), if (isMainStock) stock[slot].amount else playerStock[slot].amount, --inStockAmt) + val fixedPrice = fixedPriceItems[item.id] + if (fixedPrice != null) { + // Fixed price items: simple multiplication + cost.amount = fixedPrice * item.amount + } else { + // Dynamic pricing: calculate cost for each item as stock depletes + var amt = item.amount + var inStockAmt = inStock.amount + while(amt-- > 1) + cost.amount += getGPCost(Item(item.id, 1), if (isMainStock) stock[slot].amount else playerStock[slot].amount, --inStockAmt) + } } else { cost.amount = cost.amount * item.amount } @@ -379,7 +394,10 @@ class Shop(val title: String, val stock: Array, val general: Boolean = return TransactionStatus.Failure("Attempt to sell to full shop.") } - if(currency == Items.COINS_995 && item.amount > 1){ + val fixedPrice = fixedPriceItems[id] + if (fixedPrice != null) { + profit.amount = fixedPrice * item.amount + } else if(currency == Items.COINS_995 && item.amount > 1){ var amt = item.amount var inStockAmt = container!![shopSlot]?.amount ?: playerStock.getAmount(id) while(amt-- > 1) @@ -446,11 +464,17 @@ class Shop(val title: String, val stock: Array, val general: Boolean = return Pair(isPlayerStock, shopSlot) } - companion object { - //General stores globally share player stock (weird quirk, right?) - val generalPlayerStock = Container(40, ContainerType.SHOP) - val listenerInstances = HashMap() - } + companion object { + //General stores globally share player stock (weird quirk, right?) + val generalPlayerStock = Container(40, ContainerType.SHOP) + val listenerInstances = HashMap() + + // Items with fixed prices that don't fluctuate based on stock (both buy & sell price) + val fixedPriceItems = mapOf( + Items.SPIRIT_SHARDS_12183 to 25, + Items.POUCH_12155 to 1 + ) + } sealed class TransactionStatus { class Success : TransactionStatus() diff --git a/Server/src/main/core/game/shops/Shops.kt b/Server/src/main/core/game/shops/Shops.kt index 94408357e..b87f82d21 100644 --- a/Server/src/main/core/game/shops/Shops.kt +++ b/Server/src/main/core/game/shops/Shops.kt @@ -20,6 +20,7 @@ import org.rs09.consts.Components import org.rs09.consts.Items import org.rs09.consts.NPCs import java.io.FileReader +import content.data.Quests /** * The "controller" class for shops. Handles opening shops from various NPC interactions and updating stock, etc. @@ -67,7 +68,8 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe return@map }} } else { - items.add(ShopItem(item, amount.toInt(), tokens.getOrNull(2)?.toIntOrNull() ?: 100)) + val restockRate = tokens.getOrNull(2)?.toIntOrNull() ?: 100 + items.add(ShopItem(item, amount.toInt(), restockRate)) idsInStock[item] = true } } catch (e: Exception) { @@ -126,8 +128,8 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe override fun defineListeners() { on(IntType.NPC, "trade", "shop"){ player, node -> val npc = node as NPC - if (npc.id == 2824) { - TanningProduct.open(player, 2824) + if (npc.id == 2824 || npc.id == 1041 || npc.id == 804) { + TanningProduct.open(player, npc.id) return@on true } if (npc.id == 7601) { @@ -149,7 +151,7 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe } on(NPCs.FUR_TRADER_1316, IntType.NPC, "trade") { player, node -> - if (!isQuestComplete(player, "Fremennik Trials")) { + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { sendNPCDialogue(player, NPCs.FUR_TRADER_1316, "I don't sell to outerlanders.", core.game.dialogue.FacialExpression.ANNOYED).also { END_DIALOGUE } } else { shopsByNpc[node.id]?.openFor(player) @@ -158,7 +160,7 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe } on(NPCs.CANDLE_MAKER_562, IntType.NPC, "trade") { player, node -> - if (getQuestStage(player, "Merlin's Crystal") > 60) { + if (getQuestStage(player, Quests.MERLINS_CRYSTAL) > 60) { openId(player, 56) } else { shopsByNpc[node.id]?.openFor(player) diff --git a/Server/src/main/core/game/system/command/Command.kt b/Server/src/main/core/game/system/command/Command.kt index fdbc56d90..8fc40e222 100644 --- a/Server/src/main/core/game/system/command/Command.kt +++ b/Server/src/main/core/game/system/command/Command.kt @@ -2,6 +2,7 @@ package core.game.system.command import core.game.node.entity.player.Player import core.ServerConstants +import core.game.node.entity.player.info.Rights import core.game.world.GameWorld import kotlin.collections.ArrayList @@ -12,7 +13,10 @@ import kotlin.collections.ArrayList class Command(val name: String, val privilege: Privilege, val usage: String = "UNDOCUMENTED", val description: String = "UNDOCUMENTED", val handle: (Player, Array) -> Unit) { fun attemptHandling(player: Player, args: Array?){ args ?: return - if(player.rights.ordinal >= privilege.ordinal || GameWorld.settings?.isDevMode == true || ServerConstants.I_AM_A_CHEATER){ + val hasRights = player.rights == Rights.ADMINISTRATOR || (ServerConstants.PLAYER_COMMANDS && player.rights.ordinal >= privilege.ordinal) + val isDev = GameWorld.settings?.isDevMode == true + val isCheater = ServerConstants.I_AM_A_CHEATER + if (hasRights || isDev || isCheater) { handle(player,args) } } diff --git a/Server/src/main/core/game/system/command/CommandSystem.kt b/Server/src/main/core/game/system/command/CommandSystem.kt index 8912a531a..3601182b0 100644 --- a/Server/src/main/core/game/system/command/CommandSystem.kt +++ b/Server/src/main/core/game/system/command/CommandSystem.kt @@ -1,7 +1,7 @@ package core.game.system.command import core.game.node.entity.player.Player -import core.game.system.command.CommandSet +import core.tools.colorize /** * Represents a managing system used to dispatch incoming commands. @@ -21,9 +21,11 @@ class CommandSystem { if(command == null) { for (set in CommandSet.values()) { if (set.interpret(player, arguments[0], *arguments)) { + player.sendMessage(colorize("-->%Y${arguments[0]}: Deprecated command")) return true } } + player.sendMessage(colorize("-->%R${arguments[0]}: command not found")) } else { try { command.attemptHandling(player, arguments) diff --git a/Server/src/main/core/game/system/command/oldsys/VisualCommand.kt b/Server/src/main/core/game/system/command/oldsys/VisualCommand.kt index adaf066ad..fc2a08c28 100644 --- a/Server/src/main/core/game/system/command/oldsys/VisualCommand.kt +++ b/Server/src/main/core/game/system/command/oldsys/VisualCommand.kt @@ -48,6 +48,7 @@ class VisualCommand : CommandPlugin() { "invisible", "invis", "seti" -> { player!!.isInvisible = !player.isInvisible player.sendMessage("You are now " + (if (player.isInvisible) "invisible" else "rendering") + " for other players.") + return true } "maxkc" -> { var i = 0 @@ -120,6 +121,7 @@ class VisualCommand : CommandPlugin() { } location = Location.create(args[2]!!.toInt(), args[3]!!.toInt(), if (args.size > 4) args[4]!!.toInt() else 0) player!!.packetDispatch.sendPositionedGraphic(args[1]!!.toInt(), if (args.size > 5) args[5]!!.toInt() else 0, if (args.size > 6) args[6]!!.toInt() else 0, location) + return true } "npc" -> { if (args!!.size < 2) { @@ -174,8 +176,14 @@ class VisualCommand : CommandPlugin() { player!!.debug("Is tele allowed here? " + RegionManager.isTeleportPermitted(player!!.location)) return true } - "oib" -> player!!.interfaceManager.openInfoBars() - "char" -> CharacterDesign.open(player) + "oib" -> { + player!!.interfaceManager.openInfoBars() + return true + } + "char" -> { + CharacterDesign.open(player) + return true + } "savenpc" -> return true "objwithanim" -> { val go = Scenery(toInteger(args!![1]!!), player!!.location, 0) @@ -212,7 +220,10 @@ class VisualCommand : CommandPlugin() { player!!.interfaceManager.openComponent(componentId) return true } - "ti" -> player!!.packetDispatch.sendInterfaceConfig(90, 87, false) + "ti" -> { + player!!.packetDispatch.sendInterfaceConfig(90, 87, false) + return true + } "iconfig", "inter_config" -> { if (args!!.size < 2) { player!!.debug("syntax error: interface-id child hidden") @@ -252,6 +263,7 @@ class VisualCommand : CommandPlugin() { } }) } + return true } "loop_anim_on_i" -> { var anim = toInteger(args!![1]!!) @@ -262,12 +274,14 @@ class VisualCommand : CommandPlugin() { return false } }) + return true } "send_i_anim" -> { val iface = args?.getOrNull(0) ?: return true val anim = args.getOrNull(1) ?: return true player?.packetDispatch?.sendAnimationInterface(toInteger(anim), toInteger(iface),7) + return true } "loop_inter" -> { val st = toInteger(args!![1]!!) diff --git a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt index ada26eaca..d06258333 100644 --- a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt @@ -1,5 +1,10 @@ package core.game.system.command.sets +import core.api.animate +import core.api.delayScript +import core.api.queueScript +import core.api.stopExecuting +import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC import core.game.system.task.Pulse import core.game.world.update.flag.context.Animation @@ -27,6 +32,28 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { player.animate(animation) } + define("anims", Privilege.ADMIN, "::anims <(opt) Duration Per Animation>", "Plays animations from the From ID to the To ID, with a delay of 3 between animations, unless specified otherwise"){ player, args -> + + if (args.size < 3) { + reject(player, "Syntax error: ::anims <(opt) Duration Per Animation>") + } + val animationFrom = args[1].toInt() + val animationTo = args[2].toInt() + val animationDelay = (args.getOrNull(3) ?: "3").toInt() + + queueScript(player, 1, QueueStrength.STRONG) { stage: Int -> + val animationId = animationFrom + stage + animate(player, animationId, true) + notify(player, "Playing animation $animationId") + + if (animationId == animationTo) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayScript(player, animationDelay) + } + } + /** * Force the player to loop animation */ @@ -56,11 +83,23 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { if (args.size < 2) { reject(player, "Syntax error: ::ranim ") } - try { - player.appearance.setAnimations(Animation.create(args[1].toInt())) - player.appearance.sync() - } catch (e: NumberFormatException) { - reject(player, "Syntax error: ::ranim ") + if (args.size > 2) { + GameWorld.Pulser.submit(object : Pulse(3, player) { + var id = args[1].toInt() + override fun pulse(): Boolean { + player.appearance.setAnimations(Animation.create(id)) + player.appearance.sync() + player.sendChat("Current: $id") + return ++id >= args[2].toInt() + } + }) + } else { + try { + player.appearance.setAnimations(Animation.create(args[1].toInt())) + player.appearance.sync() + } catch (e: NumberFormatException) { + reject(player, "Syntax error: ::ranim ") + } } } diff --git a/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt b/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt index 0730bfca9..253011da6 100644 --- a/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt @@ -1,14 +1,12 @@ package core.game.system.command.sets import content.global.activity.jobs.JobManager -import content.global.skill.slayer.Master import core.api.* import core.cache.Cache import core.cache.def.impl.DataMap import core.cache.def.impl.NPCDefinition import core.cache.def.impl.VarbitDefinition import core.cache.def.impl.Struct -import core.game.dialogue.DialogueFile import core.game.node.entity.combat.ImpactHandler.HitsplatType import core.game.node.entity.player.Player import core.game.node.entity.player.link.SpellBookManager @@ -20,7 +18,6 @@ import core.plugin.Initializable import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.rs09.consts.Items -import core.tools.SystemLogger import core.game.system.command.Privilege import java.io.BufferedWriter import java.io.File @@ -28,19 +25,29 @@ import java.io.FileWriter import java.util.Arrays import core.net.packet.PacketWriteQueue import core.tools.Log -import core.game.world.update.flag.* -import core.game.world.update.flag.context.* -import core.game.node.entity.impl.* +import core.game.node.entity.player.info.Rights +import core.game.node.entity.skill.Skills import core.game.world.map.Location +import core.game.world.repository.Repository @Initializable class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { - val farmKitItems = arrayListOf(Items.RAKE_5341, Items.SPADE_952, Items.SEED_DIBBER_5343, Items.WATERING_CAN8_5340, Items.SECATEURS_5329, Items.GARDENING_TROWEL_5325) + val farmKitItems = arrayListOf(Items.RAKE_5341, Items.SPADE_952, Items.SEED_DIBBER_5343, Items.WATERING_CAN8_5340, Items.SECATEURS_5329, Items.GARDENING_TROWEL_5325,Items.COMPOST_6032, Items.SUPERCOMPOST_6034, Items.PLANT_CURE_6036) val runeKitItems = arrayListOf(Items.AIR_RUNE_556, Items.EARTH_RUNE_557, Items.FIRE_RUNE_554, Items.WATER_RUNE_555, Items.MIND_RUNE_558, Items.BODY_RUNE_559, Items.DEATH_RUNE_560, Items.NATURE_RUNE_561, Items.CHAOS_RUNE_562, Items.LAW_RUNE_563, Items.COSMIC_RUNE_564, Items.BLOOD_RUNE_565, Items.SOUL_RUNE_566, Items.ASTRAL_RUNE_9075) + + fun getPlayerFromArgs(player: Player, args: Array, startindex: Int = 1): Player? { + val n = args.slice(startindex until args.size).joinToString("_") + if (n == "") { //no argument given -> return self player + return player + } + val target = Repository.getPlayerByName(n) + if (target == null) { + reject(player, "Could not find a player named '$n'") + } + return target + } + override fun defineCommands() { - /** - * Gives the player a set of tools used to test farming stuff. - */ define("farmkit", Privilege.ADMIN, "", "Provides a kit of various farming equipment."){player,_ -> for(item in farmKitItems){ player.inventory.add(Item(item)) @@ -213,7 +220,7 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { } } - define("itemsearch") {player, args -> + define("itemsearch", Privilege.STANDARD, "itemsearch name", "Searches for items that match the name.") {player, args -> val itemName = args.copyOfRange(1, args.size).joinToString(" ").lowercase() for (i in 0 until 15000) { val name = getItemName(i).lowercase() @@ -232,6 +239,10 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { setAttribute (player, "chunkdraw", !getAttribute(player, "chunkdraw", false)) } + define("drawclipping", Privilege.ADMIN, "", "Draws the clipping flags of the region you're standing in") {player, _ -> + setAttribute (player, "clippingdraw", !getAttribute(player, "clippingdraw", false)) + } + define("drawregions", Privilege.ADMIN, "", "DRaws the border of the region you're standing in") {player, _ -> setAttribute (player, "regiondraw", !getAttribute(player, "regiondraw", false)) } @@ -290,5 +301,78 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { player.sendMessage(" ${timer.identifier}") } } + + define("setpestpoints", Privilege.ADMIN, "::setpestpoints points player_name", "Sets your (or player_name's) Pest Control points to 'points'") { player, args -> + val target = getPlayerFromArgs(player, args, 2) ?: return@define + val points = args[1].toIntOrNull() + if (points == null) { + reject(player, "No valid 'points' argument given") + } + target.savedData.activityData.pestPoints = points!! + } + + define("makeadmin", Privilege.ADMIN, "::makeadmin player_name", "Permanently gives admin rights to player_name (or self if empty)") { player, args -> + val target = getPlayerFromArgs(player, args, 1) ?: return@define + target.details.rights = Rights.ADMINISTRATOR + sendMessage(player, "Gave admin rights to ${target.username}.") + sendMessage(target, "You've been given admin rights by ${player.username}!") + } + + define("dropadmin", Privilege.ADMIN, "::dropadmin", "Permanently drops admin rights from self") { player, _ -> + player.details.rights = Rights.REGULAR_PLAYER + sendMessage(player, "Dropped admin rights.") + } + + define("max", Privilege.ADMIN, "::max player_name", "Gives all 99s to player_name (or self if empty)") { player, args -> + val target = getPlayerFromArgs(player, args, 1) ?: return@define + var index = 0 + Skills.SKILL_NAME.forEach { + target.skills.setStaticLevel(index,99) + target.skills.setLevel(index,99) + index++ + } + target.skills.updateCombatLevel() + } + + define("noobme", Privilege.ADMIN, "::noobme player_name", "Sets player_name (or self if empty) back to default stats") { player, args -> + val target = getPlayerFromArgs(player, args, 1) ?: return@define + var index = 0 + Skills.SKILL_NAME.forEach { + val level = if (index == Skills.HITPOINTS) 10 else 1 + target.skills.setStaticLevel(index, level) + target.skills.setLevel(index, level) + index++ + } + target.skills.updateCombatLevel() + } + + define("setlevel", Privilege.ADMIN, "::setlevel SKILL NAME LEVEL PLAYER", "Sets SKILL NAME to LEVEL for PLAYER (self if omitted)."){player,args -> + if (args.size < 3) reject(player,"Usage: ::setlevel skillname level") + val skillname = args[1] + val desiredLevel: Int? = args[2].toIntOrNull() + if (desiredLevel == null) { + reject(player, "Level must be an integer.") + } + if (desiredLevel!! > 99) reject(player,"Level must be 99 or lower.") + val skill = Skills.getSkillByName(skillname) + if (skill < 0) reject(player, "Must use a valid skill name!") + val target = getPlayerFromArgs(player, args, 3) ?: return@define + target.skills.setStaticLevel(skill,desiredLevel) + target.skills.setLevel(skill,desiredLevel) + target.skills.updateCombatLevel() + } + + define("addxp", Privilege.ADMIN, "::addxp skill name | id xp", "Add xp to skill") { player, args -> + if (args.size < 3) reject(player, "Usage: ::addxp skill name | id xp player(optional)") + val target = getPlayerFromArgs(player, args, 3) ?: return@define + + val skill = args[1].toIntOrNull() ?: Skills.getSkillByName(args[1]) + if (skill < 0 || skill >= Skills.NUM_SKILLS) reject(player, "Must use valid skill name or id.") + + val xp = args[2].toDoubleOrNull() + if (xp == null || xp <= 0) reject(player, "Xp must be a positive number.") + + target.skills.addExperience(skill, xp!!) + } } } diff --git a/Server/src/main/core/game/system/command/sets/FunCommandSet.kt b/Server/src/main/core/game/system/command/sets/FunCommandSet.kt index 4db7d398f..9ebd0ff36 100644 --- a/Server/src/main/core/game/system/command/sets/FunCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/FunCommandSet.kt @@ -36,18 +36,28 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { /** * Force animation + messages on all NPCs in a radius of 10 from the player. + * Add an optional 3rd argument if cycling through a range of animation ids. */ - define("npcanim", Privilege.ADMIN, "::npcanim Animation ID") { player, args -> + define("npcanim", Privilege.ADMIN, "::npcanim Animation ID Optional: End Animation ID") { player, args -> if (args.size < 2) { reject(player, "Syntax error: ::npcanim ") } npcs = RegionManager.getLocalNpcs(player.location, 10) for (n in npcs) { - n.sendChat(args.slice(1 until args.size).joinToString(" ")) n.lock(6) n.faceTemporary(player, 6) - n.animator.animate(Animation(args[1].toInt())) - n.animate(Animation.create(-1), 6) + if (args.size == 2) { + n.sendChat(args.slice(1 until args.size).joinToString(" ")) + n.animate(Animation(args[1].toInt())) + n.animate(Animation.create(-1), 6) + } + if (args.size == 3) { + var count = 0 + for(animNum in args[1].toInt()..args[2].toInt()) { + count++ + n.animate(Animation(animNum), count*6) + } + } } } @@ -207,6 +217,36 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { p.graphics(Graphics(369, 0)) } } + + /** + * Toggles pet hunger to off, normal, or dev + * 0 = No hunger/growth mode (hunger/growth does not progress) + * 1 = Normal hunger/growth mode (1x speed) + * 2 = Dev hunger/growth mode (100x speed) + */ + define("petrate", Privilege.ADMIN, "petrate 0-2", "Sets pet hunger and growth to off, normal, or dev."){ player, args -> + if(args.size < 2) { + notify(player, "Pet mode is currently ${player.getAttribute("petrate", 1)}") + return@define + } + val mode = args[1].toIntOrNull() ?: (-1).also { reject(player, "Please enter a valid number") } + if (mode in 0 .. 2) { + player.setAttribute("petrate", mode) + notify(player, "Setting pet mode to $mode") + } + else { + reject(player, "Only modes 0-2 are valid") + } + } + + /** + * Toggle instant woodcutting. + */ + define("instachop", Privilege.ADMIN, "", "Fells trees after a single log is gathered."){ player, _ -> + player.setAttribute("instachop", !player.getAttribute("instachop", false)) + notify(player,"Instachop mode " + if (player.getAttribute("instachop", false)) "on." else "off.") + } + } fun bury(player: Player){ diff --git a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt index bf9ba822c..44348e1b2 100644 --- a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt @@ -1,42 +1,40 @@ package core.game.system.command.sets +import content.global.handlers.iface.* +import content.global.skill.farming.timers.* +import content.minigame.fishingtrawler.TrawlerLoot +import content.region.misthalin.draynor.quest.anma.AnmaCutscene +import core.ServerConstants import core.api.* -import core.api.InputType +import core.api.utils.permadeath import core.cache.def.impl.NPCDefinition import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.VarbitDefinition +import core.game.bots.AIRepository import core.game.component.Component +import core.game.ge.GrandExchange import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.info.Rights import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.node.scenery.Scenery +import core.game.system.command.Command +import core.game.system.command.CommandMapping +import core.game.system.command.Privilege import core.game.system.communication.CommunicationInfo import core.game.world.map.RegionManager import core.game.world.map.build.DynamicRegion +import core.game.world.repository.Repository import core.plugin.Initializable +import core.tools.Log import core.tools.StringUtils import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.rs09.consts.Components -import core.ServerConstants -import core.game.bots.AIRepository -import core.api.utils.PlayerCamera -import content.global.ame.RandomEvents -import content.region.misthalin.draynor.quest.anma.AnmaCutscene -import core.game.ge.GrandExchange -import content.global.handlers.iface.RulesAndInfo -import content.minigame.fishingtrawler.TrawlerLoot -import core.game.system.command.CommandMapping -import core.game.system.command.Privilege -import core.game.world.repository.Repository -import core.tools.Log -import core.tools.colorize import java.awt.HeadlessException import java.awt.Toolkit import java.awt.datatransfer.StringSelection -import content.global.skill.farming.timers.* @Initializable class MiscCommandSet : CommandSet(Privilege.ADMIN){ @@ -49,10 +47,11 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ player.toggleDebug() } - define("calc_accuracy", Privilege.STANDARD, "::calc_accuracy NPC ID", "Calculates and prints your current chance to hit a given NPC."){ player, args -> + define("calcaccuracy", Privilege.STANDARD, "::calcaccuracy NPC ID", "Calculates and prints your current chance to hit a given NPC."){ player, args -> val handler = player.getSwingHandler(false) player.sendMessage("handler type: ${handler.type}") - player.sendMessage("calculateAccuracy: ${handler.calculateAccuracy(player)}") + val accuracy = handler.calculateAccuracy(player) + player.sendMessage("calculateAccuracy: ${accuracy}") if (args.size > 1) { @@ -60,7 +59,14 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ val npc = NPC(npcId) npc.initConfig() player.sendMessage("npc: ${npc.name}. npc defence: ${npc.skills.getLevel(Skills.DEFENCE)}") - player.sendMessage("calculateDefence: ${handler.calculateDefence(npc, player)}") + val defence = handler.calculateDefence(npc, player) + player.sendMessage("calculateDefence: ${defence}") + val chance: Double = if (accuracy > defence) { + 1.0 - ((defence + 2.0) / (2.0 * (accuracy + 1.0))) + } else { + accuracy / (2.0 * (defence + 1.0)) + } + player.sendMessage("chance to hit: ${chance}") } } @@ -68,6 +74,16 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ AnmaCutscene(player).start() } + define("setsaveversion", Privilege.ADMIN) { player, args -> + try{ + player.version = args[1].toInt() + notify(player, "Setting save version to ${player.version}") + } + catch (nfe: NumberFormatException){ + reject(player, "Save versions can only be an integer") + } + } + /** * Prints player's current location */ @@ -110,7 +126,7 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ define("calcmaxhit", Privilege.STANDARD, "", "Calculates and shows you your current max hit.") { player, _ -> val swingHandler = player.getSwingHandler(false) val hit = swingHandler.calculateHit(player, player, 1.0) - notify(player, "max hit (${(swingHandler as Object).getClass().getName()}): ${hit}") + notify(player, "max hit: ${hit} (${(swingHandler as Object).getClass().getName()})") } /** @@ -192,13 +208,9 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ when(mode){ "buying" -> showGeBuy(player) "selling" -> showGeSell(player) - "search" -> sendInputDialogue(player, InputType.STRING_LONG, "Enter search term:"){value -> - showOffers(player, value as String) - } + "search" -> showGeInputDialogue(player, args, ::showOffers) "bots" -> showGeBots(player) - "botsearch" -> sendInputDialogue(player, InputType.STRING_LONG, "Enter search term:"){value -> - showGeBotsearch(player, value as String) - } + "botsearch" -> showGeInputDialogue(player, args, ::showGeBotsearch) else -> reject(player, "Invalid mode used. Available modes are: buying, selling, search") } } @@ -212,11 +224,8 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ */ define("commands", Privilege.STANDARD, "::commands page", "Lists all the commands (you are here.)"){player, args -> val page = if (args.size > 1) (args[1].toIntOrNull() ?: 1) - 1 else 0 - var lineid = 11 - var pages = CommandMapping.getPageIndices(player.rights.ordinal) - var end = if (page < (pages.size - 1)) pages[page + 1] else CommandMapping.getCommands().size - - player.interfaceManager.close() + val pages = CommandMapping.getPageIndices(player.rights.ordinal) + val end = if (page < (pages.size - 1)) pages[page + 1] else CommandMapping.getCommands().size if (page < 0) { reject(player, "Usage: ::commands page") @@ -226,44 +235,106 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ reject(player, "That page number is too high, you don't have access to that many.") } - for (i in 0..310) { - player.packetDispatch.sendString("", Components.QUESTJOURNAL_SCROLL_275, i) - } + val title = "Commands" + if (pages.size > 1) " (${page + 1}/${pages.size})" else "" + var lineId = setupScrollInterface(player, title) - player.packetDispatch.sendString( - "Commands" + if (pages.size > 1) " (${page + 1}/${pages.size})" else "", - Components.QUESTJOURNAL_SCROLL_275, - 2 - ) - - for(i in pages[page] until end) { - var command = CommandMapping.getCommands()[i] - var title = "${command.name}" - var rights = command.privilege.ordinal - var icon = rights - 1 + val command = CommandMapping.getCommands()[i] + lineId = displayCommandInScroll(player, command, player.rights.ordinal, lineId) - - if (rights > player.rights.ordinal) continue - - if (rights > 0) - title = "() $title" - - player.packetDispatch.sendString(title, Components.QUESTJOURNAL_SCROLL_275, lineid++) - - if (command.usage.isNotEmpty()) - player.packetDispatch.sendString("Usage: ${command.usage}", Components.QUESTJOURNAL_SCROLL_275, lineid++) - - if (command.description.isNotEmpty()) - player.packetDispatch.sendString(command.description, Components.QUESTJOURNAL_SCROLL_275, lineid++) - - player.packetDispatch.sendString("-------------------------------", Components.QUESTJOURNAL_SCROLL_275, lineid++) - - if (lineid > 306) { - player.packetDispatch.sendString("To view the next page, use ::commands ${page + 2}", Components.QUESTJOURNAL_SCROLL_275, lineid) + if (lineId > 306) { + player.packetDispatch.sendString("To view the next page, use ::commands ${page + 2}", Components.QUESTJOURNAL_SCROLL_275, lineId) break } + } + player.interfaceManager.open(Component(Components.QUESTJOURNAL_SCROLL_275)) + } + /* + * Search for commands + */ + define("commandsearch", Privilege.STANDARD, "::commandsearch search term [--chat]", "Searches for commands containing the given term."){player, args -> + if (args.size < 2) { + reject(player, "Usage: ::commandsearch [--chat]") + return@define + } + val rawArguments = args.copyOfRange(1, args.size) + val chatMode = rawArguments.any { it.equals("--chat", ignoreCase = true) } + val searchRaw = rawArguments.filterNot { it.equals("--chat", ignoreCase = true) } + if (searchRaw.isEmpty()) { + reject(player, "Usage: ::commandsearch [--chat]") + return@define + } + val search = searchRaw.joinToString(" ").lowercase() + if (search.length < 2) { + reject(player, "Search term must be at least 2 characters long") + return@define + } + val playerRights = player.rights.ordinal + val allMatchingCommands = CommandMapping.getCommands() + .filter { command -> + command.name.lowercase().contains(search) || + command.usage.lowercase().contains(search) || + command.description.lowercase().contains(search) + } + if (allMatchingCommands.isEmpty()) { + notify(player, "No commands found matching '$search'") + return@define + } + val accessibleCommands = allMatchingCommands + .filter { it.privilege.ordinal <= playerRights } + .sortedBy { it.name } + val inaccessibleCount = allMatchingCommands.count { it.privilege.ordinal > playerRights } + if (chatMode) { + for (command in accessibleCommands) { + val cmdargs = if (command.usage.isNotEmpty()) { + val parts = command.usage.split(" ", limit = 2) + if (parts.size > 1) parts[1] else "" + } else { + "" + } + val formattedMessage = if (cmdargs.isNotEmpty()) { + "CMD: ${command.name} ARGS: $cmdargs DESC: ${command.description}" + } else { + "CMD: ${command.name} DESC: ${command.description}" + } + val chunks = chunkText(formattedMessage, 70) + chunks.forEach { notify(player, it) } + } + if (accessibleCommands.isEmpty()) { + notify(player, "Found $inaccessibleCount command(s) matching '$search' but you don't have permission to use any of them") + } else { + val summary = "Found ${accessibleCommands.size} accessible command(s) for '$search'" + if (inaccessibleCount > 0) { + notify(player, summary) + notify(player, "($inaccessibleCount additional command(s) require higher privileges)") + } else { + notify(player, summary) + } + } + return@define + } + var lineId = setupScrollInterface(player, "Command Search: '$search'") + if (accessibleCommands.isEmpty()) { + player.packetDispatch.sendString("No accessible commands found.", Components.QUESTJOURNAL_SCROLL_275, lineId++) + } + for (command in accessibleCommands) { + lineId = displayCommandInScroll(player, command, playerRights, lineId) + if (lineId > 306) { + player.packetDispatch.sendString( + "Results truncated. Use ::commandsearch $search --chat", + Components.QUESTJOURNAL_SCROLL_275, + lineId + ) + break + } + } + if (inaccessibleCount > 0 && lineId <= 306) { + player.packetDispatch.sendString( + "$inaccessibleCount more command(s) need higher privileges.", + Components.QUESTJOURNAL_SCROLL_275, + lineId + ) } player.interfaceManager.open(Component(Components.QUESTJOURNAL_SCROLL_275)) } @@ -295,78 +366,6 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ player.dialogueInterpreter.close() } - - /** - * Max account stats - */ - define("max", Privilege.ADMIN, "", "Gives you all 99s."){player,_ -> - var index = 0 - Skills.SKILL_NAME.forEach { - player.skills.setStaticLevel(index,99) - player.skills.setLevel(index,99) - index++ - } - player.skills.updateCombatLevel() - } - - define("noobme", Privilege.ADMIN, "", "Sets you back to default stats."){ player,_ -> - var index = 0 - Skills.SKILL_NAME.forEach { - if (index == Skills.HITPOINTS) { - player.skills.setStaticLevel(index,10) - player.skills.setLevel(index,10) - index++ - } else { - player.skills.setStaticLevel(index,1) - player.skills.setLevel(index,1) - index++ - } - } - player.skills.updateCombatLevel() - } - - /** - * Set a specific skill to a specific level - */ - define("setlevel", Privilege.ADMIN, "::setlevel SKILL NAME LEVEL PLAYER", "Sets SKILL NAME to LEVEL for PLAYER (self if omitted)."){player,args -> - if(args.size < 3) reject(player,"Usage: ::setlevel skillname level") - val skillname = args[1] - val desiredLevel: Int? = args[2].toIntOrNull() - if(desiredLevel == null){ - reject(player, "Level must be an integer.") - } - if(desiredLevel!! > 99) reject(player,"Level must be 99 or lower.") - val skill = Skills.getSkillByName(skillname) - if(skill < 0) reject(player, "Must use a valid skill name!") - var target = player - if (args.size > 3) { - val n = args.slice(3 until args.size).joinToString("_") - val foundtarget = Repository.getPlayerByName(n) - if (foundtarget == null) { - reject(player,"Invalid player \"${n}\" or player not online") - } - target = foundtarget!! - } - target.skills.setStaticLevel(skill,desiredLevel) - target.skills.setLevel(skill,desiredLevel) - target.skills.updateCombatLevel() - } - - /** - * Add xp to skill - */ - define("addxp", Privilege.ADMIN, "::addxp skill name | id xp", "Add xp to skill") { player, args -> - if (args.size != 3) reject(player, "Usage: ::addxp skill name | id xp") - - val skill = args[1].toIntOrNull() ?: Skills.getSkillByName(args[1]) - if (skill < 0 || skill >= Skills.NUM_SKILLS) reject(player, "Must use valid skill name or id.") - - val xp = args[2].toDoubleOrNull() - if (xp == null || xp <= 0) reject(player, "Xp must be a positive number.") - - player.skills.addExperience(skill, xp!!) - } - define("completediaries", Privilege.ADMIN, "", "Completes all diaries."){player,_ -> player.achievementDiaryManager.diarys.forEach { for(level in it.taskCompleted.indices){ @@ -377,6 +376,22 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ } } + /** + * Permadeaths a player, resetting their save + */ + define("permadeath", Privilege.ADMIN, "::permadeath PLAYER", "Permadeaths PLAYER (self if omitted), completely wiping their save."){player,args -> + var target = player + if (args.size > 1) { + val n = args.slice(1 until args.size).joinToString("_") + val foundtarget = Repository.getPlayerByName(n) + if (foundtarget == null) { + reject(player, "Invalid player \"${n}\" or player not online") + } + target = foundtarget!! + } + permadeath(target) + } + define("log", Privilege.ADMIN){player,_ -> var log: ArrayList? = player.getAttribute("loc-log") log = log ?: ArrayList() @@ -466,6 +481,20 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ setVarbit(player, index!!, value!!) } + define("setvarbits", Privilege.ADMIN, "::setvarbits FROM VARBIT ID TO VARBIT ID VALUE", ""){ + player,args -> + if(args.size != 4){ + reject(player,"Usage: ::setvarbits fromvarbit tovarbit value") + } + val fromIndex = args[1].toIntOrNull()!! + val toIndex = args[2].toIntOrNull()!! + val value = args[3].toIntOrNull()!! + + for (index in fromIndex..toIndex) { + setVarbit(player, index, value) + } + } + define("getvarbit", Privilege.ADMIN, "::getvarbit VARBIT ID", "") { player, args -> if (args.size != 2) @@ -515,6 +544,24 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ } define("finishbins", Privilege.ADMIN, "", "Finishes any in-progress compost bins."){ player, _ -> + val bins = getOrStartTimer(player).getBins() + for (bin in bins) { + if (!bin.isFinished && bin.isClosed) bin.finish() + } + } + + define("resetbins", Privilege.ADMIN, "", "Resets the player's compost bins to their initial states."){ player, _ -> + val bins = getOrStartTimer(player).getBins() + for (bin in bins) bin.reset() + } + + define("diseasecrops", Privilege.ADMIN, "", "Disease all crops"){ player, _ -> + val state = getOrStartTimer(player) + for (patch in state.getPatches()){ + patch.diseaseMod = -128 + patch.nextGrowth = System.currentTimeMillis() + 1 + } + state.run(player) } define("addcredits", Privilege.ADMIN){ player, _ -> @@ -593,6 +640,129 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ sendDialogue(player, "Wrong pin. Try again.") } } + + define("setplayerstrong", Privilege.ADMIN, "setplayerstrong <0-4>", + "Set the player progress through the Stronghold of Player Safety test."){player, args -> + /* + * 0 = Not started + * 1 = Received test + * 2 = Completed test, needs to be marked + * 3 = Test marked + * 4 = Dungeon cleared + */ + if(args.size < 2){ + notify(player, "Player Stronghold progression currently: ${player.savedData.globalData.testStage}") + return@define + } + val stage = args[1].toIntOrNull() ?: (-1).also { reject(player, "Please enter a valid number") } + if (stage in 0 .. 4){ + player.savedData.globalData.testStage = stage + notify(player, "Setting test stage to $stage") + if (stage in 0..2){ + setVarp(player, 1203, 0, true) + notify(player, "The poster passage is now hidden") + } + else{ + setVarp(player, 1203, 1 shl 29, true) + notify(player, "The poster passage is now usable") + } + if (stage == 4){ + setVarbit(player, 4499, 1,true) + notify(player, "The loot has been taken already") + } + else { + setVarbit(player, 4499, 0, true) + notify(player, "The loot can be reacquired") + } + } + else{ + reject(player, "Only stages 0-4 are valid") + } + } + + define("setplaqueread", Privilege.ADMIN, "setplaqueread ", + "Set the plaques in the player safety stronghold to read or not read."){player, args -> + if (args.size == 1) { + notify( + player, + "Currently the plaques ${if (player.savedData.globalData.hasReadPlaques()) "have" else "have not"} been read" + ) + return@define + } + when(args[1]) { + "true" -> setPlaqueReadStatus(player, true) + "false" -> setPlaqueReadStatus(player, false) + else -> reject(player, "Only true or false can be used") + + } + notify(player, "Setting plaques read to: ${args[1]}") + + } + } + + fun setupScrollInterface(player: Player, title: String): Int { + player.interfaceManager.close() + for (i in 0..310) { + player.packetDispatch.sendString("", Components.QUESTJOURNAL_SCROLL_275, i) + } + player.packetDispatch.sendString(title, Components.QUESTJOURNAL_SCROLL_275, 2) + return 11 + } + + fun displayCommandInScroll(player: Player, command: Command, playerRights: Int, lineId: Int): Int { + val privilegeLevel = command.privilege.ordinal + if (privilegeLevel > playerRights) return lineId + val titleIcon = privilegeLevel - 1 + var title = command.name + if (privilegeLevel > 0) { + title = "() $title" + } + var currentLineId = lineId + player.packetDispatch.sendString(title, Components.QUESTJOURNAL_SCROLL_275, currentLineId++) + if (command.usage.isNotEmpty()) { + val usageText = "Usage: ${command.usage}" + val usageChunks = chunkText(usageText, 70) + for (chunk in usageChunks) { + player.packetDispatch.sendString(chunk, Components.QUESTJOURNAL_SCROLL_275, currentLineId++) + } + } + if (command.description.isNotEmpty()) { + val descChunks = chunkText(command.description, 70) + for (chunk in descChunks) { + player.packetDispatch.sendString(chunk, Components.QUESTJOURNAL_SCROLL_275, currentLineId++) + } + } + player.packetDispatch.sendString("-------------------------------", Components.QUESTJOURNAL_SCROLL_275, currentLineId++) + return currentLineId + } + + fun chunkText(text: String, maxLength: Int = 70): List { + if (text.length <= maxLength) return listOf(text) + val chunks = mutableListOf() + var remaining = text + while (remaining.length > maxLength) { + var breakPoint = maxLength + for (i in maxLength downTo 1) { + if (remaining[i - 1] == ' ') { + breakPoint = i + break + } + } + chunks.add(remaining.take(breakPoint).trim()) + remaining = remaining.substring(breakPoint).trim() + } + if (remaining.isNotEmpty()) { + chunks.add(remaining) + } + return chunks + } + + fun setPlaqueReadStatus(player: Player, status: Boolean){ + // For some reason the loop has to be this way to have read write access + for (i in 0 until player.savedData.globalData.readPlaques.size){ + player.savedData.globalData.readPlaques[i] = status + } + } fun showGeBotsearch(player: Player, searchTerm: String) @@ -608,19 +778,14 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ offerPrice[offer.itemID] = offer.offeredValue } - val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) - var lineId = 11 - - closeInterface(player) - setScrollTitle(player, "Bot Stock - \"$searchTerm\"") - for(i in 0..299) { - val offer = entries.elementAtOrNull(i) - if (offer != null) - setInterfaceText(player, "${getItemName(offer.key)} (x${offer.value}) -> Price: ${offerPrice[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) - else - setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) + val leftLines = ArrayList(entries.size) + val rightLines = ArrayList(entries.size) + for (entry in entries) { + leftLines.add("${getItemName(entry.key)} (x${entry.value})") + rightLines.add("Price: ${offerPrice[entry.key]}gp") } - openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + showGeBook(player, "Bot Stock - \"$searchTerm\"", leftLines, rightLines) } fun showGeBots(player: Player) @@ -636,19 +801,14 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ offerPrice[offer.itemID] = offer.offeredValue } - val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) - var lineId = 11 - - closeInterface(player) - setScrollTitle(player, "Bot Stock") - for(i in 0..299) { - val offer = entries.elementAtOrNull(i) - if (offer != null) - setInterfaceText(player, "${getItemName(offer.key)} (x${offer.value}) -> Price: ${offerPrice[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) - else - setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) + val leftLines = ArrayList(entries.size) + val rightLines = ArrayList(entries.size) + for (entry in entries) { + leftLines.add("${getItemName(entry.key)} (x${entry.value})") + rightLines.add("Price: ${offerPrice[entry.key]}gp") } - openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + showGeBook(player, "Bot Stock", leftLines, rightLines) } fun showGeSell(player: Player){ @@ -670,19 +830,14 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ lowestPrice[offer.itemID] = price } - val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) - var lineId = 11 - - closeInterface(player) - setScrollTitle(player, "Active Sell Offers") - for(i in 0..299) { - val offer = entries.elementAtOrNull(i) - if (offer != null) - setInterfaceText(player, "${getItemName(offer.key)} (x${offer.value}) -> Lowest: ${lowestPrice[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) - else - setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) + val leftLines = ArrayList(entries.size) + val rightLines = ArrayList(entries.size) + for (entry in entries) { + leftLines.add("${getItemName(entry.key)} (x${entry.value})") + rightLines.add("Price: ${lowestPrice[entry.key]}gp") } - openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + showGeBook(player, "Active Sell Offers", leftLines, rightLines) } fun showGeBuy(player: Player){ @@ -704,28 +859,27 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ highestPrice[offer.itemID] = price } - val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) - var lineId = 11 - - closeInterface(player) - setScrollTitle(player, "Active Buy Offers") - for(i in 0..299) { - val offer = entries.elementAtOrNull(i) - if (offer != null) - setInterfaceText(player, "${getItemName(offer.key)} (x${offer.value}) -> Highest: ${highestPrice[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) - else - setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) + val leftLines = ArrayList(entries.size) + val rightLines = ArrayList(entries.size) + for (entry in entries) { + leftLines.add("${getItemName(entry.key)} (x${entry.value})") + rightLines.add("Highest: ${highestPrice[entry.key]}gp") } - openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + showGeBook(player, "Active Buy Offers", leftLines, rightLines) } fun showOffers(player: Player, searchTerm: String){ val offers = GrandExchange.getValidOffers().filter { getItemName(it.itemID).contains(searchTerm, true) || getItemName(it.itemID).equals(searchTerm, true) } - val buyingAmount = HashMap() + if (offers.isEmpty()) { + sendMessage(player, "No results.") + return + } + + val buyingAmount = HashMap() val buyingHighest = HashMap() val sellingAmount = HashMap() val sellingLowest = HashMap() - for(offer in offers) { if(offer.sell) @@ -752,39 +906,64 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ } } - closeInterface(player) - setScrollTitle(player, "Results for \"$searchTerm\"") - - var lineId = 11 - for(i in 0..299) { - if(i > buyingAmount.keys.size) - { - val offer = sellingAmount.entries.elementAtOrNull(i - buyingAmount.keys.size) - if(offer != null) { - setInterfaceText(player, "[SELLING] ${getItemName(offer.key)} (x${offer.value}) -> Lowest: ${sellingLowest[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) - continue - } + val numLines = offers.size + 1 + val leftLines = ArrayList(numLines) + val rightLines = ArrayList(numLines) + for (i in 0..numLines) { + if (i < buyingAmount.keys.size) { + val offer = buyingAmount.entries.elementAtOrNull(i) ?: continue + leftLines.add("[BUYING] ${getItemName(offer.key)} (x${offer.value})") + rightLines.add("Highest: ${buyingHighest[offer.key]}gp") + } else if (i == buyingAmount.keys.size) { + leftLines.add("") + rightLines.add("") + } else { + val offer = sellingAmount.entries.elementAtOrNull(i - buyingAmount.keys.size - 1) ?: continue + leftLines.add("[SELLING] ${getItemName(offer.key)} (x${offer.value})") + rightLines.add("Lowest: ${sellingLowest[offer.key]}gp") } - else if(i < buyingAmount.keys.size) - { - val offer = buyingAmount.entries.elementAtOrNull(i) - if(offer != null) { - setInterfaceText(player, "[BUYING] ${getItemName(offer.key)} (x${offer.value}) -> Highest: ${buyingHighest[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) - continue - } - } - else { - setInterfaceText(player, " ", Components.QUESTJOURNAL_SCROLL_275, lineId++) - continue - } - - setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) } - openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + showGeBook(player, "Results for \"$searchTerm\"", leftLines, rightLines) } - fun setScrollTitle(player: Player, text: String){ - setInterfaceText(player, text, Components.QUESTJOURNAL_SCROLL_275, 2) + private fun showGeInputDialogue(player: Player, args: Array, op: (Player, String) -> (Unit)) { + if (args.size > 2) { + val target = args.copyOfRange(2, args.size).joinToString(" ").lowercase() + op(player, target) + } else { + sendInputDialogue(player, InputType.STRING_LONG, "Enter search term:",) { value -> + op(player, value as String) + } + } } + private fun showGeBook(player: Player, title: String, leftLines: ArrayList, rightLines: ArrayList) { + if (leftLines.size == 0) { + sendMessage(player, "No results.") + return + } + val lineIds = BookInterface.FANCY_BOOK_26_LINE_IDS + val contents = ArrayList() + val leftChunks = leftLines.chunked(15) + val rightChunks = rightLines.chunked(15) + for (i in leftChunks.indices) { + val size = leftChunks[i].size + val leftPageLines = ArrayList(size) + val rightPageLines = ArrayList(size) + for (j in leftChunks[i].indices) { + leftPageLines.add(BookLine(leftChunks[i][j], lineIds[j+3])) //+3 to skip title and buttons + rightPageLines.add(BookLine(rightChunks[i][j], lineIds[j+3+15])) //+15 because right pages have different ids + } + val leftPage = Page(*leftPageLines.toTypedArray()) + val rightPage = Page(*rightPageLines.toTypedArray()) + contents.add(PageSet(leftPage, rightPage)) + } + + closeInterface(player) + fun display(player: Player, pageNum: Int, buttonID: Int): Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_26, title, contents.toTypedArray()) + return true + } + BookInterface.openBook(player, BookInterface.FANCY_BOOK_26, ::display) + } } diff --git a/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt b/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt index 6c7bc19c6..69ae5c1c5 100644 --- a/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt @@ -42,18 +42,23 @@ class QuestCommandSet : CommandSet(Privilege.ADMIN){ * Sets stage of quest */ define("setqueststage"){player,args -> - if (args.size < 2) { + if (args.size < 3) { reject(player,"You must specify the index# of a quest, and a stage number") } val quest = args[1].toIntOrNull() ?: reject(player,"INVALID QUEST") val stage = args[2].toIntOrNull() ?: reject(player,"INVALID STAGE") val questObject = player.questRepository.forIndex(quest as Int) - player.questRepository.setStageNonmonotonic(questObject, stage as Int) - if (stage == 0){ - questObject.reset(player) + if (questObject == null){ + reject(player, "$quest did not match anything in the quest repository") + } + else{ + player.questRepository.setStageNonmonotonic(questObject, stage as Int) + if (stage == 0) { + questObject.reset(player) + } + questObject.updateVarps(player) + notify(player, "Setting " + questObject.quest + " to stage $stage") } - questObject.updateVarps(player) - notify(player, "Setting " + questObject.name + " to stage $stage") } /** @@ -77,7 +82,7 @@ class QuestCommandSet : CommandSet(Privilege.ADMIN){ player.packetDispatch.sendString("" + "Available Quests" + "", 275, 2) for (q in QuestRepository.getQuests().toSortedMap().values) { // Add a space to beginning and end of string for the strikethrough - player.packetDispatch.sendString("" + (if (q.isCompleted(player)) " " else "") + q.name + " ", 275, lineId++) + player.packetDispatch.sendString("" + (if (q.isCompleted(player)) " " else "") + q.quest + " ", 275, lineId++) } } @@ -100,7 +105,7 @@ class QuestCommandSet : CommandSet(Privilege.ADMIN){ stage in 1..99 -> "ff8400" else -> "ff0000" } - admin.packetDispatch.sendString("${q.name}", 275, lineId++) + admin.packetDispatch.sendString("${q.quest}", 275, lineId++) admin.packetDispatch.sendString("Index: ${q.index} | Stage: ${lookupUser.questRepository.getStage(q)}", 275, lineId++) admin.packetDispatch.sendString(" ", 275, lineId++) } diff --git a/Server/src/main/core/game/system/command/sets/SlayerCommandSet.kt b/Server/src/main/core/game/system/command/sets/SlayerCommandSet.kt index 62e952d5b..9be1e0626 100644 --- a/Server/src/main/core/game/system/command/sets/SlayerCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/SlayerCommandSet.kt @@ -45,11 +45,12 @@ class SlayerCommandSet : CommandSet(Privilege.ADMIN){ val npc = (args[1].toIntOrNull() ?: reject(player, "Must enter valid npc id")) as Int val task = (Tasks.forId(npc) ?: reject(player, "Must enter valid npc id")) as Tasks + val masterTask = Master.Task(task, 1) val amount = args.getOrNull(2)?.toIntOrNull() ?.let { if (it !in 1..255) reject(player, "Amount must be an integer: 1-255.") else it } as Int? val slayer = SlayerManager.getInstance(player) - if (slayer.hasTask()) slayer.task = task else SlayerUtils.assign(player, task, Master.values().random()) + if (slayer.hasTask()) slayer.task = task else SlayerUtils.assign(player, masterTask, Master.values().random()) if (amount != null) slayer.amount = amount setVarp(player, 2502, slayer.flags.taskFlags shr 4) } diff --git a/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt b/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt index 4189fb575..331ecdbb4 100644 --- a/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt @@ -1,12 +1,12 @@ package core.game.system.command.sets import core.api.log +import core.api.sendMessage import core.cache.Cache import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder import core.game.node.entity.npc.NPC import core.game.node.item.Item -import core.tools.SystemLogger import core.game.system.command.CommandPlugin import core.plugin.Initializable import core.game.system.command.Privilege @@ -22,19 +22,40 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){ */ define("npc"){player,args -> if (args.size < 2) { - reject(player,"syntax error: id (optional) direction") + reject(player, "syntax: id (required) amount (optional) isWalks (optional)") return@define } - val npc = NPC.create(CommandPlugin.toInteger(args[1]!!), player!!.location) - npc.setAttribute("spawned:npc", true) - npc.isRespawn = false - npc.direction = player.direction - npc.init() - npc.isWalks = args.size > 2 - val npcString = "{" + npc.location.x + "," + npc.location.y + "," + npc.location.z + "," + (if (npc.isWalks) "1" else "0") + "," + npc.direction.ordinal + "}" + val amount = if (args.size > 2) CommandPlugin.toInteger(args[2]) else 1 + if (amount < 1) { + reject(player, "Invalid amount") + return@define + } + if (amount > 900) { + reject(player, "Based on experience, spawning that many NPCs at once is a bad idea") + return@define + } + var isWalks = false + if (args.size > 3) { + if (args[3] == "true") { + isWalks = true + } else if (args[3] != "" && args[3] != "false") { + reject(player, "The \"isWalks\" argument only accepts \"true\" and \"false\"") + return@define + } + } + var npcString = "" + for (i in 1..amount) { + val npc = NPC.create(CommandPlugin.toInteger(args[1]), player.location) + npc.setAttribute("spawned:npc", true) + npc.isRespawn = false + npc.direction = player.direction + npc.init() + npc.isWalks = isWalks + npcString = "{" + npc.location.x + "," + npc.location.y + "," + npc.location.z + "," + (if (npc.isWalks) "1" else "0") + "," + npc.direction.ordinal + "}" + println(npcString) + } val clpbrd = Toolkit.getDefaultToolkit().systemClipboard clpbrd.setContents(StringSelection(npcString), null) - println(npcString) } /** diff --git a/Server/src/main/core/game/system/command/sets/StatAttributeKeys.kt b/Server/src/main/core/game/system/command/sets/StatAttributeKeys.kt index f39d94ee6..431cb84be 100644 --- a/Server/src/main/core/game/system/command/sets/StatAttributeKeys.kt +++ b/Server/src/main/core/game/system/command/sets/StatAttributeKeys.kt @@ -7,6 +7,8 @@ const val STATS_LOGS = "logs_chopped" const val STATS_FISH = "fish_caught" const val STATS_ROCKS = "rocks_mined" const val STATS_RC = "essence_crafted" +const val STATS_FOOD_COOKED = "food_cooked" +const val STATS_CATS_RAISED = "cats_raised" const val STATS_PK_KILLS = "player_kills" const val STATS_PK_DEATHS = "player_deaths" const val STATS_ALKHARID_GATE = "alkharid_gate" diff --git a/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt b/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt index 33ff9a4bc..b9b6e320c 100644 --- a/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt @@ -11,7 +11,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs -import core.api.utils.GlobalKillCounter +import core.api.utils.PlayerStatsCounter import core.game.system.command.Privilege import core.game.world.repository.Repository import java.util.* @@ -67,6 +67,8 @@ class StatsCommandSet : CommandSet(Privilege.STANDARD) { 77 -> sendLine(player,"Rocks Mined: ${queryPlayer.getAttribute("$STATS_BASE:$STATS_ROCKS",0)}",i) 78 -> sendLine(player,"Fish Caught: ${queryPlayer.getAttribute("$STATS_BASE:$STATS_FISH",0)}",i) 79 -> sendLine(player, "Essence Crafted: ${queryPlayer.getAttribute("$STATS_BASE:$STATS_RC",0)}", i) + 80 -> sendLine(player, "Food Cooked: ${queryPlayer.getAttribute("$STATS_BASE:$STATS_FOOD_COOKED",0)}", i) + 81 -> sendLine(player, "Cats Raised: ${queryPlayer.getAttribute("$STATS_BASE:$STATS_CATS_RAISED",0)}", i) //Boss KC 82 -> sendLine(player, "KBD KC: ${globalData.bossCounters.get(BossKillCounter.KING_BLACK_DRAGON.ordinal)}",i) @@ -89,69 +91,69 @@ class StatsCommandSet : CommandSet(Privilege.STANDARD) { } 1 -> { when(i) { - 97 -> sendLine(player, "Turoths: ${GlobalKillCounter.getKills(queryPlayer, TUROTH_IDS)}", i) - 68 -> sendLine(player, "Kurasks: ${GlobalKillCounter.getKills(queryPlayer, KURASK_IDS)}", i) - 69 -> sendLine(player, "Leaf-bladed swords: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.LEAF_BLADED_SWORD_13290)}", i) + 97 -> sendLine(player, "Turoths: ${PlayerStatsCounter.getKills(queryPlayer, TUROTH_IDS)}", i) + 68 -> sendLine(player, "Kurasks: ${PlayerStatsCounter.getKills(queryPlayer, KURASK_IDS)}", i) + 69 -> sendLine(player, "Leaf-bladed swords: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.LEAF_BLADED_SWORD_13290)}", i) 70 -> sendLine(player, SPACER,i) - 71 -> sendLine(player, "Gargoyles: ${GlobalKillCounter.getKills(queryPlayer, GARGOYLE_IDS)}", i) - 72 -> sendLine(player, "Granite mauls: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.GRANITE_MAUL_4153)}", i) + 71 -> sendLine(player, "Gargoyles: ${PlayerStatsCounter.getKills(queryPlayer, GARGOYLE_IDS)}", i) + 72 -> sendLine(player, "Granite mauls: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.GRANITE_MAUL_4153)}", i) 73 -> sendLine(player, SPACER,i) - 74 -> sendLine(player, "Spiritual mages: ${GlobalKillCounter.getKills(queryPlayer, SPIRITUAL_MAGE_IDS)}", i) - 75 -> sendLine(player, "Dragon boots: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.DRAGON_BOOTS_11732)}", i) + 74 -> sendLine(player, "Spiritual mages: ${PlayerStatsCounter.getKills(queryPlayer, SPIRITUAL_MAGE_IDS)}", i) + 75 -> sendLine(player, "Dragon boots: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.DRAGON_BOOTS_11732)}", i) 76 -> sendLine(player, SPACER,i) - 77 -> sendLine(player, "Abyssal demons: ${GlobalKillCounter.getKills(queryPlayer, NPCs.ABYSSAL_DEMON_1615)}", i) - 78 -> sendLine(player, "Abyssal whips: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.ABYSSAL_WHIP_4151)}", i) + 77 -> sendLine(player, "Abyssal demons: ${PlayerStatsCounter.getKills(queryPlayer, intArrayOf(NPCs.ABYSSAL_DEMON_1615))}", i) + 78 -> sendLine(player, "Abyssal whips: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.ABYSSAL_WHIP_4151)}", i) 79 -> sendLine(player, SPACER,i) - 80 -> sendLine(player, "Dark beasts: ${GlobalKillCounter.getKills(queryPlayer, NPCs.DARK_BEAST_2783)}", i) - 81 -> sendLine(player, "Dark bows: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.DARK_BOW_11235)}", i) + 80 -> sendLine(player, "Dark beasts: ${PlayerStatsCounter.getKills(queryPlayer, intArrayOf(NPCs.DARK_BEAST_2783))}", i) + 81 -> sendLine(player, "Dark bows: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.DARK_BOW_11235)}", i) - 82 -> sendLine(player, "Green Dragons: ${GlobalKillCounter.getKills(queryPlayer, GREEN_DRAGON_IDS)}", i) - 83 -> sendLine(player, "Blue Dragons: ${GlobalKillCounter.getKills(queryPlayer, BLUE_DRAGON_IDS)}", i) - 84 -> sendLine(player, "Red Dragons: ${GlobalKillCounter.getKills(queryPlayer, RED_DRAGON_IDS)}", i) - 85 -> sendLine(player, "Black Dragons: ${GlobalKillCounter.getKills(queryPlayer, BLACK_DRAGON_IDS)}", i) + 82 -> sendLine(player, "Green Dragons: ${PlayerStatsCounter.getKills(queryPlayer, GREEN_DRAGON_IDS)}", i) + 83 -> sendLine(player, "Blue Dragons: ${PlayerStatsCounter.getKills(queryPlayer, BLUE_DRAGON_IDS)}", i) + 84 -> sendLine(player, "Red Dragons: ${PlayerStatsCounter.getKills(queryPlayer, RED_DRAGON_IDS)}", i) + 85 -> sendLine(player, "Black Dragons: ${PlayerStatsCounter.getKills(queryPlayer, BLACK_DRAGON_IDS)}", i) 86 -> sendLine(player, SPACER,i) - 87 -> sendLine(player, "Bronze Dragons: ${GlobalKillCounter.getKills(queryPlayer, BRONZE_DRAGON_IDS)}", i) - 88 -> sendLine(player, "Iron Dragons: ${GlobalKillCounter.getKills(queryPlayer, IRON_DRAGON_IDS)}", i) - 89 -> sendLine(player, "Steel Dragons: ${GlobalKillCounter.getKills(queryPlayer, STEEL_DRAGON_IDS)}", i) - 90 -> sendLine(player, "Mithril Dragons: ${GlobalKillCounter.getKills(queryPlayer, MITHRIL_DRAGON_IDS)}", i) - 91 -> sendLine(player, "Skeletal Wyverns: ${GlobalKillCounter.getKills(queryPlayer, SKELETAL_WYVERN_IDS)}", i) + 87 -> sendLine(player, "Bronze Dragons: ${PlayerStatsCounter.getKills(queryPlayer, BRONZE_DRAGON_IDS)}", i) + 88 -> sendLine(player, "Iron Dragons: ${PlayerStatsCounter.getKills(queryPlayer, IRON_DRAGON_IDS)}", i) + 89 -> sendLine(player, "Steel Dragons: ${PlayerStatsCounter.getKills(queryPlayer, STEEL_DRAGON_IDS)}", i) + 90 -> sendLine(player, "Mithril Dragons: ${PlayerStatsCounter.getKills(queryPlayer, MITHRIL_DRAGON_IDS)}", i) + 91 -> sendLine(player, "Skeletal Wyverns: ${PlayerStatsCounter.getKills(queryPlayer, SKELETAL_WYVERN_IDS)}", i) 92 -> sendLine(player, SPACER,i) - 93 -> sendLine(player, "Draconic visages: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.DRACONIC_VISAGE_11286)}", i) + 93 -> sendLine(player, "Draconic visages: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.DRACONIC_VISAGE_11286)}", i) else -> sendLine(player,"",i) } } 2 -> { when(i) { - 97 -> sendLine(player, "Ahrim's hood: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.AHRIMS_HOOD_4708)}", i) - 68 -> sendLine(player, "Ahrim's staff: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.AHRIMS_STAFF_4710)}", i) - 69 -> sendLine(player, "Ahrim's robetop: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.AHRIMS_ROBETOP_4712)}", i) - 70 -> sendLine(player, "Ahrim's robeskirt: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.AHRIMS_ROBESKIRT_4714)}", i) + 97 -> sendLine(player, "Ahrim's hood: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.AHRIMS_HOOD_4708)}", i) + 68 -> sendLine(player, "Ahrim's staff: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.AHRIMS_STAFF_4710)}", i) + 69 -> sendLine(player, "Ahrim's robetop: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.AHRIMS_ROBETOP_4712)}", i) + 70 -> sendLine(player, "Ahrim's robeskirt: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.AHRIMS_ROBESKIRT_4714)}", i) 71 -> sendLine(player, SPACER,i) - 72 -> sendLine(player, "Dharok's helm: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.DHAROKS_HELM_4716)}", i) - 73 -> sendLine(player, "Dharok's greataxe: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.DHAROKS_GREATAXE_4718)}", i) - 74 -> sendLine(player, "Dharok's platebody: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.DHAROKS_PLATEBODY_4720)}", i) - 75 -> sendLine(player, "Dharok's platelegs: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.DHAROKS_PLATELEGS_4722)}", i) + 72 -> sendLine(player, "Dharok's helm: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.DHAROKS_HELM_4716)}", i) + 73 -> sendLine(player, "Dharok's greataxe: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.DHAROKS_GREATAXE_4718)}", i) + 74 -> sendLine(player, "Dharok's platebody: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.DHAROKS_PLATEBODY_4720)}", i) + 75 -> sendLine(player, "Dharok's platelegs: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.DHAROKS_PLATELEGS_4722)}", i) 76 -> sendLine(player, SPACER,i) - 77 -> sendLine(player, "Guthan's helm: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.GUTHANS_HELM_4724)}", i) - 78 -> sendLine(player, "Guthan's warspear: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.GUTHANS_WARSPEAR_4726)}", i) - 79 -> sendLine(player, "Guthan's platebody: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.GUTHANS_PLATEBODY_4728)}", i) - 80 -> sendLine(player, "Guthan's chainskirt: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.GUTHANS_CHAINSKIRT_4730)}", i) + 77 -> sendLine(player, "Guthan's helm: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.GUTHANS_HELM_4724)}", i) + 78 -> sendLine(player, "Guthan's warspear: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.GUTHANS_WARSPEAR_4726)}", i) + 79 -> sendLine(player, "Guthan's platebody: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.GUTHANS_PLATEBODY_4728)}", i) + 80 -> sendLine(player, "Guthan's chainskirt: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.GUTHANS_CHAINSKIRT_4730)}", i) - 82 -> sendLine(player, "Karil's coif: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.KARILS_COIF_4732)}", i) - 83 -> sendLine(player, "Karil's crossbow: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.KARILS_CROSSBOW_4734)}", i) - 84 -> sendLine(player, "Karil's leathertop: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.KARILS_LEATHERTOP_4736)}", i) - 85 -> sendLine(player, "Karil's leatherskirt: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.KARILS_LEATHERSKIRT_4738)}", i) + 82 -> sendLine(player, "Karil's coif: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.KARILS_COIF_4732)}", i) + 83 -> sendLine(player, "Karil's crossbow: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.KARILS_CROSSBOW_4734)}", i) + 84 -> sendLine(player, "Karil's leathertop: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.KARILS_LEATHERTOP_4736)}", i) + 85 -> sendLine(player, "Karil's leatherskirt: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.KARILS_LEATHERSKIRT_4738)}", i) 86 -> sendLine(player, SPACER,i) - 87 -> sendLine(player, "Torag's helm: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.TORAGS_HELM_4745)}", i) - 88 -> sendLine(player, "Torag's hammers: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.TORAGS_HAMMERS_4747)}", i) - 89 -> sendLine(player, "Torag's platebody: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.TORAGS_PLATEBODY_4749)}", i) - 90 -> sendLine(player, "Torag's platelegs: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.TORAGS_PLATELEGS_4751)}", i) + 87 -> sendLine(player, "Torag's helm: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.TORAGS_HELM_4745)}", i) + 88 -> sendLine(player, "Torag's hammers: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.TORAGS_HAMMERS_4747)}", i) + 89 -> sendLine(player, "Torag's platebody: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.TORAGS_PLATEBODY_4749)}", i) + 90 -> sendLine(player, "Torag's platelegs: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.TORAGS_PLATELEGS_4751)}", i) 91 -> sendLine(player, SPACER,i) - 92 -> sendLine(player, "Verac's helm: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.VERACS_HELM_4753)}", i) - 93 -> sendLine(player, "Verac's flail: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.VERACS_FLAIL_4755)}", i) - 94 -> sendLine(player, "Verac's brassard: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.VERACS_BRASSARD_4757)}", i) - 95 -> sendLine(player, "Verac's plateskirt: ${GlobalKillCounter.getRareDrops(queryPlayer, Items.VERACS_PLATESKIRT_4759)}", i) + 92 -> sendLine(player, "Verac's helm: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.VERACS_HELM_4753)}", i) + 93 -> sendLine(player, "Verac's flail: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.VERACS_FLAIL_4755)}", i) + 94 -> sendLine(player, "Verac's brassard: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.VERACS_BRASSARD_4757)}", i) + 95 -> sendLine(player, "Verac's plateskirt: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.VERACS_PLATESKIRT_4759)}", i) else -> sendLine(player,"",i) } } diff --git a/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt b/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt index 2cbc404e2..30e4107c9 100644 --- a/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt @@ -139,6 +139,30 @@ class TeleportCommandSet : CommandSet(Privilege.ADMIN){ } } + /** + * Finds a list of objects/sceneries in a region + */ + define("findobjs", Privilege.ADMIN, "::findobjs REGION ID SCENERY ID", "Finds all locations of scenery objects of id."){player, args -> + if(args.size < 4) reject(player, "Usage: region_id scenery_id") + val regionId = args[1].toInt() + val sceneryId = args[2].toInt() + val sceneryIdEnd = args[3].toInt() + + val region = RegionManager.forId(regionId) + + GlobalScope.launch { + for (plane in region.planes) { + for (objects in plane.objects.filterNotNull()) { + for (parent in objects.filterNotNull()) { + if (parent.id in sceneryId..sceneryIdEnd) { + println(parent.location) + } + } + } + } + } + } + /** * Teleport to a specific player */ diff --git a/Server/src/main/core/game/system/communication/CommunicationInfo.java b/Server/src/main/core/game/system/communication/CommunicationInfo.java index 895d45865..8135e7315 100644 --- a/Server/src/main/core/game/system/communication/CommunicationInfo.java +++ b/Server/src/main/core/game/system/communication/CommunicationInfo.java @@ -1,12 +1,10 @@ package core.game.system.communication; -import core.cache.misc.buffer.ByteBufferUtils; import core.game.node.entity.player.Player; import core.tools.Log; import org.jetbrains.annotations.NotNull; import proto.management.PrivateMessage; import core.auth.UserAccountInfo; -import core.tools.SystemLogger; import core.game.system.mysql.SQLTable; import core.game.system.task.Pulse; import core.game.world.GameWorld; @@ -19,7 +17,6 @@ import core.net.packet.out.ContactPackets; import core.tools.StringUtils; import core.worker.ManagementEvents; -import java.nio.ByteBuffer; import java.util.*; import java.util.Map.Entry; @@ -215,27 +212,6 @@ public final class CommunicationInfo { } } - /** - * Roar temp - * @param buffer - */ - public void parsePrevious(ByteBuffer buffer) { - int size = buffer.get() & 0xFF; - for (int i = 0; i < size; i++) { - String name = ByteBufferUtils.getString(buffer); - Contact contact = new Contact(name); - contact.setRank(ClanRank.FRIEND); - contacts.put(name, contact); - } - size = buffer.get() & 0xFF; - for (int i = 0; i < size; i++) { - blocked.add(ByteBufferUtils.getString(buffer)); - } - if (buffer.get() == 1) { - ByteBufferUtils.getString(buffer); - } - } - /** * Sends a message to the target. * @param player The player sending the message. diff --git a/Server/src/main/core/game/system/config/DoorConfigLoader.kt b/Server/src/main/core/game/system/config/DoorConfigLoader.kt index 4839e713c..04d23c978 100644 --- a/Server/src/main/core/game/system/config/DoorConfigLoader.kt +++ b/Server/src/main/core/game/system/config/DoorConfigLoader.kt @@ -31,14 +31,12 @@ class DoorConfigLoader { door.isFence = e["fence"].toString().toBoolean() door.isMetal = e["metal"].toString().toBoolean() door.isAutoWalk = e["autowalk"]?.toString()?.toBoolean() ?: false - door.questRequirement = e["questRequirement"]?.toString() ?: "" DOORS[door.id] = door val replacedDoor = Door(door.replaceId) replacedDoor.replaceId = door.id replacedDoor.isFence = door.isFence replacedDoor.isMetal = door.isMetal replacedDoor.isAutoWalk = door.isAutoWalk - replacedDoor.questRequirement = door.questRequirement DOORS[door.replaceId] = replacedDoor count++ } diff --git a/Server/src/main/core/game/system/config/GroundSpawnLoader.kt b/Server/src/main/core/game/system/config/GroundSpawnLoader.kt index baf00ce02..cbf5dabc9 100644 --- a/Server/src/main/core/game/system/config/GroundSpawnLoader.kt +++ b/Server/src/main/core/game/system/config/GroundSpawnLoader.kt @@ -10,12 +10,10 @@ import org.json.simple.JSONObject import org.json.simple.parser.JSONParser import core.ServerConstants import core.api.log -import core.tools.SystemLogger import core.game.world.GameWorld import core.game.world.repository.Repository import core.tools.Log import java.io.* -import java.nio.ByteBuffer class GroundSpawnLoader { val parser = JSONParser() @@ -65,17 +63,6 @@ class GroundSpawnLoader { return "GroundSpawn [name=" + getName() + ", respawnRate=" + respawnRate + ", loc=" + getLocation() + "]" } - /** - * Method used to save this ground item to a byte buffer. - * @param buffer the buffer. - */ - fun save(buffer: ByteBuffer) { - buffer.putInt(respawnRate) - buffer.putShort(id.toShort()) - buffer.putInt(amount) - buffer.putShort((getLocation().x and 0xFFFF).toShort()).putShort((getLocation().y and 0xFFFF).toShort()).put(getLocation().z.toByte()) - } - /** * Method used to initialize this spawn. */ diff --git a/Server/src/main/core/game/system/config/MusicConfigLoader.kt b/Server/src/main/core/game/system/config/MusicConfigLoader.kt index 2b11d4fff..3f5cc60cf 100644 --- a/Server/src/main/core/game/system/config/MusicConfigLoader.kt +++ b/Server/src/main/core/game/system/config/MusicConfigLoader.kt @@ -22,10 +22,7 @@ class MusicConfigLoader { val parser = JSONParser() var reader: FileReader? = null fun load(){ - var count = 0 - reader = FileReader(ServerConstants.CONFIG_PATH + "music_configs.json") - var configs = parser.parse(reader) as JSONArray - + // Populate the server data map val songs = DataMap.get(1351) val names = DataMap.get(1345) @@ -35,7 +32,24 @@ class MusicConfigLoader { MusicEntry.getSongs().putIfAbsent(songId, entry) } - for(config in configs){ + // Parse the region-wide music config file + var count = 0 + reader = FileReader(ServerConstants.CONFIG_PATH + "music_regions.json") + var configs = parser.parse(reader) as JSONArray + for(config in configs) { + val e = config as JSONObject + val region = Integer.parseInt(e["region"].toString()) + val id = Integer.parseInt(e["id"].toString()) + RegionManager.forId(region).music = id + count++ + } + log(this::class.java, Log.FINE, "Parsed $count region music configs.") + + // Parse the file with tile-specific music locations + count = 0 + reader = FileReader(ServerConstants.CONFIG_PATH + "music_tiles.json") + configs = parser.parse(reader) as JSONArray + for(config in configs) { val e = config as JSONObject val musicId = Integer.parseInt(e["id"].toString()) val string = e["borders"].toString() @@ -43,9 +57,6 @@ class MusicConfigLoader { var tokens: Array? = null var borders: ZoneBorders? = null for (border in borderArray) { - if(border.isEmpty()){ - continue - } tokens = border.replace("{", "").replace("}", "").split(",").toTypedArray() borders = ZoneBorders(tokens[0].toInt(), tokens[1].toInt(), tokens[2].toInt(), tokens[3].toInt()) if (border.contains("[")) { //no exception borders @@ -59,8 +70,6 @@ class MusicConfigLoader { e = exception.replace("[", "").replace("]", "").split(",".toRegex()).toTypedArray() borders.addException(ZoneBorders(e[0].toInt(), e[1].toInt(), e[2].toInt(), e[3].toInt())) } - e = null - exceptions = null } val zone = MusicZone(musicId, borders) for (id in borders.getRegionIds()) { @@ -69,6 +78,6 @@ class MusicConfigLoader { } count++ } - log(this::class.java, Log.FINE, "Parsed $count music configs.") + log(this::class.java, Log.FINE, "Parsed $count tile music configs.") } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/config/RangedConfigLoader.kt b/Server/src/main/core/game/system/config/RangedConfigLoader.kt index 41a85f4b3..6c54cbd55 100644 --- a/Server/src/main/core/game/system/config/RangedConfigLoader.kt +++ b/Server/src/main/core/game/system/config/RangedConfigLoader.kt @@ -8,7 +8,6 @@ import core.game.node.entity.combat.equipment.BoltEffect import core.game.node.entity.combat.equipment.RangeWeapon import core.game.node.entity.impl.Projectile import core.game.node.entity.npc.NPC -import core.tools.SystemLogger import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics diff --git a/Server/src/main/core/game/system/config/ServerConfigParser.kt b/Server/src/main/core/game/system/config/ServerConfigParser.kt index 62b866443..273b36531 100644 --- a/Server/src/main/core/game/system/config/ServerConfigParser.kt +++ b/Server/src/main/core/game/system/config/ServerConfigParser.kt @@ -1,16 +1,15 @@ package core.game.system.config import com.moandjiezana.toml.Toml -import core.game.world.map.Location -import core.tools.mysql.Database import core.ServerConstants import core.api.log import core.api.parseEnumEntry -import core.tools.SystemLogger import core.game.world.GameSettings import core.game.world.GameWorld +import core.game.world.map.Location import core.tools.Log import core.tools.LogLevel +import core.tools.mysql.Database import java.io.File import java.net.URL import kotlin.system.exitProcess @@ -77,8 +76,6 @@ object ServerConfigParser { isQuickChat = false, isLootshare = false, msAddress = data.getString("server.msip"), - default_xp_rate = data.getDouble("world.default_xp_rate"), - allow_slayer_reroll = data.getBoolean("world.allow_slayer_reroll"), enable_default_clan = data.getBoolean("world.enable_default_clan"), enable_bots = data.getBoolean("world.enable_bots"), autostock_ge = data.getBoolean("world.autostock_ge"), @@ -149,22 +146,34 @@ object ServerConfigParser { ServerConstants.NOAUTH_DEFAULT_ADMIN = data.getBoolean("server.noauth_default_admin", false) ServerConstants.DRAGON_AXE_USE_OSRS_SPEC = data.getBoolean("world.dragon_axe_use_osrs_spec", false) ServerConstants.DISCORD_OPENRSC_HOOK = data.getString("integrations.openrsc_integration_webhook", "") - ServerConstants.ENABLE_GLOBALCHAT = data.getBoolean("world.enable_globalchat", true) + ServerConstants.ENABLE_GLOBAL_CHAT = data.getBoolean("world.enable_global_chat", false) ServerConstants.MAX_PATHFIND_DISTANCE = data.getLong("server.max_pathfind_dist", 25L).toInt() - ServerConstants.IRONMAN_ICONS = data.getBoolean("world.ironman_icons", false) + ServerConstants.XP_RATES = data.getBoolean("world.xp_rates", false) + ServerConstants.IRONMAN = data.getBoolean("world.ironman", false) ServerConstants.PLAYER_STOCK_CLEAR_INTERVAL = data.getLong("world.playerstock_clear_mins", 180L).toInt() ServerConstants.PLAYER_STOCK_RECIRCULATE = data.getBoolean("world.playerstock_bot_offers", true) ServerConstants.BOTSTOCK_LIMIT = data.getLong("world.botstock_limit", 5000L).toInt() - ServerConstants.BETTER_AGILITY_PYRAMID_GP = data.getBoolean("world.better_agility_pyramid_gp", true) + ServerConstants.BETTER_AGILITY_PYRAMID_GP = data.getBoolean("world.better_agility_pyramid_gp", false) ServerConstants.GRAFANA_PATH = data.getPath("integrations.grafana_log_path") ServerConstants.GRAFANA_LOGGING = data.getBoolean("integrations.grafana_logging", false) ServerConstants.GRAFANA_TTL_DAYS = data.getLong("integrations.grafana_log_ttl_days", 7L).toInt() - ServerConstants.BETTER_DFS = data.getBoolean("world.better_dfs", true) - ServerConstants.NEW_PLAYER_ANNOUNCEMENT = data.getBoolean("world.new_player_announcement", true) - ServerConstants.HOLIDAY_EVENT_RANDOMS = data.getBoolean("world.holiday_event_randoms", true) - ServerConstants.FORCE_HALLOWEEN_RANDOMS = data.getBoolean("world.force_halloween_randoms", false) - ServerConstants.FORCE_CHRISTMAS_RANDOMS = data.getBoolean("world.force_christmas_randoms", false) - ServerConstants.RUNECRAFTING_FORMULA_REVISION = data.getLong("world.runecrafting_formula_revision", 581).toInt() + ServerConstants.BETTER_DFS = data.getBoolean("world.better_dfs", false) + ServerConstants.NEW_PLAYER_ANNOUNCEMENT = data.getBoolean("world.new_player_announcement", false) + ServerConstants.INAUTHENTIC_CANDLELIGHT_RANDOM = data.getBoolean("world.inauthentic_candlelight_random", false) + ServerConstants.HOLIDAY_EVENT_RANDOMS = data.getBoolean("world.holiday_event_randoms", false) + ServerConstants.FORCE_HALLOWEEN_EVENTS = data.getBoolean("world.force_halloween_randoms", false) + ServerConstants.FORCE_CHRISTMAS_EVENTS = data.getBoolean("world.force_christmas_randoms", false) + ServerConstants.FORCE_EASTER_EVENTS = data.getBoolean("world.force_easter_randoms", false) + ServerConstants.RUNECRAFTING_FORMULA_REVISION = data.getLong("world.runecrafting_formula_revision", 530).toInt() + ServerConstants.ENHANCED_DEEP_WILDERNESS = data.getBoolean("world.enhanced_deep_wilderness", false) + ServerConstants.WILDERNESS_EXCLUSIVE_LOOT = data.getBoolean("world.wilderness_exclusive_loot", false) + ServerConstants.SHOOTING_STAR_RING = data.getBoolean("world.shooting_star_ring", false) + ServerConstants.RING_OF_WEALTH_TELEPORT = data.getBoolean("world.ring_of_wealth_teleport", false) + ServerConstants.SECOND_BANK = data.getBoolean("world.second_bank", false) + ServerConstants.PLAYER_COMMANDS = data.getBoolean("world.player_commands", false) + ServerConstants.BOOSTED_TRAWLER_REWARDS = data.getBoolean("world.boosted_trawler_rewards", false) + ServerConstants.CONNECTIVITY_CHECK_URL = data.getString("server.connectivity_check_url", "https://google.com,https://2009scape.org") + ServerConstants.CONNECTIVITY_TIMEOUT = data.getLong("server.connectivity_timeout", 500L).toInt() val logLevel = data.getString("server.log_level", "VERBOSE").uppercase() ServerConstants.LOG_LEVEL = parseEnumEntry(logLevel) ?: LogLevel.VERBOSE diff --git a/Server/src/main/core/game/system/config/XteaParser.kt b/Server/src/main/core/game/system/config/XteaParser.kt index 6b7f81ad8..9eb4f569c 100644 --- a/Server/src/main/core/game/system/config/XteaParser.kt +++ b/Server/src/main/core/game/system/config/XteaParser.kt @@ -13,13 +13,9 @@ import kotlin.collections.HashMap class XteaParser { companion object{ val REGION_XTEA = HashMap() - val DEFAULT_REGION_KEYS = intArrayOf(14881828, -6662814, 58238456, 146761213) - fun getRegionXTEA(regionId: Int): IntArray? { //Uses the xtea's from the sql to unlock regions - - return REGION_XTEA[regionId] - - return DEFAULT_REGION_KEYS //This one grabs the keys from the SQL - // return DEFAULT_REGION_KEYS;//This one only uses the default keys at the top,{ 14881828, -6662814, 58238456, 146761213 }. Unsure why they chose these numbers. + private val DEFAULT_REGION_KEYS = intArrayOf(0, 0, 0, 0) + fun getRegionXTEA(regionId: Int): IntArray { //Uses the xtea's from the sql to unlock regions + return REGION_XTEA.getOrDefault(regionId, DEFAULT_REGION_KEYS) } } val parser = JSONParser() diff --git a/Server/src/main/core/game/system/timer/RSTimer.kt b/Server/src/main/core/game/system/timer/RSTimer.kt index a995bac75..75770b7bc 100644 --- a/Server/src/main/core/game/system/timer/RSTimer.kt +++ b/Server/src/main/core/game/system/timer/RSTimer.kt @@ -24,8 +24,15 @@ abstract class RSTimer (var runInterval: Int, val identifier: String = "generict **/ open fun getInitialRunDelay() : Int { return runInterval } + /** + * Called by core code before the timer is first registered. Called after parse on PersistTimers. + * Called before the timer has been added to the timer list. + **/ + open fun beforeRegister (entity: Entity) {} + /** * Called by core code when the timer is first registered. Called after parse on PersistTimers. + * Called after the timer has been added to the timer list. **/ open fun onRegister (entity: Entity) {} diff --git a/Server/src/main/core/game/system/timer/TimerManager.kt b/Server/src/main/core/game/system/timer/TimerManager.kt index fd63b7ec5..92bd50e8c 100644 --- a/Server/src/main/core/game/system/timer/TimerManager.kt +++ b/Server/src/main/core/game/system/timer/TimerManager.kt @@ -13,8 +13,9 @@ class TimerManager (val entity: Entity) { val toRemoveTimers = ArrayList() fun registerTimer (timer: RSTimer) { + timer.beforeRegister(entity) + newTimers.add(timer) timer.onRegister(entity) - newTimers.add (timer) } fun processTimers () { diff --git a/Server/src/main/core/game/system/timer/impl/Disease.kt b/Server/src/main/core/game/system/timer/impl/Disease.kt index 7d270e0a9..cfc72ac84 100644 --- a/Server/src/main/core/game/system/timer/impl/Disease.kt +++ b/Server/src/main/core/game/system/timer/impl/Disease.kt @@ -20,7 +20,7 @@ class Disease : PersistTimer (30, "disease", flags = arrayOf(TimerFlag.ClearOnDe hitsLeft = root["hitsLeft"].toString().toInt() } - override fun onRegister (entity: Entity) { + override fun beforeRegister (entity: Entity) { if (hasTimerActive(entity)) removeTimer(entity, this) else if (entity is Player) diff --git a/Server/src/main/core/game/system/timer/impl/DragonFireImmunity.kt b/Server/src/main/core/game/system/timer/impl/DragonFireImmunity.kt new file mode 100644 index 000000000..2f2a2b9e5 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/DragonFireImmunity.kt @@ -0,0 +1,47 @@ +package core.game.system.timer.impl + +import core.game.system.timer.* +import core.api.* +import core.tools.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import org.json.simple.* +import org.rs09.consts.Sounds + +/** + * A timer that replicates the behavior of Dragon Fire immunity mechanics. Runs every tick. + * Will notify the player of various levels of remaining Dragon Fire immunity, and then remove itself once it has run out. + * This timer is a "soft" timer, meaning it will tick down even while other timers would normally stall (e.g. during entity delays or when the entity has a modal open.) +**/ +class DragonFireImmunity : PersistTimer (1, "dragonfire:immunity", isSoft = true, flags = arrayOf(TimerFlag.ClearOnDeath)) { + var ticksRemaining = 0 + + override fun save (root: JSONObject, entity: Entity) { + root["ticksRemaining"] = ticksRemaining.toString() + } + + override fun parse (root: JSONObject, entity: Entity) { + ticksRemaining = root["ticksRemaining"].toString().toInt() + } + + override fun run (entity: Entity) : Boolean { + ticksRemaining-- + + if (entity is Player && ticksRemaining == secondsToTicks(30)) { + sendMessage(entity, colorize("%RYou have 30 seconds remaining on your antifire potion.")) + playAudio(entity, Sounds.CLOCK_TICK_1_3120, 0, 3) + } + else if (entity is Player && ticksRemaining == 0) { + sendMessage(entity, colorize("%RYour antifire potion has expired.")) + playAudio(entity, Sounds.DRAGON_POTION_FINISHED_2607) + } + + return ticksRemaining > 0 + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = DragonFireImmunity() + t.ticksRemaining = args.getOrNull(0) as? Int ?: 100 + return t + } +} diff --git a/Server/src/main/core/game/system/timer/impl/Frozen.kt b/Server/src/main/core/game/system/timer/impl/Frozen.kt index 94c5456a4..23a2fa7e5 100644 --- a/Server/src/main/core/game/system/timer/impl/Frozen.kt +++ b/Server/src/main/core/game/system/timer/impl/Frozen.kt @@ -1,47 +1,50 @@ -package core.game.system.timer.impl - -import core.game.system.timer.* -import core.api.* -import core.game.node.entity.Entity -import core.game.node.entity.player.Player -import core.game.world.repository.Repository -import org.json.simple.* - -class Frozen : PersistTimer (1, "frozen", flags = arrayOf(TimerFlag.ClearOnDeath)) { - var shouldApplyImmunity = false - - override fun save (root: JSONObject, entity: Entity) { - root["ticksLeft"] = (nextExecution - getWorldTicks()).toString() - root["applyImmunity"] = shouldApplyImmunity - } - - override fun parse (root: JSONObject, entity: Entity) { - runInterval = root["ticksLeft"].toString().toInt() - shouldApplyImmunity = root["applyImmunity"] as? Boolean ?: false - } - - override fun onRegister (entity: Entity) { - if (hasTimerActive(entity)) { - removeTimer(entity, this) - return - } - if (hasTimerActive(entity)) { - removeTimer(entity, this) - return - } - } - - override fun run (entity: Entity) : Boolean { - if (shouldApplyImmunity) { - registerTimer (entity, spawnTimer(7)) - } else (entity as? Player)?.debug ("Can't apply immunity") - return false - } - - override fun getTimer (vararg args: Any) : RSTimer { - val inst = Frozen() - inst.runInterval = args.getOrNull(0) as? Int ?: 10 - inst.shouldApplyImmunity = args.getOrNull(1) as? Boolean ?: false - return inst - } -} +package core.game.system.timer.impl + +import core.game.system.timer.* +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.repository.Repository +import org.json.simple.* + +class Frozen : PersistTimer (1, "frozen", flags = arrayOf(TimerFlag.ClearOnDeath)) { + var shouldApplyImmunity = false + + override fun save (root: JSONObject, entity: Entity) { + root["ticksLeft"] = (nextExecution - getWorldTicks()).toString() + root["applyImmunity"] = shouldApplyImmunity + } + + override fun parse (root: JSONObject, entity: Entity) { + runInterval = root["ticksLeft"].toString().toInt() + shouldApplyImmunity = root["applyImmunity"] as? Boolean ?: false + } + + override fun beforeRegister (entity: Entity) { + if (hasTimerActive(entity)) { + removeTimer(entity, this) + return + } + if (hasTimerActive(entity)) { + removeTimer(entity, this) + return + } + if (entity is Player) { + sendMessage(entity as Player, "You have been frozen!") + } + } + + override fun run (entity: Entity) : Boolean { + if (shouldApplyImmunity) { + registerTimer (entity, spawnTimer(7)) + } else (entity as? Player)?.debug ("Can't apply immunity") + return false + } + + override fun getTimer (vararg args: Any) : RSTimer { + val inst = Frozen() + inst.runInterval = args.getOrNull(0) as? Int ?: 10 + inst.shouldApplyImmunity = args.getOrNull(1) as? Boolean ?: false + return inst + } +} diff --git a/Server/src/main/core/game/system/timer/impl/Miasmic.kt b/Server/src/main/core/game/system/timer/impl/Miasmic.kt index d9c1eca18..9af8ee3ad 100644 --- a/Server/src/main/core/game/system/timer/impl/Miasmic.kt +++ b/Server/src/main/core/game/system/timer/impl/Miasmic.kt @@ -15,7 +15,7 @@ class Miasmic : PersistTimer (1, "miasmic", flags = arrayOf(TimerFlag.ClearOnDea return false } - override fun onRegister (entity: Entity) { + override fun beforeRegister (entity: Entity) { if (hasTimerActive(entity)) removeTimer(entity, this) if (hasTimerActive(entity)) diff --git a/Server/src/main/core/game/system/timer/impl/Poison.kt b/Server/src/main/core/game/system/timer/impl/Poison.kt index 2f4c58cf9..7dbf9f2b0 100644 --- a/Server/src/main/core/game/system/timer/impl/Poison.kt +++ b/Server/src/main/core/game/system/timer/impl/Poison.kt @@ -20,14 +20,6 @@ class Poison : PersistTimer (30, "poison", flags = arrayOf(TimerFlag.ClearOnDeat lateinit var damageSource: Entity var severity = 0 - set (value) { - if (value != field - 1 && value % 10 == 8) {//This was Arios's incorrect attempt at replicating severity, convert it to correct values. - (damageSource as? Player)?.debug ("[PoisonTimer] Warning: Converting suspect Arios severity into true severity. If numbers look wrong, this could be why.") - field = (value / 10) * 5 - (damageSource as? Player)?.debug ("[PoisonTimer] Warning: New Severity: $field.") - } else field = value - } - override fun save (root: JSONObject, entity: Entity) { root["source-uid"] = (damageSource as? Player)?.details?.uid ?: -1 root["severity"] = severity.toString() @@ -62,8 +54,6 @@ class Poison : PersistTimer (30, "poison", flags = arrayOf(TimerFlag.ClearOnDeat override fun getTimer (vararg args: Any) : RSTimer { val timer = Poison() - for (arg in args) - println(arg) timer.damageSource = args[0] as? Entity ?: return timer timer.severity = args[1] as? Int ?: return timer return timer diff --git a/Server/src/main/core/game/system/timer/impl/PoisonImmunity.kt b/Server/src/main/core/game/system/timer/impl/PoisonImmunity.kt index 1695fce7b..c90608e58 100644 --- a/Server/src/main/core/game/system/timer/impl/PoisonImmunity.kt +++ b/Server/src/main/core/game/system/timer/impl/PoisonImmunity.kt @@ -31,11 +31,7 @@ class PoisonImmunity : PersistTimer (1, "poison:immunity", isSoft = true, flags override fun run (entity: Entity) : Boolean { ticksRemaining-- - if (entity is Player && ticksRemaining == secondsToTicks(30)) { - sendMessage(entity, colorize("%RYou have 30 seconds remaining on your poison immunity.")) - playAudio(entity, Sounds.CLOCK_TICK_1_3120, 0, 3) - } - else if (entity is Player && ticksRemaining == 0) { + if (entity is Player && ticksRemaining == 0) { sendMessage(entity, colorize("%RYour poison immunity has expired.")) playAudio(entity, Sounds.DRAGON_POTION_FINISHED_2607) } diff --git a/Server/src/main/core/game/system/timer/impl/SkillRestore.kt b/Server/src/main/core/game/system/timer/impl/SkillRestore.kt index 7f1a8bcac..f14d46abe 100644 --- a/Server/src/main/core/game/system/timer/impl/SkillRestore.kt +++ b/Server/src/main/core/game/system/timer/impl/SkillRestore.kt @@ -19,7 +19,7 @@ class SkillRestore : RSTimer (1, "skillrestore", isAuto = true, isSoft = true) { var skills = entity.skills for (i in 0 until 24) { - if (i == Skills.PRAYER) continue + if (i == Skills.PRAYER || i == Skills.SUMMONING) continue if (ticksSinceLastRestore[i]++ >= restoreTicks[i]) { if (i == Skills.HITPOINTS && entity.skills.lifepoints < entity.skills.maximumLifepoints) { skills.heal (getHealAmount(entity)) @@ -48,7 +48,7 @@ class SkillRestore : RSTimer (1, "skillrestore", isAuto = true, isSoft = true) { (entity as? Player)?.debug("Registered skill restoration timer.") } - private fun getHealAmount (entity: Entity) : Int { + fun getHealAmount (entity: Entity) : Int { if (entity !is Player) return 1 val gloves = getItemFromEquipment (entity, EquipmentSlot.HANDS) diff --git a/Server/src/main/core/game/system/timer/impl/Skulled.kt b/Server/src/main/core/game/system/timer/impl/Skulled.kt index fee4474ab..f8e8bcd76 100644 --- a/Server/src/main/core/game/system/timer/impl/Skulled.kt +++ b/Server/src/main/core/game/system/timer/impl/Skulled.kt @@ -19,6 +19,11 @@ class Skulled : PersistTimer (1, "skulled", flags = arrayOf(TimerFlag.ClearOnDea return false } + override fun onRemoval (entity: Entity) { + if (entity !is Player) return + entity.skullManager.reset() + } + override fun getTimer (vararg args: Any) : RSTimer { val t = Skulled() t.runInterval = args.getOrNull(0) as? Int ?: 500 diff --git a/Server/src/main/core/game/world/GameSettings.kt b/Server/src/main/core/game/world/GameSettings.kt index c75a55efd..e113dbbe3 100644 --- a/Server/src/main/core/game/world/GameSettings.kt +++ b/Server/src/main/core/game/world/GameSettings.kt @@ -69,8 +69,6 @@ class GameSettings * The address of the Management server. */ var msAddress: String, - var default_xp_rate: Double, - var allow_slayer_reroll: Boolean, var enable_default_clan: Boolean, var enable_bots: Boolean, var autostock_ge: Boolean, @@ -88,17 +86,16 @@ class GameSettings /**"Lobby" interface * The message of the week models to display - * 15 & 22 = keys & lock || 16 = fly swat || 17 = person with question marks || 18 & 447 = wise old man - * 19 = man & woman with mouth closed || 20 = man & lock & key || 21 = closed chests - * 23 = snowmen || 405 = Construction houses || 622 = Two sets of 3 people range, mage, melee - * 623 = Woodcutting || 679 = Summoning || 715 = Easter || 800 = Halloween - * Any value that isn't one listed above = random selection + * 15 & 22 = Lock w/key & keys || 16 = Fly swatters || 17 = Man & woman with question marks + * 18 & 447 = Wise old man & woman || 19 = Man with mouth zipped & woman talking || 20 = Man & lock w/key + * 21 = Chests that open & close || 23 = Christmas || 405 = Construction houses + * 622 = Two sets 3 people range, mage, melee 623 = Woodcutting || 679 = Summoning || 715 = Easter || 800 = Halloween */ var message_model: Int, /**"Lobby" interface * The message of the week text - * The "child" for writing text to these interfaces is located inside of LoginConfiguration.java + * The "child" for writing text to these interfaces is located inside LoginConfiguration.java * method: getMessageChild */ var message_string: String @@ -127,8 +124,6 @@ class GameSettings val activity = data["activity"].toString() val pvpWorld = data["pvpWorld"] as Boolean val msip = data["msip"].toString() - val default_xp_rate = data["default_xp_rate"].toString().toDouble() - val allow_slayer_reroll = data["allow_slayer_reroll"] as Boolean val enable_default_clan = data["enable_default_clan"] as Boolean val enable_bots = data["enable_bots"] as Boolean val autostock_ge = data["autostock_ge"] as Boolean @@ -158,8 +153,6 @@ class GameSettings false, false, msip, - default_xp_rate, - allow_slayer_reroll, enable_default_clan, enable_bots, autostock_ge, diff --git a/Server/src/main/core/game/world/ImmerseWorld.kt b/Server/src/main/core/game/world/ImmerseWorld.kt index 2efed45a8..2adee483f 100644 --- a/Server/src/main/core/game/world/ImmerseWorld.kt +++ b/Server/src/main/core/game/world/ImmerseWorld.kt @@ -11,6 +11,7 @@ import core.game.bots.SkillingBotAssembler import java.util.Timer import java.util.concurrent.Executors import kotlin.concurrent.schedule +import kotlin.random.Random class ImmerseWorld : StartupListener { @@ -24,6 +25,12 @@ class ImmerseWorld : StartupListener { var assembler = CombatBotAssembler() var skillingBotAssembler = SkillingBotAssembler() + private fun randomizeLocationInRanges(location: Location, xMin: Int, xMax: Int, yMin: Int, yMax: Int): Location { + val newX = location.x + Random.nextInt(xMin, xMax) + val newY = location.y + Random.nextInt(yMin, yMax) + return Location(newX, newY, 0) + } + fun spawnBots() { if(GameWorld.settings!!.enable_bots) @@ -43,10 +50,10 @@ class ImmerseWorld : StartupListener { } } - fun immerseAdventurer() { - for (i in 0..(GameWorld.settings?.max_adv_bots ?: 50)) { - var random: Long = (10000..300000).random().toLong() - Timer().schedule(random) { + fun immerseAdventurer(){ + for (i in 0..(GameWorld.settings?.max_adv_bots ?: 50)){ + var random = Random.nextInt(20000, 400000).toLong() + Timer().schedule(random){ spawn_adventurers() } } @@ -55,14 +62,17 @@ class ImmerseWorld : StartupListener { fun spawn_adventurers() { val lumbridge = Location.create(3221, 3219, 0) val tiers = listOf(CombatBotAssembler.Tier.LOW, CombatBotAssembler.Tier.MED) - GeneralBotCreator( - Adventurer(CombatStyle.MELEE), - assembler.MeleeAdventurer(tiers.random(), lumbridge) - ) - GeneralBotCreator( - Adventurer(CombatStyle.RANGE), - assembler.RangeAdventurer(tiers.random(), lumbridge) - ) + if (Random.nextBoolean()) { + GeneralBotCreator( + Adventurer(CombatStyle.MELEE), + assembler.MeleeAdventurer(tiers.random(), randomizeLocationInRanges(lumbridge,-1,1,-1,1)) + ) + } else { + GeneralBotCreator( + Adventurer(CombatStyle.RANGE), + assembler.RangeAdventurer(tiers.random(), randomizeLocationInRanges(lumbridge,-1,1,-1,1)) + ) + } } fun immerseFishingGuild() { diff --git a/Server/src/main/core/game/world/map/BuildRegionChunk.java b/Server/src/main/core/game/world/map/BuildRegionChunk.java index 26d9bc492..008937301 100644 --- a/Server/src/main/core/game/world/map/BuildRegionChunk.java +++ b/Server/src/main/core/game/world/map/BuildRegionChunk.java @@ -6,6 +6,7 @@ import core.game.node.item.GroundItem; import core.game.node.item.Item; import core.game.node.scenery.Constructed; import core.game.node.scenery.Scenery; +import core.game.node.scenery.SceneryBuilder; import core.tools.Log; import core.tools.SystemLogger; import core.game.world.map.build.LandscapeParser; @@ -16,6 +17,7 @@ import core.net.packet.out.ConstructScenery; import static core.api.ContentAPIKt.log; +import java.util.ArrayList; import java.util.Arrays; /** @@ -75,14 +77,12 @@ public class BuildRegionChunk extends RegionChunk { } } } - if (items != null) { - for (Item item : items) { - if (item != null && item.isActive() && item.getLocation() != null) { - GroundItem g = (GroundItem) item; - if (!g.isPrivate() || g.droppedBy(player)) { - ConstructGroundItem.write(buffer, item); - updated = true; - } + ArrayList totalItems = drawItems(items, player); + for (GroundItem item : totalItems) { + if (item != null && item.isActive() && item.getLocation() != null) { + if (!item.isPrivate() || item.droppedBy(player)) { + ConstructGroundItem.write(buffer, item); + updated = true; } } } @@ -101,11 +101,12 @@ public class BuildRegionChunk extends RegionChunk { for (int x = 0; x < SIZE; x++) { for (int y = 0; y < SIZE; y++) { for (int i = 0; i < objects.length; i++) { - copy[i][x][y] = objects[i][x][y]; - objects[i][x][y] = null; + Scenery object = copy[i][x][y] = objects[i][x][y]; + if (object != null) { + SceneryBuilder.remove(object); + this.remove(object); + } } - plane.getObjects()[baseX + x][baseY + y] = null; - plane.getFlags().clearFlag(baseX + x, baseY + y); } } clear(); diff --git a/Server/src/main/core/game/world/map/Region.java b/Server/src/main/core/game/world/map/Region.java index c615df34a..ff87d8e6b 100644 --- a/Server/src/main/core/game/world/map/Region.java +++ b/Server/src/main/core/game/world/map/Region.java @@ -11,7 +11,6 @@ import core.game.world.map.build.LandscapeParser; import core.game.world.map.build.MapscapeParser; import core.game.world.map.zone.RegionZone; import core.tools.Log; -import core.tools.SystemLogger; import core.game.system.config.XteaParser; import core.game.world.GameWorld; import core.game.world.repository.Repository; @@ -62,7 +61,12 @@ public class Region { private final List regionZones = new ArrayList<>(20); /** - * The music zones lying in this region. + * The region-wide music track ID for this region. + */ + private int music = -1; + + /** + * Any tile-specific music zones lying in this region. */ private final List musicZones = new ArrayList<>(20); @@ -268,14 +272,14 @@ public class Region { } } - public boolean flagInactive(boolean force) { - if (unload(this, force)) { - active = false; - return true; - } else { - return false; - } - } + public boolean flagInactive(boolean force) { + if (unload(this, force)) { + active = false; + return true; + } else { + return false; + } + } /** * Flags the region as inactive. @@ -345,7 +349,7 @@ public class Region { } } - public static boolean unload(Region r) { + public static boolean unload(Region r) { return unload(r, false); } @@ -374,10 +378,10 @@ public class Region { } } } - if (r.isBuild()) - r.setLoaded(false); - r.activityPulse.stop(); - return true; + if (r.isBuild()) + r.setLoaded(false); + r.activityPulse.stop(); + return true; } /** @@ -473,6 +477,21 @@ public class Region { return planes; } + /** + * Sets the region-wide music track. + */ + public void setMusic(int music) { + this.music = music; + } + + /** + * Gets the region-wide music track + * @return The music entry ID + */ + public int getMusic() { + return this.music; + } + /** * Gets the regionZones. * @return The regionZones. diff --git a/Server/src/main/core/game/world/map/RegionChunk.java b/Server/src/main/core/game/world/map/RegionChunk.java index e8b97a717..7b3fb71c8 100644 --- a/Server/src/main/core/game/world/map/RegionChunk.java +++ b/Server/src/main/core/game/world/map/RegionChunk.java @@ -26,32 +26,32 @@ import static core.api.ContentAPIKt.log; * */ public class RegionChunk { - + /** * The chunk size. */ public static final int SIZE = 8; - + /** * The base location of the copied region chunk. */ protected Location base; - + /** * The current base location. */ protected Location currentBase; - + /** * The region plane. */ protected RegionPlane plane; - + /** * The items in this chunk. */ protected List items; - + /** * The scenerys in this chunk. */ @@ -61,12 +61,12 @@ public class RegionChunk { * The rotation. */ protected int rotation; - + /** * The update flags. */ private List> flags = new ArrayList<>(20); - + /** * Constructs a new {@code RegionChunk} {@code Object}. * @param base The base location of the region chunk. @@ -149,64 +149,76 @@ public class RegionChunk { } } } - ArrayList totalItems = items != null ? new ArrayList(items) : new ArrayList(); - - boolean drawChunks = player.getAttribute("chunkdraw", false); - boolean drawRegions = player.getAttribute("regiondraw", false); - - if (drawChunks) { - Location l = currentBase; - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - boolean add = false; - if (y == 0 || y == SIZE - 1) - add = true; - else if (x == 0 || x == SIZE - 1) - add = true; - if (add) - totalItems.add(new GroundItem(new Item(13444), l.transform(x, y, 0), player)); - } - } - } - - if (drawRegions) { - Location l = currentBase; - int localX = l.getLocalX(); - int localY = l.getLocalY(); - - for (int x = 0; x < SIZE; x++) - for (int y = 0; y < SIZE; y++) { - boolean add = false; - if (localY == 0 || localY == 56) - if (localY == 0 && y == 0) - add = true; - else if (localY == 56 && y == SIZE - 1) - add = true; - if (localX == 0 || localX == 56) - if (localX == 0 && x == 0) - add = true; - else if (localX == 56 && x == SIZE - 1) - add = true; - - if (add) - totalItems.add(new GroundItem(new Item(13444), l.transform(x,y,0), player)); - } - } - - if (totalItems != null) { - for (Item item : totalItems) { - if (item != null && item.isActive() && item.getLocation() != null) { - GroundItem g = (GroundItem) item; - if (!g.isPrivate() || g.droppedBy(player)) { - ConstructGroundItem.write(buffer, item); - updated = true; - } + ArrayList totalItems = drawItems(items, player); + for (GroundItem item : totalItems) { + if (item != null && item.isActive() && item.getLocation() != null) { + if (!item.isPrivate() || item.droppedBy(player)) { + ConstructGroundItem.write(buffer, item); + updated = true; } } } return updated; } + + public ArrayList drawItems(List items, Player player) { + ArrayList totalItems = items != null ? new ArrayList(items) : new ArrayList(); + + if (player.getAttribute("chunkdraw", false)) { + Location l = currentBase; + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + boolean add = false; + if (y == 0 || y == SIZE - 1) + add = true; + else if (x == 0 || x == SIZE - 1) + add = true; + if (add) + totalItems.add(new GroundItem(new Item(13444), l.transform(x, y, 0), player)); + } + } + } + + if (player.getAttribute("regiondraw", false)) { + Location l = currentBase; + int localX = l.getLocalX(); + int localY = l.getLocalY(); + + for (int x = 0; x < SIZE; x++) + for (int y = 0; y < SIZE; y++) { + boolean add = false; + if (localY == 0 || localY == 56) + if (localY == 0 && y == 0) + add = true; + else if (localY == 56 && y == SIZE - 1) + add = true; + if (localX == 0 || localX == 56) + if (localX == 0 && x == 0) + add = true; + else if (localX == 56 && x == SIZE - 1) + add = true; + + if (add) + totalItems.add(new GroundItem(new Item(13444), l.transform(x,y,0), player)); + } + } + + if (player.getAttribute("clippingdraw", false)) { + Location l = currentBase; + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + int flag = RegionManager.getClippingFlag(l.getZ(), l.getX() + x, l.getY() + y); + if (flag > 0) { + totalItems.add(new GroundItem(new Item(flag), l.transform(x, y, 0), player)); + } + } + } + } + + return totalItems; + } + /** * Sends all the update flags. */ @@ -222,7 +234,7 @@ public class RegionChunk { player.getSession().write(buffer); } } - + /** * Rotates the chunk. * @param direction The direction. @@ -267,7 +279,7 @@ public class RegionChunk { } } } - + /** * Gets the new coordinates for an object/chunk tile when rotating. * @param x The current x-coordinate. @@ -306,7 +318,7 @@ public class RegionChunk { } return items; } - + /** * Sets the items. * @param items The items to set. @@ -324,7 +336,7 @@ public class RegionChunk { public Scenery[] getObjects(int chunkX, int chunkY) { return new Scenery[] { objects[chunkX][chunkY] }; } - + /** * Gets the objects. * @return The objects. @@ -387,7 +399,7 @@ public class RegionChunk { public void resetFlags() { flags.clear(); } - + /** * Gets the region plane. * @return The plane. diff --git a/Server/src/main/core/game/world/map/RegionManager.kt b/Server/src/main/core/game/world/map/RegionManager.kt index 91ccf4f25..46022fd01 100644 --- a/Server/src/main/core/game/world/map/RegionManager.kt +++ b/Server/src/main/core/game/world/map/RegionManager.kt @@ -312,23 +312,37 @@ object RegionManager { if (owner == null || node == null) { return null } - var destination: Location? = null outer@ for (i in 0..7) { val dir = Direction.get(i) - inner@for(j in 0 until node.size()) { - val l = owner.location.transform(dir, j) - for (x in 0 until node.size()) { - for (y in 0 until node.size()) { - if (isClipped(l.transform(x, y, 0))) { - continue@inner - } + var stepX = dir.stepX + var stepY = dir.stepY + // For objects that are larger than 1, the below corrects for the fact that their origin is on the SW tile + if (dir.stepX < 0) { + stepX -= (node.size() - 1) + } + if (dir.stepY < 0) { + stepY -= (node.size() - 1) + } + if (owner.size() > 1) { //e.g. if you used ::pnpc to morph yourself into a large NPC + if (dir.stepX > 0) { + stepX += (owner.size() - 1) + } + if (dir.stepY > 0) { + stepY += (owner.size() - 1) + } + } + val l = owner.location.transform(stepX, stepY, 0) + // Check if ALL target tiles are unclipped + for (x in 0 until node.size()) { + for (y in 0 until node.size()) { + if (isClipped(l.transform(x, y, 0))) { + continue@outer } } - destination = l - break@outer } + return l } - return destination + return null } /** diff --git a/Server/src/main/core/game/world/map/zone/ZoneMonitor.java b/Server/src/main/core/game/world/map/zone/ZoneMonitor.java index c592b398c..65b4a0322 100644 --- a/Server/src/main/core/game/world/map/zone/ZoneMonitor.java +++ b/Server/src/main/core/game/world/map/zone/ZoneMonitor.java @@ -5,6 +5,7 @@ import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.music.MusicEntry; import core.game.node.entity.player.link.music.MusicZone; import core.game.node.entity.player.link.request.RequestType; import core.game.node.item.Item; @@ -15,6 +16,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import core.game.world.map.Region; import org.rs09.consts.Items; /** @@ -24,12 +26,12 @@ import org.rs09.consts.Items; public final class ZoneMonitor { /** - * The set of dragonstone teleport jewellery, which allow teleporting from up to and including level 30 wildy. - * Used to check if a player can teleport from 20 < level <= 30 wildy, see canTeleportByDragonstoneJewellery. + * The set of jewellery which allow teleporting from up to and including level 30 wildy. + * Used to check if a player can teleport from 20 < level <= 30 wildy, see canTeleportByJewellery. * Note: the check is based on the nextJewellery (see EnchantedJewellery.kt), so this list should not contain the (4) items, and should contain the empty ones. * @author Player Name */ - static final Set DRAGONSTONE_TELEPORT_JEWELLERY = Set.of( + static final Set MID_WILDY_TELEPORT_JEWELLERY = Set.of( Items.AMULET_OF_GLORY_1704, Items.AMULET_OF_GLORY1_1706, Items.AMULET_OF_GLORY2_1708, @@ -49,7 +51,8 @@ public final class ZoneMonitor { Items.RING_OF_WEALTH_14638, Items.RING_OF_WEALTH1_14640, Items.RING_OF_WEALTH2_14642, - Items.RING_OF_WEALTH3_14644 + Items.RING_OF_WEALTH3_14644, + Items.RING_OF_LIFE_2570 ); /** @@ -235,7 +238,7 @@ public final class ZoneMonitor { * @return {@code True} if so. */ public boolean teleport(int type, Node node) { - if (type != -1 && entity.isTeleBlocked() && !canTeleportByDragonstoneJewellery(type, node)) { + if (type != -1 && entity.isTeleBlocked() && !canTeleportByJewellery(type, node)) { if (entity.isPlayer()) { entity.asPlayer().sendMessage("A magical force has stopped you from teleporting."); } @@ -250,11 +253,11 @@ public final class ZoneMonitor { } /** - * Checks if a player can teleport with a dragonstone jewellery piece in >= 1 <= 30 wilderness level + * Checks if a player can teleport with a jewellery piece in >= 1 <= 30 wilderness level * @return {@code True} if so. */ - private boolean canTeleportByDragonstoneJewellery(int type, Node node) { - if (type != 1 || !DRAGONSTONE_TELEPORT_JEWELLERY.contains(node.asItem().getId())) { + private boolean canTeleportByJewellery(int type, Node node) { + if (type != 1 || !MID_WILDY_TELEPORT_JEWELLERY.contains(node.asItem().getId())) { return false; } if (entity.timers.getTimer("teleblock") != null) @@ -395,21 +398,25 @@ public final class ZoneMonitor { for (Iterator it = musicZones.iterator(); it.hasNext();) { MusicZone zone = it.next(); if (!zone.getBorders().insideBorder(l.getX(), l.getY())) { - zone.leave(player, false); - it.remove(); + if (zone.leave(player, false)) { + it.remove(); + } } } - for (MusicZone zone : player.getViewport().getRegion().getMusicZones()) { - if (!zone.getBorders().insideBorder(l.getX(), l.getY())) { - continue; - } - if (!musicZones.contains(zone)) { + Region r = player.getViewport().getRegion(); + for (MusicZone zone : r.getMusicZones()) { + if (zone.getBorders().insideBorder(l.getX(), l.getY())) { zone.enter(player); - musicZones.add(zone); + return; } } - if (musicZones.isEmpty() && !player.getMusicPlayer().isPlaying()) { - player.getMusicPlayer().playDefault(); + int music = r.getMusic(); + if (music == -1) { + if (!player.getMusicPlayer().isPlaying()) { + player.getMusicPlayer().playDefault(); + } + } else { + player.getMusicPlayer().unlock(music, true); } } diff --git a/Server/src/main/core/game/world/map/zone/ZoneRestriction.java b/Server/src/main/core/game/world/map/zone/ZoneRestriction.java index f73b50014..64dd971db 100644 --- a/Server/src/main/core/game/world/map/zone/ZoneRestriction.java +++ b/Server/src/main/core/game/world/map/zone/ZoneRestriction.java @@ -31,7 +31,7 @@ public enum ZoneRestriction { */ CANNON, /** - * Do not spawn a grave if a player dies here + * Do not spawn a grave if a player dies here. */ GRAVES, @@ -39,6 +39,14 @@ public enum ZoneRestriction { * No teleporting allowed. */ TELEPORT, + + /** + * This region is not a part of the normal overworld or cave system. + * Used for temporary areas that use the 'original-loc' attribute to teleport the player back when they are done in the area. + * Example: non-dynamic/non-instanced random-event areas (e.g. Damien's bootcamp) + * Dynamic regions are implicitly off-map and do not require this attribute. + */ + OFF_MAP, ; /** @@ -48,4 +56,4 @@ public enum ZoneRestriction { public int getFlag() { return 1 << ordinal(); } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/zone/impl/DarkZone.java b/Server/src/main/core/game/world/map/zone/impl/DarkZone.java index 1340dcde2..5d955031a 100644 --- a/Server/src/main/core/game/world/map/zone/impl/DarkZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/DarkZone.java @@ -85,6 +85,7 @@ public final class DarkZone extends MapZone implements EventHook{ public void configure() { register(new ZoneBorders(1728, 5120, 1791, 5247)); registerRegion(12693); + registerRegion(12948); registerRegion(12949); register(new ZoneBorders(3306,9661,3222,9600)); register(new ZoneBorders(3717,9473,3841,9346)); diff --git a/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java b/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java index f2b5a5886..77477b3b1 100644 --- a/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java @@ -85,6 +85,9 @@ public final class MultiwayCombatZone extends MapZone { register(new ZoneBorders(3097, 4224, 3225, 4317)); register(new ZoneBorders(3116, 5412, 3362, 5584)); register(new ZoneBorders(3078, 5520, 3123, 5552, 0)); + // Ice queen + register(new ZoneBorders(2855, 9928, 2880, 9968)); + registerRegion(11318); //White wolf mountain registerRegion(11844); //Corporeal beast registerRegion(10329);//TDS registerRegion(13370);//Venenatis diff --git a/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java b/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java index 6b1f27270..623f2f080 100644 --- a/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java @@ -2,7 +2,6 @@ package core.game.world.map.zone.impl; import content.global.handlers.item.equipment.brawling_gloves.BrawlingGloves; import content.global.skill.summoning.familiar.Familiar; -import content.region.wilderness.handlers.DeepWildyThreat; import core.game.component.Component; import core.game.interaction.Option; import core.game.node.Node; @@ -23,6 +22,7 @@ import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneRestriction; import core.game.world.repository.Repository; import core.tools.RandomFunction; +import org.rs09.consts.Items; import org.rs09.consts.NPCs; @@ -41,8 +41,8 @@ public final class WildernessZone extends MapZone { */ private static final WildernessZone INSTANCE = new WildernessZone(new ZoneBorders(2944, 3525, 3400, 3975), new ZoneBorders(3070, 9924, 3135, 10002), ZoneBorders.forRegion(12192), ZoneBorders.forRegion(12193), ZoneBorders.forRegion(11937)); - public static final String WILDERNESS_PROT_ATTR = "/save:wilderness-protection-active"; - public static final String WILDERNESS_HIGHER_NEXTFEE = "/save:wilderness-higher-next-fee"; + public static final String WILDERNESS_PROT_ATTR = "/save:wilderness-protection-active"; + public static final String WILDERNESS_HIGHER_NEXTFEE = "/save:wilderness-higher-next-fee"; /** * The zone borders. @@ -92,50 +92,39 @@ public final class WildernessZone extends MapZone { } private void rollWildernessExclusiveLoot(Entity e, Entity killer) { - if (!(killer instanceof Player)) return; + if (!(killer instanceof Player)) return; + if (!(e instanceof NPC)) return; + if (!(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 || e.asNpc().getName().contains("Revenant"))) return; - boolean isDeepWildy = ((Player) killer).getSkullManager().isDeepWilderness(); - boolean isRevOrCele = e.asNpc().getName().contains("Revenant") || e.getId() == NPCs.CHAOS_ELEMENTAL_3200; - boolean isSufficientRisk = ((Player) killer).getAttribute("deepwild-value-risk", 0L) > SkullManager.DEEP_WILD_DROP_RISK_THRESHOLD; - boolean isValidTarget = e instanceof NPC && ((isDeepWildy && isSufficientRisk) || isRevOrCele); + int pvpGearRate = getNewDropRate(e.asNpc().getDefinition().getCombatLevel()); + boolean higherRate = ((Player) killer).getSkullManager().isDeepWilderness() && ((Player) killer).getAttribute("deepwild-value-risk", 0L) > SkullManager.DEEP_WILD_DROP_RISK_THRESHOLD; + if (higherRate) { + pvpGearRate /= 2; + } + int cEleGloveRate = 50; + int normalGloveRate = higherRate ? 100 : (int) ((1.0 / (1.0 - Math.pow(1.0 - (1.0 / (double) pvpGearRate), 16.0))) * 5.0 / 6.0); - if (isDeepWildy) { - DeepWildyThreat.adjustThreat((Player) killer, 50); + if (RandomFunction.roll(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? cEleGloveRate : normalGloveRate)) { + byte glove = (byte) RandomFunction.random(1, 14); + Item reward = new Item(BrawlingGloves.forIndicator(glove).getId()); + GroundItemManager.create(reward, e.asNpc().getDropLocation(), killer.asPlayer()); + String npcString = e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? "the Chaos Elemental" : ("a " + e.asNpc().getName().toLowerCase()); + Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from " + npcString + "!"); + } + for (int j : PVP_GEAR) { + boolean chance = RandomFunction.roll(pvpGearRate); + if (chance) { + Item reward; + if (j == Items.MORRIGANS_JAVELIN_13879 || j == Items.MORRIGANS_THROWING_AXE_13883) { + reward = new Item(j, RandomFunction.random(15, 50)); + } else { + reward = new Item(j); } - - if (!isValidTarget) return; - - int pvpGearRate = getNewDropRate(e.asNpc().getDefinition().getCombatLevel()); - if (isDeepWildy && isRevOrCele) - pvpGearRate /= 2; - - int cEleGloveRate = isDeepWildy ? 50 : 150; - int normalGloveRate = isDeepWildy && isRevOrCele ? 100 : (int)((1.0/(1.0-Math.pow(1.0 - (1.0/(double)pvpGearRate), 16.0))) * 5.0 / 6.0); - - if (RandomFunction.roll(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? cEleGloveRate : normalGloveRate)) { - byte glove = (byte) RandomFunction.random(1, 13); - Item reward = new Item(BrawlingGloves.forIndicator(glove).getId()); - GroundItemManager.create(reward, e.asNpc().getDropLocation(), killer.asPlayer()); - Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from a " + e.asNpc().getName() + "!"); - if (isDeepWildy) - DeepWildyThreat.adjustThreat((Player) killer, 750); - } - - for (int j : PVP_GEAR) { - boolean chance = RandomFunction.roll(pvpGearRate); - if (chance) { - Item reward; - if (j == 13879 || j == 13883) { // checks if it's a javelin or throwing axe - reward = new Item(j, RandomFunction.random(15, 50)); - } else { - reward = new Item(j); - } - Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from a " + e.asNpc().getName() + "!"); - GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer()); - if (isDeepWildy) - DeepWildyThreat.adjustThreat((Player) killer, 3000); - } - } + GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer()); + String npcString = e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? "the Chaos Elemental" : ("a " + e.asNpc().getName().toLowerCase()); + Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from " + npcString + "!"); + } + } } /** @@ -156,8 +145,6 @@ public final class WildernessZone extends MapZone { return super.interact(e, target, option); } - - @Override public boolean enter(Entity e) { if (e instanceof Player) { @@ -168,17 +155,6 @@ public final class WildernessZone extends MapZone { p.getSkullManager().setWilderness(true); p.getSkullManager().setLevel(getWilderness(p)); } - for (int i = 0; i < 7; i++) { - if (i == 5 || i == 3) { - continue; - } - if(p.getAttributes().containsKey("overload") || p.getSkills().getLevel(i) > 118){ - if (p.getSkills().getLevel(i) > p.getSkills().getStaticLevel(i)) { - p.getSkills().setLevel(i, p.getSkills().getStaticLevel(i)); - p.removeAttribute("overload"); - } - } - } if (p.getFamiliarManager().hasFamiliar() && !p.getFamiliarManager().hasPet()) { Familiar familiar = p.getFamiliarManager().getFamiliar(); familiar.transform(); @@ -324,12 +300,12 @@ public final class WildernessZone extends MapZone { * @return the level. */ public static int getWilderness(Entity e) { - int y = e.getLocation().getY(); - if(6400 < y) { - return ((y - 9920) / 8) + 1; - } else { - return ((y - 3520) / 8) + 1; - } + int y = e.getLocation().getY(); + if(6400 < y) { + return ((y - 9920) / 8) + 1; + } else { + return ((y - 3520) / 8) + 1; + } } /** diff --git a/Server/src/main/core/game/world/repository/DisconnectionQueue.kt b/Server/src/main/core/game/world/repository/DisconnectionQueue.kt index b1fcf12ea..dd0c7ee98 100644 --- a/Server/src/main/core/game/world/repository/DisconnectionQueue.kt +++ b/Server/src/main/core/game/world/repository/DisconnectionQueue.kt @@ -4,11 +4,9 @@ import core.api.log import core.game.node.entity.player.Player import core.game.node.entity.player.info.login.PlayerParser import core.game.system.task.TaskExecutor -import core.tools.SystemLogger import core.game.world.GameWorld import core.tools.Log -import java.util.* -import java.util.concurrent.ConcurrentHashMap +import core.tools.secondsToTicks /** * Handles disconnecting players queuing. @@ -37,7 +35,10 @@ class DisconnectionQueue { else { //Make sure there's no room for the disconnection queue to stroke out and leave someone logged in for 10 years. queueTimers[it.key] = (queueTimers[it.key] ?: 0) + 3 - if ((queueTimers[it.key] ?: Int.MAX_VALUE) >= 1500) { + val isValidAFKLogout = it.value?.player?.isAfkLogout == true + val seconds = if (isValidAFKLogout) 30 else 5 * 60 //30 seconds for AFK logout, 5 minutes for normal logout + val ticksNeeded = secondsToTicks(seconds) + if ((queueTimers[it.key] ?: Int.MAX_VALUE) >= ticksNeeded) { it.value?.player?.let { player -> player.finishClear() Repository.removePlayer(player) diff --git a/Server/src/main/core/game/world/repository/Repository.kt b/Server/src/main/core/game/world/repository/Repository.kt index 575bcb0f2..81d44d5f4 100644 --- a/Server/src/main/core/game/world/repository/Repository.kt +++ b/Server/src/main/core/game/world/repository/Repository.kt @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player import core.game.world.map.Location import core.game.world.map.RegionManager import core.ServerConstants +import core.api.sendMessage import core.game.world.update.UpdateSequence import java.util.* import java.util.concurrent.CopyOnWriteArrayList @@ -55,20 +56,7 @@ object Repository { */ @JvmStatic val disconnectionQueue = DisconnectionQueue() - /** - * Sends a market update message to all players. - * @param string The string. - * @param color The color. - */ - @JvmOverloads - fun sendMarketUpdate(string: String, icon: Int = 12, color: String = "") { - val players: Array = playerNames.values.toTypedArray() - val size = players.size - for (i in 0 until size) { - val player = players[i] as Player ?: continue - player.sendMessage("" + color + "Market Update: " + string) - } - } + /** * Send a news message to all players. * @param string The string. @@ -76,11 +64,12 @@ object Repository { */ @JvmStatic fun sendNews(string: String, icon: Int = 12, color: String = "CC6600") { + if (!ServerConstants.ENABLE_GLOBAL_CHAT) return val players: Array = playerNames.values.toTypedArray() val size = players.size for (i in 0 until size) { val player = players[i] as Player ?: continue - player.sendMessage("News: $string") + sendMessage(player, "News: $string") } } diff --git a/Server/src/main/core/game/worldevents/WorldEvent.kt b/Server/src/main/core/game/worldevents/WorldEvent.kt index 98f66aaf3..705d458cd 100644 --- a/Server/src/main/core/game/worldevents/WorldEvent.kt +++ b/Server/src/main/core/game/worldevents/WorldEvent.kt @@ -1,42 +1,33 @@ package core.game.worldevents -import core.tools.SystemLogger -import core.plugin.Plugin -import org.json.simple.JSONObject import core.ServerStore +import core.api.ContentInterface import core.plugin.ClassScanner +import core.plugin.Plugin import core.tools.Log +import org.json.simple.JSONObject import java.util.* /** * The class other world events should extend off of. * @author Ceikry */ -open class WorldEvent(var name: String) { +abstract class WorldEvent(var name: String) : ContentInterface { var plugins = PluginSet() /** * if the event is active or not. Can be used to check dates or just always return true * whatever you need for this specific event */ - open fun checkActive(): Boolean { + open fun checkActive(cal: Calendar): Boolean { return false } /** - * Check to see if the event should trigger. An event trigger could spawn shooting stars, or a world boss - * or whatever kind of whacky shit you need it to do. - */ - open fun checkTrigger(): Boolean{ - return true - } - - /** - * Used to initialize the event, which loads all associated plugins. + * Used to initialize the event * The WorldEventInitializer runs this if checkActive() returns true. */ - open fun initialize(){ - plugins.initialize() + open fun initEvent(){ } /** @@ -45,11 +36,6 @@ open class WorldEvent(var name: String) { fun log(message: String){ core.api.log(this::class.java, Log.FINE, "[World Events($name)] $message") } - - /** - * Used to start, or "fire", world events. - */ - open fun fireEvent() {} } @@ -74,11 +60,11 @@ object WorldEvents { private var events = hashMapOf() fun add(event: WorldEvent){ - events.put(event.name.toLowerCase(),event) + events[event.name.lowercase(Locale.getDefault())] = event } fun get(name: String): WorldEvent?{ - return events.get(name.toLowerCase()) + return events[name.lowercase(Locale.getDefault())] } fun getArchive() : JSONObject { diff --git a/Server/src/main/core/game/worldevents/WorldEventInitializer.kt b/Server/src/main/core/game/worldevents/WorldEventInitializer.kt deleted file mode 100644 index 2e0c7b5cb..000000000 --- a/Server/src/main/core/game/worldevents/WorldEventInitializer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package core.game.worldevents - -import core.plugin.Initializable -import core.plugin.Plugin -import io.github.classgraph.ClassGraph -import io.github.classgraph.ClassInfo -import java.util.function.Consumer - -/** - * Responsible for initializing world events - * @author Ceikry - */ -@Initializable -class WorldEventInitializer : Plugin{ - override fun newInstance(arg: Any?): Plugin { - /** - * Here I used ClassGraph to scan the core.game.content.global.worldevents package for subclasses of the WorldEvent class and - * then initialize them if their active setting evaluates to true. This makes it so we don't have to manually add them to - * a list. It also prevents unnecessary plugins from being loaded if an event isn't currently active. - */ - - val result = ClassGraph().enableClassInfo().whitelistPackages("rs09.game.content.global.worldevents").scan() - result.getSubclasses("rs09.game.content.global.worldevents.WorldEvent").forEach(Consumer { p: ClassInfo -> - val c = p.loadClass().newInstance() as WorldEvent - if(c.checkActive()) c.initialize().also { WorldEvents.add(c) } - }) - return this - } - - override fun fireEvent(identifier: String?, vararg args: Any?): Any { - return Unit - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/HolidayRandoms.kt b/Server/src/main/core/game/worldevents/holiday/HolidayRandoms.kt index 6ad5ed6f2..cf63a2e6d 100644 --- a/Server/src/main/core/game/worldevents/holiday/HolidayRandoms.kt +++ b/Server/src/main/core/game/worldevents/holiday/HolidayRandoms.kt @@ -75,10 +75,10 @@ class HolidayRandoms() : PersistTimer(0, "holiday", isAuto = true), Commands { fun checkIfHoliday(): String { val currentDate = LocalDate.now() - if ((!currentDate.isBefore(halloweenStartDate) && !currentDate.isAfter(halloweenEndDate)) || ServerConstants.FORCE_HALLOWEEN_RANDOMS) + if ((!currentDate.isBefore(halloweenStartDate) && !currentDate.isAfter(halloweenEndDate)) || ServerConstants.FORCE_HALLOWEEN_EVENTS) return "halloween" - if ((!currentDate.isBefore(christmasStartDate) && !currentDate.isAfter(christmasEndDate)) || ServerConstants.FORCE_CHRISTMAS_RANDOMS) + if ((!currentDate.isBefore(christmasStartDate) && !currentDate.isAfter(christmasEndDate)) || ServerConstants.FORCE_CHRISTMAS_EVENTS) return "christmas" return "none" @@ -164,7 +164,7 @@ class HolidayRandoms() : PersistTimer(0, "holiday", isAuto = true), Commands { ServerConstants.HOLIDAY_EVENT_RANDOMS = true when (event) { "halloween" -> { - ServerConstants.FORCE_HALLOWEEN_RANDOMS = true + ServerConstants.FORCE_HALLOWEEN_EVENTS = true for (p in Repository.players) { if (getTimer(p) != null || p.isArtificial) { continue @@ -174,7 +174,7 @@ class HolidayRandoms() : PersistTimer(0, "holiday", isAuto = true), Commands { } } "christmas" -> { - ServerConstants.FORCE_CHRISTMAS_RANDOMS = true + ServerConstants.FORCE_CHRISTMAS_EVENTS = true for (p in Repository.players) { if (getTimer(p) != null || p.isArtificial) { continue @@ -191,8 +191,8 @@ class HolidayRandoms() : PersistTimer(0, "holiday", isAuto = true), Commands { if (checkIfHoliday() == "none" || !ServerConstants.HOLIDAY_EVENT_RANDOMS) reject(player, "No holiday random events are currently active.") ServerConstants.HOLIDAY_EVENT_RANDOMS = false - ServerConstants.FORCE_HALLOWEEN_RANDOMS = false - ServerConstants.FORCE_CHRISTMAS_RANDOMS = false + ServerConstants.FORCE_HALLOWEEN_EVENTS = false + ServerConstants.FORCE_CHRISTMAS_EVENTS = false for (p in Repository.players) { if (getTimer(p) == null) { continue diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomDialogue.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomDialogue.kt index 79991e5bb..f0b5a76c3 100644 --- a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomDialogue.kt +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomDialogue.kt @@ -1,6 +1,5 @@ package core.game.worldevents.holiday.christmas.randoms -import core.api.addItem import core.api.addItemOrDrop import core.api.getAttribute import core.game.dialogue.DialogueFile diff --git a/Server/src/main/core/game/worldevents/holiday/easter/EasterBunnyDialogueFile.kt b/Server/src/main/core/game/worldevents/holiday/easter/EasterBunnyDialogueFile.kt deleted file mode 100644 index d8b1d98cb..000000000 --- a/Server/src/main/core/game/worldevents/holiday/easter/EasterBunnyDialogueFile.kt +++ /dev/null @@ -1,106 +0,0 @@ -package core.game.worldevents.holiday.easter - -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.link.emote.Emotes -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import org.rs09.consts.Items -import core.game.dialogue.DialogueFile -import core.game.worldevents.WorldEvents -import core.tools.END_DIALOGUE - -class EasterBunnyDialogueFile(val NEED_BASKET : Boolean) : DialogueFile() { - val EGG_ATTRIBUTE = "/save:easter:eggs" - - override fun handle(componentID: Int, buttonID: Int) { - if(NEED_BASKET){ - when(stage++){ - 0 -> npc("Hello, adventurer! Thank goodness you're here!") - 1 -> player(FacialExpression.THINKING,"Thanks goodness I'M here?") - 2 -> npc("Yes, yes, I need your help, you see!") - 3 -> npc("I have lost ALL of my eggs. What a terrible thing.") - 4 -> npc("Us easter bunnies rely on EGGS to live.") - 5 -> npc("Take this basket, please, and do me a kindness.") - 6 -> player(FacialExpression.THINKING,"What kindness might that be?") - 7 -> npc("I need you to try and gather up as many of my","lost eggs as you can.") - 8 -> npc("Please, for me? I will reward you for your time.") - 9 -> player("Fine, I suppose I will.") - 10 -> { - player!!.inventory.add(Item(Items.BASKET_OF_EGGS_4565)) - player!!.dialogueInterpreter.sendItemMessage(Items.BASKET_OF_EGGS_4565,"The Easter Bunny gives you a basket.") - if(!player!!.musicPlayer.hasUnlocked(273)) { - player!!.musicPlayer.unlock(273, true) - } - stage = END_DIALOGUE - } - } - } else { - - when (stage) { - 0 -> options("Ask about rewards", "Ask about egg location").also { stage++ } - 1 -> when (buttonID) { - 1 -> player(FacialExpression.THINKING, "What kind of rewards can I claim?").also { stage = 10 } - 2 -> player(FacialExpression.THINKING, "Where were some eggs last seen?").also { stage = 20 } - } - - 10 -> { - if (!player!!.emoteManager.isUnlocked(Emotes.BUNNY_HOP)) { - options("A credit (5 eggs)", "A Book of Knowledge (20 eggs)", "Bunny Hop Emote (30 eggs)") - } else { - options("A credit (5 eggs)", "A Book of Knowledge (20 eggs)") - } - stage++ - } - - 11 -> { - end() - when (buttonID) { - 1 -> { - if (player!!.getAttribute(EGG_ATTRIBUTE, 0) < 5) { - player!!.dialogueInterpreter.sendDialogue("You need 5 eggs to afford that.") - } else { - player!!.incrementAttribute(EGG_ATTRIBUTE, -5) - player!!.details.accountInfo.credits += 1 - player!!.dialogueInterpreter.sendDialogue( - "You turn in 5 eggs in exchange for a credit.", - "You now have ${player!!.getAttribute(EGG_ATTRIBUTE, 0)} eggs." - ) - } - } - - 2 -> { - if (player!!.getAttribute(EGG_ATTRIBUTE, 0) < 20) { - player!!.dialogueInterpreter.sendDialogue("You need 20 eggs to afford that.") - } else { - player!!.incrementAttribute(EGG_ATTRIBUTE, -20) - val item = Item(Items.BOOK_OF_KNOWLEDGE_11640) - if (!player!!.inventory.add(item)) { - GroundItemManager.create(item, player) - } - player!!.dialogueInterpreter.sendDialogue("You spend 20 eggs on a Book of Knowledge.") - } - } - - 3 -> { - if (player!!.getAttribute(EGG_ATTRIBUTE, 0) < 30) { - player!!.dialogueInterpreter.sendDialogue("You need 30 eggs to afford that.") - } else { - player!!.incrementAttribute(EGG_ATTRIBUTE, -30) - player!!.emoteManager.unlock(Emotes.BUNNY_HOP) - player!!.dialogueInterpreter.sendDialogue( - "The Easter Bunny teaches you how to bunny hop in exchange", - "for 30 eggs." - ) - } - } - - } - } - 20 ->{ - val event = WorldEvents.get("easter") as EasterEvent - npc("The last known location of some eggs is ${event.recentLoc}.").also { stage = END_DIALOGUE } - } - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt b/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt index 63a904a72..6b129b906 100644 --- a/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt +++ b/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt @@ -1,117 +1,320 @@ package core.game.worldevents.holiday.easter -import core.game.node.entity.npc.NPC +import core.ServerConstants +import core.api.* +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +import core.game.event.EventHook +import core.game.event.XPGainEvent +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.emote.Emotes import core.game.node.item.GroundItem import core.game.node.item.GroundItemManager import core.game.node.item.Item -import core.game.system.task.Pulse +import core.game.world.GameWorld import core.game.world.map.Direction import core.game.world.map.Location -import org.rs09.consts.Items -import org.rs09.consts.NPCs +import core.game.world.map.path.Pathfinder import core.game.worldevents.WorldEvent -import core.game.system.config.GroundSpawnLoader -import core.game.world.GameWorld -import core.game.world.repository.Repository -import core.tools.secondsToTicks +import core.tools.RandomFunction +import core.tools.StringUtils +import core.tools.colorize +import org.rs09.consts.Items import java.util.* -class EasterEvent : WorldEvent("easter") { - val LUMBRIDGE_SPOTS = arrayOf(Location.create(3190, 3240, 0), - Location.create(3219, 3204, 0),Location.create(3212, 3201, 0),Location.create(3205, 3209, 0), - Location.create(3205, 3217, 0),Location.create(3211, 3213, 0),Location.create(3206, 3229, 0), - Location.create(3212, 3226, 0),Location.create(3212, 3244, 0),Location.create(3202, 3252, 0), - Location.create(3197, 3220, 0),Location.create(3189, 3207, 0),Location.create(3229, 3200, 0), - Location.create(3228, 3205, 0),Location.create(3251, 3212, 0),Location.create(3232, 3237, 0)) +class EasterEvent : WorldEvent("easter"), TickListener, InteractionListener, LoginListener, Commands { + private val spawnedItems = ArrayList() + private var timeUntilNextEggSpawn = 0 + private var currentLoc = "" - val DRAYNOR_SPOTS = arrayOf(Location.create(3085, 3242, 0),Location.create(3083, 3236, 0),Location.create(3093, 3224, 0), - Location.create(3099, 3241, 0),Location.create(3095, 3255, 0),Location.create(3089, 3264, 0),Location.create(3089, 3265, 0), - Location.create(3088, 3268, 0),Location.create(3090, 3274, 0),Location.create(3100, 3281, 0),Location.create(3116, 3265, 0), - Location.create(3123, 3272, 0),Location.create(3079, 3261, 0)) + override fun tick() { + if (timeUntilNextEggSpawn-- > 0) return + timeUntilNextEggSpawn = EGG_SPAWN_TIME - val FALADOR_SPOTS = arrayOf( - Location.create(2961, 3332, 0),Location.create(2967, 3336, 0),Location.create(2974, 3329, 0), - Location.create(2979, 3346, 0),Location.create(2970, 3348, 0),Location.create(2955, 3375, 0),Location.create(2942, 3386, 0), - Location.create(2937, 3385, 0),Location.create(3005, 3370, 0),Location.create(3006, 3383, 0),Location.create(3007, 3387, 0), - Location.create(2985, 3391, 0),Location.create(2984, 3381, 0),Location.create(2980, 3384, 0),Location.create(3025, 3345, 0), - Location.create(3060, 3389, 0),Location.create(3052, 3385, 0)) + for (egg in spawnedItems) + { + GroundItemManager.destroy(egg) + } + spawnedItems.clear() - val EDGEVILLE_SPOTS = arrayOf(Location.create(3077, 3487, 0),Location.create(3082, 3487, 0), - Location.create(3089, 3481, 0),Location.create(3084, 3479, 0),Location.create(3108, 3499, 0), - Location.create(3110, 3517, 0),Location.create(3091, 3512, 0),Location.create(3092, 3507, 0), - Location.create(3081, 3513, 0),Location.create(3079, 3513, 1),Location.create(3080, 3508, 1), - Location.create(3108, 3499, 0),Location.create(3093, 3467, 0)) + val (locName, locData) = getRandomLocations() + currentLoc = locName - val TREE_GNOME_STRONGHOLD_SPOTS = arrayOf( - Location.create(2480, 3507, 0),Location.create(2486, 3513, 0),Location.create(2453, 3512, 0), - Location.create(2442, 3484, 0),Location.create(2438, 3486, 0),Location.create(2441, 3506, 0), - Location.create(2471, 3482, 0),Location.create(2482, 3478, 0),Location.create(2480, 3469, 0), - Location.create(2489, 3440, 0),Location.create(2470, 3417, 0),Location.create(2478, 3402, 0), - Location.create(2486, 3407, 0),Location.create(2492, 3404, 0),Location.create(2493, 3413, 0), - Location.create(2446, 3395, 0),Location.create(2422, 3398, 0),Location.create(2421, 3402, 0),Location.create(2418, 3398, 0), - Location.create(2401, 3415, 0),Location.create(2397, 3436, 0),Location.create(2409, 3448, 0),Location.create(2482, 3391, 0)) + for (loc in locData) + { + val gi = GroundItemManager.create(GroundItem( + Item(eggs.random()), + loc, + EGG_SPAWN_TIME, + null + )) + gi.forceVisible = true + spawnedItems.add(gi) + } - val locations = arrayOf("Lumbridge","Draynor Village","Falador","Edgeville","Tree Gnome Stronghold") - - val spawnedItems = ArrayList() - val eggs = arrayOf(Items.EASTER_EGG_11027,Items.EASTER_EGG_11028,Items.EASTER_EGG_11029,Items.EASTER_EGG_11030) - var recentLoc = "" - - override fun checkActive(): Boolean { - val isApril = Calendar.getInstance().get(Calendar.MONTH) == Calendar.APRIL - val isBefore9th = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) < 9 - return false + sendNews("Eggs have appeared in $locName!") } - override fun initialize() { - super.initialize() - val easterBunny = NPC(NPCs.EASTER_BUNNY_7197) - easterBunny.isNeverWalks = true - easterBunny.direction = Direction.WEST - easterBunny.location = Location.create(3225, 3212, 0) - for(i in 0 until 5){ - val bunny = NPC(NPCs.EASTER_BUNNY_3688) - bunny.location = Location.create(3223, 3213, 0) - bunny.isWalks = true - bunny.walkRadius = 4 - bunny.init() + override fun login(player: Player) { + if (spawnedItems.isNotEmpty()) + sendMessage(player, colorize("%GEggs were last spotted in $currentLoc!")) + player.hook(Event.XpGained, xpEventHook) + } + + override fun defineCommands() { + define("eggspawntest") {player, _ -> + notify(player, "Spawning 10 eggs nearby...") + repeat(10) { spawnEggFor(player) } } - easterBunny.init() - GameWorld.settings?.message_model = 715 - GameWorld.settings?.message_string = "Happy Easter!" - GameWorld.Pulser.submit(object : Pulse(){ - override fun pulse(): Boolean { - if(delay == 1){ - delay = secondsToTicks(if(GameWorld.settings?.isDevMode == true) 60 else 3600) - } - for(item in spawnedItems){ - GroundItemManager.destroy(item) - } - spawnedItems.clear() - val locName = locations.random() - val locs = when(locName){ - "Lumbridge" -> LUMBRIDGE_SPOTS - "Draynor Village" -> DRAYNOR_SPOTS - "Falador" -> FALADOR_SPOTS - "Edgeville" -> EDGEVILLE_SPOTS - "Tree Gnome Stronghold" -> TREE_GNOME_STRONGHOLD_SPOTS - else -> LUMBRIDGE_SPOTS - }.toMutableList() + } - Repository.sendNews("A bunch of eggs have been spotted in $locName!") - recentLoc = locName - val itemLocs = ArrayList() - for(i in 0 until locs.size / 2){ - val loc = locs.random() - locs.remove(loc) - itemLocs.add(loc) - } + object xpEventHook : EventHook + { + override fun process(entity: Entity, event: XPGainEvent) { + if (entity !is Player) return - for(i in itemLocs){ - spawnedItems.add(GroundSpawnLoader.GroundSpawn(-1, Item(eggs.random()),i).init()) - } - return false + val lastRoll = getAttribute(entity, LAST_EGG_ROLL, 0) + if (getWorldTicks() - lastRoll < 10) return + setAttribute(entity, LAST_EGG_ROLL, getWorldTicks()) + + val activeEggRate = if (GameWorld.settings!!.isDevMode) EGG_RATE_DEV else EGG_RATE + + if (RandomFunction.roll(activeEggRate)) + spawnEggFor(entity) + } + } + + + fun getRandomLocations() : Pair> + { + val toReturn = ArrayList() + val name = locNames.random() + val locs = locationsForName(name) + for (loc in locs) + if (RandomFunction.nextBool()) toReturn.add(loc) + return Pair(name, toReturn) + } + + override fun checkActive(cal: Calendar): Boolean { + return cal.get(Calendar.MONTH) == Calendar.APRIL || ServerConstants.FORCE_EASTER_EVENTS + } + + private fun onEggBroken (player: Player) + { + val eggsBroken = getAttribute(player, EGGS_BROKEN, 0) + 1 + + if (eggsBroken == 10) + { + player.emoteManager.unlock(Emotes.BUNNY_HOP) + Emotes.BUNNY_HOP.play(player) + sendMessage(player, colorize("%RYou have unlocked the 'Bunny Hop' emote!")) + } + else if (eggsBroken == 15) + { + addItemOrDrop(player, BUN_EARS) + sendMessage(player, colorize("%RYou have been given bunny ears!")) + } + else if (eggsBroken % 5 == 0) + { + var trackUnlocked = false; + for (track in tracks) + { + if (player.musicPlayer.hasUnlocked(track)) + continue + player.musicPlayer.unlock(track) + trackUnlocked = true + break } - }) + if (!trackUnlocked) + giveRandomLoot(player) + } + else giveRandomLoot(player) + + setAttribute(player, EGGS_BROKEN, eggsBroken) + } + + private fun giveRandomLoot (player: Player) + { + //Give some loot + val loot = lootTable.roll(player)[0] + addItemOrDrop(player, loot.id, loot.amount) + val term = if (loot.amount == 1) + if (StringUtils.isPlusN(loot.name)) "an" + else "a" + else "some" + + val name = if (loot.amount == 1) loot.name else StringUtils.plusS(loot.name) + sendMessage(player, "Inside the egg you find $term ${name.lowercase()}.") + } + + override fun defineListeners() { + on (eggs, IntType.GROUNDITEM, "take") {player, node -> + if (node !is GroundItem) return@on true + queueScript(player, strength = QueueStrength.NORMAL) { stage -> + when (stage) + { + 0 -> { + animate(player, STOMP_ANIM) + return@queueScript delayScript(player, 1) + } + 1 -> { + val item = GroundItemManager.get(node.id, node.location, null) + if (item == null || !item.isActive) return@queueScript stopExecuting(player) + sendGraphics(gfxForEgg(node.id), node.location) + GroundItemManager.destroy(node) + spawnedItems.remove(node) + onEggBroken(player) + return@queueScript stopExecuting(player) + } + } + return@queueScript keepRunning(player) + } + return@on true + } + on (eggs, IntType.ITEM, "release") {player, node -> + if (node !is Item) return@on true + queueScript(player, strength = QueueStrength.NORMAL) { stage -> + when (stage) + { + 0 -> { + animate(player, STOMP_ANIM) + return@queueScript delayScript(player, 1) + } + 1 -> { + if (!removeItem(player, node)) return@queueScript stopExecuting(player) + sendGraphics(gfxForEgg(node.id), player.location) + onEggBroken(player) + return@queueScript stopExecuting(player) + } + } + return@queueScript keepRunning(player) + } + return@on true + } + } + + companion object + { + const val LOC_LUM = "Lumbridge" + const val LOC_DRAYNOR = "Draynor Village" + const val LOC_FALADOR = "Falador" + const val LOC_EDGE = "Edgeville" + const val LOC_TGS = "Tree Gnome Stronghold" + const val EGG_SPAWN_TIME = 5_000 + const val EGG_A = 11027 + const val EGG_B = 11028 + const val EGG_C = 11029 + const val EGG_D = 11030 + const val GFX_A = 1040 + const val GFX_B = 1045 + const val GFX_C = 1042 + const val GFX_D = 1043 + const val STOMP_ANIM = 10017 + const val BUN_EARS = 14658 + const val TRACK_BSB = 502 + const val TRACK_EJ = 273 + const val TRACK_FB = 603 + const val EGG_RATE = 64 + const val EGG_RATE_DEV = 3 + + val EGGS_BROKEN = "/save:easter${ServerConstants.STARTUP_MOMENT.get(Calendar.YEAR)}:eggs" + val LAST_EGG_ROLL = "easter:lasteggroll" + val tracks = arrayOf(TRACK_BSB, TRACK_EJ, TRACK_FB) + val eggs = intArrayOf(EGG_A, EGG_B, EGG_C, EGG_D) + val locNames = arrayOf(LOC_LUM, LOC_DRAYNOR, LOC_FALADOR, LOC_EDGE, LOC_TGS) + val LUMBRIDGE_SPOTS = arrayOf(Location.create(3190, 3240, 0), + Location.create(3219, 3204, 0),Location.create(3212, 3201, 0),Location.create(3205, 3209, 0), + Location.create(3205, 3217, 0),Location.create(3211, 3213, 0),Location.create(3206, 3229, 0), + Location.create(3212, 3226, 0),Location.create(3212, 3244, 0),Location.create(3202, 3252, 0), + Location.create(3197, 3220, 0),Location.create(3189, 3207, 0),Location.create(3229, 3200, 0), + Location.create(3228, 3205, 0),Location.create(3251, 3212, 0),Location.create(3232, 3237, 0)) + + val DRAYNOR_SPOTS = arrayOf(Location.create(3085, 3242, 0), + Location.create(3083, 3236, 0),Location.create(3093, 3224, 0),Location.create(3099, 3241, 0), + Location.create(3095, 3255, 0),Location.create(3089, 3264, 0),Location.create(3089, 3265, 0), + Location.create(3088, 3268, 0),Location.create(3090, 3274, 0),Location.create(3100, 3281, 0), + Location.create(3116, 3265, 0),Location.create(3123, 3272, 0),Location.create(3079, 3261, 0), + Location.create(3127, 3280, 0),Location.create(3118, 3287, 0),Location.create(3111, 3270, 0), + Location.create(3119, 3277, 0),Location.create(3113, 3283, 0)) + + val FALADOR_SPOTS = arrayOf( + Location.create(2961, 3332, 0),Location.create(2967, 3336, 0),Location.create(2974, 3329, 0), + Location.create(2979, 3346, 0),Location.create(2970, 3348, 0),Location.create(2955, 3375, 0), + Location.create(2942, 3386, 0),Location.create(2937, 3385, 0),Location.create(3005, 3370, 0), + Location.create(3006, 3383, 0),Location.create(3007, 3387, 0),Location.create(2985, 3391, 0), + Location.create(2984, 3381, 0),Location.create(2980, 3384, 0),Location.create(3025, 3345, 0), + Location.create(3060, 3389, 0),Location.create(3052, 3385, 0)) + + val EDGEVILLE_SPOTS = arrayOf(Location.create(3077, 3487, 0),Location.create(3082, 3487, 0), + Location.create(3089, 3481, 0),Location.create(3084, 3479, 0),Location.create(3108, 3499, 0), + Location.create(3110, 3517, 0),Location.create(3091, 3512, 0),Location.create(3092, 3507, 0), + Location.create(3081, 3513, 0),Location.create(3079, 3513, 1),Location.create(3080, 3508, 1), + Location.create(3093, 3467, 0)) + + val TREE_GNOME_STRONGHOLD_SPOTS = arrayOf( + Location.create(2480, 3507, 0),Location.create(2486, 3513, 0),Location.create(2453, 3512, 0), + Location.create(2442, 3484, 0),Location.create(2438, 3486, 0),Location.create(2441, 3506, 0), + Location.create(2471, 3482, 0),Location.create(2482, 3478, 0),Location.create(2480, 3469, 0), + Location.create(2489, 3440, 0),Location.create(2470, 3417, 0),Location.create(2478, 3402, 0), + Location.create(2486, 3407, 0),Location.create(2492, 3404, 0),Location.create(2493, 3413, 0), + Location.create(2446, 3395, 0),Location.create(2422, 3398, 0),Location.create(2421, 3402, 0), + Location.create(2418, 3398, 0),Location.create(2401, 3415, 0),Location.create(2397, 3436, 0), + Location.create(2409, 3448, 0),Location.create(2482, 3391, 0)) + + fun locationsForName (name: String) : Array + { + return when (name) + { + LOC_LUM -> LUMBRIDGE_SPOTS + LOC_DRAYNOR -> DRAYNOR_SPOTS + LOC_FALADOR -> FALADOR_SPOTS + LOC_TGS -> TREE_GNOME_STRONGHOLD_SPOTS + LOC_EDGE -> EDGEVILLE_SPOTS + else -> LUMBRIDGE_SPOTS + } + } + + fun gfxForEgg (egg: Int) : Int + { + return when (egg) + { + EGG_A -> GFX_A + EGG_B -> GFX_B + EGG_C -> GFX_C + EGG_D -> GFX_D + else -> GFX_A + } + } + + fun spawnEggFor (player: Player) + { + val dirs = Direction.values() + val dir = dirs[RandomFunction.random(dirs.size)] + var loc = player.location.transform(dir, 3) + val path = Pathfinder.find(player, loc) + loc = Location.create(path.points.last.x, path.points.last.y, loc.z) + GroundItemManager.create(Item(eggs.random()), loc, player) + sendMessage(player, colorize("%RAn egg has appeared nearby.")) + } + + val lootTable = WeightBasedTable.create( + WeightedItem(Items.EASTER_EGG_1962, 1, 15, 0.10), + WeightedItem(Items.PURPLE_SWEETS_10476, 5, 15, 0.10), + WeightedItem(Items.COINS_995, 500, 2500, 0.15), + WeightedItem(Items.ESS_IMPLING_JAR_11246, 1, 1, 0.05), + WeightedItem(Items.BABY_IMPLING_JAR_11238, 1, 1, 0.05), + WeightedItem(Items.EARTH_IMPLING_JAR_11244, 1, 1, 0.05), + WeightedItem(Items.GOURM_IMPLING_JAR_11242, 1, 1, 0.05), + WeightedItem(Items.MAGPIE_IMPLING_JAR_11252, 1, 1, 0.025), + WeightedItem(Items.ECLECTIC_IMPLING_JAR_11248, 1, 1, 0.025), + WeightedItem(Items.ESS_IMPLING_JAR_11246, 1, 1, 0.025), + WeightedItem(Items.NATURE_IMPLING_JAR_11250, 1, 1, 0.015), + WeightedItem(Items.NINJA_IMPLING_JAR_11254, 1, 1, 0.015), + WeightedItem(Items.DRAGON_IMPLING_JAR_11256, 1, 1, 0.005) + ) } } \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt b/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt index 4dd777cf3..2fdc30c28 100644 --- a/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt +++ b/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt @@ -1,50 +1,10 @@ package core.game.worldevents.holiday.easter -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.node.item.GroundItemManager -import org.rs09.consts.Items -import org.rs09.consts.NPCs import core.game.interaction.InteractionListener -import core.game.interaction.IntType class EasterEventListeners : InteractionListener { - - val EGG_ATTRIBUTE = "/save:easter:eggs" - val eggs = intArrayOf(Items.EASTER_EGG_11027, Items.EASTER_EGG_11028, Items.EASTER_EGG_11029, Items.EASTER_EGG_11030) - override fun defineListeners() { - on(eggs, IntType.ITEM, "take"){ player, node -> - player.pulseManager.run(object : MovementPulse(player,node, DestinationFlag.ITEM){ - override fun pulse(): Boolean { - if(player.inventory.contains(Items.BASKET_OF_EGGS_4565,1) || player.equipment.contains(Items.BASKET_OF_EGGS_4565,1)){ - val item = GroundItemManager.get(node.id,node.location,null) - GroundItemManager.destroy(item) - player.incrementAttribute(EGG_ATTRIBUTE) - player.sendMessage("You place the egg in the basket. You now have ${player.getAttribute(EGG_ATTRIBUTE,0)} eggs!") - } else { - player.sendMessage("You need to have your egg basket with you to collect these.") - } - return true - } - }) - return@on true - } - - on(Items.BASKET_OF_EGGS_4565, IntType.ITEM, "operate"){ player, node -> - val numEggs = player.getAttribute(EGG_ATTRIBUTE) ?: 0 - player.dialogueInterpreter.sendDialogue("You have $numEggs eggs in the basket.") - return@on true - } - - on(NPCs.EASTER_BUNNY_7197, IntType.NPC, "talk-to"){ player, node -> - val npc = node.asNpc() - npc.faceLocation(player.location) - val NEED_BASKET = !(player!!.inventory.contains(Items.BASKET_OF_EGGS_4565,1) || player!!.equipment.contains(Items.BASKET_OF_EGGS_4565,1) || player!!.bank.contains(Items.BASKET_OF_EGGS_4565,1)) - player.dialogueInterpreter.open(EasterBunnyDialogueFile(NEED_BASKET),npc) - return@on true - } } diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/SimpleHalloweenEvent.kt b/Server/src/main/core/game/worldevents/holiday/halloween/SimpleHalloweenEvent.kt deleted file mode 100644 index 50c26daea..000000000 --- a/Server/src/main/core/game/worldevents/holiday/halloween/SimpleHalloweenEvent.kt +++ /dev/null @@ -1,21 +0,0 @@ -package core.game.worldevents.holiday.halloween - -import core.game.worldevents.PluginSet -import core.game.worldevents.WorldEvent -import core.game.world.GameWorld - -class SimpleHalloweenEvent : WorldEvent("hween"){ - override fun checkActive(): Boolean { - return false - } - - override fun initialize() { - plugins = PluginSet( - GrimDialogue() - ) - super.initialize() - GameWorld.settings?.message_model = 800 - GameWorld.settings?.message_string = "A mysterious figure has appeared in Draynor! You should go investigate!" - log("Initialized.") - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/SpiderHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/SpiderHolidayRandomNPC.kt index dea7ff063..037c9ba10 100644 --- a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/SpiderHolidayRandomNPC.kt +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/SpiderHolidayRandomNPC.kt @@ -15,7 +15,7 @@ class SpiderHolidayRandomNPC() : HolidayRandomEventNPC(61) { this.behavior = SpiderHolidayRandomBehavior() playGlobalAudio(this.location, Sounds.SPIDER_4375) var stomped = false - queueScript(this,4, QueueStrength.SOFT) { stage: Int -> + queueScript(this, 4, QueueStrength.SOFT) { stage: Int -> when (stage) { 0 -> { sendChat(player, "Eww a spider!") @@ -32,7 +32,7 @@ class SpiderHolidayRandomNPC() : HolidayRandomEventNPC(61) { } 2 -> { if (stomped) { - impact(this, 1, ImpactHandler.HitsplatType.NORMAL) + impact(this, 2, ImpactHandler.HitsplatType.NORMAL) } else { sendMessage(player, "The spider runs away.") playGlobalAudio(this.location, Sounds.SPIDER_4375) @@ -47,4 +47,4 @@ class SpiderHolidayRandomNPC() : HolidayRandomEventNPC(61) { override fun talkTo(npc: NPC) { } -} \ No newline at end of file +} diff --git a/Server/src/main/core/net/packet/PacketProcessor.kt b/Server/src/main/core/net/packet/PacketProcessor.kt index 78be623b3..d76b82db8 100644 --- a/Server/src/main/core/net/packet/PacketProcessor.kt +++ b/Server/src/main/core/net/packet/PacketProcessor.kt @@ -15,6 +15,7 @@ import core.game.node.entity.player.info.Rights import core.game.node.entity.player.info.login.LoginConfiguration import core.game.node.entity.player.link.SpellBookManager import core.game.node.entity.combat.spell.MagicSpell +import content.global.ame.events.maze.MazeInterface import content.global.skill.summoning.familiar.FamiliarSpecial import core.game.node.item.GroundItemManager import core.game.node.item.Item @@ -187,7 +188,7 @@ object PacketProcessor { offer.itemID = pkt.itemId offer.sell = false if (!PriceIndex.canTrade(pkt.itemId)) { - sendMessage(pkt.player, "That item is blacklisted from the grand exchange.") + sendMessage(pkt.player, "That item is blacklisted from the Grand Exchange.") return } offer.player = pkt.player @@ -207,13 +208,14 @@ object PacketProcessor { if (pkt.player.details.isMuted) pkt.player.sendMessage("You have been muted due to breaking a rule.") else { - if (ServerConstants.ENABLE_GLOBALCHAT && pkt.message.startsWith("//")) { + if (ServerConstants.ENABLE_GLOBAL_CHAT && pkt.message.startsWith("//")) { if (getAttribute(pkt.player, GlobalChat.ATTR_GLOBAL_MUTE, false)) return val messages = splitChatMessage(pkt.message.substring(2), pkt.player.name.length + 3, false) for (message in messages) { if (message.isNotBlank()) + PlayerMonitor.logChat(pkt.player, "global", message) GlobalChat.process(pkt.player.username, message, Rights.getChatIcon(pkt.player)) } return @@ -227,6 +229,7 @@ object PacketProcessor { builder.clanName = pkt.player.communication.clan.owner.lowercase().replace(" ", "_") builder.message = message builder.rank = Rights.getChatIcon(pkt.player) + PlayerMonitor.logChat(pkt.player, "clan", message) ManagementEvents.publish(builder.build()) } return @@ -308,7 +311,10 @@ object PacketProcessor { } is Packet.TrackingAfkTimeout -> { if (pkt.player.details.rights != Rights.ADMINISTRATOR) + { + pkt.player.isAfkLogout = true pkt.player.packetDispatch.sendLogout() + } } is Packet.TrackingCameraPos -> { //TODO Refactor the player monitor to be actually useful and log this @@ -521,7 +527,7 @@ object PacketProcessor { player.debug("Slot: ${pkt.slot}, ItemID: ${pkt.itemId}") player.debug("RCM Index: ${pkt.optIndex}, Op: ${pkt.opcode}") player.debug("-------------------------------------") - if (player.dialogueInterpreter.dialogue != null && pkt.opcode != 132 && pkt.iface != 64) + if (player.dialogueInterpreter.dialogue != null && pkt.opcode != 132 && pkt.iface != 64 && pkt.iface != 746) player.dialogueInterpreter.close() if (player.locks.isComponentLocked) return @@ -667,6 +673,11 @@ object PacketProcessor { if (pkt.id == 6899) scenery = Scenery(6899, Location(3221, 9618)) + // Random Event Maze chests are overridden by the walls, which needs to be hacked in. + if ((scenery?.id == 3626 || scenery?.id == 3635) && (objId in 3635..3636)) { + scenery = MazeInterface.overrideScenery(scenery, objId) + } + // Family crest levers don't have varps associated with them, so their state is validated with attributes // instead, and they always appear as their down/odd variant in the server's map if (objId in 2421..2426 && objId % 2 == 0) { diff --git a/Server/src/main/core/net/packet/PacketRepository.java b/Server/src/main/core/net/packet/PacketRepository.java index 60c6fddb0..e2656b02a 100644 --- a/Server/src/main/core/net/packet/PacketRepository.java +++ b/Server/src/main/core/net/packet/PacketRepository.java @@ -75,9 +75,10 @@ public final class PacketRepository { OUTGOING_PACKETS.put(InstancedLocationUpdate.class, new InstancedLocationUpdate()); // OUTGOING_PACKETS.put(CSConfigPacket.class, new CSConfigPacket()); // OUTGOING_PACKETS.put(Varbit.class, new Varbit()); - OUTGOING_PACKETS.put(ResetInterface.class, new ResetInterface()); + OUTGOING_PACKETS.put(ResetInterface.class, new ResetInterface()); OUTGOING_PACKETS.put(VarcUpdate.class, new VarcUpdate()); OUTGOING_PACKETS.put(InterfaceSetAngle.class, new InterfaceSetAngle()); + OUTGOING_PACKETS.put(LastLoginInfo.class, new LastLoginInfo()); } /** @@ -105,5 +106,4 @@ public final class PacketRepository { } } } - -} +} \ No newline at end of file diff --git a/Server/src/main/core/net/packet/context/ContainerContext.java b/Server/src/main/core/net/packet/context/ContainerContext.java index 7b4a3ea9c..411a812cd 100644 --- a/Server/src/main/core/net/packet/context/ContainerContext.java +++ b/Server/src/main/core/net/packet/context/ContainerContext.java @@ -34,7 +34,9 @@ public final class ContainerContext implements Context { /** * The items. */ - private final Item[] items; + private Item[] items; + + public int[] ids; /** * The length of the array to send. @@ -115,6 +117,17 @@ public final class ContainerContext implements Context { this.slots = null; } + public ContainerContext(Player player, int interfaceId, int childId, int containerId, int[] items) { + this.player = player; + this.interfaceId = interfaceId; + this.childId = childId; + this.containerId = containerId; + this.ids = items; + this.length = items.length; + this.split = false; + this.slots = null; + } + /** * Constructs a new {@code ContainerContext} {@code Object}. * @param player The player. diff --git a/Server/src/main/core/net/packet/out/ContainerPacket.java b/Server/src/main/core/net/packet/out/ContainerPacket.java index 9e3b8eda8..8c669186e 100644 --- a/Server/src/main/core/net/packet/out/ContainerPacket.java +++ b/Server/src/main/core/net/packet/out/ContainerPacket.java @@ -42,19 +42,30 @@ public final class ContainerPacket implements OutgoingPacket { } } } else { - buffer.putShort(context.getItems().length); - for (Item item : context.getItems()) - if (item != null) { - int amount = item.getAmount(); - if (amount < 0 || amount > 254) { - buffer.putS(255).putInt(amount); - } else { - buffer.putS(amount); - } - buffer.putShort(item.getId() + 1); - } else { - buffer.putS(0).putShort(0); + if (context.ids != null) + { + buffer.p2(context.getLength()); + for (int i = 0; i < context.getLength(); i++) + { + buffer.putS(1); + buffer.p2(context.ids[i] + 1); } + } + else { + buffer.putShort(context.getItems().length); + for (Item item : context.getItems()) + if (item != null) { + int amount = item.getAmount(); + if (amount < 0 || amount > 254) { + buffer.putS(255).putInt(amount); + } else { + buffer.putS(amount); + } + buffer.putShort(item.getId() + 1); + } else { + buffer.putS(0).putShort(0); + } + } } } buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getSession().write(buffer); diff --git a/Server/src/main/core/net/packet/out/LastLoginInfo.kt b/Server/src/main/core/net/packet/out/LastLoginInfo.kt new file mode 100644 index 000000000..1892cf487 --- /dev/null +++ b/Server/src/main/core/net/packet/out/LastLoginInfo.kt @@ -0,0 +1,15 @@ +package core.net.packet.out + +import core.net.packet.IoBuffer +import core.net.packet.OutgoingPacket +import core.net.packet.context.PlayerContext +import java.net.Inet4Address + +class LastLoginInfo : OutgoingPacket { + override fun send(context: PlayerContext) { + val buffer = IoBuffer(164) + buffer.imp4(Inet4Address.getByName(context.player.details.ipAddress).hashCode()) + buffer.cypherOpcode(context.player.session.isaacPair.output) + context.player.session.write(buffer) + } +} \ No newline at end of file diff --git a/Server/src/main/core/net/packet/out/UpdateSceneGraph.java b/Server/src/main/core/net/packet/out/UpdateSceneGraph.java index 5601c6757..a93435b18 100644 --- a/Server/src/main/core/net/packet/out/UpdateSceneGraph.java +++ b/Server/src/main/core/net/packet/out/UpdateSceneGraph.java @@ -24,10 +24,7 @@ public final class UpdateSceneGraph implements OutgoingPacket for (int regionY = (player.getLocation().getRegionY() - 6) / 8; regionY <= ((player.getLocation().getRegionY() + 6) / 8); regionY++) { int[] keys = XteaParser.Companion.getRegionXTEA(regionX << 8 | regionY); for (int i = 0; i < 4; i++) { - if (keys != null) buffer.putIntB(keys[i]); - else - buffer.putIntB(0); } } } diff --git a/Server/src/main/core/plugin/ClassScanner.kt b/Server/src/main/core/plugin/ClassScanner.kt index 30bea7201..2ad6e5f4b 100644 --- a/Server/src/main/core/plugin/ClassScanner.kt +++ b/Server/src/main/core/plugin/ClassScanner.kt @@ -1,29 +1,32 @@ package core.plugin +import core.ServerConstants import core.api.* import core.game.activity.ActivityManager import core.game.activity.ActivityPlugin -import core.game.node.entity.Entity -import core.game.node.entity.player.info.login.LoginConfiguration -import core.game.node.entity.player.link.quest.Quest -import core.game.node.entity.player.link.quest.QuestRepository -import core.game.world.map.Location -import core.game.world.map.zone.MapZone -import core.game.world.map.zone.ZoneBuilder -import io.github.classgraph.ClassGraph -import io.github.classgraph.ClassInfo -import io.github.classgraph.ScanResult -import core.game.system.timer.* import core.game.bots.PlayerScripts import core.game.interaction.InteractionListener import core.game.interaction.InterfaceListener +import core.game.node.entity.Entity import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.info.login.LoginConfiguration import core.game.node.entity.player.info.login.PlayerSaveParser import core.game.node.entity.player.info.login.PlayerSaver -import core.tools.SystemLogger -import core.tools.SystemLogger.logStartup +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.player.link.quest.QuestRepository +import core.game.system.timer.RSTimer +import core.game.system.timer.TimerRegistry import core.game.world.GameWorld +import core.game.world.map.Location +import core.game.world.map.zone.MapZone +import core.game.world.map.zone.ZoneBuilder +import core.game.worldevents.WorldEvent +import core.game.worldevents.WorldEvents import core.tools.Log +import core.tools.SystemLogger.logStartup +import io.github.classgraph.ClassGraph +import io.github.classgraph.ClassInfo +import io.github.classgraph.ScanResult import java.util.function.Consumer /** @@ -90,6 +93,10 @@ object ClassScanner { scanResults.getClassesImplementing("core.api.ContentInterface").filter { !it.isAbstract }.forEach { try { val clazz = it.loadClass().newInstance() + if(clazz is WorldEvent) { //Check world event first so if it's not active we don't register tick listeners, etc. + if (!clazz.checkActive(ServerConstants.STARTUP_MOMENT)) return@forEach + WorldEvents.add(clazz) + } if(clazz is LoginListener) GameWorld.loginListeners.add(clazz) if(clazz is LogoutListener) GameWorld.logoutListeners.add(clazz) if(clazz is TickListener) GameWorld.tickListeners.add(clazz) diff --git a/Server/src/main/core/tools/NetworkReachability.kt b/Server/src/main/core/tools/NetworkReachability.kt new file mode 100644 index 000000000..d27f3bd60 --- /dev/null +++ b/Server/src/main/core/tools/NetworkReachability.kt @@ -0,0 +1,6 @@ +package core.tools + +enum class NetworkReachability { + Reachable, + Unreachable +} \ No newline at end of file diff --git a/Server/src/main/core/tools/RandomFunction.java b/Server/src/main/core/tools/RandomFunction.java index 7e8dd580f..61d3275ad 100644 --- a/Server/src/main/core/tools/RandomFunction.java +++ b/Server/src/main/core/tools/RandomFunction.java @@ -49,9 +49,9 @@ public class RandomFunction { * @param chance the 1/chance rate for the roll to succeed * @return true if you hit the roll, false otherwise */ - public static boolean roll(int chance){ + public static boolean roll(int chance) { if (chance <= 1) return true; - return random(chance + 1) == chance / 2; + return random(chance) == 1; } /** diff --git a/Server/src/main/core/tools/SystemLogger.kt b/Server/src/main/core/tools/SystemLogger.kt index eb65de9ea..dc5b96560 100644 --- a/Server/src/main/core/tools/SystemLogger.kt +++ b/Server/src/main/core/tools/SystemLogger.kt @@ -19,7 +19,7 @@ import java.util.* object SystemLogger { val t = Terminal() val errT = t.forStdErr() - val formatter = SimpleDateFormat("HH:mm:ss") + val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX") private fun getTime(): String{ return "[" + formatter.format(Date(System.currentTimeMillis())) +"]" diff --git a/Server/src/main/core/worker/MajorUpdateWorker.kt b/Server/src/main/core/worker/MajorUpdateWorker.kt index e4553a6ec..29d48faa8 100644 --- a/Server/src/main/core/worker/MajorUpdateWorker.kt +++ b/Server/src/main/core/worker/MajorUpdateWorker.kt @@ -1,25 +1,25 @@ package core.worker -import core.api.submitWorldPulse -import core.game.system.task.Pulse -import core.plugin.CorePluginTypes.Managers import core.Server import core.ServerConstants import core.ServerStore import core.api.log -import core.tools.SystemLogger +import core.api.submitWorldPulse +import core.game.system.task.Pulse import core.game.world.GameWorld import core.game.world.repository.Repository import core.game.world.update.UpdateSequence +import core.integrations.grafana.Grafana import core.net.packet.PacketProcessor import core.net.packet.PacketWriteQueue +import core.plugin.CorePluginTypes.Managers import core.tools.Log +import core.tools.NetworkReachability import core.tools.colorize import java.lang.Long.max import java.text.SimpleDateFormat import java.util.* import kotlin.system.exitProcess -import core.integrations.grafana.* /** * Handles the running of pulses and writing of masks, etc @@ -38,7 +38,11 @@ class MajorUpdateWorker { Grafana.startTick() val start = System.currentTimeMillis() Server.heartbeat() - handleTickActions() + + if (Server.networkReachability == NetworkReachability.Reachable) + handleTickActions() + else + tickOffline() for (player in Repository.players.filter { !it.isArtificial }) { if (System.currentTimeMillis() - player.session.lastPing > 20000L) { @@ -62,12 +66,18 @@ class MajorUpdateWorker { ServerStore.clearDailyEntries() if (ServerConstants.DAILY_RESTART) { + for (player in Repository.players.filter { !it.isArtificial }) { + player.packetDispatch.sendSystemUpdate(500) + } Repository.sendNews(colorize("%RSERVER GOING DOWN FOR DAILY RESTART IN 5 MINUTES!")) ServerConstants.DAILY_RESTART = false submitWorldPulse(object : Pulse(100) { var counter = 0 override fun pulse(): Boolean { counter++ + for (player in Repository.players.filter { !it.isArtificial }) { + player.packetDispatch.sendSystemUpdate((5 - counter) * 100) + } if (counter == 5) { exitProcess(0) } @@ -89,6 +99,12 @@ class MajorUpdateWorker { log(this::class.java, Log.FINE, "Update worker stopped.") } + fun tickOffline() + { + Repository.disconnectionQueue.update() //continue processing disconnection queue + GameWorld.pulse() //continue incrementing the global tick count + } + fun handleTickActions(skipPulseUpdate: Boolean = false) { try { var packetStart = System.currentTimeMillis() diff --git a/Server/src/test/kotlin/PlayerStatsCounterTests.kt b/Server/src/test/kotlin/PlayerStatsCounterTests.kt new file mode 100644 index 000000000..1b2ce9bc7 --- /dev/null +++ b/Server/src/test/kotlin/PlayerStatsCounterTests.kt @@ -0,0 +1,111 @@ +import core.api.utils.PlayerStatsCounter +import core.game.node.item.Item +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import java.io.File + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class PlayerStatsCounterTests { + companion object { + private const val TEST_DB_PATH = "player_stats_test.db" + private val counter = PlayerStatsCounter(TEST_DB_PATH) + init { + TestUtils.preTestSetup() + counter.startup() + } + @AfterAll @JvmStatic fun cleanup() { + File(TEST_DB_PATH).delete() + } + } + + @Test fun testKillIncrementShouldAddOneKillForNewPlayer() { + val testPlayer = TestUtils.getMockPlayer("test_kill_inc") + val testNPCId = 10 + + val oldKills = PlayerStatsCounter.getKills(testPlayer, IntArray(testNPCId)) + Assertions.assertEquals(0, oldKills) + + PlayerStatsCounter.incrementKills(testPlayer,testNPCId) + + val newKills = PlayerStatsCounter.getKills(testPlayer, intArrayOf(testNPCId)) + Assertions.assertEquals(1, newKills) + } + + @Test fun testGetKillShouldReturnKillsForSinglePlayer() { + val testPlayer = TestUtils.getMockPlayer("kill_single_player_1") + val testPlayer2 = TestUtils.getMockPlayer("kill_single_player_2") + val testNPCId = 10 + + PlayerStatsCounter.incrementKills(testPlayer,testNPCId) + + PlayerStatsCounter.incrementKills(testPlayer2,testNPCId) + PlayerStatsCounter.incrementKills(testPlayer2,testNPCId) + + val kills = PlayerStatsCounter.getKills(testPlayer2, intArrayOf(testNPCId)) + Assertions.assertEquals(2, kills) + } + + @Test fun testGetKillShouldReturnSumForAllNPCsForAGivenPlayer() { + val testPlayer = TestUtils.getMockPlayer("test_sum_npcs") + val testNPCId1 = 10 + val testNPCId2 = 11 + val testNPCId3 = 12 + + PlayerStatsCounter.incrementKills(testPlayer,testNPCId1) + + PlayerStatsCounter.incrementKills(testPlayer,testNPCId2) + PlayerStatsCounter.incrementKills(testPlayer,testNPCId2) + PlayerStatsCounter.incrementKills(testPlayer,testNPCId2) + + PlayerStatsCounter.incrementKills(testPlayer,testNPCId3) + PlayerStatsCounter.incrementKills(testPlayer,testNPCId3) + + val killsForNPCs1And2 = PlayerStatsCounter.getKills(testPlayer, intArrayOf(testNPCId1, testNPCId2)) + Assertions.assertEquals(4, killsForNPCs1And2) + val killsForAllNPCs = PlayerStatsCounter.getKills(testPlayer, intArrayOf(testNPCId1, testNPCId2, testNPCId3)) + Assertions.assertEquals(6, killsForAllNPCs) + } + + @Test fun testRewardIncrementShouldAddOneRewardForNewPlayer() { + val testPlayer = TestUtils.getMockPlayer("test_reward_inc") + val itemId = 10 + + val oldRareDrops = PlayerStatsCounter.getRareDrops(testPlayer, itemId) + Assertions.assertEquals(0, oldRareDrops) + + PlayerStatsCounter.incrementRareDrop(testPlayer, Item(itemId)) + + val newRareDrops = PlayerStatsCounter.getRareDrops(testPlayer, itemId) + Assertions.assertEquals(1, newRareDrops) + } + + @Test fun testGetRewardsShouldReturnRewardsForSinglePlayer() { + val testPlayer = TestUtils.getMockPlayer("reward_single_player_1") + val testPlayer2 = TestUtils.getMockPlayer("reward_single_player_2") + val testItemId = 10 + + PlayerStatsCounter.incrementRareDrop(testPlayer,Item(testItemId)) + + PlayerStatsCounter.incrementRareDrop(testPlayer2,Item(testItemId)) + PlayerStatsCounter.incrementRareDrop(testPlayer2,Item(testItemId)) + + val rareDrops = PlayerStatsCounter.getRareDrops(testPlayer2, testItemId) + Assertions.assertEquals(2, rareDrops) + } + + @Test fun testGetRewardsShouldHonourItemAmountWhenIncrementing() { + val testPlayer = TestUtils.getMockPlayer("test_reward_inc_itemamount") + val itemId = 10 + + val itemAmount: Long = 5 + + val oldRareDrops = PlayerStatsCounter.getRareDrops(testPlayer, itemId) + Assertions.assertEquals(0, oldRareDrops) + + PlayerStatsCounter.incrementRareDrop(testPlayer, Item(itemId, itemAmount.toInt())) + + val newRareDrops = PlayerStatsCounter.getRareDrops(testPlayer, itemId) + Assertions.assertEquals(itemAmount, newRareDrops) + } +} diff --git a/Server/src/test/kotlin/QuestTests.kt b/Server/src/test/kotlin/QuestTests.kt index f400e1bb2..0a4fa7bc8 100644 --- a/Server/src/test/kotlin/QuestTests.kt +++ b/Server/src/test/kotlin/QuestTests.kt @@ -1,3 +1,4 @@ +import content.data.Quests import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.player.link.quest.QuestRepository import org.junit.jupiter.api.Assertions @@ -10,7 +11,7 @@ class QuestTests { testPlayer = TestUtils.getMockPlayer("test") } - class TestQuest : Quest("Test Quest", 0, 0, 1, 1, 0, 1, 2) { + class TestQuest : Quest(Quests.TEST_QUEST, 0, 0, 1, 1, 0, 1, 2) { override fun newInstance(`object`: Any?): Quest { return this } @@ -25,13 +26,13 @@ class QuestTests { @Test fun registerShouldMakeQuestImmediatelyAvailable() { QuestRepository.register(testQuest) - Assertions.assertNotNull(QuestRepository.getQuests()[testQuest.name]) + Assertions.assertNotNull(QuestRepository.getQuests()[testQuest.quest]) } @Test fun registerShouldMakeQuestImmediatelyAvailableToInstances() { QuestRepository.register(testQuest) val instance = QuestRepository(testPlayer) - Assertions.assertNotNull(instance.getQuest(testQuest.name)) + Assertions.assertNotNull(instance.getQuest(testQuest.quest)) } @Test fun getStageOnUnstartedQuestShouldNotThrowException() { @@ -54,8 +55,8 @@ class QuestTests { Assertions.assertThrows(IllegalStateException::class.java, { QuestRepository.register(testQuest) val repo = QuestRepository(testPlayer) - repo.getQuest("Test Quest").finish(testPlayer) - repo.getQuest("Test Quest").finish(testPlayer) + repo.getQuest(Quests.TEST_QUEST).finish(testPlayer) + repo.getQuest(Quests.TEST_QUEST).finish(testPlayer) }, "Quest completed twice without throwing an exception or threw wrong exception!") } } \ No newline at end of file diff --git a/Server/src/test/kotlin/content/CombatTests.kt b/Server/src/test/kotlin/content/CombatTests.kt index 55ffc715e..762ed8406 100644 --- a/Server/src/test/kotlin/content/CombatTests.kt +++ b/Server/src/test/kotlin/content/CombatTests.kt @@ -2,6 +2,10 @@ package content import TestUtils import content.global.handlers.item.equipment.special.ChinchompaSwingHandler +import core.api.EquipmentSlot +import core.game.container.impl.EquipmentContainer.updateBonuses +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners import core.game.node.entity.combat.MagicSwingHandler import core.game.node.entity.combat.MeleeSwingHandler import core.game.node.entity.combat.RangeSwingHandler @@ -9,8 +13,10 @@ import core.game.node.entity.combat.SwingHandlerFlag import core.game.node.entity.combat.equipment.WeaponInterface import core.game.node.entity.player.link.prayer.PrayerType import core.game.node.entity.skill.Skills +import core.game.node.item.Item import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import org.rs09.consts.Items class CombatTests { init { @@ -115,12 +121,17 @@ class CombatTests { } } - @Test fun chinchompaSwingHandlerIgnoresStatsForDamage() { + @Test fun chinchompaSwingHandlerIgnoresAmmoSlotForDamage() { val handler = ChinchompaSwingHandler() TestUtils.getMockPlayer("chinchompaStatTest").use { p -> + p.skills.staticLevels[Skills.RANGE] = 99 + p.skills.dynamicLevels[Skills.RANGE] = 99 + p.equipment.replace(Item(Items.CHINCHOMPA_10033), EquipmentSlot.WEAPON.ordinal) + updateBonuses(p) val damageBaseline = handler.calculateHit(p, p, 1.0) - p.properties.bonuses[14] = 250 + p.equipment.replace(Item(Items.DRAGON_ARROW_11212), EquipmentSlot.AMMO.ordinal) + updateBonuses(p) Assertions.assertEquals(damageBaseline, handler.calculateHit(p, p, 1.0)) } } diff --git a/Server/src/test/kotlin/content/RandomEventTests.kt b/Server/src/test/kotlin/content/RandomEventTests.kt index 9ab1aa02f..e28f1045b 100644 --- a/Server/src/test/kotlin/content/RandomEventTests.kt +++ b/Server/src/test/kotlin/content/RandomEventTests.kt @@ -7,6 +7,9 @@ import core.api.* import core.game.system.timer.impl.AntiMacro import core.game.world.map.Location import core.game.world.map.zone.impl.WildernessZone +import content.region.wilderness.handlers.KingBlackDragonArea +import core.game.world.map.zone.MapZone +import core.game.world.map.zone.ZoneBuilder import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.rs09.consts.Items @@ -132,6 +135,28 @@ class RandomEventTests { } } + @Test fun randomEventShouldNotSpawnInKingBlackDragonLair() { + TestUtils.getMockPlayer("antimacronospawninkbdlair").use { p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer was null!") + TestUtils.advanceTicks(5, false) + + // Manually configure KBD lair zone like ClassScanner does for MapArea instances + val kbdArea = KingBlackDragonArea() + val zone = object : MapZone(kbdArea.javaClass.simpleName + "MapArea", true, *kbdArea.getRestrictions()) {} + for(border in kbdArea.defineAreaBorders()) zone.register(border) + ZoneBuilder.configure(zone) + + val kbdLocation = Location.create(2273, 4698, 0) // KBD spawn location + p.location = kbdLocation + + Assertions.assertEquals(kbdLocation, p.location) + + timer.nextExecution = getWorldTicks() + 5 + TestUtils.advanceTicks(10, false) + Assertions.assertNull(AntiMacro.getEventNpc(p), "Random event should not spawn in KBD lair but one was found!") + } + } + //FIXME //@Test fun randomEventShouldNotSpawnIfOneAlreadyActive() { // TestUtils.getMockPlayer("antimacronospawnifalreadyhas").use { p -> diff --git a/Server/src/test/kotlin/core/game/node/entity/skill/SkillsTests.kt b/Server/src/test/kotlin/core/game/node/entity/skill/SkillsTests.kt new file mode 100644 index 000000000..22c9dcffb --- /dev/null +++ b/Server/src/test/kotlin/core/game/node/entity/skill/SkillsTests.kt @@ -0,0 +1,43 @@ +package core.game.node.entity.skill + +import TestUtils.getMockPlayer +import core.game.node.entity.player.info.login.PlayerSaveParser +import core.game.node.entity.player.info.login.PlayerSaver +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class SkillsTests { + init { + TestUtils.preTestSetup() + } + + @Test + fun saveDynamicLevelsTest() { + val player = getMockPlayer("") + + // Test migration of old save versions with incorrectly-saved dynamic levels + val jsonparser = JSONParser() + val json = jsonparser.parse("[{\"static\":\"55\",\"dynamic\":\"55\",\"id\":\"0\",\"experience\":\"177895.0\"},{\"static\":\"21\",\"dynamic\":\"21\",\"id\":\"1\",\"experience\":\"5120.0\"},{\"static\":\"23\",\"dynamic\":\"23\",\"id\":\"2\",\"experience\":\"6682.799999999999\"},{\"static\":\"26\",\"dynamic\":\"20\",\"id\":\"3\",\"experience\":\"9001.000000000002\"},{\"static\":\"1\",\"dynamic\":\"1\",\"id\":\"4\",\"experience\":\"0.0\"},{\"static\":\"23\",\"dynamic\":\"20\",\"id\":\"5\",\"experience\":\"6772.5\"},{\"static\":\"37\",\"dynamic\":\"37\",\"id\":\"6\",\"experience\":\"28180.0\"},{\"static\":\"54\",\"dynamic\":\"54\",\"id\":\"7\",\"experience\":\"164425.0\"},{\"static\":\"40\",\"dynamic\":\"40\",\"id\":\"8\",\"experience\":\"40975.5\"},{\"static\":\"24\",\"dynamic\":\"24\",\"id\":\"9\",\"experience\":\"7260.0\"},{\"static\":\"40\",\"dynamic\":\"40\",\"id\":\"10\",\"experience\":\"40200.0\"},{\"static\":\"35\",\"dynamic\":\"35\",\"id\":\"11\",\"experience\":\"23750.0\"},{\"static\":\"60\",\"dynamic\":\"60\",\"id\":\"12\",\"experience\":\"292409.0\"},{\"static\":\"11\",\"dynamic\":\"11\",\"id\":\"13\",\"experience\":\"1371.5\"},{\"static\":\"75\",\"dynamic\":\"75\",\"id\":\"14\",\"experience\":\"1254300.0\"},{\"static\":\"1\",\"dynamic\":\"1\",\"id\":\"15\",\"experience\":\"0.0\"},{\"static\":\"42\",\"dynamic\":\"42\",\"id\":\"16\",\"experience\":\"47742.5\"},{\"static\":\"10\",\"dynamic\":\"10\",\"id\":\"17\",\"experience\":\"1160.0\"},{\"static\":\"1\",\"dynamic\":\"1\",\"id\":\"18\",\"experience\":\"0.0\"},{\"static\":\"1\",\"dynamic\":\"1\",\"id\":\"19\",\"experience\":\"0.0\"},{\"static\":\"5\",\"dynamic\":\"5\",\"id\":\"20\",\"experience\":\"500.0\"},{\"static\":\"1\",\"dynamic\":\"1\",\"id\":\"21\",\"experience\":\"0.0\"},{\"static\":\"1\",\"dynamic\":\"1\",\"id\":\"22\",\"experience\":\"0.0\"},{\"static\":\"11\",\"dynamic\":\"11\",\"id\":\"23\",\"experience\":\"1429.0\"}]") as JSONArray + player.version = 1 + player.skills.parse(json) + Assertions.assertTrue(player.skills.prayerPoints == 20.0) + Assertions.assertTrue(player.skills.lifepoints == 20) + Assertions.assertTrue(player.skills.dynamicLevels[Skills.PRAYER] == 23) + Assertions.assertTrue(player.skills.dynamicLevels[Skills.HITPOINTS] == 26) + + // Test that serializing and parsing them again correctly updates the dynamic levels and keeps the hp/prayer points + player.version = 2 + val root = JSONObject() + PlayerSaver(player).saveSkills(root) + val saveparser = PlayerSaveParser(player) + saveparser.saveFile = root + saveparser.parseSkills() + Assertions.assertTrue(player.skills.prayerPoints == 20.0) + Assertions.assertTrue(player.skills.lifepoints == 20) + Assertions.assertTrue(player.skills.dynamicLevels[Skills.PRAYER] == 23) + Assertions.assertTrue(player.skills.dynamicLevels[Skills.HITPOINTS] == 26) + } +} diff --git a/Server/src/test/kotlin/core/game/worldevents/holiday/easter/EasterEventTests.kt b/Server/src/test/kotlin/core/game/worldevents/holiday/easter/EasterEventTests.kt new file mode 100644 index 000000000..74f5af5d0 --- /dev/null +++ b/Server/src/test/kotlin/core/game/worldevents/holiday/easter/EasterEventTests.kt @@ -0,0 +1,100 @@ +package core.game.worldevents.holiday.easter + +import core.game.world.map.Location +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.util.* + +class EasterEventTests { + @Test fun eventShouldOnlyTriggerInApril() + { + val inst = EasterEvent() + val calInst = Calendar.getInstance() + + calInst.set(Calendar.YEAR, 2024) + calInst.set(Calendar.MONTH, Calendar.APRIL) + calInst.set(Calendar.DAY_OF_MONTH, 1) + Assertions.assertEquals(true, inst.checkActive(calInst)) + + calInst.set(Calendar.MONTH, Calendar.MARCH) + Assertions.assertEquals(false, inst.checkActive(calInst)) + + calInst.set(Calendar.DAY_OF_MONTH, 31) + Assertions.assertEquals(false, inst.checkActive(calInst)) + + calInst.set(Calendar.MONTH, Calendar.MAY) + calInst.set(Calendar.DAY_OF_MONTH, 1) + Assertions.assertEquals(false, inst.checkActive(calInst)) + } + + @Test fun getLocationsForNameShouldReturnExpectedList() + { + val expectations = arrayOf( + Pair(EasterEvent.LOC_LUM, EasterEvent.LUMBRIDGE_SPOTS), + Pair(EasterEvent.LOC_DRAYNOR, EasterEvent.DRAYNOR_SPOTS), + Pair(EasterEvent.LOC_TGS, EasterEvent.TREE_GNOME_STRONGHOLD_SPOTS), + Pair(EasterEvent.LOC_EDGE, EasterEvent.EDGEVILLE_SPOTS), + Pair(EasterEvent.LOC_FALADOR, EasterEvent.FALADOR_SPOTS), + Pair("Random Invalid Dummy Data", EasterEvent.LUMBRIDGE_SPOTS) + ) + + for ((locName, locList) in expectations) + { + Assertions.assertEquals(locList, EasterEvent.locationsForName(locName)) + } + } + + @Test fun getRandomLocationDataShouldReturnARandomLocationNameAndListOfUniqueRandomLocations() + { + val inst = EasterEvent() + + val sampleA = inst.getRandomLocations() + var sampleB = inst.getRandomLocations() + + //Assert that they are equal when they contain the same information (just in case JVM is goofy) + Assertions.assertEquals(sampleA, Pair(sampleA.first, sampleA.second.toTypedArray().toList())) + + //build in some safety against extreme unlikelihood + var similarityTolerance = 3 + //Check 100 times just to be very sure that we're not getting the same data + for (i in 0 until 100) { + if (sampleA == sampleB) similarityTolerance-- + sampleB = inst.getRandomLocations() + } + Assertions.assertEquals(true, similarityTolerance > 0) + } + + @Test fun eachLocationGroupShouldContainNoDuplicateLocations() { + for (locSet in arrayOf( + EasterEvent.FALADOR_SPOTS, + EasterEvent.EDGEVILLE_SPOTS, + EasterEvent.DRAYNOR_SPOTS, + EasterEvent.LUMBRIDGE_SPOTS, + EasterEvent.TREE_GNOME_STRONGHOLD_SPOTS + )) + { + val usedLocations = ArrayList() + for (loc in locSet) + { + if (!usedLocations.contains(loc)) + usedLocations.add(loc) + else + Assertions.fail("Loc $loc appeared more than once!") + } + } + } + + @Test fun gfxForEggShouldReturnCorrectGfx() + { + val expectations = arrayOf( + Pair(EasterEvent.EGG_A, EasterEvent.GFX_A), + Pair(EasterEvent.EGG_B, EasterEvent.GFX_B), + Pair(EasterEvent.EGG_C, EasterEvent.GFX_C), + Pair(EasterEvent.EGG_D, EasterEvent.GFX_D), + Pair(-1, EasterEvent.GFX_A) + ) + + for ((egg, gfx) in expectations) + Assertions.assertEquals(gfx, EasterEvent.gfxForEgg(egg)) + } +} \ No newline at end of file diff --git a/Server/src/test/resources/test.conf b/Server/src/test/resources/test.conf index fd8697901..b143db9e0 100644 --- a/Server/src/test/resources/test.conf +++ b/Server/src/test/resources/test.conf @@ -27,8 +27,6 @@ members = true #activity as displayed on the world list activity = "2009Scape Classic." pvp = false -default_xp_rate = 5.0 -allow_slayer_reroll = false #enables a default clan for players to join automatically. Should be an account with the same name as @name, with a clan set up already. enable_default_clan = true enable_bots = true @@ -49,6 +47,7 @@ max_adv_bots = 100 enable_doubling_money_scammers = true wild_pvp_enabled = false jad_practice_enabled = false +enable_global_chat = false enable_castle_wars = false personalized_shops = false diff --git a/Server/worldprops/default.conf b/Server/worldprops/default.conf index 3a8310e91..fcb90f72b 100644 --- a/Server/worldprops/default.conf +++ b/Server/worldprops/default.conf @@ -22,7 +22,9 @@ noauth_default_admin = true #NOTE: If we are not using auth, this determines whe #------------------------------------------------------------------------------------------------------ #The limit on how many different accounts a player can log into per day. daily_accounts_per_ip = 3 -watchdog_enabled = false +watchdog_enabled = true +connectivity_check_url = "https://google.com,https://2009scape.org" +connectivity_timeout = 500 [database] database_name = "global" @@ -48,7 +50,7 @@ name_ge = "2009Scape" debug = true dev = true start_gui = false -daily_restart = true +daily_restart = false #world number world_id = "1" country_id = "0" @@ -56,8 +58,6 @@ members = true #activity as displayed on the world list activity = "2009Scape Classic." pvp = false -default_xp_rate = 5.0 -allow_slayer_reroll = false #enables a default clan for players to join automatically. Should be an account with the same name as @name, with a clan set up already. enable_default_clan = true enable_bots = true @@ -70,40 +70,61 @@ new_player_location = "2524,5002,0" #the location of home teleport home_location = "3222,3218,0" autostock_ge = false -allow_token_purchase = true -skillcape_perks = true +allow_token_purchase = false +skillcape_perks = false increased_door_time = false enable_botting = false max_adv_bots = 100 enable_doubling_money_scammers = true -wild_pvp_enabled = true -jad_practice_enabled = true +wild_pvp_enabled = false +jad_practice_enabled = false +enable_global_chat = false #minimum HA value for announcements of bots selling on ge ge_announcement_limit = 500 enable_castle_wars = false -personalized_shops = true +personalized_shops = false bots_influence_ge_price = true #verbose cutscene logging (for cutscenes in the new system) verbose_cutscene = false #show the rules the first time a player logs in -show_rules = true +show_rules = false #the number of revenants active at a time revenant_population = 30 #enable auto-buy/auto-sell on the GE. i_want_to_cheat = false #better agility pyramid gp reward (gp reward = 1000 + ((agility level / 99) * 9000)) -better_agility_pyramid_gp = true +better_agility_pyramid_gp = false #better dragonfire shield attack (30 second cooldown instead of 2 minutes) -better_dfs = true +better_dfs = false #new player announcement -new_player_announcement = true +new_player_announcement = false +#enables inauthentic candlelight random event (adds an additional normal random event) +inauthentic_candlelight_random = false #enables holiday random events (no effect on normal random events) -holiday_event_randoms = true +holiday_event_randoms = false #force holiday randoms (can only force one at a time) force_halloween_randoms = false force_christmas_randoms = false -# runecrafting formula revision (573 introduced probabilistic multiple runes, 581 extrapolated probabilistic runes past 99) -runecrafting_formula_revision = 581 +#runecrafting formula revision (573 introduced probabilistic multiple runes, 581 extrapolated probabilistic runes past 99) +runecrafting_formula_revision = 530 +#enables the enhanced deep wilderness, where the area past the members' fence applies a red skull that boosts brawler/pvp drop rates +enhanced_deep_wilderness = false +#enables wilderness-exclusive loot, i.e. brawling gloves and PvP gear, from revenants and the Chaos Elemental +wilderness_exclusive_loot = false +#enables the xp rates option on tutorial island +xp_rates = false +#enables the ironman option on tutorial island and the inauthentic game protocol addition to transmit chat icons for ironmen +ironman = false +#enables the custom-content ancient blueprint and ring of the star sprite +shooting_star_ring = false +#enables the inauthentic teleport option on the ring of wealth +ring_of_wealth_teleport = false +#enables second bank +second_bank = false +#enables inauthentic but non-dangerous commands for regular players +player_commands = false +#enables boosted rewards from fishing trawler consisting of key halves and pirate outfit pieces +boosted_trawler_rewards = false [paths] #path to the data folder, which contains the cache subfolder and such diff --git a/build b/build index 86471b26f..43f08b78b 100755 --- a/build +++ b/build @@ -2,139 +2,90 @@ SCRIPT_DIR=$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P) -BUILD_MS=0 +CLEAN=0 BUILD_GS=0 -CLEAN_MS=0 -CLEAN_GS=0 SKIPTEST="" GS_SRC=$SCRIPT_DIR/Server -MS_SRC=$SCRIPT_DIR/Management-Server -CLEANOPT="" _JAR_DIR="$SCRIPT_DIR/builddir" help() { - echo "Usage: $0 [-h] <-m | -g> [-c ] [-o ]"; + echo "Usage: $0 [-h] [-q] <-g|-c> [-o ]"; echo " -h: Display this message."; - echo " -m: Build the management server."; echo " -g: Build the game server."; - echo " -c: Clean: m: management, g: gameserver. " - echo " Can specify both. Need at least one if present."; - echo " -o: Specify jar-file directory." + echo " -c: Clean previous build."; + echo " -o: Specify jar-file directory."; echo " -q: Quick build - will skip tests."; } error() { - echo $1; - exit -1; -} - -clean_ms() -{ - cd $MS_SRC - - if [[ "$CLEANOPT" == *"m"* ]]; then - echo "Cleaning the management server." - sh mvnw clean || error "Failed to clean the MS." - fi + echo "$1"; + exit 1; } clean_gs() { - cd $GS_SRC; - if [[ "$CLEANOPT" == *"g"* ]]; then + cd $GS_SRC || error "Could not change dir to game server directory." echo "Cleaning the game server." - sh mvnw clean || error "Failed to clean the game server. Giving up." - fi -} - -build_ms() -{ - cd $MS_SRC - - sh mvnw package $SKIPTEST || \ - error "Failed to build the management server. Giving up"; + sh mvnw clean || error "Failed to clean the game server. Giving up." } build_gs() { - cd $GS_SRC; + cd "$GS_SRC" || error "ERROR: Could not chdir to server src. Abort." sh mvnw package $SKIPTEST || \ error "Failed to build the game server. Giving up." } -while getopts "hqmgc:o:d" arg; do +while getopts "hqgc:o:d" arg; do case $arg in h) help exit 0 ;; - m) - BUILD_MS=1 - ;; g) BUILD_GS=1 ;; c) - CLEANOPT=${OPTARG} + CLEAN=1 ;; o) _JAR_DIR=${OPTARG} ;; d) - echo "BUILD_MS=$BUILD_MS" - echo "BUILD_GS=$BUILD_GS" - echo "CLEANOPT=$CLEANOPT" echo "_JAR_DIR=$_JAR_DIR" ;; q) SKIPTEST="-DskipTests" ;; + *) + error "Argument -$arg is not a known or valid argument." + ;; esac done -if [[ $CLEANOPT != "m" ]] \ - && [[ $CLEANOPT != "g" ]] \ - && [[ $CLEANOPT != "mg" ]] \ - && [[ $CLEANOPT != "" ]]; -then - error "Invalid cleaning option '$CLEANOPT'. Valid options are 'm', 'g', 'mg' or none." +if [ $BUILD_GS -eq 0 ] && [ $CLEAN -eq 0 ]; then + error "Need to either build or clean at least. See -h for details." fi -if [ $BUILD_MS -eq 0 ] && [ $BUILD_GS -eq 0 ] && [[ $CLEANOPT == "" ]]; then - error "Need to build or clean at least one of the modules. See -h for details." -fi +# Inside func is cd so mgmt prob fails if no condition +(( CLEAN )) && clean_gs -# Conditionals inside the functions. -clean_gs -clean_ms # ----------- -if [ -d $_JAR_DIR ]; then - rm -r $_JAR_DIR +if [ -d "$_JAR_DIR" ]; then + rm -r "$_JAR_DIR" fi -if [ $BUILD_MS -ne 1 ] && [ $BUILD_GS -ne 1 ]; then - echo "No build options specified. Stop." - exit 0 -fi - -mkdir -p $_JAR_DIR || error "Failed to create build directory."; - -if [ $BUILD_MS -eq 1 ]; then - build_ms - - # Will never execute if build_ms fails because it quits upon failure. - mv -v $MS_SRC/target/*-with-dependencies.jar $_JAR_DIR/ms.jar -fi +mkdir -p "$_JAR_DIR" || error "Failed to create build directory."; if [ $BUILD_GS -eq 1 ]; then build_gs # Will never execute if build_gs fails because it quits upon failure. - mv -v $GS_SRC/target/*-with-dependencies.jar $_JAR_DIR/server.jar + mv -v "$GS_SRC"/target/*-with-dependencies.jar "$_JAR_DIR"/server.jar fi diff --git a/docker-compose.yml b/docker-compose.yml index 02614f351..3c192f791 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,29 +1,33 @@ -version: '3.3' services: - app: + server: build: . - container_name: "2009scape_app" depends_on: - - database - restart: unless-stopped - volumes: - - "2009scape_app:/app" - ports: - - "43595:43595" - - database: - image: mysql:5.7 - container_name: "2009scape_db" + - db restart: always - ports: - - "3306:3306" volumes: - - "2009scape_db:/var/lib/mysql" + - "bcache:/app/Server/target" + - "mcache:/root/.m2" + - "./Server/data:/app/Server/data" + - "./config:/app/Server/worldprops" + ports: + - "43594-43600:43594-43600" + + db: + # current LTS (Long-Term Support) version + image: mariadb:11.4-noble + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + start_period: 30s + interval: 60s + timeout: 10s + retries: 3 + restart: always + volumes: + - "./db:/var/lib/mysql" - "./Server/db_exports/global.sql:/docker-entrypoint-initdb.d/global.sql" - environment: - MYSQL_ALLOW_EMPTY_PASSWORD: "yes" - MYSQL_ROOT_PASSWORD: "" - MYSQL_ROOT_USER: "root" + env_file: + - mysql.env + volumes: - 2009scape_app: - 2009scape_db: + bcache: + mcache: diff --git a/mysql.env.example b/mysql.env.example new file mode 100644 index 000000000..8772412d4 --- /dev/null +++ b/mysql.env.example @@ -0,0 +1,20 @@ +# Prefer no and generating a strong password for the root (+ special) user +MYSQL_ALLOW_EMPTY_PASSWORD="no" + +# You can change the db name also for obscurity. +# Has to be changed in the default.conf and in the global.sql init file then also. +MYSQL_DATABASE="global" + +# Beware: if not using root user, the database 'global' requires granting permissions for that user +# Root user is created automatically so dont provide it here. +#MYSQL_USER="dbuser" +#MYSQL_PASSWORD="CHANGEMEIFNOTUSINGROOTANDINGLOBALCONF" + +# Comment this out if you are using random root pw (below). +MYSQL_ROOT_PASSWORD="ALWAYSCHANGEMEHEREANDINGLOBALCONFIFUSINGROOT" + +# Or you can use a special non-root db user and generate a random pw for the root user instead. +# To improve security +# However, either initial root user access +# or editing Server/db_exports/global.sql to add grants for 'global' to the special user above. +MYSQL_RANDOM_ROOT_PASSWORD="no" diff --git a/run b/run index e1c4fdc10..a854c3200 100755 --- a/run +++ b/run @@ -3,15 +3,11 @@ SCRIPT_DIR=$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P) LOG_DIR=$SCRIPT_DIR/logs BUILD_DIR=$SCRIPT_DIR/builddir GS_SRC=$SCRIPT_DIR/Server -MS_SRC=$SCRIPT_DIR/Management-Server FORCE_REBUILD=0 REFRESH_BUILD=1 -GAMESERVER_ONLY=1 - GS_EXEC="cd $GS_SRC && java -Dnashorn.args=--no-deprecation-warning -jar $BUILD_DIR/server.jar" -MS_EXEC="cd $MS_SRC && java -Dnashorn.args=--no-deprecation-warning -jar $BUILD_DIR/ms.jar" # 0: parallel # 1: fancy tmux @@ -20,13 +16,12 @@ RUN_MODE=0 help() { - echo "Usage: $0 [-h] [-q] [-r] [-t] [-x] [-g] [-e ] [-o ]" + echo "Usage: $0 [-h] [-q] [-r] [-t] [-x] [-e ] [-o ]" echo " -h: Display this message." echo " -q: Don't perform the incremental build." echo " -r: Force clean rebuild." echo " -t: Only run tests, not the JARs." echo " -x: Run in a fancy tmux session." - echo " -g: Build + run only the game server (no management server)." echo " -e: Write STDERR to 'logs/filename' as well as the terminal."; echo " -o: Write STDOUT to 'logs/filename' as well as the terminal." } @@ -39,31 +34,17 @@ error() rebuild_project() { - if [ $GAMESERVER_ONLY ]; then - $SCRIPT_DIR/build -qgcg - else - $SCRIPT_DIR/build -qmgcmg - fi + "$SCRIPT_DIR/build" -qgc } refresh_project() { - if [ $GAMESERVER_ONLY ]; then - $SCRIPT_DIR/build -qg - else - $SCRIPT_DIR/build -qmg - fi + "$SCRIPT_DIR/build" -qg } verify_binaries_exist() { - if ! [ $GAMESERVER_ONLY ]; then - if [ ! -f $SCRIPT_DIR/builddir/ms.jar ]; then - error "Management server binary does not exist."; - fi - fi - - if [ ! -f $SCRIPT_DIR/build/server.jar ]; then + if [ ! -f "$SCRIPT_DIR/build/server.jar" ]; then error "Game server binary does not exist."; fi } @@ -76,11 +57,7 @@ run_server_parallel() echo "Running in parallel. All logs redirected to proper files." - if [ $GAMESERVER_ONLY ]; then - sh -c "$GS_EXEC" & wait - else - sh -c "$MS_EXEC > $LOG_DIR/ms_stdout.log 2> $LOG_DIR/ms_stderr.log & $GS_EXEC" & wait - fi + sh -c "$GS_EXEC" & wait } run_server_tmux() @@ -89,21 +66,16 @@ run_server_tmux() error "tmux is not installed. Install tmux or don't use this option." fi - if [ $GAMESERVER_ONLY ]; then - tmux new-session "$GS_EXEC" - else - tmux new-session "$MS_EXEC" \; \ - split-window -h "$GS_EXEC" - fi + tmux new-session "$GS_EXEC" } run_server_tests() { - cd "$GS_SRC" || error "Could not change to GameServer source directory." + cd "$GS_SRC" || error "Could not change to game server source directory." sh mvnw test } -while getopts ":ghqrtxe:o:" arg; do +while getopts ":hqrtxe:o:" arg; do case $arg in h) help @@ -127,9 +99,6 @@ while getopts ":ghqrtxe:o:" arg; do x) RUN_MODE=1 ;; - g) - GAMESERVER_ONLY=0 - ;; *) error "Argument -$arg is not a known or valid argument." ;; diff --git a/run-ms.bat b/run-ms.bat deleted file mode 100644 index 494ccb692..000000000 --- a/run-ms.bat +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -cd Management-Server - -if NOT exist DoNotCreateThisFile.txt ( - .\mvnw.cmd package -DskipTests - xcopy /Y target\*-with-dependencies.jar ms.jar* - java -jar ms.jar -) \ No newline at end of file